# HG changeset patch # User David Barts # Date 1558057342 25200 # Node ID 560c8fb55e4a1d867cea1c853fa55d898ad50bde # Parent 9d0497dc19f873ffc086efdf48e894814aeab3fc Fix bugs in #python directive, make code-behind class loading simpler. diff -r 9d0497dc19f8 -r 560c8fb55e4a tincan.py --- a/tincan.py Thu May 16 12:04:20 2019 -0700 +++ b/tincan.py Thu May 16 18:42:22 2019 -0700 @@ -384,10 +384,13 @@ if oheader.hidden and not oheader.errors: return # Get the code-behind #python - if self._header.python is not None: + if self._header.python is None: + self._python_specified = False + else: if not self._header.python.endswith(_PEXTEN): raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN)) self._python = self._header.python + self._python_specified = True # Obtain a class object by importing and introspecting a module. self._getclass() # Build body object (#template) @@ -440,11 +443,16 @@ raise TinCanError(str(e)) from e def _getclass(self): - pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python))) + try: + pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python))) + except IndexError as e: + raise TinCanError("{0}: invalid #python".format(self._urlpath)) from e klass = ErrorPage if self._header.errors else Page # Give 'em a default code-behind if they don't furnish one pytime = self._gettime(pypath) if not pytime: + if self._python_specified: + raise TinCanError("{0}: #python file not found".format(self._urlpath)) self._class = klass return # Else load the code-behind from a .py file @@ -463,18 +471,14 @@ spec.loader.exec_module(mod) except Exception as e: raise TinCanError("{0}: error importing".format(pycpath)) from e + # Locate a suitable class self._class = None for i in dir(mod): v = getattr(mod, i) - if isclass(v) and issubclass(v, klass): - if self._class is None: - self._class = v - else: - if self._class is klass: - self._class = v - elif v is not klass: - raise TinCanError("{0}: contains multiple {1} classes".format( - pypath, klass.__name__)) + if isclass(v) and issubclass(v, klass) and v is not klass: + if self._class is not None: + raise TinCanError("{0}: contains multiple {1} classes".format(pypath, klass.__name__)) + self._class = v if self._class is None: raise TinCanError("{0}: contains no {1} classes".format(pypath, klass.__name__))