Mercurial > hg > martINI
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3c3522ce6e3a |
---|---|
1 """ | |
2 a view with webob + genshi for editing and viewing .ini files | |
3 """ | |
4 import os | |
5 import re | |
6 | |
7 from genshi.template import TemplateLoader | |
8 from martini.config import ConfigMunger | |
9 from martini.utils import getbool | |
10 from martini.utils import getlist | |
11 from paste.httpexceptions import HTTPExceptionHandler | |
12 from paste.urlparser import StaticURLParser | |
13 from pkg_resources import resource_filename | |
14 from webob import Request, Response, exc | |
15 | |
16 class MartiniWebView(object): | |
17 | |
18 ### class level variables | |
19 defaults = { 'auto_reload': 'True', | |
20 'files': None, | |
21 'directories': None } | |
22 | |
23 def __init__(self, **kw): | |
24 | |
25 # set instance parameters from kw and defaults | |
26 for key in self.defaults: | |
27 setattr(self, key, kw.get(key, self.defaults[key])) | |
28 self.auto_reload = getbool(self.auto_reload) | |
29 | |
30 # files | |
31 self.files = getlist(self.files) | |
32 if self.files: | |
33 assert [ f for f in self.files if os.path.isabs(f) ] | |
34 self.files = dict([(os.path.basename(f), f) | |
35 for f in self.files]) | |
36 else: | |
37 self.files = {} | |
38 | |
39 # directories | |
40 self.directories = getlist(self.directories) | |
41 if self.directories: | |
42 assert [ d for d in self.directories if os.path.isabs(d) ] | |
43 if len(self.directories) > 1 or self.files: | |
44 # namespace directories | |
45 self.directories = dict([(os.path.basename(d), d) | |
46 for d in self.directories]) | |
47 else: | |
48 # don't namespace a single directory | |
49 self.directories = { '': self.directories[0] } | |
50 else: | |
51 self.directories = {} | |
52 | |
53 # have to have something to serve! | |
54 assert self.files or self.directories | |
55 | |
56 # methods to respond to | |
57 self.response_functions = { 'GET': self.get, | |
58 'POST': self.post, | |
59 } | |
60 | |
61 # template loader | |
62 templates_dir = resource_filename(__name__, 'templates') | |
63 self.loader = TemplateLoader(templates_dir, | |
64 auto_reload=self.auto_reload) | |
65 | |
66 # fileserver | |
67 self.fileserver = StaticURLParser(resource_filename(__name__, 'static')) | |
68 | |
69 def file(self, path): | |
70 path = path.strip('/') | |
71 if not path: | |
72 return None | |
73 f = self.files.get(path, None) | |
74 if f is None: | |
75 if path in self.directories: | |
76 return self.directories[path] | |
77 if '/' in path: | |
78 path = path.split('/', 1)[-1] | |
79 for d in self.directories.values(): | |
80 filename = os.path.join(d, path) | |
81 if os.path.exists(filename): | |
82 return filename | |
83 else: | |
84 return f | |
85 | |
86 ### methods dealing with HTTP | |
87 def __call__(self, environ, start_response): | |
88 request = Request(environ) | |
89 | |
90 if request.path_info.strip('/') and os.path.exists(os.path.join(resource_filename(__name__, 'static'), request.path_info.strip('/'))): | |
91 return self.fileserver(environ, start_response) | |
92 | |
93 if request.path_info.endswith('/'): | |
94 if request.path_info.strip('/'): | |
95 raise exc.HTTPFound(location=request.path_info.rstrip('/')) | |
96 else: | |
97 if request.path_info != '/': | |
98 raise exc.HTTPFound(location='/') | |
99 res = self.make_response(request) | |
100 return res(environ, start_response) | |
101 | |
102 def make_response(self, request): | |
103 return self.response_functions.get(request.method, self.error)(request) | |
104 | |
105 def get_response(self, text, content_type='text/html'): | |
106 """make an HTTP response from text""" | |
107 res = Response(content_type=content_type, body=text) | |
108 return res | |
109 | |
110 def get(self, request): | |
111 """ | |
112 return response to a GET requst | |
113 """ | |
114 | |
115 # index of all resources | |
116 if not request.path_info.strip('/'): | |
117 template = self.loader.load('index.html') | |
118 items = self.files.keys() + self.directories.keys() | |
119 res = template.generate(request=request, items=items).render('html', doctype='html') | |
120 return self.get_response(res) | |
121 | |
122 # get the file | |
123 f = self.file(request.path_info) | |
124 if not f: | |
125 raise exc.HTTPNotFound() | |
126 | |
127 # index page of a directory | |
128 if os.path.isdir(f): | |
129 template = self.loader.load('index.html') | |
130 items = os.listdir(f) | |
131 res = template.generate(request=request, items=items).render('html', doctype='html') | |
132 return self.get_response(res) | |
133 | |
134 # get configuration | |
135 conf = ConfigMunger(f) | |
136 | |
137 # render the template | |
138 template = self.loader.load('table.html') | |
139 res = template.generate(request=request, sections=conf.dict()).render('html', doctype='html') | |
140 # generate the response | |
141 return self.get_response(res) | |
142 | |
143 def post(self, request): | |
144 """ | |
145 return response to a POST request | |
146 """ | |
147 | |
148 if not request.path_info.strip('/'): | |
149 raise exc.HTTPMethodNotAllowed() | |
150 | |
151 # get the file | |
152 f = self.file(request.path_info) | |
153 if not f: | |
154 raise exc.HTTPNotFound() | |
155 if os.path.isdir(f): | |
156 raise exc.HTTPMethodNotAllowed() | |
157 | |
158 regex = '\[([^\]]+)\](.*)' | |
159 | |
160 conf = ConfigMunger(f) | |
161 | |
162 delete = request.POST.getall('delete') | |
163 if delete: | |
164 del request.POST['delete'] | |
165 | |
166 for key, value in request.POST.items(): | |
167 value = ' '.join(value.strip().split()) | |
168 match = re.match(regex, key) | |
169 section, option = match.groups() | |
170 if option: | |
171 conf.set(section, option, value) | |
172 else: | |
173 if value: | |
174 conf.move_section(section, value) | |
175 else: | |
176 conf.add_section(section) | |
177 | |
178 for d in delete: | |
179 match = re.match(regex, d) | |
180 section, option = match.groups() | |
181 if conf.has_section(section): | |
182 if option: | |
183 conf.remove_option(section, option) | |
184 else: | |
185 conf.remove_section(section) | |
186 | |
187 output = file(f, 'w') | |
188 conf.write(output) | |
189 | |
190 return exc.HTTPSeeOther(location=request.path_info) | |
191 | |
192 def error(self, request): | |
193 """deal with non-supported methods""" | |
194 return exc.HTTPMethodNotAllowed("Only %r operations are allowed" % self.response_functions.keys()) | |
195 | |
196 | |
197 ### paste factory | |
198 | |
199 def factory(global_conf, **app_conf): | |
200 """create a webob view and wrap it in middleware""" | |
201 keystr = 'martini.' | |
202 args = dict([(key.split(keystr, 1)[-1], value) | |
203 for key, value in app_conf.items() | |
204 if key.startswith(keystr) ]) | |
205 app = MartiniWebView(**args) | |
206 return HTTPExceptionHandler(app) |