# HG changeset patch # User David Barts # Date 1558399338 25200 # Node ID 5d9a1b82251aa857244472e6c2a6daae42afa833 # Parent e88ab99914cfa89a05f4af0270ae475b29c5d9c2 Return the "deepest" subclass; this allows subclassing tincan.Page and making a standard base page class for a webapp. diff -r e88ab99914cf -r 5d9a1b82251a tincan.py --- 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: