Mailing List Archive

__dunder__ = None
There is a special handling of `__hash__` set to None in the interpreter
core. This is because every class inherited the `__hash__` attribute
from "object", and setting `__hash__ = None` is a simple way to make it
unhashable. It makes hash() raising the correct type of exception
(TypeError), but with unhelpful error message "'NoneType' object is not
callable". The special case was added to make the error message more
relevant: "unhashable type: '{typename}'".

There is similar situation with other special methods defined in
"object" or other common classes. Sometimes we want to cancel the
default inherited behavior.

>>> dir(object)
[.'__class__', '__delattr__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__']

I propose to support officially the idiom "__dunder__ = None" and add
special cases to raise more specialized exception instead of "TypeError:
'NoneType' object is not callable" for most of special method where
cancelling the default behavior makes sense (for example I do not think
that we need better error message for `__repr__ = None`).

The question is how to interpret value None:

* Always raise TypeError (with changed message)? This is what happen
currently when you set the method to None, this is the most compatible
option.
* Always raise an error, but allow to change it to more appropriate type
(for example AttributeError for __setattr__)?
* Interpret value None the same way as an absent attribute?

For `__hash__` or `__class_getitem__` all three options mean the same.
But absent `__mro_entries__` and `__mro_entries__ = None` currently give
different results. It is even more complicated for pickling: absent
`__reduce_ex__` and `__reduce_ex__ = None` mean the same in the Python
implementation, but give different results in the C implementation.

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/YGAK34DRWJFSIV2VZ4NC2J24XO37GCMM/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: __dunder__ = None [ In reply to ]
It actually is documented as being supported here: https://docs.python.org/3.10/reference/datamodel.html#id2, and as mentioned there __iter__(), __reversed__() and __contains__() also have special support so they avoid trying to fallback to __getitem__ etc. Also some discussion at https://github.com/python/cpython/issues/70146. For most methods the conclusion back then makes sense, a specific exception message being raised would just potentially slow the interpreter.

- Spencer

On 1 May 2022, at 3:22 pm, Serhiy Storchaka <storchaka@gmail.com> wrote:

?There is a special handling of `__hash__` set to None in the interpreter core. This is because every class inherited the `__hash__` attribute from "object", and setting `__hash__ = None` is a simple way to make it unhashable. It makes hash() raising the correct type of exception (TypeError), but with unhelpful error message "'NoneType' object is not callable". The special case was added to make the error message more relevant: "unhashable type: '{typename}'".

There is similar situation with other special methods defined in "object" or other common classes. Sometimes we want to cancel the default inherited behavior.

>>> dir(object)
[.'__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

I propose to support officially the idiom "__dunder__ = None" and add special cases to raise more specialized exception instead of "TypeError: 'NoneType' object is not callable" for most of special method where cancelling the default behavior makes sense (for example I do not think that we need better error message for `__repr__ = None`).

The question is how to interpret value None:

* Always raise TypeError (with changed message)? This is what happen currently when you set the method to None, this is the most compatible option.
* Always raise an error, but allow to change it to more appropriate type (for example AttributeError for __setattr__)?
* Interpret value None the same way as an absent attribute?

For `__hash__` or `__class_getitem__` all three options mean the same. But absent `__mro_entries__` and `__mro_entries__ = None` currently give different results. It is even more complicated for pickling: absent `__reduce_ex__` and `__reduce_ex__ = None` mean the same in the Python implementation, but give different results in the C implementation.

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/YGAK34DRWJFSIV2VZ4NC2J24XO37GCMM/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: __dunder__ = None [ In reply to ]
01.05.22 13:02, Spencer Brown ????:
> It actually is documented as being supported here:
> https://docs.python.org/3.10/reference/datamodel.html#id2
> <https://docs.python.org/3.10/reference/datamodel.html#id2>, and as
> mentioned there __iter__(), __reversed__() and __contains__() also have
> special support so they avoid trying to fallback to __getitem__ etc.
> Also some discussion at https://github.com/python/cpython/issues/70146
> <https://github.com/python/cpython/issues/70146>. For most methods the
> conclusion back then makes sense, a specific exception message being
> raised would just potentially slow the interpreter.

Thank you Spencer. It answers my questions.

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/LNE2COY3OAV7EJOMPBFBHSVPBTYM4KUQ/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: __dunder__ = None [ In reply to ]
On 01/05/2022 06:20, Serhiy Storchaka wrote:
> The question is how to interpret value None:
>
> * Always raise TypeError (with changed message)? This is what happen
> currently when you set the method to None, this is the most compatible
> option.
> * Always raise an error, but allow to change it to more appropriate
> type (for example AttributeError for __setattr__)?
> * Interpret value None the same way as an absent attribute?
What about binary operators (__add__, __eq__, etc)? Should they act as
if they'd returned NotImplemented? Or immediately unconditionally raise
a TypeError?
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/PLWKIT7FWXLKIGQXL3X5GFT3MGTC53R3/
Code of Conduct: http://python.org/psf/codeofconduct/
Re: __dunder__ = None [ In reply to ]
Non should behave as closely as possible to it not being defined at all. So
return NotImplemented.

On Sun, May 1, 2022 at 09:53 Patrick Reader <_@pxeger.com> wrote:

> On 01/05/2022 06:20, Serhiy Storchaka wrote:
> > The question is how to interpret value None:
> >
> > * Always raise TypeError (with changed message)? This is what happen
> > currently when you set the method to None, this is the most compatible
> > option.
> > * Always raise an error, but allow to change it to more appropriate
> > type (for example AttributeError for __setattr__)?
> > * Interpret value None the same way as an absent attribute?
> What about binary operators (__add__, __eq__, etc)? Should they act as
> if they'd returned NotImplemented? Or immediately unconditionally raise
> a TypeError?
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-leave@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-dev@python.org/message/PLWKIT7FWXLKIGQXL3X5GFT3MGTC53R3/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
--
--Guido (mobile)
Re: __dunder__ = None [ In reply to ]
01.05.22 19:32, Guido van Rossum ????:
> Non should behave as closely as possible to it not being defined at all.
> So return NotImplemented.

But, as was noted by Spencer Brown, it is not how it works for
__iter__(), __reversed__() and __contains__() (which have a special path
for producing explicit error), and not how it currently works for
__add__() and __eq__() (although they raise TypeError implicitly). It is
all documented inhttps://docs.python.org/3.10/reference/datamodel.html#id2

If we are going to reconsider this, it is a large breaking change.

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/V5XNDNA3I3C5VAARBDSSF72ILYWKVI2M/
Code of Conduct: http://python.org/psf/codeofconduct/