TL;DR:
cowlinator is correct, order counts.
Formally:
According to the formal documentation for the abstract base class ABC
When abstractmethod() is applied in combination with other method
descriptors, it should be applied as the innermost decorator, as shown
in the following usage examples:...
(bold is mine for emphasis: not only is ordering important, but abstractmethod
must be innermost)
Not-too-satisfactory explanation:
Decorators like @property
and @abstractmethod
are functions that wrap the functions (methods) they decorate. So two decorators result in nested wrappings, with the one closest to the function definition (the lowest in your editor) being the innermost wrapping function.
In the @property
decorator, the __isabstractmethod__
internal attribute is declared as a property itself, with only a getter, no setter - making it readonly. This is from the python code for property
in builtins.py
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
The first arg is the getter, the second the setter - None in this case - see the constructor here if you don't believe me:
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
In the @abstractmethod
wrapping the __isabstractmethod__
attribute is set to True
. See here:
funcobj.__isabstractmethod__ = True
That's from the abc
module, where funcobj
is the method being decorated. Now, usually that's your would-be-abstract method, but in this case, it's the property decorator function wrapping the original method. And as we saw above, that function made __isabstractmethod__
readonly.
I say "not-too-satisfactory" because there is no proper justification of this - just obscure documentation that you won't find until you run into the problem. It's really just a limitation of the way decorators work I guess.
For extra background, I tracked this change here in GitHub (gotta love open-source!) which includes the update to these release notes, which state - also without a lot of explanation:
abc.abstractproperty has been deprecated, use property with
abc.abstractmethod() instead.
For deep-deep source of the exception you got, you can see the C source code for python descriptors here and search down for 'is not writable' to see where it throws the exception while trying to generate the setter.