changeset 15:560c8fb55e4a draft

Fix bugs in #python directive, make code-behind class loading simpler.
author David Barts <n5jrn@me.com>
date Thu, 16 May 2019 18:42:22 -0700
parents 9d0497dc19f8
children 448fc3d534f8
files tincan.py
diffstat 1 files changed, 15 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- 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__))