diff tincan.py @ 19:5d9a1b82251a draft before-template-changes

Return the "deepest" subclass; this allows subclassing tincan.Page and making a standard base page class for a webapp.
author David Barts <n5jrn@me.com>
date Mon, 20 May 2019 17:42:18 -0700
parents e88ab99914cf
children ca2029ce95c7
line wrap: on
line diff
--- a/tincan.py	Mon May 20 08:40:56 2019 -0700
+++ b/tincan.py	Mon May 20 17:42:18 2019 -0700
@@ -489,16 +489,43 @@
             spec.loader.exec_module(mod)
         except Exception as e:
             raise TinCanError("{0}: error importing: {1!s}".format(pycpath, e)) from e
-        # Locate a suitable class
+        # Locate a suitable class. We look for the "deepest" class object
+        # we can find in the inheritance tree.
         self._class = None
+        score = -1
+        ambig = False
         for i in dir(mod):
             v = getattr(mod, i)
-            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__))
+            if not isclass(v):
+                continue
+            d = self._cldepth(klass, v)
+            if d > score:
                 self._class = v
+                score = d
+                ambig = False
+            elif d == score:
+                ambig = True
         if self._class is None:
             raise TinCanError("{0}: contains no {1} classes".format(pypath, klass.__name__))
+        if ambig:
+            raise TinCanError("{0}: contains ambiguous {1} classes".format(pypath, klass.__name__))
+
+    # This might fail for complex inheritance schemes from the classes of
+    # interest (so don't use them!).
+    def _cldepth(self, base, klass, count=0):
+        if klass is object:
+            # not found
+            return -1
+        elif klass is base:
+            # just found
+            return count
+        else:
+            # must recurse
+            for c in klass.__bases__:
+                result = self._cldepth(base, c, count=count+1)
+                if result > 0:
+                    return result
+            return -1
 
     def _redirect(self):
         try: