[pypy-issue] Issue #2039: delattr() and del can raise TypeError when CPython would raise AttributeError (pypy/pypy)

Jason Madden issues-reply at bitbucket.org
Tue May 5 14:21:55 CEST 2015


New issue 2039: delattr() and del can raise TypeError when CPython would raise AttributeError
https://bitbucket.org/pypy/pypy/issue/2039/delattr-and-del-can-raise-typeerror-when

Jason Madden:

Given an instance whose class contains a data descriptor that does not implement `__delete__`, using `delattr()` (or `del`) on that descriptor raises `TypeError` under PyPy, but `AttributeError` under CPython. 

CPython (2.7 here, but the same thing happens with 3.4):
```
$ python
Python 2.7.9 (default, Dec 13 2014, 15:13:49)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class FieldProperty(object):
...     def __get__(self, inst, klass):
...         if inst is None: return self
...         return 42
...     def __set__(self, inst, value):
...         pass
...
>>> class WithProperty(object):
...     field = FieldProperty()
...
>>> with_property = WithProperty()
>>> delattr(with_property, 'field')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __delete__
```

PyPy:
```
$ pypy
Python 2.7.9 (9c4588d731b7, Mar 23 2015, 16:20:40)
[PyPy 2.5.1 with GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>> class FieldProperty(object):
....     def __get__(self, inst, klass):
....         if inst is None: return self
....         return 42
....     def __set__(self, inst, value):
....         pass
....
....
>>>> class WithProperty(object):
....     field = FieldProperty()
....
....
>>>> with_property = WithProperty()
>>>> delattr(with_property, 'field')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'FieldProperty' object is not a descriptor with delete
```

We ran into this testing a large Zope-based application where these kind of descriptors are common. It can break code expecting to catch only `AttributeError`.

As far as I can tell, the Python language spec is silent on what happens in this scenario. The closest I've found is when it gives this example in [section 3.4.2.3](https://docs.python.org/2/reference/datamodel.html#invoking-descriptors):

> Instance Binding
> 
> If binding to a new-style object instance, `a.x` is transformed into the call: `type(a).__dict__['x'].__get__(a, type(a))`.

That seems to suggest the `AttributeError` is to be expected. An `AttributeError` is also least surprising because that's what happens in both implementations when deleting a standard `@property` that doesn't specify a `__delete__`, or when deleting an attribute that just doesn't exist:

PyPy, but CPython behaves the same:
```
>>>> class WithBuiltinProperty(object):
....     @property
....     def x(self):
....         return 1
....
....
>>>> with_builtin_property = WithBuiltinProperty()
>>>> delattr(with_builtin_property, 'x')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
>>>> delattr(with_builtin_property, 'dne')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'WithBuiltinProperty' object has no attribute 'dne'
```





More information about the pypy-issue mailing list