# HG changeset patch # User David Barts # Date 1557755588 25200 # Node ID c6902cded64d3340b6a957ae18110eb0c7813433 # Parent ca6f8ca38cf208eceb46f55f522baef32299544d Corrections and reorg. diff -r ca6f8ca38cf2 -r c6902cded64d pspx.html --- a/pspx.html Sun May 12 22:51:34 2019 -0700 +++ b/pspx.html Mon May 13 06:53:08 2019 -0700 @@ -18,10 +18,9 @@ header lines may appear only once in a given file.

#errors
-
This is an error page which handles the specified HTTP error codes. - The codes are specified in numeric form, separated by whitespace. If no - error codes are specified, this page handles all possible HTTP error - codes.
+
Ignore other headers and make this is an error page which handles + the specified HTTP error codes. See the subsection on error pages below. +
#forward
Ignore everything else in this template (and any code-behind associated with it), using the specified route to serve it instead. The @@ -46,7 +45,8 @@
The rest of the line is treated as a remark (comment) and is ignored.
#template
Ignore the body of this file and instead use the template in the body - of the specified file, which must end in .pspx.
+ of the specified file, which must end in .pspx. + Any headers in the referred template file are ignored.

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

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:

@@ -63,7 +63,10 @@
request
The 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

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: