Create an abstract class property decorator

Viewed 150

I'm interested in creating a @classproperty decorator that can be chained together with the @abstractmethod decorator on a user-defined ABC class.

The desired functionality is:

  1. Raise TypeError when attempting to access the abstract class property from a non-instantiated ABC class.
  2. Raise TypeError when attempting to instantiate a concrete subclass that does not have the abstract class property implemented.
  3. Raise TypeError when attempting to access the abstract class property from a non-instantiated concrete subclass.

The TypeError referred to above should be the same type error coming from @abstractmethod (Ex. TypeError: Can't instantiate abstract class ConcreteNotImplemented with abstract methods abstract_class_property)

from abc import ABC, abstractmethod

class classproperty(property):
    """
    A decorator that decorates class attributes. This means that it works off classes attributes instead of instance variables.

    See Also:
        property

    References:
        SqlAlchemy
    """

    def __init__(self, fget, *arg, **kw):
        super(classproperty, self).__init__(fget, *arg, **kw)
        self.__doc__ = fget.__doc__

    def __get__(desc, self, cls):
        return desc.fget(cls)

    __isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None)  # Potentially some manipulation here??


class MyAbstractClass(ABC):

    @classproperty
    def non_abstract_class_property(self) -> str:
        """Example of classproperty with no abstract method. Works as desired."""
        return "value 1"

    @classproperty
    @abstractmethod
    def abstract_class_property(self) -> str:
        """
        Desired Syntax - Wrong Functionality The @abstractmethod has no effect on this classproperty and subclass is allowed to be instantiated and accessed.
        """


class ConcreteNotImplemented(MyAbstractClass):
    """
    Expected that 'MyAbstractClass' would force me to implement 'abstract_class_property' and raise the abstractmethod TypeError:
    (TypeError: Can't instantiate abstract class ConcreteNotImplemented with abstract methods abstract_class_property)
    but does not and simply returns None.
    """


print("MyAbstractBaseClass TESTS")
print(MyAbstractClass.non_abstract_class_property)
print(MyAbstractClass.abstract_class_property)  # Expected: TypeError (case 1)
print()

print("ConcreteNotImplemented TESTS")
print(ConcreteNotImplemented.non_abstract_class_property)
print(ConcreteNotImplemented().abstract_class_property)  # Expected: TypeError (case 2)
print(ConcreteNotImplemented.abstract_class_property)  # Expected: TypeError (case 3)
print(ConcreteNotImplemented())  # Expected: TypeError (symptom of case 1)

Output:

MyAbstractBaseClass TESTS
value 1
None

ConcreteNotImplemented TESTS
value 1
None
None
<__main__.ConcreteNotImplemented object at 0x000001693BB79730>

After poking around in the Python property implementation, I found __isabstractmethod__ and threw that on my classproperty implementation hoping that it would resolve some of the desired functionality. Potentially some more manipulation would get me closer.

0 Answers
Related