Mercurial > cgi-bin > hgweb.cgi > tincan
comparison tincan.py @ 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 | df27cf08c093 |
comparison
equal
deleted
inserted
replaced
36:4ed261056057 | 37:ce67eac10fc7 |
---|---|
192 | 192 |
193 class ChameleonTemplate(bottle.BaseTemplate): | 193 class ChameleonTemplate(bottle.BaseTemplate): |
194 def prepare(self, **options): | 194 def prepare(self, **options): |
195 from chameleon import PageTemplate, PageTemplateFile | 195 from chameleon import PageTemplate, PageTemplateFile |
196 if self.source: | 196 if self.source: |
197 self.tpl = PageTemplate(self.source, encoding=self.encoding, | 197 self.tpl = PageTemplate(self.source, **options) |
198 **options) | |
199 else: | 198 else: |
200 self.tpl = PageTemplateFile(self.filename, encoding=self.encoding, | 199 self.tpl = PageTemplateFile(self.filename, encoding=self.encoding, |
201 search_path=self.lookup, **options) | 200 search_path=self.lookup, **options) |
201 # XXX - work around broken Chameleon decoding | |
202 self.tpl.default_encoding = self.encoding | |
202 | 203 |
203 def render(self, *args, **kwargs): | 204 def render(self, *args, **kwargs): |
204 for dictarg in args: | 205 for dictarg in args: |
205 kwargs.update(dictarg) | 206 kwargs.update(dictarg) |
206 _defaults = self.defaults.copy() | 207 _defaults = self.defaults.copy() |
352 raise ValueError("file does not end in {0}".format(_IEXTEN)) | 353 raise ValueError("file does not end in {0}".format(_IEXTEN)) |
353 | 354 |
354 # Using a cache is likely to help efficiency a lot, since many pages | 355 # Using a cache is likely to help efficiency a lot, since many pages |
355 # will typically #load the same standard stuff. | 356 # will typically #load the same standard stuff. |
356 _tcache = {} | 357 _tcache = {} |
357 def _get_template(name, direct): | 358 def _get_template(name, direct, coding): |
358 aname = os.path.abspath(os.path.join(direct, name)) | 359 aname = os.path.abspath(os.path.join(direct, name)) |
359 if aname not in _tcache: | 360 if aname not in _tcache: |
360 tmpl = ChameleonTemplate(name=name, lookup=[direct]) | 361 tmpl = ChameleonTemplate(name=name, lookup=[direct], encoding=coding) |
361 tmpl.prepare() | 362 tmpl.prepare() |
362 assert aname == tmpl.filename | 363 assert aname == tmpl.filename |
363 _tcache[aname] = tmpl | 364 _tcache[aname] = tmpl |
364 return _tcache[aname] | 365 return _tcache[aname] |
365 | 366 |
423 self._origin = self._urlpath | 424 self._origin = self._urlpath |
424 self._subdir = subdir | 425 self._subdir = subdir |
425 self._seen = set() | 426 self._seen = set() |
426 self._app = launcher.app | 427 self._app = launcher.app |
427 self._save_loads = launcher.debug | 428 self._save_loads = launcher.debug |
429 self._encoding = launcher.encoding | |
428 | 430 |
429 def launch(self): | 431 def launch(self): |
430 """ | 432 """ |
431 Launch a single page. | 433 Launch a single page. |
432 """ | 434 """ |
478 tfile = TemplateFile(tpath) | 480 tfile = TemplateFile(tpath) |
479 except OSError as e: | 481 except OSError as e: |
480 raise TinCanError("{0}: invalid #template: {1!s}".format(self._urlpath, e)) from e | 482 raise TinCanError("{0}: invalid #template: {1!s}".format(self._urlpath, e)) from e |
481 except IndexError as e: | 483 except IndexError as e: |
482 raise TinCanError("{0}: invalid #template".format(self._urlpath)) from e | 484 raise TinCanError("{0}: invalid #template".format(self._urlpath)) from e |
483 self._body = ChameleonTemplate(source=tfile.body) | 485 self._body = ChameleonTemplate(source=tfile.body, encoding=self._encoding) |
484 else: | 486 else: |
485 self._body = ChameleonTemplate(source=self._template.body) | 487 self._body = ChameleonTemplate(source=self._template.body, encoding=self._encoding) |
486 self._body.prepare() | 488 self._body.prepare() |
487 # Process loads | 489 # Process loads |
488 self._loads = {} | 490 self._loads = {} |
489 for load in self._header.load: | 491 for load in self._header.load: |
490 try: | 492 try: |
494 if load.in_lib: | 496 if load.in_lib: |
495 fdir = os.path.join(self._fsroot, _WINF, "tlib") | 497 fdir = os.path.join(self._fsroot, _WINF, "tlib") |
496 else: | 498 else: |
497 fdir = os.path.join(self._fsroot, *self._subdir) | 499 fdir = os.path.join(self._fsroot, *self._subdir) |
498 try: | 500 try: |
499 tmpl = _get_template(load.fname, fdir) | 501 tmpl = _get_template(load.fname, fdir, self._encoding) |
500 except Exception as e: | 502 except Exception as e: |
501 raise TinCanError("{0}: bad #load: {1!s}".format(self._urlpath, e)) from e | 503 raise TinCanError("{0}: bad #load: {1!s}".format(self._urlpath, e)) from e |
502 self._loads[load.vname] = tmpl.tpl | 504 self._loads[load.vname] = tmpl.tpl |
503 # If this is an #errors page, register it as such. | 505 # If this is an #errors page, register it as such. |
504 if oheader.errors is not None: | 506 if oheader.errors is not None: |
523 errors = [ int(i) for i in rerrors.split() ] | 525 errors = [ int(i) for i in rerrors.split() ] |
524 except ValueError as e: | 526 except ValueError as e: |
525 raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e | 527 raise TinCanError("{0}: bad #errors line".format(self._urlpath)) from e |
526 if not errors: | 528 if not errors: |
527 errors = range(_ERRMIN, _ERRMAX+1) | 529 errors = range(_ERRMIN, _ERRMAX+1) |
528 route = _TinCanErrorRoute(ChameleonTemplate(source=self._template.body), | 530 route = _TinCanErrorRoute( |
531 ChameleonTemplate(source=self._template.body, encoding=self._encoding), | |
529 self._loads, self._class) | 532 self._loads, self._class) |
530 for error in errors: | 533 for error in errors: |
531 if error < _ERRMIN or error > _ERRMAX: | 534 if error < _ERRMIN or error > _ERRMAX: |
532 raise TinCanError("{0}: bad #errors code".format(self._urlpath)) | 535 raise TinCanError("{0}: bad #errors code".format(self._urlpath)) |
533 self._app.error_handler[error] = route # XXX | 536 self._app.error_handler[error] = route # XXX |
674 | 677 |
675 # L a u n c h e r | 678 # L a u n c h e r |
676 | 679 |
677 _WINF = "WEB-INF" | 680 _WINF = "WEB-INF" |
678 _BANNED = set([_WINF]) | 681 _BANNED = set([_WINF]) |
682 ENCODING = "utf-8" | |
679 | 683 |
680 class _Launcher(object): | 684 class _Launcher(object): |
681 """ | 685 """ |
682 Helper class for launching webapps. | 686 Helper class for launching webapps. |
683 """ | 687 """ |
689 self.urlroot = urlroot | 693 self.urlroot = urlroot |
690 self.logger = logger | 694 self.logger = logger |
691 self.app = None | 695 self.app = None |
692 self.errors = 0 | 696 self.errors = 0 |
693 self.debug = False | 697 self.debug = False |
698 self.encoding = ENCODING | |
694 | 699 |
695 def launch(self): | 700 def launch(self): |
696 """ | 701 """ |
697 Does the actual work of launching something. XXX - modifies sys.path | 702 Does the actual work of launching something. XXX - modifies sys.path |
698 and never un-modifies it. | 703 and never un-modifies it. |
749 | 754 |
750 def _logger(message): | 755 def _logger(message): |
751 sys.stderr.write(message) | 756 sys.stderr.write(message) |
752 sys.stderr.write('\n') | 757 sys.stderr.write('\n') |
753 | 758 |
754 def launch(fsroot=None, urlroot='/', logger=_logger, debug=False): | 759 def launch(fsroot=None, urlroot='/', logger=_logger, debug=False, encoding=ENCODING): |
755 """ | 760 """ |
756 Launch and return a TinCan webapp. Does not run the app; it is the | 761 Launch and return a TinCan webapp. Does not run the app; it is the |
757 caller's responsibility to call app.run() | 762 caller's responsibility to call app.run() |
758 """ | 763 """ |
759 if fsroot is None: | 764 if fsroot is None: |
760 fsroot = os.getcwd() | 765 fsroot = os.getcwd() |
761 launcher = _Launcher(fsroot, urlroot, logger) | 766 launcher = _Launcher(fsroot, urlroot, logger) |
762 launcher.debug = debug | 767 launcher.debug = debug |
768 launcher.encoding = encoding | |
763 launcher.launch() | 769 launcher.launch() |
764 return launcher.app, launcher.errors | 770 return launcher.app, launcher.errors |
765 | 771 |
766 # XXX - We cannot implement a command-line launcher here; see the | 772 # XXX - We cannot implement a command-line launcher here; see the |
767 # launcher script for why. | 773 # launcher script for why. |