Search code examples
pythonpython-2.7lxmlabc

class attribute considered abstract method in python 2.7 - abc module


I am trying to implement an abstract superclass (Base) with an abstract method (addfeature), which the Child class will override.

from lxml.builder import ElementMaker
from abc import ABCMeta, abstractmethod

class Base(object):
    __metaclass__ = ABCMeta

    ns = "http://www.foo.com/bar"
    em = ElementMaker(namespace=ns, nsmap={'bar': ns})

    @abstractmethod
    def addfeature(self):
        pass

class Child(Base):
    def addfeature(self):
        pass

child_instance = Child()

This code fails however with

"TypeError: Can't instantiate abstract class Child with abstract methods em"

Why? em is supposed to be a class attribute, not a method (and surely not an abstract method)


Solution

  • ABCMeta check whether the method is abstract or not by using __isabstractmethod__ attribute. The lxml.builder.ElementMaker dynamically generate a method (using __getattr__); access to __isabstractmethod__ confuse the ABCMeta.

    >>> ns = "http://www.foo.com/bar"
    >>> em = ElementMaker(namespace=ns, nsmap={'bar': ns})
    >>> em.child_element
    <functools.partial object at 0x0000000002B55598>
    >>> em.child_element()
    <Element {http://www.foo.com/bar}child_element at 0x27a8828>
    >>> em.__isabstractmethod__
    <functools.partial object at 0x0000000002B55598>
    >>> bool(em.__isabstractmethod__)
    True
    

    By assigning __isabstractmethod__ as a False, you can work around it.

    class Base(object):
        __metaclass__ = ABCMeta
    
        ns = "http://www.foo.com/bar"
        em = ElementMaker(namespace=ns, nsmap={'bar': ns})
        em.__isabstractmethod__ = False  # <-----
    
        @abstractmethod
        def addfeature(self):
            pass