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('_'):