Mercurial > cgi-bin > hgweb.cgi > tincan
comparison tincan.py @ 1:94b36e721500 draft
Another check in to back stuff up.
author | David Barts <n5jrn@me.com> |
---|---|
date | Sun, 12 May 2019 19:19:40 -0700 |
parents | e726fafcffac |
children | ca6f8ca38cf2 |
comparison
equal
deleted
inserted
replaced
0:e726fafcffac | 1:94b36e721500 |
---|---|
262 ret[name] = value | 262 ret[name] = value |
263 | 263 |
264 # Represents a route in TinCan. Our launcher creates these on-the-fly based | 264 # Represents a route in TinCan. Our launcher creates these on-the-fly based |
265 # on the files it finds. | 265 # on the files it finds. |
266 | 266 |
267 EXTENSION = ".pspx" | |
268 CONTENT = "text/html" | |
269 _WINF = "WEB-INF" | |
270 _BANNED = set([_WINF]) | |
271 _CODING = "utf-8" | |
272 _CLASS = "Page" | |
273 _FLOOP = "tincan.forwards" | |
274 _FORIG = "tincan.origin" | |
275 | |
276 class TinCanErrorRoute(object): | |
277 """ | |
278 A route to an error page. These never have code-behind, don't get | |
279 routes created for them, and are only reached if an error routes them | |
280 there. Error templates only have two variables available: e (the | |
281 HTTPError object associated with the error) and request. | |
282 """ | |
283 def __init__(self, template): | |
284 self._template = template | |
285 self._template.prepare() | |
286 | |
287 def __call__(self, e): | |
288 return self._template.render(e=e, request=bottle.request).lstrip('\n') | |
289 | |
267 class TinCanRoute(object): | 290 class TinCanRoute(object): |
268 """ | 291 """ |
269 A route created by the TinCan launcher. | 292 A route created by the TinCan launcher. |
270 """ | 293 """ |
271 def __init__(self, launcher, name, subdir): | 294 def __init__(self, launcher, name, subdir): |
300 self._redirect() | 323 self._redirect() |
301 # If this is a hidden page, we ignore it for now, since hidden pages | 324 # If this is a hidden page, we ignore it for now, since hidden pages |
302 # don't get routes made for them. | 325 # don't get routes made for them. |
303 if hidden: | 326 if hidden: |
304 return | 327 return |
328 # If this is an error page, register it as such. | |
329 if self._header.error is not None: | |
330 try: | |
331 errors = [ int(i) for i in self._header.error.split() ] | |
332 except ValueError as e: | |
333 raise TinCanError("{0}: bad #error line".format(self._urlpath)) from e | |
334 if not errors: | |
335 errors = range(400, 600) | |
336 route = TinCanErrorRoute(self._tclass(source=self._template.body)) | |
337 for error in errors: | |
338 self._app.error(code=error, callback=route) | |
339 return # this implies #hidden | |
305 # Get methods for this route | 340 # Get methods for this route |
306 if self._header.methods is None: | 341 if self._header.methods is None: |
307 methods = [ 'GET' ] | 342 methods = [ 'GET' ] |
308 else: | 343 else: |
309 methods = [ i.upper() for i in self._header.methods.split() ] | 344 methods = [ i.upper() for i in self._header.methods.split() ] |
337 if issubclass(v, Page): | 372 if issubclass(v, Page): |
338 if self._class is not None: | 373 if self._class is not None: |
339 raise TinCanError("{0}: contains multiple Page classes", pypath) | 374 raise TinCanError("{0}: contains multiple Page classes", pypath) |
340 self._class = v | 375 self._class = v |
341 # Build body object (Chameleon template) | 376 # Build body object (Chameleon template) |
342 self._body = self._tclass(source=self._template.body) | 377 if self._header.template is not None: |
378 tpath = os.path.join(self._fsroot, *self._splitpath(self._header.template)) | |
379 tfile = TemplateFile(tpath) | |
380 self._body = self._tclass(source=tfile.body) | |
381 else: | |
382 self._body = self._tclass(source=self._template.body) | |
343 self._body.prepare() | 383 self._body.prepare() |
344 # Register this thing with Bottle | 384 # Register this thing with Bottle |
345 print("adding route:", self._origin) # debug | 385 print("adding route:", self._origin) # debug |
346 self._app.route(self._origin, methods, self) | 386 self._app.route(self._origin, methods, self) |
347 | 387 |
369 args = list(args) | 409 args = list(args) |
370 if args[0] == '/': | 410 if args[0] == '/': |
371 args[0] = '' | 411 args[0] = '' |
372 return '/'.join(args) | 412 return '/'.join(args) |
373 | 413 |
374 def __call__(self, request): | 414 def __call__(self): |
375 """ | 415 """ |
376 This gets called by the framework AFTER the page is launched. | 416 This gets called by the framework AFTER the page is launched. |
377 """ | 417 """ |
378 ### needs to honor self._header.error if set | 418 target = None |
379 mod = importlib.import_module(self._mangled) | 419 try: |
380 cls = getattr(mod, _CLASS) | 420 obj = self._class(bottle.request, bottle.response) |
381 obj = cls(request) | 421 obj.handle() |
382 return Response(self._body.render(**self._mkdict(obj)).lstrip("\n"), | 422 return self._body.render(obj.export()) |
383 content_type=self._content) | 423 except ForwardException as fwd: |
424 target = fwd.target | |
425 if target is None: | |
426 raise TinCanError("Unexpected null target!") | |
427 # We get here if we are doing a server-side programmatic | |
428 # forward. | |
429 environ = bottle.request.environ | |
430 if _FLOOP not in environ: | |
431 environ[_FLOOP] = set() | |
432 if _FORIG not in environ: | |
433 environ[_FORIG] = self._urlpath | |
434 elif target in environ[_FLOOP]: | |
435 TinCanError("{0}: forward loop detected".format(environ[_FORIG])) | |
436 environ[_FLOOP].add(target) | |
437 environ['bottle.raw_path'] = target | |
438 environ['PATH_INFO'] = urllib.parse.quote(target) | |
439 route, args = self._app.router.match(environ) | |
440 environ['route.handle'] = environ['bottle.route'] = route | |
441 environ['route.url_args'] = args | |
442 return route.call(**args) | |
384 | 443 |
385 def _mkdict(self, obj): | 444 def _mkdict(self, obj): |
386 ret = {} | 445 ret = {} |
387 for name in dir(obj): | 446 for name in dir(obj): |
388 if name.startswith('_'): | 447 if name.startswith('_'): |