annotate tincan.py @ 4:0d47859f792a draft

Finally got "hello, world" working. Still likely many bugs.
author David Barts <n5jrn@me.com>
date Mon, 13 May 2019 12:38:26 -0700
parents c6902cded64d
children 31bb8400e6e3
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
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
11 import functools
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
12 import importlib
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
13 from inspect import isclass
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
14 import io
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
15 import py_compile
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
16 from stat import S_ISDIR, S_ISREG
0
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 import bottle
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
19
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
20 # E x c e p t i o n s
0
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 class TinCanException(Exception):
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 The parent class of all exceptions we raise.
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 pass
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 class TemplateHeaderException(TinCanException):
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 Raised upon encountering a syntax error in the template headers.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
31 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
32 def __init__(self, message, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
33 super().__init__(message, line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
34 self.message = message
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
35 self.line = line
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
36
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
37 def __str__(self):
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
38 return "line {0}: {1}".format(self.line, self.message)
0
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 class ForwardException(TinCanException):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
41 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
42 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
43 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
44 there seems to be no good alternative.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
45 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
46 def __init__(self, target):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
47 self.target = target
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 class TinCanError(TinCanException):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
50 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
51 General-purpose exception thrown by TinCan when things go wrong, often
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
52 when attempting to launch webapps.
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 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
55
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
56 # T e m p l a t e s
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
57 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
58 # Template (.pspx) files. These are standard templates for a supported
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
59 # 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
60 # with '#'.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
61
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
62 class TemplateFile(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
63 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
64 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
65 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
66 same format regardless of the template body. The template body varies
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
67 depending on the selected templating engine. The body part has
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
68 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
69 line numbering when processing the body. The added newlines are normally
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
70 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
71 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
72 def __init__(self, raw, encoding='utf-8'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
73 if isinstance(raw, io.TextIOBase):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
74 self._do_init(raw)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
75 elif isinstance(raw, str):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
76 with open(raw, "r", encoding=encoding) as fp:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
77 self._do_init(fp)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
78 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
79 raise TypeError("Expecting a string or Text I/O object.")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
80
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
81 def _do_init(self, fp):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
82 self._hbuf = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
83 self._bbuf = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
84 self._state = self._header
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
85 while True:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
86 line = fp.readline()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
87 if line == '':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
88 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
89 self._state(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
90 self.header = ''.join(self._hbuf)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
91 self.body = ''.join(self._bbuf)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
92
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
93 def _header(self, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
94 if not line.startswith('#'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
95 self._state = self._body
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
96 self._state(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
97 return
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
98 self._hbuf.append(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
99 self._bbuf.append("\n")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
100
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
101 def _body(self, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
102 self._bbuf.append(line)
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 class TemplateHeader(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
105 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
106 Parses and represents a set of header lines.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
107 """
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
108 _NAMES = [ "errors", "forward", "methods", "python", "template" ]
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
109 _FNAMES = [ "hidden" ]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
110
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
111 def __init__(self, string):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
112 # Initialize our state
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
113 for i in self._NAMES:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
114 setattr(self, i, None)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
115 for i in self._FNAMES:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
116 setattr(self, i, False)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
117 # Parse the string
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
118 count = 0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
119 nameset = set(self._NAMES + self._FNAMES)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
120 seen = set()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
121 lines = string.split("\n")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
122 if lines and lines[-1] == "":
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
123 del lines[-1]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
124 for line in lines:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
125 # Get line
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
126 count += 1
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
127 if not line.startswith("#"):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
128 raise TemplateHeaderException("Does not start with '#'.", count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
129 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
130 rna, rpa = line.split(maxsplit=1)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
131 except ValueError:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
132 raise TemplateHeaderException("Missing parameter.", count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
133 # Get name, ignoring remarks.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
134 name = rna[1:]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
135 if name == "rem":
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
136 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
137 if name not in nameset:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
138 raise TemplateHeaderException("Invalid directive: {0!r}".format(rna), count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
139 if name in seen:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
140 raise TemplateHeaderException("Duplicate {0!r} directive.".format(rna), count)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
141 seen.add(name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
142 # Flags
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
143 if name in self._FLAGS:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
144 setattr(self, name, True)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
145 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
146 # Get parameter
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
147 param = rpa.strip()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
148 for i in [ "'", '"']:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
149 if param.startswith(i) and param.endswith(i):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
150 param = ast.literal_eval(param)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
151 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
152 # Update this object
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
153 setattr(self, name, param)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
154
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
155 # C h a m e l e o n
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
156 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
157 # Support for Chameleon templates (the kind TinCan uses by default).
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
158
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
159 class ChameleonTemplate(bottle.BaseTemplate):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
160 def prepare(self, **options):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
161 from chameleon import PageTemplate, PageTemplateFile
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
162 if self.source:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
163 self.tpl = PageTemplate(self.source, encoding=self.encoding,
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
164 **options)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
165 else:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
166 self.tpl = PageTemplateFile(self.filename, encoding=self.encoding,
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
167 search_path=self.lookup, **options)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
168
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
169 def render(self, *args, **kwargs):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
170 for dictarg in args:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
171 kwargs.update(dictarg)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
172 _defaults = self.defaults.copy()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
173 _defaults.update(kwargs)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
174 return self.tpl.render(**_defaults)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
175
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
176 chameleon_template = functools.partial(bottle.template, template_adapter=ChameleonTemplate)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
177 chameleon_view = functools.partial(bottle.view, template_adapter=ChameleonTemplate)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
178
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
179 # U t i l i t i e s
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
180
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
181 def _normpath(base, unsplit):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
182 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
183 Split, normalize and ensure a possibly relative path is absolute. First
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
184 argument is a list of directory names, defining a base. Second
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
185 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
186 absolute. Only '/' is supported as a separator.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
187 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
188 scratch = unsplit.strip('/').split('/')
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
189 if not unsplit.startswith('/'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
190 scratch = base + scratch
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
191 ret = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
192 for i in scratch:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
193 if i == '.':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
194 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
195 if i == '..':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
196 ret.pop() # may raise IndexError
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
197 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
198 ret.append(i)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
199 return ret
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
200
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
201 def _mangle(string):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
202 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
203 Turn a possibly troublesome identifier into a mangled one.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
204 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
205 first = True
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
206 ret = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
207 for ch in string:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
208 if ch == '_' or not (ch if first else "x" + ch).isidentifier():
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
209 ret.append('_')
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
210 ret.append(b16encode(ch.encode("utf-8")).decode("us-ascii"))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
211 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
212 ret.append(ch)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
213 first = False
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
214 return ''.join(ret)
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 # 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
217 # the code-behind can call request.app.forward().
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
218
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
219 class TinCan(bottle.Bottle):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
220 def forward(self, target):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
221 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
222 Forward this request to the specified target route.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
223 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
224 source = bottle.request.environ['PATH_INFO']
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
225 base = source.strip('/').split('/')[:-1]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
226 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
227 exc = ForwardException('/' + '/'.join(_normpath(base, target)))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
228 except IndexError as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
229 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
230 raise exc
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
231
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
232 # C o d e B e h i n d
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
233 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
234 # 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
235 # course.
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 class Page(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
238 # Non-private things we refuse to export anyhow.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
239 __HIDDEN = set([ "request", "response" ])
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
240
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
241 def __init__(self, req, resp):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
242 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
243 Constructor. This is a lightweight operation.
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 self.request = req # app context is request.app in Bottle
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
246 self.response = resp
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
247
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
248 def handle(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
249 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
250 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
251 to be overridden.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
252 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
253 pass
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 def export(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
256 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
257 Export template variables. The default behavior is to export all
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
258 non-hidden non-callables that don't start with an underscore,
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
259 plus a an export named page that contains this object itself.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
260 This method can be overridden if a different behavior is
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
261 desired. It should always return a dict or dict-like object.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
262 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
263 ret = { "page": self } # feature: will be clobbered if self.page exists
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
264 for name in dir(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
265 if name in self.__HIDDEN or name.startswith('_'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
266 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
267 value = getattr(self, name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
268 if callable(value):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
269 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
270 ret[name] = value
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
271 return ret
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
272
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
273 # R o u t e s
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
274 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
275 # 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
276 # on the files it finds.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
277
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
278 _ERRMIN = 400
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
279 _ERRMAX = 599
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
280 _PEXTEN = ".py"
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
281 _TEXTEN = ".pspx"
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
282 _FLOOP = "tincan.forwards"
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
283 _FORIG = "tincan.origin"
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
284
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
285 class _TinCanErrorRoute(object):
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
286 """
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
287 A route to an error page. These never have code-behind, don't get
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
288 routes created for them, and are only reached if an error routes them
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
289 there. Error templates only have two variables available: e (the
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
290 HTTPError object associated with the error) and request.
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
291 """
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
292 def __init__(self, template):
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
293 self._template = template
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
294 self._template.prepare()
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
295
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
296 def __call__(self, e):
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
297 return self._template.render(e=e, request=bottle.request).lstrip('\n')
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
298
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
299 class _TinCanRoute(object):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
300 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
301 A route created by the TinCan launcher.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
302 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
303 def __init__(self, launcher, name, subdir):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
304 self._fsroot = launcher.fsroot
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
305 self._urlroot = launcher.urlroot
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
306 self._name = name
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
307 self._python = name + _PEXTEN
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
308 self._fspath = os.path.join(launcher.fsroot, *subdir, name + _TEXTEN)
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
309 self._urlpath = self._urljoin(launcher.urlroot, *subdir, name + _TEXTEN)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
310 self._origin = self._urlpath
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
311 self._subdir = subdir
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
312 self._seen = set()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
313 self._tclass = launcher.tclass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
314 self._app = launcher.app
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
315
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
316 def launch(self):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
317 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
318 Launch a single page.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
319 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
320 # Build master and header objects, process #forward directives
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
321 hidden = None
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
322 while True:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
323 self._template = TemplateFile(self._fspath)
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
324 try:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
325 self._header = TemplateHeader(self._template.header)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
326 except TemplateHeaderException as e:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
327 raise TinCanError("{0}: {1!s}".format(self._fspath, e)) from e
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
328 if hidden is None:
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
329 if self._header.errors is not None:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
330 break
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
331 hidden = self._header.hidden
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
332 elif self._header.errors is not None:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
333 raise TinCanError("{0}: #forward to #errors not allowed".format(self._origin))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
334 if self._header.forward is None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
335 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
336 self._redirect()
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
337 # If this is a #hidden page, we ignore it for now, since hidden pages
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
338 # don't get routes made for them.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
339 if hidden:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
340 return
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
341 # If this is an #errors page, register it as such.
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
342 if self._header.errors is not None:
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
343 self._mkerror()
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
344 return # this implies #hidden
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
345 # Get #methods for this route
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
346 if self._header.methods is None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
347 methods = [ 'GET' ]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
348 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
349 methods = [ i.upper() for i in self._header.methods.split() ]
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
350 if not methods:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
351 raise TinCanError("{0}: no #methods specified".format(self._urlpath))
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
352 # Get the code-behind #python
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
353 if self._header.python is not None:
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
354 if not self._header.python.endswith(_PEXTEN):
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
355 raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
356 self._python = self._header.python
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
357 # Obtain a class object by importing and introspecting a module.
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
358 self._getclass()
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
359 # Build body object (#template)
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
360 if self._header.template is not None:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
361 if not self._header.template.endswith(_TEXTEN):
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
362 raise TinCanError("{0}: #template files must end in {1}".format(self._urlpath, _TEXTEN))
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
363 tpath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._header.template)))
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
364 tfile = TemplateFile(tpath)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
365 self._body = self._tclass(source=tfile.body)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
366 else:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
367 self._body = self._tclass(source=self._template.body)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
368 self._body.prepare()
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
369 # Register this thing with Bottle
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
370 print("adding route:", self._origin) # debug
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
371 self._app.route(self._origin, methods, self)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
372
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
373 def _splitpath(self, unsplit):
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
374 return _normpath(self._subdir, unsplit)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
375
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
376 def _mkerror(self):
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
377 try:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
378 errors = [ int(i) for i in self._header.errors.split() ]
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
379 except ValueError as e:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
380 raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
381 if not errors:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
382 errors = range(_ERRMIN, _ERRMAX+1)
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
383 route = _TinCanErrorRoute(self._tclass(source=self._template.body))
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
384 for error in errors:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
385 if error < _ERRMIN or error > _ERRMAX:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
386 raise TinCanError("{0}: bad #errors code".format(self._urlpath))
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
387 self._app.error(code=error, callback=route)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
388
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
389 def _getclass(self):
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
390 pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python)))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
391 pycpath = pypath + 'c'
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
392 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
393 pyctime = os.stat(pycpath).st_mtime
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
394 except OSError:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
395 pyctime = 0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
396 try:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
397 if pyctime < os.stat(pypath).st_mtime:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
398 py_compile.compile(pypath, cfile=pycpath, doraise=True)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
399 except py_compile.PyCompileError as e:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
400 raise TinCanError(str(e)) from e
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
401 except Exception as e:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
402 raise TinCanError("{0}: {1!s}".format(pypath, e)) from e
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
403 try:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
404 spec = importlib.util.spec_from_file_location(_mangle(self._name), pycpath)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
405 mod = importlib.util.module_from_spec(spec)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
406 spec.loader.exec_module(mod)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
407 except Exception as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
408 raise TinCanError("{0}: error importing".format(pycpath)) from e
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
409 self._class = None
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
410 for i in dir(mod):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
411 v = getattr(mod, i)
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
412 if isclass(v) and issubclass(v, Page):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
413 if self._class is not None:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
414 raise TinCanError("{0}: contains multiple Page classes".format(pypath))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
415 self._class = v
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
416 if self._class is None:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
417 raise TinCanError("{0}: contains no Page classes".format(pypath))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
418
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
419 def _redirect(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
420 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
421 rlist = self._splitpath(self._header.forward)
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
422 forw = '/' + '/'.join(rlist)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
423 if forw in self.seen:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
424 raise TinCanError("{0}: #forward loop".format(self._origin))
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
425 self._seen.add(forw)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
426 rname = rlist.pop()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
427 except IndexError as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
428 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
429 name, ext = os.path.splitext(rname)[1]
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
430 if ext != _TEXTEN:
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
431 raise TinCanError("{0}: invalid #forward".format(self._urlpath))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
432 self._subdir = rlist
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
433 self._python = name + _PEXTEN
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
434 self._fspath = os.path.join(self._fsroot, *self._subdir, rname)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
435 self._urlpath = self._urljoin(*self._subdir, rname)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
436
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
437 def _urljoin(self, *args):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
438 args = list(args)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
439 if args[0] == '/':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
440 args[0] = ''
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
441 return '/'.join(args)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
442
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
443 def __call__(self):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
444 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
445 This gets called by the framework AFTER the page is launched.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
446 """
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
447 target = None
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
448 obj = self._class(bottle.request, bottle.response)
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
449 try:
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
450 obj.handle()
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
451 return self._body.render(obj.export()).lstrip('\n')
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
452 except ForwardException as fwd:
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
453 target = fwd.target
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
454 if target is None:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
455 raise TinCanError("{0}: unexpected null target".format(self._urlpath))
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
456 # We get here if we are doing a server-side programmatic
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
457 # forward.
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
458 environ = bottle.request.environ
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
459 if _FORIG not in environ:
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
460 environ[_FORIG] = self._urlpath
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
461 if _FLOOP not in environ:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
462 environ[_FLOOP] = set([self._urlpath])
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
463 elif target in environ[_FLOOP]:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
464 raise TinCanError("{0}: forward loop detected".format(environ[_FORIG]))
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
465 environ[_FLOOP].add(target)
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
466 environ['bottle.raw_path'] = target
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
467 environ['PATH_INFO'] = urllib.parse.quote(target)
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
468 route, args = self._app.router.match(environ)
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
469 environ['route.handle'] = environ['bottle.route'] = route
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
470 environ['route.url_args'] = args
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
471 return route.call(**args)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
472
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
473 def _mkdict(self, obj):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
474 ret = {}
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
475 for name in dir(obj):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
476 if name.startswith('_'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
477 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
478 value = getattr(obj, name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
479 if not callable(value):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
480 ret[name] = value
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
481 return ret
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
482
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
483 # L a u n c h e r
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
484
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
485 _WINF = "WEB-INF"
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
486 _BANNED = set([_WINF])
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
487
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
488 class _Launcher(object):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
489 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
490 Helper class for launching webapps.
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
491 """
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
492 def __init__(self, fsroot, urlroot, tclass, logger):
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
493 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
494 Lightweight constructor. The real action happens in .launch() below.
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
495 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
496 self.fsroot = fsroot
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
497 self.urlroot = urlroot
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
498 self.tclass = tclass
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
499 self.logger = logger
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
500 self.app = None
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
501 self.errors = 0
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
502 self.debug = False
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
503
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
504 def launch(self):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
505 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
506 Does the actual work of launching something. XXX - modifies sys.path
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
507 and never un-modifies it.
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
508 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
509 # Sanity checks
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
510 if not self.urlroot.startswith("/"):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
511 raise TinCanError("urlroot must be absolute")
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
512 if not os.path.isdir(self.fsroot):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
513 raise TinCanError("no such directory: {0!r}".format(self.fsroot))
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
514 # Make WEB-INF, if needed
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
515 winf = os.path.join(self.fsroot, _WINF)
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
516 lib = os.path.join(winf, "lib")
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
517 for i in [ winf, lib ]:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
518 if not os.path.isdir(i):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
519 os.mkdir(i)
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
520 # Add our private lib directory to sys.path
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
521 sys.path.insert(1, os.path.abspath(lib))
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
522 # Do what we gotta do
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
523 self.app = TinCan()
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
524 self._launch([])
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
525 return self
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
526
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
527 def _launch(self, subdir):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
528 for entry in os.listdir(os.path.join(self.fsroot, *subdir)):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
529 if not subdir and entry in _BANNED:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
530 continue
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
531 etype = os.stat(os.path.join(self.fsroot, *subdir, entry)).st_mode
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
532 if S_ISREG(etype):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
533 ename, eext = os.path.splitext(entry)
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
534 if eext != _TEXTEN:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
535 continue # only look at interesting files
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
536 route = _TinCanRoute(self, ename, subdir)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
537 try:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
538 route.launch()
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
539 except TinCanError as e:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
540 self.logger(str(e))
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
541 if self.debug:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
542 while e.__cause__ != None:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
543 e = e.__cause__
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
544 self.logger("\t{0}: {1!s}".format(e.__class__.__name__, e))
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
545 self.errors += 1
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
546 elif S_ISDIR(etype):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
547 self._launch(subdir + [entry])
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
548
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
549 def _logger(message):
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
550 sys.stderr.write(message)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
551 sys.stderr.write('\n')
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
552
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
553 def launch(fsroot=None, urlroot='/', tclass=ChameleonTemplate, logger=_logger):
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
554 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
555 Launch and return a TinCan webapp. Does not run the app; it is the
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
556 caller's responsibility to call app.run()
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
557 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
558 if fsroot is None:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
559 fsroot = os.getcwd()
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
560 launcher = _Launcher(fsroot, urlroot, tclass, logger)
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
561 # launcher.debug = True
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
562 launcher.launch()
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
563 return launcher.app, launcher.errors
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
564
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
565 # XXX - We cannot implement a command-line launcher here; see the
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
566 # launcher script for why.