On Thu, Jun 9, 2011 at 1:01 AM, Darren Dale <dsdale24@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@gmail.com | Brisbane, Australia