Skip to content

Commit

Permalink
pythongh-122102: Fix/improve docs + tests of descriptor tools in `ins…
Browse files Browse the repository at this point in the history
…pect`
  • Loading branch information
zuo committed Jul 21, 2024
1 parent 0dcbc83 commit 5e7ce51
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 71 deletions.
54 changes: 33 additions & 21 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -513,20 +513,19 @@ 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.

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
:func:`ismethod`.
Objects implemented as descriptors that also pass one of the other tests
(:func:`isclass`, :func:`ismethod` or :func:`isfunction`) make this function
return ``False``, simply because the other tests promise more -- you can,
e.g., count on having the :attr:`~method.__func__` attribute (etc.) when an
object passes :func:`ismethod`.

.. versionchanged:: 3.13
This function no longer incorrectly reports objects with :meth:`~object.__get__`
Expand All @@ -536,35 +535,48 @@ 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 include *properties* (see: :func:`property`),
*getset descriptors* and *member descriptors* (for the latter two, more
specific tests are available as well: :func:`isgetsetdescriptor` and
:func:`ismemberdescriptor`, respectively).

Typically, data descriptors have also :attr:`~definition.__name__` and
:attr:`!__doc__` attributes (*properties*, *getsets*, and *members* have
both of them), but this is not guaranteed.

.. 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)

Return ``True`` if the object is a getset descriptor.
Return ``True`` if the object is a *getset descriptor*.

.. impl-detail::

getsets are attributes defined in extension modules via
*getsets* are attributes defined in extension modules via
:c:type:`PyGetSetDef` structures. For Python implementations without such
types, this method will always return ``False``.


.. function:: ismemberdescriptor(object)

Return ``True`` if the object is a member descriptor.
Return ``True`` if the object is a *member descriptor*.

.. impl-detail::

Member descriptors are attributes defined in extension modules via
*Member descriptors* are attributes defined in extension modules via
:c:type:`PyMemberDef` structures. For Python implementations without such
types, this method will always return ``False``.

Expand Down Expand Up @@ -1511,11 +1523,11 @@ but avoids executing code when it fetches attributes.
.. versionadded:: 3.2

:func:`getattr_static` does not resolve descriptors, for example slot descriptors or
getset descriptors on objects implemented in C. The descriptor object
*getset* descriptors on objects implemented in C. The descriptor object
is returned instead of the underlying attribute.

You can handle these with code like the following. Note that
for arbitrary getset descriptors invoking these may trigger
for arbitrary *getset* descriptors invoking these may trigger
code execution::

# example code for resolving the builtin descriptor types
Expand Down
14 changes: 10 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 (etc.) when an
object passes ismethod()."""
if isclass(object) or ismethod(object) or isfunction(object):
# mutual exclusion
return False
Expand All @@ -327,8 +328,13 @@ 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, 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
Loading

0 comments on commit 5e7ce51

Please sign in to comment.