Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2037,12 +2037,15 @@ Basic customization
.. index:: single: __len__() (mapping object method)

Called to implement truth value testing and the built-in operation
``bool()``; should return ``False`` or ``True``. When this method is not
:func:`bool`; should return ``False`` or ``True``. When this method is not
defined, :meth:`~object.__len__` is called, if it is defined, and the object is
considered true if its result is nonzero. If a class defines neither
:meth:`!__len__` nor :meth:`!__bool__`, all its instances are considered
true.

Two successive calls to :meth:`!__bool__` on the same object must
return same value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not if the object is mutated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that means - some other method was called on the object in between. Thus, calls to __bool__ weren't actually successive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The object could have been mutated by another thread between the two calls...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by another thread

... that calls some other object's method

between the two calls...

:)

Copy link
Contributor Author

@skirpichev skirpichev Sep 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, how about this: "The __bool__ method can't mutate any objects."? (Ditto for __len__.)

This probably is a more strong requirement than actually need in current optimizations, but I doubt it blocks something useful.

Comment on lines +2046 to +2047
Copy link
Contributor

@ncoghlan ncoghlan Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Two successive calls to :meth:`!__bool__` on the same object must
return same value.
Note: to help optimize logical expressions, implementations are permitted to assume
that calls to :meth:`!__bool__` will not mutate that object, nor any other object.
While this expected invariant is not explicitly enforced, failing to abide
by it will result in implementation dependent runtime behaviour. This
implementation dependent behaviour may also be encountered when mutable
objects are shared across threads without appropriate synchronization.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure if this needs to be explicitly documented as the behavior is dependent on the user's choice of implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is dependent on the user's choice of implementation.

Rather on bugs in the user code, if someone will implement __bool__(), doing crazy things (see issue). Added docs say that implementation may assume certain behaviour from the user code, just as for the __hash__() method (same hash value for equal objects - the invariant, which we also can't enforce, user code might break this).

PS: I think that part of discussion rather belongs to the issue thread, which has some other arguments on why we want document this. See e.g. this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the note warn against side effects in general (like I/O), not just mutation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the note warn against side effects in general (like I/O), not just mutation?

Side effects, including in fact object mutation - are fine, unless they break idempotence.

But I think we don't loose something practically relevant if just forbid mutation of any objects in __bool__() (a shortened version of @ncoghlan suggestion):

Suggested change
Two successive calls to :meth:`!__bool__` on the same object must
return same value.
Calls to :meth:!__bool__` shouldn't mutate any objects.



.. _attribute-access:

Expand Down Expand Up @@ -2947,6 +2950,9 @@ through the object's keys; for sequences, it should iterate through the values.
:meth:`~object.__bool__` method and whose :meth:`!__len__` method returns zero is
considered to be false in a Boolean context.

Two successive calls to :meth:`!__len__` on the same object must
return same value.
Comment on lines +2953 to +2954
Copy link
Contributor

@ncoghlan ncoghlan Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Two successive calls to :meth:`!__len__` on the same object must
return same value.
Note: to help optimize logical expressions, implementations are permitted to assume
that calls to :meth:`!__len__` will not mutate that object, nor any other object.
While this expected invariant is not explicitly enforced, failing to abide
by it will result in implementation dependent runtime behaviour. This
implementation dependent behaviour may also be encountered when mutable
objects are shared across threads without appropriate synchronization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a typo (__len__ -> __bool__). But if we are going with this lengthly wording - I think it's better just point to the __bool__ docs.


.. impl-detail::

In CPython, the length is required to be at most :data:`sys.maxsize`.
Expand Down
Loading