annotate tincan.py @ 0:e726fafcffac draft

For backup purposes, UNFINISHED!!
author David Barts <n5jrn@me.com>
date Sun, 12 May 2019 15:26:28 -0700
parents
children 94b36e721500
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
1 #!/usr/bin/env python3
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
3 # As with Bottle, it's all in one big, ugly file. For now.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
4
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
5 # I m p o r t s
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
6
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
7 import os, sys
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
8 import ast
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
9 import binascii
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
10 from base64 import b16encode, b16decode
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
11 import importlib, py_compile
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
12 import io
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
13
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
14 import bottle
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
15
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
16 # C l a s s e s
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
17
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
18 # Exceptions
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
19
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
20 class TinCanException(Exception):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
21 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
22 The parent class of all exceptions we raise.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
23 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
24 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
25
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
26 class TemplateHeaderException(TinCanException):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
27 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
28 Raised upon encountering a syntax error in the template headers.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
29 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
30 def __init__(self, message, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
31 super().__init__(message, line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
32 self.message = message
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
33 self.line = line
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
34
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
35 def __str__(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
36 return "Line {0}: {1}".format(self.line, self.message)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
37
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
38 class ForwardException(TinCanException):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
39 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
40 Raised to effect the flow control needed to do a forward (server-side
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
41 redirect). It is ugly to do this, but other Python frameworks do and
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
42 there seems to be no good alternative.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
43 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
44 def __init__(self, target):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
45 self.target = target
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
46
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
47 class TinCanError(TinCanException):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
48 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
49 General-purpose exception thrown by TinCan when things go wrong, often
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
50 when attempting to launch webapps.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
51 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
52 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
53
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
54 # Template (.pspx) files. These are standard templates for a supported
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
55 # template engine, but with an optional set of header lines that begin
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
56 # with '#'.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
57
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
58 class TemplateFile(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
59 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
60 Parse a template file into a header part and the body part. The header
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
61 is always a leading set of lines, each starting with '#', that is of the
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
62 same format regardless of the template body. The template body varies
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
63 depending on the selected templating engine. The body part has
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
64 each header line replaced by a blank line. This preserves the overall
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
65 line numbering when processing the body. The added newlines are normally
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
66 stripped out before the rendered page is sent back to the client.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
67 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
68 def __init__(self, raw, encoding='utf-8'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
69 if isinstance(raw, io.TextIOBase):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
70 self._do_init(raw)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
71 elif isinstance(raw, str):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
72 with open(raw, "r", encoding=encoding) as fp:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
73 self._do_init(fp)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
74 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
75 raise TypeError("Expecting a string or Text I/O object.")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
76
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
77 def _do_init(self, fp):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
78 self._hbuf = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
79 self._bbuf = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
80 self._state = self._header
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
81 while True:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
82 line = fp.readline()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
83 if line == '':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
84 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
85 self._state(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
86 self.header = ''.join(self._hbuf)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
87 self.body = ''.join(self._bbuf)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
88
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
89 def _header(self, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
90 if not line.startswith('#'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
91 self._state = self._body
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
92 self._state(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
93 return
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
94 self._hbuf.append(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
95 self._bbuf.append("\n")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
96
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
97 def _body(self, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
98 self._bbuf.append(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
99
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
100 class TemplateHeader(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
101 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
102 Parses and represents a set of header lines.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
103 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
104 _NAMES = [ "error", "forward", "methods", "python", "template" ]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
105 _FNAMES = [ "hidden" ]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
106
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
107 def __init__(self, string):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
108 # Initialize our state
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
109 for i in self._NAMES:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
110 setattr(self, i, None)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
111 for i in self._FNAMES:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
112 setattr(self, i, False)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
113 # Parse the string
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
114 count = 0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
115 nameset = set(self._NAMES + self._FNAMES)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
116 seen = set()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
117 lines = string.split("\n")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
118 if lines and lines[-1] == "":
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
119 del lines[-1]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
120 for line in lines:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
121 # Get line
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
122 count += 1
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
123 if not line.startswith("#"):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
124 raise TemplateHeaderException("Does not start with '#'.", count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
125 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
126 rna, rpa = line.split(maxsplit=1)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
127 except ValueError:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
128 raise TemplateHeaderException("Missing parameter.", count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
129 # Get name, ignoring remarks.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
130 name = rna[1:]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
131 if name == "rem":
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
132 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
133 if name not in nameset:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
134 raise TemplateHeaderException("Invalid directive: {0!r}".format(rna), count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
135 if name in seen:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
136 raise TemplateHeaderException("Duplicate {0!r} directive.".format(rna), count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
137 seen.add(name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
138 # Flags
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
139 if name in self._FLAGS:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
140 setattr(self, name, True)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
141 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
142 # Get parameter
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
143 param = rpa.strip()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
144 for i in [ "'", '"']:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
145 if param.startswith(i) and param.endswith(i):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
146 param = ast.literal_eval(param)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
147 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
148 # Update this object
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
149 setattr(self, name, param)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
150
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
151 # Support for Chameleon templates (the kind TinCan uses by default).
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
152
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
153 class ChameleonTemplate(bottle.BaseTemplate):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
154 def prepare(self, **options):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
155 from chameleon import PageTemplate, PageTemplateFile
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
156 if self.source:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
157 self.tpl = chameleon.PageTemplate(self.source,
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
158 encoding=self.encoding, **options)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
159 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
160 self.tpl = chameleon.PageTemplateFile(self.filename,
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
161 encoding=self.encoding, search_path=self.lookup, **options)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
162
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
163 def render(self, *args, **kwargs):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
164 for dictarg in args:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
165 kwargs.update(dictarg)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
166 _defaults = self.defaults.copy()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
167 _defaults.update(kwargs)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
168 return self.tpl.render(**_defaults)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
169
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
170 chameleon_template = functools.partial(template, template_adapter=ChameleonTemplate)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
171 chameleon_view = functools.partial(view, template_adapter=ChameleonTemplate)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
172
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
173 # Utility functions, used in various places.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
174
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
175 def _normpath(base, unsplit):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
176 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
177 Split, normalize and ensure a possibly relative path is absolute. First
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
178 argument is a list of directory names, defining a base. Second
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
179 argument is a string, which may either be relative to that base, or
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
180 absolute. Only '/' is supported as a separator.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
181 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
182 scratch = unsplit.strip('/').split('/')
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
183 if not unsplit.startswith('/'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
184 scratch = base + scratch
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
185 ret = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
186 for i in scratch:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
187 if i == '.':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
188 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
189 if i == '..':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
190 ret.pop() # may raise IndexError
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
191 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
192 ret.append(i)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
193 return ret
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
194
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
195 def _mangle(string):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
196 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
197 Turn a possibly troublesome identifier into a mangled one.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
198 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
199 first = True
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
200 ret = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
201 for ch in string:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
202 if ch == '_' or not (ch if first else "x" + ch).isidentifier():
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
203 ret.append('_')
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
204 ret.append(b16encode(ch.encode("utf-8")).decode("us-ascii"))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
205 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
206 ret.append(ch)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
207 first = False
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
208 return ''.join(ret)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
209
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
210 # The TinCan class. Simply a Bottle webapp that contains a forward method, so
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
211 # the code-behind can call request.app.forward().
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
212
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
213 class TinCan(bottle.Bottle):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
214 def forward(self, target):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
215 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
216 Forward this request to the specified target route.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
217 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
218 source = bottle.request.environ['PATH_INFO']
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
219 base = source.strip('/').split('/')[:-1]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
220 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
221 exc = ForwardException('/' + '/'.join(_normpath(base, target)))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
222 except IndexError as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
223 raise TinCanError("{0}: invalid forward to {1!r}".format(source, target)) from e
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
224 raise exc
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
225
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
226 # Represents the code-behind of one of our pages. This gets subclassed, of
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
227 # course.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
228
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
229 class Page(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
230 # Non-private things we refuse to export anyhow.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
231 __HIDDEN = set([ "request", "response" ])
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
232
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
233 def __init__(self, req, resp):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
234 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
235 Constructor. This is a lightweight operation.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
236 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
237 self.request = req # app context is request.app in Bottle
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
238 self.response = resp
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
239
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
240 def handle(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
241 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
242 This is the entry point for the code-behind logic. It is intended
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
243 to be overridden.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
244 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
245 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
246
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
247 def export(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
248 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
249 Export template variables. The default behavior is to export all
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
250 non-hidden non-callables that don't start with an underscore,
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
251 plus a an export named page that contains this object itself.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
252 This method can be overridden if a different behavior is
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
253 desired. It should always return a dict or dict-like object.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
254 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
255 ret = { "page": self } # feature: will be clobbered if self.page exists
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
256 for name in dir(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
257 if name in self.__HIDDEN or name.startswith('_'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
258 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
259 value = getattr(self, name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
260 if callable(value):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
261 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
262 ret[name] = value
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
263
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
264 # Represents a route in TinCan. Our launcher creates these on-the-fly based
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
265 # on the files it finds.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
266
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
267 class TinCanRoute(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
268 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
269 A route created by the TinCan launcher.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
270 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
271 def __init__(self, launcher, name, subdir):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
272 self._plib = launcher.plib
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
273 self._fsroot = launcher.fsroot
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
274 self._urlroot = launcher.urlroot
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
275 self._name = name
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
276 self._python = name + ".py"
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
277 self._content = CONTENT
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
278 self._fspath = os.path.join(launcher.fsroot, *subdir, name + EXTENSION)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
279 self._urlpath = self._urljoin(launcher.urlroot, *subdir, name + EXTENSION)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
280 self._origin = self._urlpath
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
281 self._subdir = subdir
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
282 self._seen = set()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
283 self._class = None
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
284 self._tclass = launcher.tclass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
285 self._app = launcher.app
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
286
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
287 def launch(self, config):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
288 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
289 Launch a single page.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
290 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
291 # Build master and header objects, process #forward directives
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
292 hidden = None
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
293 while True:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
294 self._template = TemplateFile(self._fspath)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
295 self._header = TemplateHeader(self._template.header)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
296 if hidden is None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
297 hidden = self._header.hidden
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
298 if self._header.forward is None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
299 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
300 self._redirect()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
301 # If this is a hidden page, we ignore it for now, since hidden pages
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
302 # don't get routes made for them.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
303 if hidden:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
304 return
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
305 # Get methods for this route
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
306 if self._header.methods is None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
307 methods = [ 'GET' ]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
308 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
309 methods = [ i.upper() for i in self._header.methods.split() ]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
310 # Process other header entries
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
311 if self._header.python is not None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
312 if not self._header.python.endswith('.py'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
313 raise TinCanError("{0}: #python files must end in .py", self._urlpath)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
314 self._python = self._header.python
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
315 # Obtain a class object by importing and introspecting a module.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
316 pypath = os.path.normpath(os.path.join(self._fsroot, *self._subdir, self._python))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
317 pycpath = pypath + 'c'
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
318 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
319 pyctime = os.stat(pycpath).st_mtime
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
320 except OSError:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
321 pyctime = 0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
322 if pyctime < os.stat(pypath).st_mtime:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
323 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
324 py_compile.compile(pypath, cfile=pycpath)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
325 except Exception as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
326 raise TinCanError("{0}: error compiling".format(pypath)) from e
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
327 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
328 self._mangled = self._manage_module()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
329 spec = importlib.util.spec_from_file_location(_mangle(self._name), cpath)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
330 mod = importlib.util.module_from_spec(spec)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
331 spec.loader.exec_module(mod)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
332 except Exception as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
333 raise TinCanError("{0}: error importing".format(pycpath)) from e
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
334 self._class = None
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
335 for i in dir(mod):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
336 v = getattr(mod, i)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
337 if issubclass(v, Page):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
338 if self._class is not None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
339 raise TinCanError("{0}: contains multiple Page classes", pypath)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
340 self._class = v
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
341 # Build body object (Chameleon template)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
342 self._body = self._tclass(source=self._template.body)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
343 self._body.prepare()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
344 # Register this thing with Bottle
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
345 print("adding route:", self._origin) # debug
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
346 self._app.route(self._origin, methods, self)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
347
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
348 def _splitpath(self, unsplit):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
349 return _normpath(self._subdir, unsplit)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
350
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
351 def _redirect(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
352 if self._header.forward in self._seen:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
353 raise TinCanError("{0}: #forward loop".format(self._origin))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
354 self._seen.add(self._header.forward)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
355 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
356 rlist = self._splitpath(self._header.forward)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
357 rname = rlist.pop()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
358 except IndexError as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
359 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
360 name, ext = os.path.splitext(rname)[1]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
361 if ext != EXTENSION:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
362 raise TinCanError("{0}: invalid #forward".format(self._urlpath))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
363 self._subdir = rlist
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
364 self._python = name + ".py"
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
365 self._fspath = os.path.join(self._fsroot, *self._subdir, rname)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
366 self._urlpath = self._urljoin(*self._subdir, rname)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
367
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
368 def _urljoin(self, *args):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
369 args = list(args)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
370 if args[0] == '/':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
371 args[0] = ''
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
372 return '/'.join(args)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
373
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
374 def __call__(self, request):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
375 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
376 This gets called by the framework AFTER the page is launched.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
377 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
378 ### needs to honor self._header.error if set
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
379 mod = importlib.import_module(self._mangled)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
380 cls = getattr(mod, _CLASS)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
381 obj = cls(request)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
382 return Response(self._body.render(**self._mkdict(obj)).lstrip("\n"),
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
383 content_type=self._content)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
384
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
385 def _mkdict(self, obj):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
386 ret = {}
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
387 for name in dir(obj):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
388 if name.startswith('_'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
389 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
390 value = getattr(obj, name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
391 if not callable(value):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
392 ret[name] = value
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
393 return ret