From dcc561b95a5f701ac6523b6c63e39d41807f9113 Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Wed, 12 Jun 2024 05:33:47 +0200 Subject: [PATCH 01/12] gh-120381: Fix inspect.ismethoddescriptor() The inspect.ismethoddescriptor() function did not check for the lack of __delete__() and, consequently, erroneously returned True when applied to *data* descriptors with __get__() and __delete__(). --- Doc/library/inspect.rst | 6 +- Lib/inspect.py | 11 +- Lib/test/test_inspect/test_inspect.py | 110 +++++++++++++++++- ...-06-12-04-44-13.gh-issue-120381.GwTGOV.rst | 2 + 4 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 7130faa4b5b696..1a6b8648c0eadb 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -504,9 +504,9 @@ attributes (see :ref:`import-mod-attrs` for module attributes): 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, but beyond that the set of attributes varies. A - :attr:`~definition.__name__` attribute is usually + 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 diff --git a/Lib/inspect.py b/Lib/inspect.py index 5570a43ebfea19..0c18afddab9457 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -301,9 +301,10 @@ def ismethoddescriptor(object): But not if ismethod() or isclass() or isfunction() are true. This is new in Python 2.2, and, for example, is true of int.__add__. - An object passing this test has a __get__ attribute but not a __set__ - attribute, but beyond that the set of attributes varies. __name__ is - usually sensible, and __doc__ often is. + An object passing this test has a __get__ attribute, but not a + __set__ attribute or a __delete__ attribute. Beyond that, the set + of attributes varies; __name__ is usually sensible, and __doc__ + often is. Methods implemented via descriptors that also pass one of the other tests return false from the ismethoddescriptor() test, simply because @@ -313,7 +314,9 @@ def ismethoddescriptor(object): # mutual exclusion return False tp = type(object) - return hasattr(tp, "__get__") and not hasattr(tp, "__set__") + if hasattr(tp, "__set__") or hasattr(tp, "__delete__"): + return False + return hasattr(tp, "__get__") def isdatadescriptor(object): """Return true if the object is a data descriptor. diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 0a4fa9343f15e0..d6bff875723750 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -53,9 +53,8 @@ # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, # isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, # getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, -# getclasstree, getargvalues, formatargvalues, -# currentframe, stack, trace, isdatadescriptor, -# ismethodwrapper +# getclasstree, getargvalues, formatargvalues, currentframe, +# stack, trace, ismethoddescriptor, isdatadescriptor, ismethodwrapper # NOTE: There are some additional tests relating to interaction with # zipimport in the test_zipimport_support test module. @@ -177,6 +176,7 @@ def test_excluding_predicates(self): self.istest(inspect.ismethod, 'git.argue') self.istest(inspect.ismethod, 'mod.custom_method') self.istest(inspect.ismodule, 'mod') + self.istest(inspect.ismethoddescriptor, 'int.__add__') self.istest(inspect.isdatadescriptor, 'collections.defaultdict.default_factory') self.istest(inspect.isgenerator, '(x for x in range(2))') self.istest(inspect.isgeneratorfunction, 'generator_function_example') @@ -1700,6 +1700,110 @@ def test_typing_replacement(self): self.assertEqual(inspect.formatannotation(ann1), 'Union[List[testModule.typing.A], int]') +class TestIsMethodDescriptor(unittest.TestCase): + + def test_custom_descriptors(self): + class MethodDescriptor: + def __get__(self, *_): pass + class MethodDescriptorSub(MethodDescriptor): + pass + class DataDescriptorWithNoGet: + def __set__(self, *_): pass + class DataDescriptorWithGetSet: + def __get__(self, *_): pass + def __set__(self, *_): pass + class DataDescriptorWithGetDelete: + def __get__(self, *_): pass + def __delete__(self, *_): pass + class DataDescriptorSub(DataDescriptorWithNoGet, + DataDescriptorWithGetDelete, + MethodDescriptor): + pass + self.assertTrue( + inspect.ismethoddescriptor(MethodDescriptor()), + '__get__ and no __set__/__delete__ => method descriptor') + self.assertTrue( + inspect.ismethoddescriptor(MethodDescriptorSub()), + '__get__ (inherited) and no __set__/__delete__' + ' => method descriptor') + self.assertFalse( + inspect.ismethoddescriptor(DataDescriptorWithNoGet()), + '__set__ (and no __get__) => not a method descriptor') + self.assertFalse( + inspect.ismethoddescriptor(DataDescriptorWithGetSet()), + '__get__ and __set__ => not a method descriptor') + self.assertFalse( + inspect.ismethoddescriptor(DataDescriptorWithGetDelete()), + '__get__ and __delete__ => not a method descriptor') + self.assertFalse( + inspect.ismethoddescriptor(DataDescriptorSub()), + '__get__, __set__ and __delete__ => not a method descriptor') + # Note: descriptor classes themselves are *not* descriptors. + self.assertFalse(inspect.ismethoddescriptor(MethodDescriptor)) + self.assertFalse(inspect.ismethoddescriptor(MethodDescriptorSub)) + + def test_builtin_descriptors(self): + builtin_slot_wrapper = int.__add__ # This one is mentioned in docs. + class Owner: + def instance_method(self): pass + @classmethod + def class_method(cls): pass + @staticmethod + def static_method(): pass + @property + def a_property(self): pass + def function(): + pass + a_lambda = lambda: None + class Slotted: + __slots__ = 'foo', + # Example builtin method descriptors: + self.assertTrue( + inspect.ismethoddescriptor(builtin_slot_wrapper), + 'a builtin slot wrapper is a method descriptor') + self.assertTrue( + inspect.ismethoddescriptor(Owner.__dict__['class_method']), + 'a classmethod object is a method descriptor') + self.assertTrue( + inspect.ismethoddescriptor(Owner.__dict__['static_method']), + 'a staticmethod object is a method descriptor') + # Example builtin data descriptors: + self.assertFalse( + inspect.ismethoddescriptor(Slotted.foo), + 'a slot is not a method descriptor') + self.assertFalse( + inspect.ismethoddescriptor(Owner.a_property), + 'a property is not a method descriptor') + # `types.MethodType`/`types.FunctionType` instances (they *are* + # method descriptors, but `ismethoddescriptor()` explicitly + # excludes them): + self.assertFalse(inspect.ismethoddescriptor(Owner().instance_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner().class_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner().static_method)) + self.assertFalse(inspect.ismethoddescriptor(function)) + self.assertFalse(inspect.ismethoddescriptor(a_lambda)) + + def test_descriptor_being_a_class(self): + class MethodDescriptorMeta(type): + def __get__(self, *_): pass + class ClassBeingMethodDescriptor(metaclass=MethodDescriptorMeta): + pass + # `ClassBeingMethodDescriptor` itself *is* a method descriptor, + # but it is *also* a class, and `ismethoddescriptor()` explicitly + # excludes classes. + self.assertFalse( + inspect.ismethoddescriptor(ClassBeingMethodDescriptor), + 'classes (instances of type) are explicitly excluded') + + def test_non_descriptors(self): + class Test: + pass + self.assertFalse(inspect.ismethoddescriptor(Test())) + self.assertFalse(inspect.ismethoddescriptor(Test)) + self.assertFalse(inspect.ismethoddescriptor([42])) + self.assertFalse(inspect.ismethoddescriptor(42)) + + class TestIsDataDescriptor(unittest.TestCase): def test_custom_descriptors(self): diff --git a/Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst b/Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst new file mode 100644 index 00000000000000..20628275b1155a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst @@ -0,0 +1,2 @@ +Correct ``inspect.ismethoddescriptor`` to check also for the lack of +``__delete__``. Patch by Jan Kaliszewski. From 2dc216813166ea893896dff2a6b03f7b9d3f7b25 Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Wed, 12 Jun 2024 11:54:25 +0200 Subject: [PATCH 02/12] Fix the blurb --- .../next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst | 2 -- .../next/Library/2024-06-12-11-54-05.gh-issue-120381.O-BNLs.rst | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst create mode 100644 Misc/NEWS.d/next/Library/2024-06-12-11-54-05.gh-issue-120381.O-BNLs.rst diff --git a/Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst b/Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst deleted file mode 100644 index 20628275b1155a..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-06-12-04-44-13.gh-issue-120381.GwTGOV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Correct ``inspect.ismethoddescriptor`` to check also for the lack of -``__delete__``. Patch by Jan Kaliszewski. diff --git a/Misc/NEWS.d/next/Library/2024-06-12-11-54-05.gh-issue-120381.O-BNLs.rst b/Misc/NEWS.d/next/Library/2024-06-12-11-54-05.gh-issue-120381.O-BNLs.rst new file mode 100644 index 00000000000000..44f49bc19a4c99 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-12-11-54-05.gh-issue-120381.O-BNLs.rst @@ -0,0 +1,2 @@ +Correct :func:`inspect.ismethoddescriptor` to check also for the lack of +:meth:`~object.__delete__`. Patch by Jan Kaliszewski. From 52f579f95702eeac66f0a5eafe8d1a835edfff92 Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sat, 15 Jun 2024 12:12:25 +0200 Subject: [PATCH 03/12] Clean up/improve/refactor tests --- Lib/test/test_inspect/test_inspect.py | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index fc613c953b5c8c..1835bf5b12f551 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1819,9 +1819,10 @@ class DataDescriptorWithGetDelete: def __get__(self, *_): pass def __delete__(self, *_): pass class DataDescriptorSub(DataDescriptorWithNoGet, - DataDescriptorWithGetDelete, - MethodDescriptor): + DataDescriptorWithGetDelete): pass + + # Custom method descriptors: self.assertTrue( inspect.ismethoddescriptor(MethodDescriptor()), '__get__ and no __set__/__delete__ => method descriptor') @@ -1829,6 +1830,8 @@ class DataDescriptorSub(DataDescriptorWithNoGet, inspect.ismethoddescriptor(MethodDescriptorSub()), '__get__ (inherited) and no __set__/__delete__' ' => method descriptor') + + # Custom data descriptors: self.assertFalse( inspect.ismethoddescriptor(DataDescriptorWithNoGet()), '__set__ (and no __get__) => not a method descriptor') @@ -1841,9 +1844,11 @@ class DataDescriptorSub(DataDescriptorWithNoGet, self.assertFalse( inspect.ismethoddescriptor(DataDescriptorSub()), '__get__, __set__ and __delete__ => not a method descriptor') - # Note: descriptor classes themselves are *not* descriptors. + + # Classes of descriptors (are *not* descriptors themselves): self.assertFalse(inspect.ismethoddescriptor(MethodDescriptor)) self.assertFalse(inspect.ismethoddescriptor(MethodDescriptorSub)) + self.assertFalse(inspect.ismethoddescriptor(DataDescriptorSub)) def test_builtin_descriptors(self): builtin_slot_wrapper = int.__add__ # This one is mentioned in docs. @@ -1855,11 +1860,11 @@ def class_method(cls): pass def static_method(): pass @property def a_property(self): pass - def function(): - pass - a_lambda = lambda: None class Slotted: __slots__ = 'foo', + def function(): + pass + # Example builtin method descriptors: self.assertTrue( inspect.ismethoddescriptor(builtin_slot_wrapper), @@ -1870,21 +1875,21 @@ class Slotted: self.assertTrue( inspect.ismethoddescriptor(Owner.__dict__['static_method']), 'a staticmethod object is a method descriptor') + # Example builtin data descriptors: self.assertFalse( - inspect.ismethoddescriptor(Slotted.foo), - 'a slot is not a method descriptor') - self.assertFalse( - inspect.ismethoddescriptor(Owner.a_property), + inspect.ismethoddescriptor(Owner.__dict__['a_property']), 'a property is not a method descriptor') + self.assertFalse( + inspect.ismethoddescriptor(Slotted.__dict__['foo']), + 'a slot is not a method descriptor') + # `types.MethodType`/`types.FunctionType` instances (they *are* # method descriptors, but `ismethoddescriptor()` explicitly # excludes them): self.assertFalse(inspect.ismethoddescriptor(Owner().instance_method)) - self.assertFalse(inspect.ismethoddescriptor(Owner().class_method)) - self.assertFalse(inspect.ismethoddescriptor(Owner().static_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner.class_method)) self.assertFalse(inspect.ismethoddescriptor(function)) - self.assertFalse(inspect.ismethoddescriptor(a_lambda)) def test_descriptor_being_a_class(self): class MethodDescriptorMeta(type): From 57875a7dbbbef0d3150651d7b9a5d39bac7aa9eb Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sat, 15 Jun 2024 12:12:38 +0200 Subject: [PATCH 04/12] Refactor --- Lib/inspect.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index fb9d7db22c47ac..6e403772d0325b 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -320,9 +320,9 @@ def ismethoddescriptor(object): # mutual exclusion return False tp = type(object) - if hasattr(tp, "__set__") or hasattr(tp, "__delete__"): - return False - return hasattr(tp, "__get__") + return (hasattr(tp, "__get__") + and not hasattr(tp, "__set__") + and not hasattr(tp, "__delete__")) def isdatadescriptor(object): """Return true if the object is a data descriptor. From 01178fab7a0f50537e8aa83eef830f03ad8dd467 Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sat, 15 Jun 2024 12:13:04 +0200 Subject: [PATCH 05/12] Doc: add `versionchanged`... --- Doc/library/inspect.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 1a6b8648c0eadb..46b3f3afb5dd8d 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -515,6 +515,12 @@ attributes (see :ref:`import-mod-attrs` for module attributes): :attr:`~method.__func__` attribute (etc) when an object passes :func:`ismethod`. + .. versionchanged:: 3.13 + For objects with :meth:`~object.__get__` and :meth:`~object.__delete__` + but without :meth:`~object.__set__`, ``False`` is now returned + (previously, ``True`` was returned for such objects, which was + incorrect). + .. function:: isdatadescriptor(object) From 71c3735411f955fbe28e4b71956204bcb8fb06db Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sat, 15 Jun 2024 12:21:27 +0200 Subject: [PATCH 06/12] Test: added more method/function cases (requested by @eendebakpt) --- Lib/test/test_inspect/test_inspect.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 1835bf5b12f551..4640e07b535aac 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1864,6 +1864,7 @@ class Slotted: __slots__ = 'foo', def function(): pass + a_lambda = lambda: None # Example builtin method descriptors: self.assertTrue( @@ -1888,8 +1889,12 @@ def function(): # method descriptors, but `ismethoddescriptor()` explicitly # excludes them): self.assertFalse(inspect.ismethoddescriptor(Owner().instance_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner().class_method)) self.assertFalse(inspect.ismethoddescriptor(Owner.class_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner().static_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner.static_method)) self.assertFalse(inspect.ismethoddescriptor(function)) + self.assertFalse(inspect.ismethoddescriptor(a_lambda)) def test_descriptor_being_a_class(self): class MethodDescriptorMeta(type): From 2daa16fb55949ea600d419227583aa3b7a8ffbed Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sat, 15 Jun 2024 12:26:43 +0200 Subject: [PATCH 07/12] Test: added one more method/function case... --- Lib/test/test_inspect/test_inspect.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 4640e07b535aac..81b251319bcf01 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1890,8 +1890,9 @@ def function(): # excludes them): self.assertFalse(inspect.ismethoddescriptor(Owner().instance_method)) self.assertFalse(inspect.ismethoddescriptor(Owner().class_method)) - self.assertFalse(inspect.ismethoddescriptor(Owner.class_method)) self.assertFalse(inspect.ismethoddescriptor(Owner().static_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner.instance_method)) + self.assertFalse(inspect.ismethoddescriptor(Owner.class_method)) self.assertFalse(inspect.ismethoddescriptor(Owner.static_method)) self.assertFalse(inspect.ismethoddescriptor(function)) self.assertFalse(inspect.ismethoddescriptor(a_lambda)) From 0c7bb3fe56864dcf1f687a04102f34804c4d622e Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sat, 15 Jun 2024 18:50:05 +0200 Subject: [PATCH 08/12] Update Doc/library/inspect.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/inspect.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 46b3f3afb5dd8d..15d93c17e7da0d 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -516,10 +516,9 @@ attributes (see :ref:`import-mod-attrs` for module attributes): :func:`ismethod`. .. versionchanged:: 3.13 - For objects with :meth:`~object.__get__` and :meth:`~object.__delete__` - but without :meth:`~object.__set__`, ``False`` is now returned - (previously, ``True`` was returned for such objects, which was - incorrect). + Objects with :meth:`~object.__get__` and :meth:`~object.__delete__` + but without :meth:`~object.__set__` are no more considered as method + descriptors. In previous versions, they were incorrectly deemed as such. .. function:: isdatadescriptor(object) From 84aed74cd8d922687b31a719c08e34d92a6481c2 Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sun, 16 Jun 2024 14:57:15 +0200 Subject: [PATCH 09/12] A couple of renames --- Lib/test/test_inspect/test_inspect.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 81b251319bcf01..e945f16925a8ef 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1860,8 +1860,8 @@ def class_method(cls): pass def static_method(): pass @property def a_property(self): pass - class Slotted: - __slots__ = 'foo', + class Slotermeyer: + __slots__ = 'a_slot', def function(): pass a_lambda = lambda: None @@ -1882,7 +1882,7 @@ def function(): inspect.ismethoddescriptor(Owner.__dict__['a_property']), 'a property is not a method descriptor') self.assertFalse( - inspect.ismethoddescriptor(Slotted.__dict__['foo']), + inspect.ismethoddescriptor(Slotermeyer.__dict__['a_slot']), 'a slot is not a method descriptor') # `types.MethodType`/`types.FunctionType` instances (they *are* From 0390f853dd7fec477bafbf2baf759f85bd40d1ca Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Sun, 16 Jun 2024 14:58:33 +0200 Subject: [PATCH 10/12] Improve the `versionchanged` description --- Doc/library/inspect.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 15d93c17e7da0d..7a913c15d93f37 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -517,8 +517,9 @@ attributes (see :ref:`import-mod-attrs` for module attributes): .. versionchanged:: 3.13 Objects with :meth:`~object.__get__` and :meth:`~object.__delete__` - but without :meth:`~object.__set__` are no more considered as method - descriptors. In previous versions, they were incorrectly deemed as such. + but without :meth:`~object.__set__` are no more considered by this + function as method descriptors (in previous versions, they were + incorrectly deemed as such). .. function:: isdatadescriptor(object) From bcfeeb69a261ad5565a9f250f95a08a59bd93ea8 Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Mon, 17 Jun 2024 01:41:59 +0200 Subject: [PATCH 11/12] Update Doc/library/inspect.rst Co-authored-by: Alyssa Coghlan --- Doc/library/inspect.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 7a913c15d93f37..b1eeb6500714b8 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -516,10 +516,9 @@ attributes (see :ref:`import-mod-attrs` for module attributes): :func:`ismethod`. .. versionchanged:: 3.13 - Objects with :meth:`~object.__get__` and :meth:`~object.__delete__` - but without :meth:`~object.__set__` are no more considered by this - function as method descriptors (in previous versions, they were - incorrectly deemed as such). + This function no longer incorrectly reports objects with :meth:`~object.__get__ + and :meth:`~object.__delete__`, but not :meth:`~object.__set__`, as being method + descriptors (such objects are data descriptors, not method descriptors). .. function:: isdatadescriptor(object) From eb4d852dc1b9aa7c312bbeeb9d1b10a92855941f Mon Sep 17 00:00:00 2001 From: Jan Kaliszewski Date: Mon, 17 Jun 2024 01:43:51 +0200 Subject: [PATCH 12/12] minor reST syntax fix --- Doc/library/inspect.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index b1eeb6500714b8..0ec7d7cecb89e0 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -516,7 +516,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): :func:`ismethod`. .. versionchanged:: 3.13 - This function no longer incorrectly reports objects with :meth:`~object.__get__ + This function no longer incorrectly reports objects with :meth:`~object.__get__` and :meth:`~object.__delete__`, but not :meth:`~object.__set__`, as being method descriptors (such objects are data descriptors, not method descriptors).