Skip to content

gh-122102: Fix/improve docs of descriptor-related tools in inspect #122104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
41 changes: 27 additions & 14 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -513,19 +513,18 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
.. function:: ismethoddescriptor(object)

Return ``True`` if the object is a method descriptor, but not if
:func:`ismethod`, :func:`isclass`, :func:`isfunction` or :func:`isbuiltin`
are true.
:func:`isclass`, :func:`ismethod` or :func:`isfunction` are true.
Copy link
Member

Choose a reason for hiding this comment

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

Should we say "X, Y or Z is true" or "X, Y or Z are true"? @python/proofreaders


This, for example, is true of ``int.__add__``. An object passing this test
has a :meth:`~object.__get__` method, but not a :meth:`~object.__set__`
method or a :meth:`~object.__delete__` method. Beyond that, the set of
attributes varies. A :attr:`~definition.__name__` attribute is usually
sensible, and :attr:`!__doc__` often is.

Methods implemented via descriptors that also pass one of the other tests
return ``False`` from the :func:`ismethoddescriptor` test, simply because the
other tests promise more -- you can, e.g., count on having the
:attr:`~method.__func__` attribute (etc) when an object passes
Method descriptors that also pass any of the other tests mentioned above
(:func:`isclass`, :func:`ismethod` or :func:`isfunction`) make this function
Copy link
Member

Choose a reason for hiding this comment

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

It's a bit weird to say "mentioned above" and re-mention them. Either you mention them again (which I wouldn't suggest) or you just say "mentioned above".

return ``False``, simply because the other tests promise more -- you can, e.g.,
count on having the :attr:`~method.__func__` attribute when an object passes
:func:`ismethod`.

.. versionchanged:: 3.13
Expand All @@ -536,15 +535,29 @@ attributes (see :ref:`import-mod-attrs` for module attributes):

.. function:: isdatadescriptor(object)

Return ``True`` if the object is a data descriptor.
Return ``True`` if the object is a data descriptor, but not if
:func:`isclass`, :func:`ismethod` or :func:`isfunction` are true.

Data descriptors have a :attr:`~object.__set__` or a :attr:`~object.__delete__` method.
Examples are properties (defined in Python), getsets, and members. The
latter two are defined in C and there are more specific tests available for
those types, which is robust across Python implementations. Typically, data
descriptors will also have :attr:`~definition.__name__` and :attr:`!__doc__` attributes
(properties, getsets, and members have both of these attributes), but this is
not guaranteed.
Data descriptors always have a :meth:`~object.__set__` method and/or
a :meth:`~object.__delete__` method. Optionally, they may also have a
:meth:`~object.__get__` method.

Examples of data descriptors are properties (see: :func:`property`), getset
descriptors and member descriptors. Note that for the latter two (which can
be defined only at the C level, in extension modules) more specific tests
are available: :func:`isgetsetdescriptor` and :func:`ismemberdescriptor`,
respectively.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
descriptors and member descriptors. Note that for the latter two (which can
be defined only at the C level, in extension modules) more specific tests
are available: :func:`isgetsetdescriptor` and :func:`ismemberdescriptor`,
respectively.
descriptors and member descriptors. Note that specific tests robust
across different Python implementations are available for the latter
two (defined in C extension modules), namely :func:`isgetsetdescriptor`
and :func:`ismemberdescriptor` respectively.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I find the fragment about those two specific tests being robust across different Python implementations quite problematic: in a way, those two tests can be considered less robust across different Python implementations. (If I needed a test returning true for, e.g., a frame's f_locals regardless of the Python implementation being used, I'd use isdatadescriptor() rather than isgetsetdescriptor()...).

That's why I propose to remove that fragment.

IMHO, it causes more confusion than good.

Also I believe that the statement that those two tests are more specific, together with the docs of those two tests themselves, convey enough information.


Typically, data descriptors have also :attr:`~definition.__name__` and
:attr:`!__doc__` attributes (properties, getsets and member descriptors have
them), but this is not guaranteed.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Typically, data descriptors have also :attr:`~definition.__name__` and
:attr:`!__doc__` attributes (properties, getsets and member descriptors have
them), but this is not guaranteed.
While data descriptors such as properties, getsets or member descriptors
have :attr:`~definition.__name__` and :attr:`!__doc__` attributes, this
is not necessarily the case in general.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe that the fragment referring to properties, getsets or member descriptors should remain in parentheses, as it conveys here only additional (extra) information, not the crucial one (which is that, in general, the presence of the data descriptors' attributes __name__ and __doc__ is likely but optional).


.. versionchanged:: 3.8
Now this function reports objects with only a :meth:`~object.__set__` method
as being data descriptors (the presence of :meth:`~object.__get__` is no
longer required for that). Moreover, objects with :meth:`~object.__delete__`,
but not :meth:`~object.__set__`, are now properly recognized as data
descriptors as well (formerly, they were not).


.. function:: isgetsetdescriptor(object)
Expand Down
15 changes: 11 additions & 4 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,10 @@ def ismethoddescriptor(object):
often is.

Methods implemented via descriptors that also pass one of the other
tests return false from the ismethoddescriptor() test, simply because
the other tests promise more -- you can, e.g., count on having the
__func__ attribute (etc) when an object passes ismethod()."""
tests (ismethod(), isclass() or isfunction()) make this function
return false, simply because those other tests promise more -- you
can, e.g., count on having the __func__ attribute when an object
passes ismethod()."""
if isclass(object) or ismethod(object) or isfunction(object):
# mutual exclusion
return False
Expand All @@ -327,8 +328,14 @@ def ismethoddescriptor(object):
def isdatadescriptor(object):
"""Return true if the object is a data descriptor.

But not if ismethod() or isclass() or isfunction() are true.

Data descriptors have a __set__ or a __delete__ attribute. Examples are
properties (defined in Python) and getsets and members (defined in C).
properties, getsets and members. For the latter two (which can be
defined only at the C level, in extension modules) more specific tests
are available as well: isgetsetdescriptor() and ismemberdescriptor(),
respectively.

Typically, data descriptors will also have __name__ and __doc__ attributes
(properties, getsets, and members have both of these attributes), but this
is not guaranteed."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix and improve the descriptions (and docstrings) of
:func:`inspect.ismethoddescriptor` and :func:`inspect.isdatadescriptor`.
Patch by Jan Kaliszewski.
Loading