diff martini/web.py @ 0:3c3522ce6e3a

initial import of martINI from https://svn.openplans.org/svn/standalone/martINI/
author k0s <k0scist@gmail.com>
date Tue, 08 Dec 2009 15:13:28 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/martini/web.py	Tue Dec 08 15:13:28 2009 -0500
@@ -0,0 +1,206 @@
+"""
+a view with webob + genshi for editing and viewing .ini files
+"""
+import os
+import re
+
+from genshi.template import TemplateLoader
+from martini.config import ConfigMunger
+from martini.utils import getbool
+from martini.utils import getlist
+from paste.httpexceptions import HTTPExceptionHandler
+from paste.urlparser import StaticURLParser
+from pkg_resources import resource_filename
+from webob import Request, Response, exc
+
+class MartiniWebView(object):
+
+    ### class level variables
+    defaults = { 'auto_reload': 'True',
+                 'files': None,
+                 'directories': None }
+
+    def __init__(self, **kw):
+
+        # set instance parameters from kw and defaults
+        for key in self.defaults:
+            setattr(self, key, kw.get(key, self.defaults[key]))
+        self.auto_reload = getbool(self.auto_reload)
+
+        # files
+        self.files = getlist(self.files)
+        if self.files:
+            assert [ f for f in self.files if os.path.isabs(f) ]
+            self.files = dict([(os.path.basename(f), f) 
+                               for f in self.files])
+        else:
+            self.files = {}
+
+        # directories
+        self.directories = getlist(self.directories)
+        if self.directories:
+            assert [ d for d in self.directories if os.path.isabs(d) ]
+            if len(self.directories) > 1 or self.files:
+                # namespace directories
+                self.directories = dict([(os.path.basename(d), d)
+                                         for d in self.directories])
+            else:
+                # don't namespace a single directory
+                self.directories = { '': self.directories[0] }
+        else:
+            self.directories = {}
+
+        # have to have something to serve!
+        assert self.files or self.directories
+
+        # methods to respond to
+        self.response_functions = { 'GET': self.get,
+                                    'POST': self.post,
+                                    }
+
+        # template loader
+        templates_dir = resource_filename(__name__, 'templates')
+        self.loader = TemplateLoader(templates_dir,
+                                     auto_reload=self.auto_reload)
+
+        # fileserver
+        self.fileserver = StaticURLParser(resource_filename(__name__, 'static'))
+
+    def file(self, path):
+        path = path.strip('/')
+        if not path:
+            return None
+        f = self.files.get(path, None)
+        if f is None:
+            if path in self.directories:
+                return self.directories[path]
+            if '/' in path:
+                path = path.split('/', 1)[-1]
+                for d in self.directories.values():
+                    filename = os.path.join(d, path)
+                    if os.path.exists(filename):
+                        return filename
+        else:
+            return f
+
+    ### methods dealing with HTTP
+    def __call__(self, environ, start_response):
+        request = Request(environ)
+
+        if request.path_info.strip('/') and os.path.exists(os.path.join(resource_filename(__name__, 'static'), request.path_info.strip('/'))):
+            return self.fileserver(environ, start_response)
+
+        if request.path_info.endswith('/'):
+            if request.path_info.strip('/'):
+                raise exc.HTTPFound(location=request.path_info.rstrip('/'))
+            else:
+                if request.path_info != '/':
+                    raise exc.HTTPFound(location='/')
+        res = self.make_response(request)
+        return res(environ, start_response)
+                                
+    def make_response(self, request):
+        return self.response_functions.get(request.method, self.error)(request)
+
+    def get_response(self, text, content_type='text/html'):
+        """make an HTTP response from text"""
+        res = Response(content_type=content_type, body=text)
+        return res
+
+    def get(self, request):
+        """
+        return response to a GET requst
+        """
+
+        # index of all resources
+        if not request.path_info.strip('/'):
+            template = self.loader.load('index.html')
+            items = self.files.keys() + self.directories.keys()
+            res = template.generate(request=request, items=items).render('html', doctype='html')
+            return self.get_response(res)
+
+        # get the file
+        f = self.file(request.path_info)
+        if not f:
+            raise exc.HTTPNotFound()
+        
+        # index page of a directory
+        if os.path.isdir(f):
+            template = self.loader.load('index.html')
+            items = os.listdir(f)
+            res = template.generate(request=request, items=items).render('html', doctype='html')
+            return self.get_response(res)
+
+        # get configuration
+        conf = ConfigMunger(f)
+
+        # render the template
+        template = self.loader.load('table.html')
+        res = template.generate(request=request, sections=conf.dict()).render('html', doctype='html')
+        # generate the response
+        return self.get_response(res)
+
+    def post(self, request):
+        """
+        return response to a POST request
+        """
+
+        if not request.path_info.strip('/'):
+            raise exc.HTTPMethodNotAllowed()
+
+        # get the file
+        f = self.file(request.path_info)
+        if not f:
+            raise exc.HTTPNotFound()
+        if os.path.isdir(f):
+            raise exc.HTTPMethodNotAllowed()
+
+        regex = '\[([^\]]+)\](.*)'
+
+        conf = ConfigMunger(f)
+
+        delete = request.POST.getall('delete')
+        if delete:
+            del request.POST['delete']
+
+        for key, value in request.POST.items():
+            value = ' '.join(value.strip().split())
+            match = re.match(regex, key)
+            section, option = match.groups()
+            if option:
+                conf.set(section, option, value)
+            else:
+                if value:
+                    conf.move_section(section, value)
+                else:
+                    conf.add_section(section)
+        
+        for d in delete:
+            match = re.match(regex, d)
+            section, option = match.groups()
+            if conf.has_section(section):
+                if option:
+                    conf.remove_option(section, option)
+                else:
+                    conf.remove_section(section)
+
+        output = file(f, 'w')
+        conf.write(output)
+
+        return exc.HTTPSeeOther(location=request.path_info)
+
+    def error(self, request):
+        """deal with non-supported methods"""
+        return exc.HTTPMethodNotAllowed("Only %r operations are allowed" % self.response_functions.keys())
+
+
+### paste factory
+        
+def factory(global_conf, **app_conf):
+    """create a webob view and wrap it in middleware"""
+    keystr = 'martini.'
+    args = dict([(key.split(keystr, 1)[-1], value)
+                 for key, value in app_conf.items()
+                 if key.startswith(keystr) ])
+    app = MartiniWebView(**args)
+    return HTTPExceptionHandler(app)