[Python-Dev] Can we improve support for abstract base classes with desciptors

Nick Coghlan ncoghlan at gmail.com
Wed Jun 8 17:55:28 CEST 2011


On Thu, Jun 9, 2011 at 1:01 AM, Darren Dale <dsdale24 at gmail.com> wrote:
[snip excellent analysis of the problem]

I have some suggestions regarding a few details of your current code,
but your basic proposal looks sound to me.

I would tweak __new__ along the following lines though:

   def __new__(mcls, name, bases, namespace):
       cls = super().__new__(mcls, name, bases, namespace)
       # Compute set of abstract method names

       # CHANGE 1: refactor descriptor and abstract method scan to
happen in a single pass
       def is_descriptor(value):
           return (hasattr(value, '__get__') or hasattr(value, '__set__')
                     or hasattr(value, '__delete__'))
       def is_abstract(value):
           return getattr(value, "__isabstractmethod__", False)
       def get_abstract_names_for_item(item):
           name, value = item
           if is_abstract(value):
               return [name]
           elif is_descriptor(value):
               # CHANGE 2: Deliberately ignore descriptors on the
descriptor objects
               # CHANGE 3: Use new-style string formatting
               return ['{}.{}'.format(name, attr) for attr in
dir(value) if is_abstract(getattr(value, attr))]
           return []
       def get_abstract_names(ns):
           names = []
           for item in ns.items():
               names.extend(get_abstract_names_for_item(item))
           return names

       abstract_names = get_abstract_names(namespace.items())

       for base in bases:
           for name in getattr(base, "__abstractmethods__", ()):
               # CHANGE 4: Using rpartition better tolerates weird
naming in the metaclass
               # (weird naming in descriptors will still blow up in
the earlier search for abstract names)
               descr_name, is_descr, attr = name.rpartition('.')
               if is_descr:
                   # base class identified a descriptor abstract method:
                   descr = getattr(cls, descr_name, None)
                   val = getattr(descr, attr, None)
               else:
                   val = getattr(cls, name, None)
               if val is None or is_abstract(val):
                   abstract_names.append(name)

       cls.__abstractmethods__ = frozenset(abstract_names)

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list