# HG changeset patch
# User David Barts
#errors
#forward
#template
It is possible to include whitespace and special characters in arguments
to the #forward
, #python
, and #template
@@ -54,7 +54,7 @@
example, #python "space case.py"
.
Error pages supersede the standard Bottle error handling, and are created
- by using the #error
page header. Error pages have no
+ by using the #errors
page header. Error pages have no
associated code-behind; they consist of templates only. Error page
templates are provided with two variables when rendering:
request
bottle.Request
object associated with this error.The behavior of specifying multiple error pages for the same error code +
The #errors
directive takes a list of numeric error codes
+ (values from 400 to 599 are allowed); the page is created to handle the
+ specified errors. If no error codes are specified, the page will handle all
+ errors. The behavior of specifying multiple error pages for the same error code
is undefined; doing so is best avoided.
The body begins with the first line that does not start with #
diff -r ca6f8ca38cf2 -r c6902cded64d tincan.py
--- a/tincan.py Sun May 12 22:51:34 2019 -0700
+++ b/tincan.py Mon May 13 06:53:08 2019 -0700
@@ -321,7 +321,11 @@
self._template = TemplateFile(self._fspath)
self._header = TemplateHeader(self._template.header)
if hidden is None:
+ if self._header.errors is not None:
+ break
hidden = self._header.hidden
+ elif self._header.errors is not None:
+ raise TinCanError("{0}: #forward to #errors not allowed".format(self._origin))
if self._header.forward is None:
break
self._redirect()
@@ -331,32 +335,53 @@
return
# If this is an error page, register it as such.
if self._header.errors is not None:
- if self._header.template is not None:
- tpath = os.path.join(self._fsroot, *self._splitpath(self._header.template))
- self._template =
- try:
- errors = [ int(i) for i in self._header.errors.split() ]
- except ValueError as e:
- raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e
- if not errors:
- errors = range(_ERRMIN, _ERRMAX+1)
- route = TinCanErrorRoute(self._tclass(source=self._template.body))
- for error in errors:
- if error < _ERRMIN or error > _ERRMAX:
- raise TinCanError("{0}: bad #errors code".format(self._urlpath))
- self._app.error(code=error, callback=route)
+ self._mkerror()
return # this implies #hidden
# Get methods for this route
if self._header.methods is None:
methods = [ 'GET' ]
else:
methods = [ i.upper() for i in self._header.methods.split() ]
+ if not methods:
+ raise TinCanError("{0}: no #methods specified".format(self._urlpath))
# Process other header entries
if self._header.python is not None:
if not self._header.python.endswith(_PEXTEN):
raise TinCanError("{0}: #python files must end in {1}", self._urlpath, _PEXTEN)
self._python = self._header.python
# Obtain a class object by importing and introspecting a module.
+ self._getclass()
+ # Build body object (Chameleon template)
+ if self._header.template is not None:
+ if not self._header.template.endswith(_TEXTEN):
+ raise TinCanError("{0}: #template files must end in {1}", self._urlpath, _TEXTEN)
+ tpath = os.path.join(self._fsroot, *self._splitpath(self._header.template))
+ tfile = TemplateFile(tpath)
+ self._body = self._tclass(source=tfile.body)
+ else:
+ self._body = self._tclass(source=self._template.body)
+ self._body.prepare()
+ # Register this thing with Bottle
+ print("adding route:", self._origin) # debug
+ self._app.route(self._origin, methods, self)
+
+ def _splitpath(self, unsplit):
+ return _normpath(self._subdir, unsplit)
+
+ def _mkerror(self):
+ try:
+ errors = [ int(i) for i in self._header.errors.split() ]
+ except ValueError as e:
+ raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e
+ if not errors:
+ errors = range(_ERRMIN, _ERRMAX+1)
+ route = TinCanErrorRoute(self._tclass(source=self._template.body))
+ for error in errors:
+ if error < _ERRMIN or error > _ERRMAX:
+ raise TinCanError("{0}: bad #errors code".format(self._urlpath))
+ self._app.error(code=error, callback=route)
+
+ def _getclass(self):
pypath = os.path.normpath(os.path.join(self._fsroot, *self._subdir, self._python))
pycpath = pypath + 'c'
try:
@@ -381,20 +406,8 @@
if self._class is not None:
raise TinCanError("{0}: contains multiple Page classes", pypath)
self._class = v
- # Build body object (Chameleon template)
- if self._header.template is not None:
- tpath = os.path.join(self._fsroot, *self._splitpath(self._header.template))
- tfile = TemplateFile(tpath)
- self._body = self._tclass(source=tfile.body)
- else:
- self._body = self._tclass(source=self._template.body)
- self._body.prepare()
- # Register this thing with Bottle
- print("adding route:", self._origin) # debug
- self._app.route(self._origin, methods, self)
-
- def _splitpath(self, unsplit):
- return _normpath(self._subdir, unsplit)
+ if self._class is None:
+ raise TinCanError("{0}: contains no Page classes", pypath)
def _redirect(self):
if self._header.forward in self._seen: