# HG changeset patch # User David Barts # Date 1557903645 25200 # Node ID 8037bad7d5a825e7665df6209a9ca6636644bbfe # Parent 84998cd4e1232dc37353e9a2704038818e74f92b Update documentation, fix some #forward bugs. diff -r 84998cd4e123 -r 8037bad7d5a8 code_behind.html --- a/code_behind.html Mon May 13 21:24:48 2019 -0700 +++ b/code_behind.html Wed May 15 00:00:45 2019 -0700 @@ -15,7 +15,7 @@ file names match while only the extensions differ; e.g. foo.py will contain the code-behind logic associated with foo.pspx. Use of the #python and/or #template header - directives will of course change this default association.

+ directives can of course change this default association.

Pretty much anything can be in the code-behind files, with one restriction. There must be one and only one instance of a subclass of either Page (normal pages) or ErrorPage (error @@ -42,6 +42,21 @@ method; the default behavior as described in the Default Template Variables section of the template documentation is normally sufficient.

+

Page Objects

+

These contain the code-behind for normal pages. When the handle + method begins executing, they contain two instance variables: request + (a bottle.Request object) and response (a bottle.Response + object).

+

ErrorPage Objects

+

These contain the code-behind for error pages.  When the handle + method begins executing, they contain two instance variables: request + (a bottle.Request object) and error (a bottle.HTTPError + object).

+

The Application Context

+

There is no separate standard instance variable in either Page + or ErrorPage that provides such, because it is available via + request.app. See the Bottle documentation for more + information.

Code-Behind is Optional

If you define a template with no code-behind file, TinCan will use either Page or ErrorPage as appropriate, which will diff -r 84998cd4e123 -r 8037bad7d5a8 pspx.html --- a/pspx.html Mon May 13 21:24:48 2019 -0700 +++ b/pspx.html Wed May 15 00:00:45 2019 -0700 @@ -18,8 +18,10 @@ header lines may appear only once in a given file.

#end
-
Marks the last line of the headers. Needed only for templating - languages where lines often start with #, such as Cheetah.
+
Marks the last line of the headers. Since the end of the header lines + is implicitly marked by the first line that does not start with #, + this is needed only for templating languages where lines often start + with #, such as Cheetah.
#errors
This is an error page which handles the specified HTTP error codes. See the subsection on error pages below.
@@ -74,10 +76,11 @@ class as appropriate.

The Body

The body begins with the first line that does not start with # - and has the exact same syntax that the templates are in for this webapp. - By default, Chameleon templates are used. Cheetah, Jinja2, Mako, and - Bottle SimpleTemplate templates are also supported, provided the webapp - was launched to use them. (Only one template style per webapp is + (or the first line after the #end directive, whichever comes + first) and has the exact same syntax that the templates are in for this + webapp. By default, Chameleon templates are used. Cheetah, Jinja2, Mako, + and Bottle SimpleTemplate templates are also supported, provided the + webapp was launched to use them. (Only one template style per webapp is supported.)

In order to make line numbers match file line numbers for reported errors, the template engine will be passed a blank line for each header diff -r 84998cd4e123 -r 8037bad7d5a8 tincan.py --- a/tincan.py Mon May 13 21:24:48 2019 -0700 +++ b/tincan.py Wed May 15 00:00:45 2019 -0700 @@ -250,7 +250,7 @@ class BasePage(object): """ - The parent class of both error and normal pages. + The parent class of both error and normal pages' code-behind. """ def handle(self): """ @@ -277,6 +277,9 @@ return ret class Page(BasePage): + """ + The code-behind for a normal page. + """ # Non-private things we refuse to export anyhow. _HIDDEN = set([ "request", "response" ]) @@ -326,6 +329,7 @@ self._class = klass def __call__(self, e): + bottle.request.environ[_FTYPE] = True obj = self._class(bottle.request, e) obj.handle() return self._template.render(obj.export()).lstrip('\n') @@ -352,26 +356,28 @@ Launch a single page. """ # Build master and header objects, process #forward directives - hidden = oerrors = None + oheader = None while True: - if oerrors is not None and oerrors != self._header.errors: - raise TinCanError("{0}: invalid redirect") - self._template = TemplateFile(self._fspath) + try: + self._template = TemplateFile(self._fspath) + except IOError as e: + raise TinCanError(str(e)) from e try: self._header = TemplateHeader(self._template.header) except TemplateHeaderError as e: raise TinCanError("{0}: {1!s}".format(self._fspath, e)) from e - oerrors = self._header.errors - if hidden is None: - hidden = self._header.hidden - elif self._header.errors is not None: - raise TinCanError("{0}: #forward to #errors not allowed".format(self._origin)) + if oheader is None: + oheader = self._header # save original header + elif (oheader.errors is None) != (self._header.errors is None): + raise TinCanError("{0}: invalid #forward".format(self._origin)) if self._header.forward is None: break + print("forwarding from:", self._urlpath) # debug self._redirect() + print("forwarded to:", self._urlpath) # debug # If this is a #hidden page, we ignore it for now, since hidden pages # don't get routes made for them. - if hidden and not self._headers.errors: + if oheader.hidden and not oheader.errors: return # Get the code-behind #python if self._header.python is not None: @@ -391,8 +397,8 @@ self._body = self._tclass(source=self._template.body) self._body.prepare() # If this is an #errors page, register it as such. - if self._header.errors is not None: - self._mkerror() + if oheader.errors is not None: + self._mkerror(oheader.errors) return # this implies #hidden # Get #methods for this route if self._header.methods is None: @@ -408,9 +414,9 @@ def _splitpath(self, unsplit): return _normpath(self._subdir, unsplit) - def _mkerror(self): + def _mkerror(self, rerrors): try: - errors = [ int(i) for i in self._header.errors.split() ] + errors = [ int(i) for i in rerrors.split() ] except ValueError as e: raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e if not errors: @@ -467,13 +473,13 @@ try: rlist = self._splitpath(self._header.forward) forw = '/' + '/'.join(rlist) - if forw in self.seen: + if forw in self._seen: raise TinCanError("{0}: #forward loop".format(self._origin)) self._seen.add(forw) rname = rlist.pop() except IndexError as e: raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e - name, ext = os.path.splitext(rname)[1] + name, ext = os.path.splitext(rname) if ext != _TEXTEN: raise TinCanError("{0}: invalid #forward".format(self._urlpath)) self._subdir = rlist