changeset 3:c6902cded64d draft

Corrections and reorg.
author David Barts <n5jrn@me.com>
date Mon, 13 May 2019 06:53:08 -0700
parents ca6f8ca38cf2
children 0d47859f792a
files pspx.html tincan.py
diffstat 2 files changed, 51 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- 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.</p>
     <dl>
       <dt><code>#errors</code></dt>
-      <dd>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.</dd>
+      <dd>Ignore other headers and make this is an error page which handles
+        the specified HTTP error codes. See the subsection on error pages below.
+      </dd>
       <dt><code>#forward</code></dt>
       <dd>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 @@
       <dd>The rest of the line is treated as a remark (comment) and is ignored.</dd>
       <dt><code>#template</code></dt>
       <dd>Ignore the body of this file and instead use the template in the body
-        of the specified file, which must end in <span class="kbd">.pspx</span>.</dd>
+        of the specified file, which must end in <span class="kbd">.pspx</span>.
+        Any headers in the referred template file are ignored.</dd>
     </dl>
     <p>It is possible to include whitespace and special characters in arguments
       to the <code>#forward</code>, <code>#python</code>, and <code>#template</code>
@@ -54,7 +54,7 @@
       example, <code>#python "space case.py"</code>.</p>
     <h3>Error Pages</h3>
     <p>Error pages supersede the standard Bottle error handling, and are created
-      by using the <code>#error</code> page header. <em>Error pages have no
+      by using the <code>#errors</code> page header. <em>Error pages have no
         associated code-behind;</em> they consist of templates only. Error page
       templates are provided with two variables when rendering:</p>
     <dl>
@@ -63,7 +63,10 @@
       <dt><code>request</code></dt>
       <dd>The <code>bottle.Request</code> object associated with this error.</dd>
     </dl>
-    <p>The behavior of specifying multiple error pages for the same error code
+    <p>The <code>#errors</code> 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.</p>
     <h2>The Body</h2>
     <p>The body begins with the first line that <em>does not</em> start with <code>#</code>
--- 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: