comparison tincan.py @ 5:31bb8400e6e3 draft

Add #end header, fix #errors.
author David Barts <n5jrn@me.com>
date Mon, 13 May 2019 14:47:04 -0700
parents 0d47859f792a
children a3823da7bb45
comparison
equal deleted inserted replaced
4:0d47859f792a 5:31bb8400e6e3
12 import importlib 12 import importlib
13 from inspect import isclass 13 from inspect import isclass
14 import io 14 import io
15 import py_compile 15 import py_compile
16 from stat import S_ISDIR, S_ISREG 16 from stat import S_ISDIR, S_ISREG
17 from string import whitespace
17 18
18 import bottle 19 import bottle
19 20
20 # E x c e p t i o n s 21 # E x c e p t i o n s
21 22
67 depending on the selected templating engine. The body part has 68 depending on the selected templating engine. The body part has
68 each header line replaced by a blank line. This preserves the overall 69 each header line replaced by a blank line. This preserves the overall
69 line numbering when processing the body. The added newlines are normally 70 line numbering when processing the body. The added newlines are normally
70 stripped out before the rendered page is sent back to the client. 71 stripped out before the rendered page is sent back to the client.
71 """ 72 """
73 _END = "#end"
74 _LEND = len(_END)
75 _WS = set(whitespace)
76
72 def __init__(self, raw, encoding='utf-8'): 77 def __init__(self, raw, encoding='utf-8'):
73 if isinstance(raw, io.TextIOBase): 78 if isinstance(raw, io.TextIOBase):
74 self._do_init(raw) 79 self._do_init(raw)
75 elif isinstance(raw, str): 80 elif isinstance(raw, str):
76 with open(raw, "r", encoding=encoding) as fp: 81 with open(raw, "r", encoding=encoding) as fp:
93 def _header(self, line): 98 def _header(self, line):
94 if not line.startswith('#'): 99 if not line.startswith('#'):
95 self._state = self._body 100 self._state = self._body
96 self._state(line) 101 self._state(line)
97 return 102 return
103 if line.startswith(self._END) and (len(line) == self._LEND or line[self._LEND] in self._WS):
104 self._state = self._body
98 self._hbuf.append(line) 105 self._hbuf.append(line)
99 self._bbuf.append("\n") 106 self._bbuf.append("\n")
100 107
101 def _body(self, line): 108 def _body(self, line):
102 self._bbuf.append(line) 109 self._bbuf.append(line)
127 if not line.startswith("#"): 134 if not line.startswith("#"):
128 raise TemplateHeaderException("Does not start with '#'.", count) 135 raise TemplateHeaderException("Does not start with '#'.", count)
129 try: 136 try:
130 rna, rpa = line.split(maxsplit=1) 137 rna, rpa = line.split(maxsplit=1)
131 except ValueError: 138 except ValueError:
132 raise TemplateHeaderException("Missing parameter.", count) 139 rna = line.rstrip()
140 rpa = None
133 # Get name, ignoring remarks. 141 # Get name, ignoring remarks.
134 name = rna[1:] 142 name = rna[1:]
135 if name == "rem": 143 if name == "rem":
136 continue 144 continue
145 if name == "end":
146 break
137 if name not in nameset: 147 if name not in nameset:
138 raise TemplateHeaderException("Invalid directive: {0!r}".format(rna), count) 148 raise TemplateHeaderException("Invalid directive: {0!r}".format(rna), count)
139 if name in seen: 149 if name in seen:
140 raise TemplateHeaderException("Duplicate {0!r} directive.".format(rna), count) 150 raise TemplateHeaderException("Duplicate {0!r} directive.".format(rna), count)
141 seen.add(name) 151 seen.add(name)
142 # Flags 152 # Flags
143 if name in self._FLAGS: 153 if name in self._FNAMES:
144 setattr(self, name, True) 154 setattr(self, name, True)
145 continue 155 continue
146 # Get parameter 156 # Get parameter
157 if rpa is None:
158 raise TemplateHeaderException("Missing parameter.", count)
147 param = rpa.strip() 159 param = rpa.strip()
148 for i in [ "'", '"']: 160 for i in [ "'", '"']:
149 if param.startswith(i) and param.endswith(i): 161 if param.startswith(i) and param.endswith(i):
150 param = ast.literal_eval(param) 162 param = ast.literal_eval(param)
151 break 163 break
292 def __init__(self, template): 304 def __init__(self, template):
293 self._template = template 305 self._template = template
294 self._template.prepare() 306 self._template.prepare()
295 307
296 def __call__(self, e): 308 def __call__(self, e):
297 return self._template.render(e=e, request=bottle.request).lstrip('\n') 309 return self._template.render(error=e, request=bottle.request).lstrip('\n')
298 310
299 class _TinCanRoute(object): 311 class _TinCanRoute(object):
300 """ 312 """
301 A route created by the TinCan launcher. 313 A route created by the TinCan launcher.
302 """ 314 """
382 errors = range(_ERRMIN, _ERRMAX+1) 394 errors = range(_ERRMIN, _ERRMAX+1)
383 route = _TinCanErrorRoute(self._tclass(source=self._template.body)) 395 route = _TinCanErrorRoute(self._tclass(source=self._template.body))
384 for error in errors: 396 for error in errors:
385 if error < _ERRMIN or error > _ERRMAX: 397 if error < _ERRMIN or error > _ERRMAX:
386 raise TinCanError("{0}: bad #errors code".format(self._urlpath)) 398 raise TinCanError("{0}: bad #errors code".format(self._urlpath))
387 self._app.error(code=error, callback=route) 399 self._app.error_handler[error] = route # XXX
388 400
389 def _getclass(self): 401 def _getclass(self):
390 pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python))) 402 pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python)))
391 pycpath = pypath + 'c' 403 pycpath = pypath + 'c'
392 try: 404 try: