New submission from Arn Vollebregt (KPN) <kpn.arn.vollebr...@gmail.com>:

When concretely implementing an abstract ABC class with an abstract property 
getter, setter and deleter it is not enfored that the setter and deleter are 
implemented. Instead, the property is treated as a read-only property (as would 
normally be the case without a setter/deleter definition for a property) and 
the setter/deleter code from the abstract class is not present in the child 
class.

I would expect a TypeError exception when an abstract property is defined with 
a getter, setter and deleter but only the getter is implemented in a subclass 
(as is the case when not implementing the property getter). As a fallback, I 
would find it acceptable the code from the abstract class to be present in the 
child class, so at least the code that is defined there (in this case raising a 
NotImplementedError exception) would be executed.

An interactive interpreter session to replicate this behavior:

arn@hacktop:~$ python3
Python 3.7.5 (default, Nov 20 2019, 09:21:52)
[GCC 9.2.1 20191008] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import abc
>>>
>>> # Define the (abstract) interface.
... class MyInterface(abc.ABC):
...
...     # Property getter.
...     @property
...     @abc.abstractmethod
...     def myProperty(self) -> str:
...         raise NotImplementedError
...
...     # Property setter.
...     @myProperty.setter
...     @abc.abstractmethod
...     def myProperty(self, value: str) -> None:
...         raise NotImplementedError
...
...     # Property deleter.
...     @myProperty.deleter
...     @abc.abstractmethod
...     def myProperty(self) -> None:
...         raise NotImplementedError
...
>>> # Implemented the interface.
... class MyImplementation(MyInterface):
...     
...     # No abstract method implementation(s).
...     pass
...
>>> # Creation of MyImplementation object raises TypeError as expected.
... obj = MyImplementation()
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: Can't instantiate abstract class MyImplementation with abstract 
methods myProperty
>>> import dis
>>> # The property getter code would raise an exception as defined in 
>>> MyInterface.
... dis.dis(MyImplementation.myProperty.fget.__code__.co_code)
          0 LOAD_GLOBAL              0 (0)
          2 RAISE_VARARGS            1
          4 LOAD_CONST               0 (0)
          6 RETURN_VALUE
>>> # The property setter code would raise an exception as defined in 
>>> MyInterface.
... dis.dis(MyImplementation.myProperty.fset.__code__.co_code)
          0 LOAD_GLOBAL              0 (0)
          2 RAISE_VARARGS            1
          4 LOAD_CONST               0 (0)
          6 RETURN_VALUE
>>> # The property deleter code would raise an exception as defined in 
>>> MyInterface.
... dis.dis(MyImplementation.myProperty.fdel.__code__.co_code)
          0 LOAD_GLOBAL              0 (0)
          2 RAISE_VARARGS            1
          4 LOAD_CONST               0 (0)
          6 RETURN_VALUE
>>> # Let's reimplement with only the property getter.
... class MyImplementation(MyInterface):
...
...     # Only implement abstract property getter.
...     @property
...     def myProperty(self) -> str:
...         return "foobar"
...
>>> # Object can be created (against expectations).
... obj = MyImplementation()
>>> # The property getter works as defined.
... obj.myProperty
'foobar'
>>> # The property cannot be set (read-only).
... obj.myProperty = "barfoo"
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: can't set attribute
>>> # The property cannot be deleted (read-only).
... del obj.myProperty
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: can't delete attribute
>>> # The property getter code returns a string as defined in MyImplementation.
... type(MyImplementation.myProperty.fget)
<class 'function'>
>>> dis.dis(MyImplementation.myProperty.fget.__code__.co_code)
          0 LOAD_CONST               1 (1)
          2 RETURN_VALUE
>>> # The property setter code however does not exist, although defined in 
>>> MyInterface.
... type(MyImplementation.myProperty.fset)
<class 'NoneType'>
>>> # Nor does the property deleter code, although defined in MyInterface.
... type(MyImplementation.myProperty.fdel)
<class 'NoneType'>

----------
messages: 362403
nosy: arn.vollebregt.kpn
priority: normal
severity: normal
status: open
title: Abstract property setter/deleter implementation not enforced.
type: behavior
versions: Python 3.7

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue39707>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to