In Python, how to enforce an abstract method to be static on the child class?
Question: This is the setup I want: A should be an abstract base class with a static &
abstract method f(). B should inherit from A. Requirements: 1\. You should not
be able to instantiate A 2\. You should not be able to instantiate B, unless
it implements a static f()

Taking inspiration from
[this](http://stackoverflow.com/questions/4474395/staticmethod-and-abc-
abstractmethod-will-it-blend) question, I've tried a couple of approaches.
With these definitions:

    
    
    class abstractstatic(staticmethod):
        __slots__ = ()
        def __init__(self, function):
            super(abstractstatic, self).__init__(function)
            function.__isabstractmethod__ = True
        __isabstractmethod__ = True
    
    class A:
        __metaclass__ = abc.ABCMeta
        @abstractstatic
        def f():
            pass
    
    class B(A):
        def f(self):
            print 'f'
    
    class A2:
        __metaclass__ = abc.ABCMeta
        @staticmethod
        @abc.abstractmethod
        def f():
            pass
    
    class B2(A2):
        def f(self):
            print 'f'
    

Here A2 and B2 are defined using usual Python conventions and A & B are
defined using the way suggested in
[this](http://stackoverflow.com/questions/4474395/staticmethod-and-abc-
abstractmethod-will-it-blend) answer. Following are some operations I tried
and the results that were undesired.

With classes A/B:

    
    
    >>> B().f()
    f
    #This should have thrown, since B doesn't implement a static f()
    

With classes A2/B2:

    
    
    >>> A2()
    <__main__.A2 object at 0x105beea90>
    #This should have thrown since A2 should be an uninstantiable abstract class
    
    >>> B2().f()
    f
    #This should have thrown, since B2 doesn't implement a static f()
    

Since neither of these approaches give me the output I want, how do I achieve
what I want?


Answer: You can't do what you want with just `ABCMeta`. ABC enforcement doesn't do
_any_ type checking, only the presence of _an attribute_ with the correct name
is enforced.

Take for example:

    
    
    >>> from abc import ABCMeta, abstractmethod, abstractproperty
    >>> class Abstract(object):
    ...     __metaclass__ = ABCMeta
    ...     @abstractmethod
    ...     def foo(self): pass
    ...     @abstractproperty
    ...     def bar(self): pass
    ... 
    >>> class Concrete(Abstract):
    ...     foo = 'bar'
    ...     bar = 'baz'
    ... 
    >>> Concrete()
    <__main__.Concrete object at 0x104b4df90>
    

I was able to construct `Concrete()` even though both `foo` and `bar` are
simple attributes.

The `ABCMeta` metaclass only tracks how many objects are left with the
`__isabstractmethod__` attribute being true; when creating a class from the
metaclass (`ABCMeta.__new__` is called) the `cls.__abstractmethods__`
attribute is then set to a `frozenset` object with all the names that are
still abstract.

`type.__new__` then tests for that `frozenset` and throws a `TypeError` if you
try to create an instance.

You'd have to produce your _own_ `__new__` method here; subclass `ABCMeta` and
add type checking in a new `__new__` method. That method should look for
`__abstractmethods__` sets on the base classes, find the corresponding objects
with the `__isabstractmethod__` attribute in the MRO, then does typechecking
on the current class attributes.

This'd mean that you'd throw the exception when defining the _class_ , not an
instance, however. For that to work you'd add a `__call__` method to your
`ABCMeta` subclass and have that throw the exception based on information
gathered by your own `__new__` method about what types were wrong; a similar
two-stage process as what `ABCMeta` and `type.__new__` do at the moment.
Alternatively, update the `__abstractmethods__` set on the class to add any
names that were implemented but with the wrong type and leave it to
`type.__new__` to throw the exception.

The following implementation takes that last tack; add names back to
`__abstractmethods__` if the implemented type doesn't match (using a mapping):

    
    
    from types import FunctionType
    
    class ABCMetaTypeCheck(ABCMeta):
        _typemap = {  # map abstract type to expected implementation type
            abstractproperty: property,
            abstractstatic: staticmethod,
            # abstractmethods return function objects
            FunctionType: FunctionType,
        }
        def __new__(mcls, name, bases, namespace):
            cls = super(ABCMetaTypeCheck, mcls).__new__(mcls, name, bases, namespace)
            wrong_type = set()
            seen = set()
            abstractmethods = cls.__abstractmethods__
            for base in bases:
                for name in getattr(base, "__abstractmethods__", set()):
                    if name in seen or name in abstractmethods:
                        continue  # still abstract or later overridden
                    value = base.__dict__.get(name)  # bypass descriptors
                    if getattr(value, "__isabstractmethod__", False):
                        seen.add(name)
                        expected = mcls._typemap[type(value)]
                        if not isinstance(namespace[name], expected):
                            wrong_type.add(name)
            if wrong_type:
                cls.__abstractmethods__ = abstractmethods | frozenset(wrong_type)
            return cls
    

With this metaclass you get your expected output:

    
    
    >>> class Abstract(object):
    ...     __metaclass__ = ABCMetaTypeCheck
    ...     @abstractmethod
    ...     def foo(self): pass
    ...     @abstractproperty
    ...     def bar(self): pass
    ...     @abstractstatic
    ...     def baz(): pass
    ... 
    >>> class ConcreteWrong(Abstract):
    ...     foo = 'bar'
    ...     bar = 'baz'
    ...     baz = 'spam'
    ... 
    >>> ConcreteWrong()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: Can't instantiate abstract class ConcreteWrong with abstract methods bar, baz, foo
    >>> 
    >>> class ConcreteCorrect(Abstract):
    ...     def foo(self): return 'bar'
    ...     @property
    ...     def bar(self): return 'baz'
    ...     @staticmethod
    ...     def baz(): return  'spam'
    ... 
    >>> ConcreteCorrect()
    <__main__.ConcreteCorrect object at 0x104ce1d10>
    

