Mercurial > cgi-bin > hgweb.cgi > tincan
comparison tincan.py @ 11:8037bad7d5a8 draft
Update documentation, fix some #forward bugs.
author | David Barts <n5jrn@me.com> |
---|---|
date | Wed, 15 May 2019 00:00:45 -0700 |
parents | 75e375b1976a |
children | 496d43d551d2 |
comparison
equal
deleted
inserted
replaced
10:84998cd4e123 | 11:8037bad7d5a8 |
---|---|
248 # Represents the code-behind of one of our pages. This gets subclassed, of | 248 # Represents the code-behind of one of our pages. This gets subclassed, of |
249 # course. | 249 # course. |
250 | 250 |
251 class BasePage(object): | 251 class BasePage(object): |
252 """ | 252 """ |
253 The parent class of both error and normal pages. | 253 The parent class of both error and normal pages' code-behind. |
254 """ | 254 """ |
255 def handle(self): | 255 def handle(self): |
256 """ | 256 """ |
257 This is the entry point for the code-behind logic. It is intended | 257 This is the entry point for the code-behind logic. It is intended |
258 to be overridden. | 258 to be overridden. |
275 continue | 275 continue |
276 ret[name] = value | 276 ret[name] = value |
277 return ret | 277 return ret |
278 | 278 |
279 class Page(BasePage): | 279 class Page(BasePage): |
280 """ | |
281 The code-behind for a normal page. | |
282 """ | |
280 # Non-private things we refuse to export anyhow. | 283 # Non-private things we refuse to export anyhow. |
281 _HIDDEN = set([ "request", "response" ]) | 284 _HIDDEN = set([ "request", "response" ]) |
282 | 285 |
283 def __init__(self, req, resp): | 286 def __init__(self, req, resp): |
284 """ | 287 """ |
324 self._template = template | 327 self._template = template |
325 self._template.prepare() | 328 self._template.prepare() |
326 self._class = klass | 329 self._class = klass |
327 | 330 |
328 def __call__(self, e): | 331 def __call__(self, e): |
332 bottle.request.environ[_FTYPE] = True | |
329 obj = self._class(bottle.request, e) | 333 obj = self._class(bottle.request, e) |
330 obj.handle() | 334 obj.handle() |
331 return self._template.render(obj.export()).lstrip('\n') | 335 return self._template.render(obj.export()).lstrip('\n') |
332 | 336 |
333 class _TinCanRoute(object): | 337 class _TinCanRoute(object): |
350 def launch(self): | 354 def launch(self): |
351 """ | 355 """ |
352 Launch a single page. | 356 Launch a single page. |
353 """ | 357 """ |
354 # Build master and header objects, process #forward directives | 358 # Build master and header objects, process #forward directives |
355 hidden = oerrors = None | 359 oheader = None |
356 while True: | 360 while True: |
357 if oerrors is not None and oerrors != self._header.errors: | 361 try: |
358 raise TinCanError("{0}: invalid redirect") | 362 self._template = TemplateFile(self._fspath) |
359 self._template = TemplateFile(self._fspath) | 363 except IOError as e: |
364 raise TinCanError(str(e)) from e | |
360 try: | 365 try: |
361 self._header = TemplateHeader(self._template.header) | 366 self._header = TemplateHeader(self._template.header) |
362 except TemplateHeaderError as e: | 367 except TemplateHeaderError as e: |
363 raise TinCanError("{0}: {1!s}".format(self._fspath, e)) from e | 368 raise TinCanError("{0}: {1!s}".format(self._fspath, e)) from e |
364 oerrors = self._header.errors | 369 if oheader is None: |
365 if hidden is None: | 370 oheader = self._header # save original header |
366 hidden = self._header.hidden | 371 elif (oheader.errors is None) != (self._header.errors is None): |
367 elif self._header.errors is not None: | 372 raise TinCanError("{0}: invalid #forward".format(self._origin)) |
368 raise TinCanError("{0}: #forward to #errors not allowed".format(self._origin)) | |
369 if self._header.forward is None: | 373 if self._header.forward is None: |
370 break | 374 break |
375 print("forwarding from:", self._urlpath) # debug | |
371 self._redirect() | 376 self._redirect() |
377 print("forwarded to:", self._urlpath) # debug | |
372 # If this is a #hidden page, we ignore it for now, since hidden pages | 378 # If this is a #hidden page, we ignore it for now, since hidden pages |
373 # don't get routes made for them. | 379 # don't get routes made for them. |
374 if hidden and not self._headers.errors: | 380 if oheader.hidden and not oheader.errors: |
375 return | 381 return |
376 # Get the code-behind #python | 382 # Get the code-behind #python |
377 if self._header.python is not None: | 383 if self._header.python is not None: |
378 if not self._header.python.endswith(_PEXTEN): | 384 if not self._header.python.endswith(_PEXTEN): |
379 raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN)) | 385 raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN)) |
389 self._body = self._tclass(source=tfile.body) | 395 self._body = self._tclass(source=tfile.body) |
390 else: | 396 else: |
391 self._body = self._tclass(source=self._template.body) | 397 self._body = self._tclass(source=self._template.body) |
392 self._body.prepare() | 398 self._body.prepare() |
393 # If this is an #errors page, register it as such. | 399 # If this is an #errors page, register it as such. |
394 if self._header.errors is not None: | 400 if oheader.errors is not None: |
395 self._mkerror() | 401 self._mkerror(oheader.errors) |
396 return # this implies #hidden | 402 return # this implies #hidden |
397 # Get #methods for this route | 403 # Get #methods for this route |
398 if self._header.methods is None: | 404 if self._header.methods is None: |
399 methods = [ 'GET' ] | 405 methods = [ 'GET' ] |
400 else: | 406 else: |
406 self._app.route(self._origin, methods, self) | 412 self._app.route(self._origin, methods, self) |
407 | 413 |
408 def _splitpath(self, unsplit): | 414 def _splitpath(self, unsplit): |
409 return _normpath(self._subdir, unsplit) | 415 return _normpath(self._subdir, unsplit) |
410 | 416 |
411 def _mkerror(self): | 417 def _mkerror(self, rerrors): |
412 try: | 418 try: |
413 errors = [ int(i) for i in self._header.errors.split() ] | 419 errors = [ int(i) for i in rerrors.split() ] |
414 except ValueError as e: | 420 except ValueError as e: |
415 raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e | 421 raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e |
416 if not errors: | 422 if not errors: |
417 errors = range(_ERRMIN, _ERRMAX+1) | 423 errors = range(_ERRMIN, _ERRMAX+1) |
418 route = _TinCanErrorRoute(self._tclass(source=self._template.body), self._class) | 424 route = _TinCanErrorRoute(self._tclass(source=self._template.body), self._class) |
465 | 471 |
466 def _redirect(self): | 472 def _redirect(self): |
467 try: | 473 try: |
468 rlist = self._splitpath(self._header.forward) | 474 rlist = self._splitpath(self._header.forward) |
469 forw = '/' + '/'.join(rlist) | 475 forw = '/' + '/'.join(rlist) |
470 if forw in self.seen: | 476 if forw in self._seen: |
471 raise TinCanError("{0}: #forward loop".format(self._origin)) | 477 raise TinCanError("{0}: #forward loop".format(self._origin)) |
472 self._seen.add(forw) | 478 self._seen.add(forw) |
473 rname = rlist.pop() | 479 rname = rlist.pop() |
474 except IndexError as e: | 480 except IndexError as e: |
475 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e | 481 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e |
476 name, ext = os.path.splitext(rname)[1] | 482 name, ext = os.path.splitext(rname) |
477 if ext != _TEXTEN: | 483 if ext != _TEXTEN: |
478 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) | 484 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) |
479 self._subdir = rlist | 485 self._subdir = rlist |
480 self._python = name + _PEXTEN | 486 self._python = name + _PEXTEN |
481 self._fspath = os.path.join(self._fsroot, *self._subdir, rname) | 487 self._fspath = os.path.join(self._fsroot, *self._subdir, rname) |