changeset 37:ce67eac10fc7 draft header-includes

Allow global character encoding specification.
author David Barts <n5jrn@me.com>
date Tue, 28 May 2019 17:08:36 -0700
parents 4ed261056057
children 336bc2f622e4
files launch tincan.py
diffstat 2 files changed, 21 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/launch	Tue May 28 15:54:47 2019 -0700
+++ b/launch	Tue May 28 17:08:36 2019 -0700
@@ -7,7 +7,7 @@
 
 import os, sys
 from argparse import ArgumentParser
-from tincan import launch
+from tincan import launch, ENCODING
 
 # V a r i a b l e s
 
@@ -17,14 +17,16 @@
 
 parser = ArgumentParser(prog=sys.argv[0], usage="%(prog)s [options] [directory [path]]")
 opt = parser.add_argument
-opt("-b", "--bind", default="localhost", help="address to bind to")
+opt("-b", "--bind", default="localhost", help="address to bind to (default: localhost)")
 opt("-d", "--debug", action="store_true", help="enable debug mode")
+opt("-e", "--encoding", default=ENCODING, help="encoding to use (default {0})".format(ENCODING))
 opt("-f", "--force", action="store_true", help="do not abort on errors")
-opt("-p", "--port", default=8080, help="port to listen on")
+opt("-p", "--port", default=8080, help="port to listen on (default: 8080)")
 opt("directory", default=".", help="directory to serve", nargs='?')
 opt("path", default="/", help="URL path to serve", nargs='?')
 args = parser.parse_args(sys.argv[1:])
-app, errors = launch(fsroot=args.directory, urlroot=args.path, debug=args.debug)
+app, errors = launch(fsroot=args.directory, urlroot=args.path, debug=args.debug,
+    encoding=args.encoding)
 if errors:
     action = "continuing" if args.force else "aborting"
     sys.stderr.write("{0}: {1} error{2} detected, {3}\n".format(
--- a/tincan.py	Tue May 28 15:54:47 2019 -0700
+++ b/tincan.py	Tue May 28 17:08:36 2019 -0700
@@ -194,11 +194,12 @@
     def prepare(self, **options):
         from chameleon import PageTemplate, PageTemplateFile
         if self.source:
-            self.tpl = PageTemplate(self.source, encoding=self.encoding,
-                **options)
+            self.tpl = PageTemplate(self.source, **options)
         else:
             self.tpl = PageTemplateFile(self.filename, encoding=self.encoding,
                 search_path=self.lookup, **options)
+            # XXX - work around broken Chameleon decoding
+            self.tpl.default_encoding = self.encoding
 
     def render(self, *args, **kwargs):
         for dictarg in args:
@@ -354,10 +355,10 @@
 # Using a cache is likely to help efficiency a lot, since many pages
 # will typically #load the same standard stuff.
 _tcache = {}
-def _get_template(name, direct):
+def _get_template(name, direct, coding):
     aname = os.path.abspath(os.path.join(direct, name))
     if aname not in _tcache:
-        tmpl = ChameleonTemplate(name=name, lookup=[direct])
+        tmpl = ChameleonTemplate(name=name, lookup=[direct], encoding=coding)
         tmpl.prepare()
         assert aname == tmpl.filename
         _tcache[aname] = tmpl
@@ -425,6 +426,7 @@
         self._seen = set()
         self._app = launcher.app
         self._save_loads = launcher.debug
+        self._encoding = launcher.encoding
 
     def launch(self):
         """
@@ -480,9 +482,9 @@
                 raise TinCanError("{0}: invalid #template: {1!s}".format(self._urlpath, e)) from e
             except IndexError as e:
                 raise TinCanError("{0}: invalid #template".format(self._urlpath)) from e
-            self._body = ChameleonTemplate(source=tfile.body)
+            self._body = ChameleonTemplate(source=tfile.body, encoding=self._encoding)
         else:
-            self._body = ChameleonTemplate(source=self._template.body)
+            self._body = ChameleonTemplate(source=self._template.body, encoding=self._encoding)
         self._body.prepare()
         # Process loads
         self._loads = {}
@@ -496,7 +498,7 @@
             else:
                 fdir = os.path.join(self._fsroot, *self._subdir)
             try:
-                tmpl = _get_template(load.fname, fdir)
+                tmpl = _get_template(load.fname, fdir, self._encoding)
             except Exception as e:
                 raise TinCanError("{0}: bad #load: {1!s}".format(self._urlpath, e)) from e
             self._loads[load.vname] = tmpl.tpl
@@ -525,7 +527,8 @@
             raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e
         if not errors:
             errors = range(_ERRMIN, _ERRMAX+1)
-        route = _TinCanErrorRoute(ChameleonTemplate(source=self._template.body),
+        route = _TinCanErrorRoute(
+            ChameleonTemplate(source=self._template.body, encoding=self._encoding),
             self._loads, self._class)
         for error in errors:
             if error < _ERRMIN or error > _ERRMAX:
@@ -676,6 +679,7 @@
 
 _WINF = "WEB-INF"
 _BANNED = set([_WINF])
+ENCODING = "utf-8"
 
 class _Launcher(object):
     """
@@ -691,6 +695,7 @@
         self.app = None
         self.errors = 0
         self.debug = False
+        self.encoding = ENCODING
 
     def launch(self):
         """
@@ -751,7 +756,7 @@
     sys.stderr.write(message)
     sys.stderr.write('\n')
 
-def launch(fsroot=None, urlroot='/', logger=_logger, debug=False):
+def launch(fsroot=None, urlroot='/', logger=_logger, debug=False, encoding=ENCODING):
     """
     Launch and return a TinCan webapp. Does not run the app; it is the
     caller's responsibility to call app.run()
@@ -760,6 +765,7 @@
         fsroot = os.getcwd()
     launcher = _Launcher(fsroot, urlroot, logger)
     launcher.debug = debug
+    launcher.encoding = encoding
     launcher.launch()
     return launcher.app, launcher.errors