annotate tincan.py @ 72:e8b3b336e63e draft default tip

Update version.
author David Barts <n5jrn@me.com>
date Mon, 15 Jul 2019 13:17:48 -0700
parents a78c74c73d98
children
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
57
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
11 from chameleon import PageTemplate, PageTemplateFile, TemplateError
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
12 import email.utils
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
13 import functools
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
14 import importlib
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
15 from inspect import isclass
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
16 import io
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
17 import logging
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
18 import mimetypes
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
19 import py_compile
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
20 from stat import S_ISDIR, S_ISREG
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
21 from string import whitespace
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
22 from threading import Lock
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
23 import time
17
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
24 import urllib
0
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 import bottle
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
27
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
28 # E x c e p t i o n s
0
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 class TinCanException(Exception):
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 The parent class of all exceptions we raise.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
33 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
34 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
35
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
36 class TemplateHeaderError(TinCanException):
0
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 Raised upon encountering a syntax error in the template headers.
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 def __init__(self, message, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
41 super().__init__(message, line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
42 self.message = message
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
43 self.line = line
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
44
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
45 def __str__(self):
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
46 return "line {0}: {1}".format(self.line, self.message)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
47
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
48 class LoadError(TinCanException):
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
49 """
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
50 Raised when we run into problems #load'ing something, usually
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
51 because it doesn't exist.
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
52 """
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
53 def __init__(self, message, source):
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
54 super().__init__(message, source)
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
55 self.message = message
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
56 self.source = source
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
57
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
58 def __str__(self):
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
59 return "{0}: #load error: {1}".format(self.source, self.message)
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
60
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
61 class ForwardException(TinCanException):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
62 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
63 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
64 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
65 there seems to be no good alternative.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
66 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
67 def __init__(self, target):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
68 self.target = target
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
69
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
70 class TinCanError(TinCanException):
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 General-purpose exception thrown by TinCan when things go wrong, often
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
73 when attempting to launch webapps.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
74 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
75 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
76
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
77 # T e m p l a t e s
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
78 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
79 # Template (.pspx) files. These are standard templates for a supported
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
80 # 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
81 # with '#'.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
82
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
83 class TemplateFile(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
84 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
85 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
86 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
87 same format regardless of the template body. The template body varies
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
88 depending on the selected templating engine. The body part has
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
89 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
90 line numbering when processing the body. The added newlines are normally
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
91 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
92 """
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
93 _END = "#end"
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
94 _LEND = len(_END)
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
95 _WS = set(whitespace)
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
96
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
97 def __init__(self, raw, encoding='utf-8'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
98 if isinstance(raw, io.TextIOBase):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
99 self._do_init(raw)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
100 elif isinstance(raw, str):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
101 with open(raw, "r", encoding=encoding) as fp:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
102 self._do_init(fp)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
103 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
104 raise TypeError("Expecting a string or Text I/O 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 def _do_init(self, fp):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
107 self._hbuf = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
108 self._bbuf = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
109 self._state = self._header
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
110 while True:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
111 line = fp.readline()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
112 if line == '':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
113 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
114 self._state(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
115 self.header = ''.join(self._hbuf)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
116 self.body = ''.join(self._bbuf)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
117
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
118 def _header(self, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
119 if not line.startswith('#'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
120 self._state = self._body
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
121 self._state(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
122 return
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
123 if line.startswith(self._END) and (len(line) == self._LEND or line[self._LEND] in self._WS):
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
124 self._state = self._body
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
125 self._hbuf.append(line)
36
4ed261056057 Put old newline logic back.
David Barts <n5jrn@me.com>
parents: 35
diff changeset
126 self._bbuf.append("\n")
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
127
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
128 def _body(self, line):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
129 self._bbuf.append(line)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
130
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
131 class TemplateHeader(object):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
132 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
133 Parses and represents a set of header lines.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
134 """
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
135 _NAMES = [ "errors", "forward", "methods", "python", "template" ]
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
136 _FNAMES = [ "hidden" ]
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
137 _ANAMES = [ "load" ]
67
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
138 _ONAMES = [ "errors" ]
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
139
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
140 def __init__(self, string):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
141 # Initialize our state
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
142 for i in self._NAMES:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
143 setattr(self, i, None)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
144 for i in self._FNAMES:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
145 setattr(self, i, False)
25
e93e5e746cc5 Preliminary debugging, still not fully tested.
David Barts <n5jrn@me.com>
parents: 24
diff changeset
146 for i in self._ANAMES:
e93e5e746cc5 Preliminary debugging, still not fully tested.
David Barts <n5jrn@me.com>
parents: 24
diff changeset
147 setattr(self, i, [])
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
148 # Parse the string
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
149 count = 0
25
e93e5e746cc5 Preliminary debugging, still not fully tested.
David Barts <n5jrn@me.com>
parents: 24
diff changeset
150 nameset = set(self._NAMES + self._FNAMES + self._ANAMES)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
151 seen = set()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
152 lines = string.split("\n")
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
153 if lines and lines[-1] == "":
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
154 del lines[-1]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
155 for line in lines:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
156 # Get line
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
157 count += 1
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
158 if not line.startswith("#"):
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
159 raise TemplateHeaderError("Does not start with '#'.", count)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
160 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
161 rna, rpa = line.split(maxsplit=1)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
162 except ValueError:
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
163 rna = line.rstrip()
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
164 rpa = None
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
165 # Get name, ignoring remarks.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
166 name = rna[1:]
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
167 if name == "rem":
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
168 continue
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
169 if name == "end":
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
170 break
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
171 if name not in nameset:
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
172 raise TemplateHeaderError("Invalid directive: {0!r}".format(rna), count)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
173 if name in seen:
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
174 raise TemplateHeaderError("Duplicate {0!r} directive.".format(rna), count)
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
175 if name not in self._ANAMES:
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
176 seen.add(name)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
177 # Flags
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
178 if name in self._FNAMES:
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
179 setattr(self, name, True)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
180 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
181 # Get parameter
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
182 if rpa is None:
67
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
183 if name in self._ONAMES:
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
184 rpa = ""
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
185 else:
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
186 raise TemplateHeaderError("Missing parameter.", count)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
187 param = rpa.strip()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
188 for i in [ "'", '"']:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
189 if param.startswith(i) and param.endswith(i):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
190 param = ast.literal_eval(param)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
191 break
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
192 # Update this object
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
193 if name in self._ANAMES:
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
194 getattr(self, name).append(param)
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
195 else:
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
196 setattr(self, name, param)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
197
23
e8b6ee7e5b6b Well, *that* attempt at includes didn't work. Revert.
David Barts <n5jrn@me.com>
parents: 22
diff changeset
198 # C h a m e l e o n
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
199 #
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
200 # Support for Chameleon templates (the kind TinCan uses).
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
201
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
202 class ChameleonTemplate(bottle.BaseTemplate):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
203 def prepare(self, **options):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
204 if self.source:
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
205 self.tpl = PageTemplate(self.source, **options)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
206 else:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
207 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
208 search_path=self.lookup, **options)
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
209 # XXX - work around broken Chameleon decoding
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
210 self.tpl.default_encoding = self.encoding
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
211
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
212 def render(self, *args, **kwargs):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
213 for dictarg in args:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
214 kwargs.update(dictarg)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
215 _defaults = self.defaults.copy()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
216 _defaults.update(kwargs)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
217 return self.tpl.render(**_defaults)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
218
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
219 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
220 chameleon_view = functools.partial(bottle.view, template_adapter=ChameleonTemplate)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
221
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
222 # U t i l i t i e s
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
223
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
224 def _hterror(**kwargs):
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
225 """
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
226 Make a suitable bottle.HttpError object, with a message that is
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
227 always meaningful.
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
228 """
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
229 if "status" not in kwargs:
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
230 raise ValueError("status argument is mandatory")
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
231 if "body" not in kwargs:
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
232 kwargs["body"] = "No further details available."
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
233 return bottle.HTTPError(**kwargs)
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
234
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
235 def _normpath(base, unsplit):
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 Split, normalize and ensure a possibly relative path is absolute. First
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
238 argument is a list of directory names, defining a base. Second
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
239 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
240 absolute. Only '/' is supported as a separator.
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 scratch = unsplit.strip('/').split('/')
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
243 if not unsplit.startswith('/'):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
244 scratch = base + scratch
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
245 ret = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
246 for i in scratch:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
247 if i == '.':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
248 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
249 if i == '..':
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
250 ret.pop() # may raise IndexError
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
251 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
252 ret.append(i)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
253 return ret
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 _mangle(string):
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 Turn a possibly troublesome identifier into a mangled one.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
258 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
259 first = True
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
260 ret = []
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
261 for ch in string:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
262 if ch == '_' or not (ch if first else "x" + ch).isidentifier():
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
263 ret.append('_')
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
264 ret.append(b16encode(ch.encode("utf-8")).decode("us-ascii"))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
265 else:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
266 ret.append(ch)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
267 first = False
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
268 return ''.join(ret)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
269
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
270 _FOLDS_CASE = sys.platform in ['darwin', 'win32']
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
271
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
272 def _casef(string, case="lower"):
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
273 """
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
274 If we're on an OS with case-insensitive file names, fold case.
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
275 Else leave things alone.
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
276 """
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
277 return getattr(string, case)() if _FOLDS_CASE else string
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
278
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
279 # 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
280 # the code-behind can call request.app.forward().
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
281
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
282 class TinCan(bottle.Bottle):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
283 def forward(self, target):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
284 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
285 Forward this request to the specified target route.
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 source = bottle.request.environ['PATH_INFO']
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
288 base = source.strip('/').split('/')[:-1]
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
289 if bottle.request.environ.get(_FTYPE, False):
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
290 raise TinCanError("{0}: forward from error page".format(source))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
291 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
292 exc = ForwardException('/' + '/'.join(_normpath(base, target)))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
293 except IndexError as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
294 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
295 raise exc
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
296
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
297 # C o d e B e h i n d
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
298 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
299 # 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
300 # course.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
301
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
302 class BasePage(object):
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
303 """
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
304 The parent class of both error and normal pages' code-behind.
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
305 """
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
306 def handle(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
307 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
308 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
309 to be overridden.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
310 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
311 pass
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
312
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
313 def export(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
314 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
315 Export template variables. The default behavior is to export all
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
316 non-hidden non-callables that don't start with an underscore.
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
317 This method can be overridden if a different behavior is
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
318 desired. It should always return a dict or dict-like object.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
319 """
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
320 ret = { 'page': self }
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
321 for name in dir(self):
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
322 if name in self._HIDDEN or name.startswith('_'):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
323 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
324 value = getattr(self, name)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
325 if callable(value):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
326 continue
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
327 ret[name] = value
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
328 return ret
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
329
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
330 class Page(BasePage):
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
331 """
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
332 The code-behind for a normal page.
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
333 """
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
334 # Non-private things we refuse to export anyhow.
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
335 _HIDDEN = set([ "request", "response" ])
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
336
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
337 def __init__(self, req, resp):
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
338 """
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
339 Constructor. This is a lightweight operation.
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
340 """
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
341 self.request = req # app context is request.app in Bottle
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
342 self.response = resp
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
343
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
344 class ErrorPage(BasePage):
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
345 """
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
346 The code-behind for an error page.
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
347 """
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
348 _HIDDEN = set()
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
349
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
350 def __init__(self, req, err):
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
351 """
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
352 Constructor. This is a lightweight operation.
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
353 """
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
354 self.request = req
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
355 self.error = err
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
356
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
357 # I n c l u s i o n
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
358 #
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
359 # Most processing is in the TinCanRoute class; this just interprets and
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
360 # represents arguments to the #load header directive.
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
361
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
362 class _LoadedFile(object):
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
363 def __init__(self, raw):
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
364 if raw.startswith('<') and raw.endswith('>'):
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
365 raw = raw[1:-1]
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
366 self.in_lib = True
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
367 else:
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
368 self.in_lib = False
31
443a0001d841 Improve the #include syntax a bit.
David Barts <n5jrn@me.com>
parents: 30
diff changeset
369 equals = raw.find('=')
443a0001d841 Improve the #include syntax a bit.
David Barts <n5jrn@me.com>
parents: 30
diff changeset
370 if equals < 0:
33
cc975bf7a3fa Fix bug in auto-generated include variables.
David Barts <n5jrn@me.com>
parents: 32
diff changeset
371 self.vname = os.path.splitext(os.path.basename(raw))[0]
31
443a0001d841 Improve the #include syntax a bit.
David Barts <n5jrn@me.com>
parents: 30
diff changeset
372 self.fname = raw
443a0001d841 Improve the #include syntax a bit.
David Barts <n5jrn@me.com>
parents: 30
diff changeset
373 else:
443a0001d841 Improve the #include syntax a bit.
David Barts <n5jrn@me.com>
parents: 30
diff changeset
374 self.vname = raw[:equals]
443a0001d841 Improve the #include syntax a bit.
David Barts <n5jrn@me.com>
parents: 30
diff changeset
375 self.fname = raw[equals+1:]
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
376 if self.vname == "":
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
377 raise ValueError("empty variable name")
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
378 if self.fname == "":
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
379 raise ValueError("empty file name")
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
380 if not _casef(self.fname).endswith(_IEXTEN):
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
381 raise ValueError("file does not end in {0}".format(_IEXTEN))
24
34d3cfcd37ef First batch of work on getting a #include header. Unfinished.
David Barts <n5jrn@me.com>
parents: 23
diff changeset
382
34
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
383 # Using a cache is likely to help efficiency a lot, since many pages
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
384 # will typically #load the same standard stuff. Except if we're
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
385 # multithreading, then we want each page's templates to be private.
34
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
386 _tcache = {}
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
387 def _get_template_cache(name, direct, coding):
34
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
388 aname = os.path.abspath(os.path.join(direct, name))
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
389 if aname not in _tcache:
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
390 tmpl = ChameleonTemplate(name=name, lookup=[direct], encoding=coding)
34
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
391 assert aname == tmpl.filename
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
392 _tcache[aname] = tmpl
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
393 return _tcache[aname]
0fb455b46e5f Add cache.
David Barts <n5jrn@me.com>
parents: 33
diff changeset
394
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
395 def _get_template_nocache(name, direct, coding):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
396 return ChameleonTemplate(name=name, lookup=[direct], encoding=coding)
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
397
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
398 # R o u t e s
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
399 #
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
400 # 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
401 # on the files it finds.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
402
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
403 _ERRMIN = 400
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
404 _ERRMAX = 599
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
405 _IEXTEN = ".pt"
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
406 _PEXTEN = ".py"
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
407 _TEXTEN = ".pspx"
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
408 _FLOOP = "tincan.forwards"
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
409 _FORIG = "tincan.origin"
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
410 _FTYPE = "tincan.iserror"
58
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
411 _INDEX = "/index" + _TEXTEN
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
412 _LINDEX = len(_INDEX)
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
413 _INDICES = [ _INDEX, "/index.html", "/index.htm" ]
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
414
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
415 class _TinCanBaseRoute(object):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
416 """
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
417 The base class for all NON ERROR routes. Error routes are just a little
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
418 bit different.
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
419 """
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
420 def __init__(self, launcher, name, subdir):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
421 global _get_template_cache, _get_template_nocache
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
422 if launcher.multithread:
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
423 self.lock = Lock()
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
424 self.get_template = _get_template_nocache
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
425 else:
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
426 self.lock = _DummyLock()
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
427 self.get_template = _get_template_cache
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
428 self.logger = launcher.logger
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
429
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
430 def urljoin(self, *args):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
431 """
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
432 Normalize a parsed-out URL fragment.
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
433 """
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
434 args = list(args)
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
435 if args[0] == '/':
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
436 args[0] = ''
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
437 return '/'.join(args)
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
438
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
439 def launch(self):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
440 raise NotImplementedError("This must be overridden.")
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
441
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
442 def __call__(self):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
443 raise NotImplementedError("This must be overridden.")
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
444
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
445 class _TinCanStaticRoute(_TinCanBaseRoute):
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
446 """
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
447 A route to a static file. These are useful for test servers. For
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
448 production servers, one is better off using a real web server and
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
449 a WSGI plugin, and having that handle static files. Much of this
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
450 logic is cribbed from the Bottle source code (we don't call it
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
451 directly because it is undocumented and thus subject to change).
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
452 """
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
453 def __init__(self, launcher, name, subdir):
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
454 super().__init__(launcher, name, subdir)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
455 self._app = launcher.app
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
456 self._fspath = os.path.join(launcher.fsroot, *subdir, name)
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
457 self._urlpath = self.urljoin(launcher.urlroot, *subdir, name)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
458 self._type = mimetypes.guess_type(name)[0]
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
459 if self._type is None:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
460 self._type = "application/octet-stream"
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
461 if self._type.startswith("text/"):
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
462 self._encoding = launcher.encoding
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
463 self._type += "; charset=" + launcher.encoding
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
464 else:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
465 self._encoding = None
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
466
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
467 def launch(self):
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
468 self.logger.info("adding static route: %s", self._urlpath)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
469 self._app.route(self._urlpath, 'GET', self)
58
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
470 for i in _INDICES:
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
471 if _casef(self._urlpath).endswith(i):
58
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
472 li = len(i)
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
473 for j in [ self._urlpath[:1-li], self._urlpath[:-li] ]:
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
474 if j:
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
475 self.logger.info("adding static route: %s", j)
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
476 self._app.route(j, 'GET', self)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
477
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
478 def _parse_date(self, ims):
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
479 """
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
480 Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch.
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
481 """
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
482 try:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
483 ts = email.utils.parsedate_tz(ims)
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
484 return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
485 except (TypeError, ValueError, IndexError, OverflowError):
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
486 return None
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
487
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
488 def __call__(self):
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
489 # Get file contents and time stamp. If we can't, return an
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
490 # appropriate HTTP error response.
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
491 try:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
492 with open(self._fspath, "rb") as fp:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
493 mtime = os.fstat(fp.fileno()).st_mtime
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
494 bytes = fp.read()
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
495 except FileNotFoundError as e:
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
496 return _hterror(status=404, exception=e)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
497 except PermissionError as e:
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
498 return _hterror(status=403, exception=e)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
499 except OSError as e:
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
500 self.logger.exception("unexpected exception reading %r", self._fspath)
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
501 return _hterror(status=500, exception=e)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
502 # Establish preliminary standard headers.
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
503 headers = {
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
504 "Content-Type": self._type,
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
505 "Last-Modified": time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime)),
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
506 }
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
507 if self._encoding:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
508 headers["Content-Encoding"] = self._encoding
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
509 # Support the If-Modified-Since request header.
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
510 ims = bottle.request.environ.get('HTTP_IF_MODIFIED_SINCE')
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
511 if ims:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
512 ims = self._parse_date(ims.split(";")[0].strip())
41
9335865ae0bb Fix bug.
David Barts <n5jrn@me.com>
parents: 40
diff changeset
513 if ims is not None and ims >= int(mtime):
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
514 headers["Content-Length"] = "0"
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
515 return bottle.HTTPResponse(body=b"", status=304, headers=headers)
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
516 # Standard response.
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
517 headers["Content-Length"] = str(len(bytes))
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
518 return bottle.HTTPResponse(body=bytes, status=200, headers=headers)
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
519
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
520 class _TinCanErrorRoute(object):
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
521 """
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
522 A route to an error page. These don't get routes created for them,
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
523 and are only reached if an error routes them there. Unless you create
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
524 custom code-behind, only two variables are available to your template:
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
525 request (bottle.Request) and error (bottle.HTTPError).
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
526 """
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
527 def __init__(self, template, loads, klass, lock, logger):
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
528 self._template = template
23
e8b6ee7e5b6b Well, *that* attempt at includes didn't work. Revert.
David Barts <n5jrn@me.com>
parents: 22
diff changeset
529 self._template.prepare()
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
530 self._loads = loads
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
531 self._class = klass
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
532 self.lock = lock
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
533 self.logger = logger
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
534
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
535 def __call__(self, e):
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
536 bottle.request.environ[_FTYPE] = True
17
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
537 try:
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
538 obj = self._class(bottle.request, e)
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
539 obj.handle()
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
540 tvars = self._loads.copy()
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
541 tvars.update(obj.export())
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
542 with self.lock:
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
543 return self._template.render(tvars).lstrip('\n')
17
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
544 except bottle.HTTPResponse as e:
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
545 return e
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
546 except Exception as e:
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
547 self.logger.exception("unexpected exception in error page")
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
548 # Bottle doesn't allow error handlers to themselves cause
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
549 # errors, most likely as a measure to prevent looping. So
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
550 # this will cause a "Critical error while processing request"
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
551 # page to be displayed, and any installed error pages to be
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
552 # ignored.
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
553 raise _hterror(status=500, exception=e)
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
554
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
555 class _TinCanRoute(_TinCanBaseRoute):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
556 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
557 A route created by the TinCan launcher.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
558 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
559 def __init__(self, launcher, name, subdir):
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
560 super().__init__(launcher, name, subdir)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
561 self._fsroot = launcher.fsroot
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
562 self._urlroot = launcher.urlroot
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
563 self._name = name
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
564 self._python = name + _PEXTEN
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
565 self._fspath = os.path.join(launcher.fsroot, *subdir, name + _TEXTEN)
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
566 self._urlpath = self.urljoin(launcher.urlroot, *subdir, name + _TEXTEN)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
567 self._origin = self._urlpath
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
568 self._subdir = subdir
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
569 self._seen = set()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
570 self._app = launcher.app
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
571 self._encoding = launcher.encoding
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
572
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
573 def launch(self):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
574 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
575 Launch a single page.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
576 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
577 # Build master and header objects, process #forward directives
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
578 oheader = None
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
579 while True:
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
580 try:
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
581 self._template = TemplateFile(self._fspath)
69
c168aad7a731 Fix template error reportage.
David Barts <n5jrn@me.com>
parents: 68
diff changeset
582 except (OSError, UnicodeError) as e:
c168aad7a731 Fix template error reportage.
David Barts <n5jrn@me.com>
parents: 68
diff changeset
583 raise TinCanError("{0}: {1!s}".format(self._fspath, e)) from e
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
584 try:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
585 self._header = TemplateHeader(self._template.header)
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
586 except TemplateHeaderError as e:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
587 raise TinCanError("{0}: {1!s}".format(self._fspath, e)) from e
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
588 if oheader is None:
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
589 oheader = self._header # save original header
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
590 elif (oheader.errors is None) != (self._header.errors is None):
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
591 raise TinCanError("{0}: invalid #forward".format(self._origin))
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
592 if self._header.forward is None:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
593 break
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
594 # self.logger.debug("forwarding from: %s", self._urlpath) # debug
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
595 self._redirect()
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
596 # self.logger.debug("forwarded to: %s", self._urlpath) # debug
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
597 # 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
598 # don't get routes made for them.
67
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
599 if oheader.hidden and oheader.errors is None:
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
600 return
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
601 # Get the code-behind #python
15
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
602 if self._header.python is None:
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
603 self._python_specified = False
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
604 else:
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
605 if not _casef(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
606 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
607 self._python = self._header.python
15
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
608 self._python_specified = True
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
609 # Obtain a class object by importing and introspecting a module.
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
610 self._getclass()
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
611 # Build body object (#template) and obtain #loads.
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
612 if self._header.template is not None:
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
613 if not _casef(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
614 raise TinCanError("{0}: #template files must end in {1}".format(self._urlpath, _TEXTEN))
69
c168aad7a731 Fix template error reportage.
David Barts <n5jrn@me.com>
parents: 68
diff changeset
615 tpath = None
16
448fc3d534f8 Improve error reportage.
David Barts <n5jrn@me.com>
parents: 15
diff changeset
616 try:
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
617 rtpath = self._splitpath(self._header.template)
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
618 tpath = os.path.normpath(os.path.join(self._fsroot, *rtpath))
16
448fc3d534f8 Improve error reportage.
David Barts <n5jrn@me.com>
parents: 15
diff changeset
619 tfile = TemplateFile(tpath)
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
620 except OSError as e:
16
448fc3d534f8 Improve error reportage.
David Barts <n5jrn@me.com>
parents: 15
diff changeset
621 raise TinCanError("{0}: invalid #template: {1!s}".format(self._urlpath, e)) from e
69
c168aad7a731 Fix template error reportage.
David Barts <n5jrn@me.com>
parents: 68
diff changeset
622 except UnicodeError as e:
c168aad7a731 Fix template error reportage.
David Barts <n5jrn@me.com>
parents: 68
diff changeset
623 raise TinCanError("{0}: {1!s}".format(tpath, e)) from e
16
448fc3d534f8 Improve error reportage.
David Barts <n5jrn@me.com>
parents: 15
diff changeset
624 except IndexError as e:
448fc3d534f8 Improve error reportage.
David Barts <n5jrn@me.com>
parents: 15
diff changeset
625 raise TinCanError("{0}: invalid #template".format(self._urlpath)) from e
58
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
626 self._body = self._mktemplate(tfile.body, self.urljoin(*rtpath))
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
627 else:
57
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
628 self._body = self._mktemplate(self._template.body, self._urlpath)
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
629 self._body.prepare()
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
630 # Process loads
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
631 self._loads = {}
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
632 for load in self._header.load:
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
633 try:
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
634 load = _LoadedFile(load)
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
635 except ValueError as e:
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
636 raise TinCanError("{0}: bad #load: {1!s}".format(self._urlpath, e)) from e
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
637 if load.in_lib:
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
638 fdir = os.path.join(self._fsroot, _WINF, "tlib")
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
639 else:
32
3382799f3905 Refinements.
David Barts <n5jrn@me.com>
parents: 31
diff changeset
640 fdir = os.path.join(self._fsroot, *self._subdir)
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
641 try:
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
642 tmpl = self.get_template(load.fname, fdir, self._encoding)
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
643 except Exception as e:
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
644 raise TinCanError("{0}: bad #load: {1!s}".format(self._urlpath, e)) from e
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
645 self._loads[load.vname] = tmpl.tpl
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
646 # If this is an #errors page, register it as such.
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
647 if oheader.errors is not None:
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
648 self._mkerror(oheader.errors)
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
649 return # this implies #hidden
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
650 # Get #methods for this route
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
651 if self._header.methods is None:
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
652 methods = [ 'GET' ]
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
653 else:
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
654 methods = [ i.upper() for i in self._header.methods.split() ]
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
655 if not methods:
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
656 raise TinCanError("{0}: no #methods specified".format(self._urlpath))
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
657 # Register this thing with Bottle
58
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
658 mtxt = ','.join(methods)
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
659 self.logger.info("adding route: %s (%s)", self._origin, mtxt)
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
660 self._app.route(self._origin, methods, self)
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
661 if _casef(self._origin).endswith(_INDEX):
58
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
662 for i in [ self._origin[:1-_LINDEX], self._origin[:-_LINDEX] ]:
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
663 if i:
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
664 self.logger.info("adding route: %s (%s)", i, mtxt)
e08e24707da1 Recognize index.pspx (and index.html, and index.htm if static).
David Barts <n5jrn@me.com>
parents: 57
diff changeset
665 self._app.route(i, methods, self)
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
666
57
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
667 def _mktemplate(self, source, name):
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
668 try:
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
669 return ChameleonTemplate(source=source, encoding=self._encoding)
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
670 except TemplateError as e:
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
671 raise TinCanError("{0}: {1!s}".format(name, e)) from e
935f8013f540 Fix lack of error detection and recovery in template parsing.
David Barts <n5jrn@me.com>
parents: 56
diff changeset
672
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
673 def _splitpath(self, unsplit):
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
674 return _normpath(self._subdir, unsplit)
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
675
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
676 def _mkerror(self, rerrors):
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
677 try:
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
678 errors = [ int(i) for i in rerrors.split() ]
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
679 except ValueError as e:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
680 raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
681 if not errors:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
682 errors = range(_ERRMIN, _ERRMAX+1)
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
683 route = _TinCanErrorRoute(
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
684 ChameleonTemplate(source=self._template.body, encoding=self._encoding),
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
685 self._loads, self._class, self.lock, self.logger)
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
686 for error in errors:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
687 if error < _ERRMIN or error > _ERRMAX:
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
688 raise TinCanError("{0}: bad #errors code".format(self._urlpath))
5
31bb8400e6e3 Add #end header, fix #errors.
David Barts <n5jrn@me.com>
parents: 4
diff changeset
689 self._app.error_handler[error] = route # XXX
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
690
7
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
691 def _gettime(self, path):
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
692 try:
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
693 return os.stat(path).st_mtime
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
694 except FileNotFoundError:
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
695 return 0
8
9aaa91247b14 Fix minor buglet.
David Barts <n5jrn@me.com>
parents: 7
diff changeset
696 except OSError as e:
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
697 raise TinCanError(str(e)) from e
7
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
698
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
699 def _getclass(self):
15
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
700 try:
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
701 pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python)))
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
702 except IndexError as e:
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
703 raise TinCanError("{0}: invalid #python".format(self._urlpath)) from e
67
b5e72129ef72 Support #errors w/ no params, as documented.
David Barts <n5jrn@me.com>
parents: 66
diff changeset
704 klass = ErrorPage if self._header.errors is not None else Page
7
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
705 # Give 'em a default code-behind if they don't furnish one
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
706 pytime = self._gettime(pypath)
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
707 if not pytime:
15
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
708 if self._python_specified:
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
709 raise TinCanError("{0}: #python file not found".format(self._urlpath))
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
710 self._class = klass
7
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
711 return
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
712 # Else load the code-behind from a .py file
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
713 pycpath = pypath + 'c'
7
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
714 pyctime = self._gettime(pycpath)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
715 try:
7
57ec65f527e9 Eliminate a stat() call, allow no code-behind on pages.
David Barts <n5jrn@me.com>
parents: 6
diff changeset
716 if pyctime < pytime:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
717 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
718 except py_compile.PyCompileError as e:
68
61667621e19d Work around bug in py_compile.
David Barts <n5jrn@me.com>
parents: 67
diff changeset
719 msg = str(e)
61667621e19d Work around bug in py_compile.
David Barts <n5jrn@me.com>
parents: 67
diff changeset
720 if pypath not in msg:
61667621e19d Work around bug in py_compile.
David Barts <n5jrn@me.com>
parents: 67
diff changeset
721 msg = pypath + ": " + msg
61667621e19d Work around bug in py_compile.
David Barts <n5jrn@me.com>
parents: 67
diff changeset
722 raise TinCanError(msg) from e
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
723 except Exception as e:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
724 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
725 try:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
726 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
727 mod = importlib.util.module_from_spec(spec)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
728 spec.loader.exec_module(mod)
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
729 except Exception as e:
16
448fc3d534f8 Improve error reportage.
David Barts <n5jrn@me.com>
parents: 15
diff changeset
730 raise TinCanError("{0}: error importing: {1!s}".format(pycpath, e)) from e
19
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
731 # Locate a suitable class. We look for the "deepest" class object
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
732 # we can find in the inheritance tree.
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
733 self._class = None
19
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
734 score = -1
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
735 ambig = False
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
736 for i in dir(mod):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
737 v = getattr(mod, i)
19
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
738 if not isclass(v):
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
739 continue
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
740 d = self._cldepth(klass, v)
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
741 if d > score:
15
560c8fb55e4a Fix bugs in #python directive, make code-behind class loading simpler.
David Barts <n5jrn@me.com>
parents: 14
diff changeset
742 self._class = v
19
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
743 score = d
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
744 ambig = False
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
745 elif d == score:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
746 ambig = True
3
c6902cded64d Corrections and reorg.
David Barts <n5jrn@me.com>
parents: 2
diff changeset
747 if self._class is None:
9
75e375b1976a Error pages now can have code-behind.
David Barts <n5jrn@me.com>
parents: 8
diff changeset
748 raise TinCanError("{0}: contains no {1} classes".format(pypath, klass.__name__))
19
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
749 if ambig:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
750 raise TinCanError("{0}: contains ambiguous {1} classes".format(pypath, klass.__name__))
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
751
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
752 # This might fail for complex inheritance schemes from the classes of
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
753 # interest (so don't use them!).
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
754 def _cldepth(self, base, klass, count=0):
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
755 if klass is object:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
756 # not found
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
757 return -1
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
758 elif klass is base:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
759 # just found
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
760 return count
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
761 else:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
762 # must recurse
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
763 for c in klass.__bases__:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
764 result = self._cldepth(base, c, count=count+1)
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
765 if result > 0:
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
766 return result
5d9a1b82251a Return the "deepest" subclass; this allows subclassing tincan.Page and
David Barts <n5jrn@me.com>
parents: 18
diff changeset
767 return -1
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
768
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
769 def _redirect(self):
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
770 try:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
771 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
772 forw = '/' + '/'.join(rlist)
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
773 if forw in self._seen:
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
774 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
775 self._seen.add(forw)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
776 rname = rlist.pop()
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
777 except IndexError as e:
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
778 raise TinCanError("{0}: invalid #forward".format(self._urlpath)) from e
11
8037bad7d5a8 Update documentation, fix some #forward bugs.
David Barts <n5jrn@me.com>
parents: 9
diff changeset
779 name, ext = os.path.splitext(rname)
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
780 if _casef(ext) != _TEXTEN:
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
781 raise TinCanError("{0}: invalid #forward".format(self._urlpath))
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
782 self._subdir = rlist
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
783 self._python = name + _PEXTEN
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
784 self._fspath = os.path.join(self._fsroot, *self._subdir, rname)
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
785 self._urlpath = '/' + self.urljoin(*self._subdir, rname)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
786
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
787 def __call__(self):
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
788 """
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
789 This gets called by the framework AFTER the page is launched.
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
790 """
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
791 target = None
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
792 try:
17
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
793 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
794 obj.handle()
35
41da0b3d2156 Rename #include to #load (more descriptive).
David Barts <n5jrn@me.com>
parents: 34
diff changeset
795 tvars = self._loads.copy()
29
2e3ac3d7b0a4 A possible workaround for the drainbamage (needs testing)?
David Barts <n5jrn@me.com>
parents: 26
diff changeset
796 tvars.update(obj.export())
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
797 with self.lock:
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
798 return self._body.render(tvars).lstrip('\n')
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
799 except ForwardException as fwd:
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
800 target = fwd.target
17
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
801 except bottle.HTTPResponse as e:
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
802 return e
8186de188daf Improve the run-time error handling in code-behinds.
David Barts <n5jrn@me.com>
parents: 16
diff changeset
803 except Exception as e:
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
804 self.logger.exception("%s: unexpected exception", self._urlpath)
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
805 raise _hterror(status=500, exception=e)
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
806 if target is None:
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
807 message = "{0}: unexpected null target".format(self._urlpath)
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
808 self.logger.error(message)
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
809 raise _hterror(status=500, exception=TinCanError(message))
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
810 # 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
811 # forward.
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
812 environ = bottle.request.environ
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
813 if _FORIG not in environ:
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
814 environ[_FORIG] = self._urlpath
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
815 if _FLOOP not in environ:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
816 environ[_FLOOP] = set([self._urlpath])
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
817 elif target in environ[_FLOOP]:
70
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
818 message = "{0}: forward loop detected".format(environ[_FORIG])
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
819 self.logger.error(message)
a78c74c73d98 Get rid of bogus "None" messages in the standard error pages.
David Barts <n5jrn@me.com>
parents: 69
diff changeset
820 raise _hterror(status=500, exception=TinCanError(message))
1
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
821 environ[_FLOOP].add(target)
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
822 environ['bottle.raw_path'] = target
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
823 environ['PATH_INFO'] = urllib.parse.quote(target)
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
824 route, args = self._app.router.match(environ)
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
825 environ['route.handle'] = environ['bottle.route'] = route
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
826 environ['route.url_args'] = args
94b36e721500 Another check in to back stuff up.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
827 return route.call(**args)
0
e726fafcffac For backup purposes, UNFINISHED!!
David Barts <n5jrn@me.com>
parents:
diff changeset
828
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
829 # M u t e x
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
830 #
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
831 # A dummy lock class, which is what we use if we don't need locking.
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
832
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
833 class _DummyLock(object):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
834 def acquire(self, blocking=True, timeout=-1):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
835 pass
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
836
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
837 def release(self):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
838 pass
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
839
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
840 def __enter__(self):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
841 self.acquire()
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
842 return self
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
843
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
844 def __exit__(self, exc_type, exc_value, traceback):
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
845 self.release()
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
846 return False
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
847
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
848 # L a u n c h e r
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
849
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
850 _WINF = "WEB-INF"
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
851 _EBANNED = set([_IEXTEN, _TEXTEN, _PEXTEN, _PEXTEN+"c"])
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
852 ENCODING = "utf-8"
53
3a6180c1feea Remove some needless repetition.
David Barts <n5jrn@me.com>
parents: 46
diff changeset
853 _BITBUCKET = logging.getLogger(__name__)
3a6180c1feea Remove some needless repetition.
David Barts <n5jrn@me.com>
parents: 46
diff changeset
854 _BITBUCKET.addHandler(logging.NullHandler)
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
855
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
856 class _Launcher(object):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
857 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
858 Helper class for launching webapps.
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
859 """
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
860 def __init__(self, fsroot, urlroot, multithread):
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
861 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
862 Lightweight constructor. The real action happens in .launch() below.
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
863 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
864 self.fsroot = fsroot
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
865 self.urlroot = urlroot
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
866 self.app = None
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
867 self.errors = 0
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
868 self.debug = False
37
ce67eac10fc7 Allow global character encoding specification.
David Barts <n5jrn@me.com>
parents: 36
diff changeset
869 self.encoding = ENCODING
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
870 self.static = False
43
0bd9b9ae9998 Make it thread-safe.
David Barts <n5jrn@me.com>
parents: 42
diff changeset
871 self.multithread = multithread
53
3a6180c1feea Remove some needless repetition.
David Barts <n5jrn@me.com>
parents: 46
diff changeset
872 self.logger = _BITBUCKET
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
873
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
874 def launch(self):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
875 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
876 Does the actual work of launching something. XXX - modifies sys.path
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
877 and never un-modifies it.
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
878 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
879 # Sanity checks
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
880 if not self.urlroot.startswith("/"):
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
881 self.errors = 1
66
f33cb3e93473 Fix bad log invocations.
David Barts <n5jrn@me.com>
parents: 59
diff changeset
882 self.logger.error("urlroot not absolute: %r", self.urlroot)
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
883 return self
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
884 if not os.path.isdir(self.fsroot):
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
885 self.errors = 1
66
f33cb3e93473 Fix bad log invocations.
David Barts <n5jrn@me.com>
parents: 59
diff changeset
886 self.logger.error("no such directory: %r", self.fsroot)
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
887 return self
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
888 # Make any needed directories. Refuse to launch things that don't
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
889 # contain WEB-INF, to prevent accidental launches of undesired
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
890 # directory trees containing sensitive files.
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
891 winf = os.path.join(self.fsroot, _WINF)
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
892 if not os.path.isdir(winf):
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
893 self.errors = 1
66
f33cb3e93473 Fix bad log invocations.
David Barts <n5jrn@me.com>
parents: 59
diff changeset
894 self.logger.error("no WEB-INF directory in %r", self.fsroot)
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
895 return self
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
896 lib = os.path.join(winf, "lib")
18
e88ab99914cf More improvements to the error reportage.
David Barts <n5jrn@me.com>
parents: 17
diff changeset
897 for i in [ lib ]:
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
898 if not os.path.isdir(i):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
899 os.mkdir(i)
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
900 # Add our private lib directory to sys.path
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
901 sys.path.insert(1, os.path.abspath(lib))
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
902 # Do what we gotta do
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
903 self.app = TinCan()
56
f177756200be Add standard config parameters.
David Barts <n5jrn@me.com>
parents: 55
diff changeset
904 config = self.app.config
f177756200be Add standard config parameters.
David Barts <n5jrn@me.com>
parents: 55
diff changeset
905 config['tincan.fsroot'] = os.path.abspath(self.fsroot)
f177756200be Add standard config parameters.
David Barts <n5jrn@me.com>
parents: 55
diff changeset
906 config['tincan.urlroot'] = self.urlroot
f177756200be Add standard config parameters.
David Barts <n5jrn@me.com>
parents: 55
diff changeset
907 config['tincan.logger'] = self.logger
f177756200be Add standard config parameters.
David Barts <n5jrn@me.com>
parents: 55
diff changeset
908 config['tincan.encoding'] = self.encoding
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
909 self._launch([])
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
910 return self
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
911
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
912 def _launch(self, subdir):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
913 for entry in os.listdir(os.path.join(self.fsroot, *subdir)):
42
8948020c54fd Remove some debug deadwood, ignore hidden files.
David Barts <n5jrn@me.com>
parents: 41
diff changeset
914 if entry.startswith("."):
8948020c54fd Remove some debug deadwood, ignore hidden files.
David Barts <n5jrn@me.com>
parents: 41
diff changeset
915 continue # hidden file
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
916 if not subdir and _casef(entry, "upper") == _WINF:
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
917 continue
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
918 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
919 if S_ISREG(etype):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
920 ename, eext = os.path.splitext(entry)
59
60907204a265 Support case-insensitive filesystems properly.
David Barts <n5jrn@me.com>
parents: 58
diff changeset
921 eext = _casef(eext)
40
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
922 if eext == _TEXTEN:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
923 route = _TinCanRoute(self, ename, subdir)
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
924 else:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
925 if eext in _EBANNED:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
926 continue
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
927 if self.static:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
928 route = _TinCanStaticRoute(self, entry, subdir)
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
929 else:
df27cf08c093 Add support for serving static files.
David Barts <n5jrn@me.com>
parents: 37
diff changeset
930 continue
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
931 try:
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
932 route.launch()
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
933 except TinCanError as e:
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
934 if self.logger.getEffectiveLevel() <= logging.DEBUG:
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
935 self.logger.exception(str(e))
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
936 else:
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
937 self.logger.error(str(e))
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
938 self.errors += 1
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
939 elif S_ISDIR(etype):
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
940 self._launch(subdir + [entry])
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
941
53
3a6180c1feea Remove some needless repetition.
David Barts <n5jrn@me.com>
parents: 46
diff changeset
942 def launch(fsroot=None, urlroot='/', multithread=True, **kwargs):
2
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
943 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
944 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
945 caller's responsibility to call app.run()
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
946 """
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
947 if fsroot is None:
ca6f8ca38cf2 Another backup commit.
David Barts <n5jrn@me.com>
parents: 1
diff changeset
948 fsroot = os.getcwd()
45
969f515b505b Make it easy to leave stdout and stderr alone (untested).
David Barts <n5jrn@me.com>
parents: 44
diff changeset
949 launcher = _Launcher(fsroot, urlroot, multithread)
54
cb5a6e200c95 Detect bad args, too.
David Barts <n5jrn@me.com>
parents: 53
diff changeset
950 allowed = set(["logger", "encoding", "static"])
cb5a6e200c95 Detect bad args, too.
David Barts <n5jrn@me.com>
parents: 53
diff changeset
951 for k, v in kwargs.items():
cb5a6e200c95 Detect bad args, too.
David Barts <n5jrn@me.com>
parents: 53
diff changeset
952 if k not in allowed:
cb5a6e200c95 Detect bad args, too.
David Barts <n5jrn@me.com>
parents: 53
diff changeset
953 raise TypeError("launch() got an unexpected keyword argument {0!r}".format(k))
cb5a6e200c95 Detect bad args, too.
David Barts <n5jrn@me.com>
parents: 53
diff changeset
954 setattr(launcher, k, v)
4
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
955 launcher.launch()
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
956 return launcher.app, launcher.errors
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
957
0d47859f792a Finally got "hello, world" working. Still likely many bugs.
David Barts <n5jrn@me.com>
parents: 3
diff changeset
958 # 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
959 # launcher script for why.