From d9fd5297f4bb48994f4d35127730f987059fcead Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Mon, 5 Jun 2017 17:47:43 -0400 Subject: [PATCH 01/17] update documentation --- Doc/howto/descriptor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index c2bf473e1ff9eaf..f0cd016035cfb06 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -58,7 +58,7 @@ That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up as an attribute. -If an object defines both :meth:`__get__` and :meth:`__set__`, it is considered +If an object defines either :meth:`__set__` or :meth:`__delete__`, it is considered a data descriptor. Descriptors that only define :meth:`__get__` are called non-data descriptors (they are typically used for methods but other uses are possible). From 42a007bab50f64ad1fea25f147f33859e4a2da00 Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Mon, 5 Jun 2017 17:59:19 -0400 Subject: [PATCH 02/17] update inspect function --- Lib/inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 9c072eb0747fbd1..16e96471bc24c7a 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -111,7 +111,7 @@ def ismethoddescriptor(object): def isdatadescriptor(object): """Return true if the object is a data descriptor. - Data descriptors have both a __get__ and a __set__ attribute. Examples are + Data descriptors have either a __set__ or a __delete__ attribute. Examples are properties (defined in Python) and getsets and members (defined in C). Typically, data descriptors will also have __name__ and __doc__ attributes (properties, getsets, and members have both of these attributes), but this @@ -120,7 +120,7 @@ def isdatadescriptor(object): # mutual exclusion return False tp = type(object) - return hasattr(tp, "__set__") and hasattr(tp, "__get__") + return hasattr(tp, "__set__") or hasattr(tp, "__delete__") if hasattr(types, 'MemberDescriptorType'): # CPython and equivalent From 7d06227bf13a92c99f52724cfc058302e5c71faf Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Tue, 6 Jun 2017 12:01:59 -0400 Subject: [PATCH 03/17] add Misc/NEWS entry for abc.ABC slots --- Misc/NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Misc/NEWS b/Misc/NEWS index 34f1c1561ac82b4..24ebea673380ada 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -992,6 +992,10 @@ Library - Issue #29581: ABCMeta.__new__ now accepts ``**kwargs``, allowing abstract base classes to use keyword parameters in __init_subclass__. Patch by Nate Soares. +- Issue #30463: Add ``__slots__ = ()`` to ``abc.ABC`` to allow + subclassers to deny ``__dict__`` and ``__weakref__`` creation. + Patch by Aaron Hall. + Windows ------- From aada79a2ab1ed6045d927ef51aa4cf23e7487679 Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Tue, 6 Jun 2017 12:41:27 -0400 Subject: [PATCH 04/17] add Misc/NEWS entry for inspect.isdatadescriptor --- Misc/NEWS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Misc/NEWS b/Misc/NEWS index 34f1c1561ac82b4..3200c5f96e159e0 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -992,6 +992,11 @@ Library - Issue #29581: ABCMeta.__new__ now accepts ``**kwargs``, allowing abstract base classes to use keyword parameters in __init_subclass__. Patch by Nate Soares. +- Issue #26103: Correct ``inspect.isdatadescriptor`` to look for + ``__set__`` or ``__delete__``. Patch by Aaron Hall. + + + Windows ------- From 6aa2d87bdb9b2f80b5e8f8629b1912ff4c0f0cbf Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Tue, 6 Jun 2017 14:34:44 -0400 Subject: [PATCH 05/17] add tests for isdatadescriptor custom, slots, property and functions --- Lib/test/test_inspect.py | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index c55efd69425173f..6710aa7594c2598 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1077,6 +1077,51 @@ class C(metaclass=M): attrs = [a[0] for a in inspect.getmembers(C)] self.assertNotIn('missing', attrs) +class TestIsDataDescriptor(unittest.TestCase): + + def test_custom_descriptors(self): + class NonDataDescriptor(object): + def __get__(self, value, type=None): "" + class DataDescriptor0(object): + def __set__(self, name, value): "" + class DataDescriptor1(object): + def __delete__(self, name): "" + class DataDescriptor2(object): + __set__ = None + self.assertFalse(inspect.isdatadescriptor(NonDataDescriptor), + 'class with __get__ not a data descriptor') + self.assertTrue(inspect.isdatadescriptor(DataDescriptor0), + 'class with __set__ is a data descriptor') + self.assertTrue(inspect.isdatadescriptor(DataDescriptor1), + 'class with __delete__ is a data descriptor') + self.assertTrue(inspect.isdatadescriptor(DataDescriptor2), + 'class with __set__ = None is a data descriptor') + + def test_slot(self): + class Slotted(object): __slots__ = 'foo', + self.assertTrue(inspect.isdatadescriptor(type(Slotted.foo)), + 'a slot is a data descriptor') + + def test_property(self): + self.assertTrue(inspect.isdatadescriptor(property), + 'property is a data descriptor') + + def test_functions(self): + class Test(object): + def instance_method(self): '' + def function(): '' + a_lambda = lambda: '' + self.assertFalse(inspect.isdatadescriptor(type(Test().instance_method)), + 'a instance method is not a data descriptor') + self.assertFalse(inspect.isdatadescriptor(classmethod), + 'a class method is not a data descriptor') + self.assertFalse(inspect.isdatadescriptor(staticmethod), + 'a static method is not a data descriptor') + self.assertFalse(inspect.isdatadescriptor(type(function)), + 'a function is not a data descriptor') + self.assertFalse(inspect.isdatadescriptor(type(a_lambda)), + 'a lambda is not a data descriptor') + _global_ref = object() class TestGetClosureVars(unittest.TestCase): @@ -2018,7 +2063,7 @@ def test_staticmethod(*args): # NOQA ((('args', ..., ..., 'var_positional'),), ...)) self.assertEqual(self.signature(A.f3), ((('args', ..., ..., 'var_positional'),), ...)) - self.assertEqual(self.signature(A.f4), + self.assertEqual(self.signature(A.f4), ((('args', ..., ..., 'var_positional'), ('kwargs', ..., ..., 'var_keyword')), ...)) @cpython_only From 0858e03d1e9812f79e24431070faaf1aa720ed20 Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Tue, 6 Jun 2017 14:50:21 -0400 Subject: [PATCH 06/17] add self to Misc/ACKS --- Misc/ACKS | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/ACKS b/Misc/ACKS index 85d2fc65c471812..52a2aecd3ec9195 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -578,6 +578,7 @@ Peter Haight Václav Haisman Zbigniew Halas Walker Hale IV +Aaron Christopher Hall Bob Halley Jesse Hallio Jun Hamano From 24392778a2c50e445de2b4b283df5c93dd8c9158 Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Wed, 7 Jun 2017 11:28:20 -0400 Subject: [PATCH 07/17] be more specific in test msg --- Lib/test/test_inspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index e25f698860f68d1..797b259df4ca4d3 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1089,7 +1089,7 @@ def __delete__(self, name): "" class DataDescriptor2(object): __set__ = None self.assertFalse(inspect.isdatadescriptor(NonDataDescriptor), - 'class with __get__ not a data descriptor') + 'class with only __get__ not a data descriptor') self.assertTrue(inspect.isdatadescriptor(DataDescriptor0), 'class with __set__ is a data descriptor') self.assertTrue(inspect.isdatadescriptor(DataDescriptor1), From 4bc76c9c0c8da3670773e7e54678bdfde5016f7f Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Wed, 7 Jun 2017 11:30:59 -0400 Subject: [PATCH 08/17] clarify docstring --- Lib/inspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index a21f032b906ac17..6e7e799cca87211 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -111,7 +111,7 @@ def ismethoddescriptor(object): def isdatadescriptor(object): """Return true if the object is a data descriptor. - Data descriptors have either a __set__ or a __delete__ attribute. Examples are + Data descriptors have either a __set__ and/or a __delete__ attribute. Examples are properties (defined in Python) and getsets and members (defined in C). Typically, data descriptors will also have __name__ and __doc__ attributes (properties, getsets, and members have both of these attributes), but this From ef3d4d3e0c76503d4ec10ed8d9f6c57b928b4f0d Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Wed, 7 Jun 2017 11:33:32 -0400 Subject: [PATCH 09/17] clarify "or" is not exclusive --- Doc/howto/descriptor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index f3bce71aaf9d103..e392f60ec603468 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -58,7 +58,7 @@ That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up as an attribute. -If an object defines either :meth:`__set__` or :meth:`__delete__`, it is considered +If an object defines :meth:`__set__` and/or :meth:`__delete__`, it is considered a data descriptor. Descriptors that only define :meth:`__get__` are called non-data descriptors (they are typically used for methods but other uses are possible). From e2f767cb463f8b76bb027a1a6c2fe0f7d00149d8 Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Wed, 7 Jun 2017 11:35:39 -0400 Subject: [PATCH 10/17] remove "either" - "or" is not exclusive --- Lib/inspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 6e7e799cca87211..40feca3bf1abe92 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -111,7 +111,7 @@ def ismethoddescriptor(object): def isdatadescriptor(object): """Return true if the object is a data descriptor. - Data descriptors have either a __set__ and/or a __delete__ attribute. Examples are + Data descriptors have a __set__ and/or a __delete__ attribute. Examples are properties (defined in Python) and getsets and members (defined in C). Typically, data descriptors will also have __name__ and __doc__ attributes (properties, getsets, and members have both of these attributes), but this From 2ab592f1ca3e9af17502a3022ba261c3e4739f67 Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Wed, 13 Sep 2017 22:09:13 -0400 Subject: [PATCH 11/17] Enabled tests, tests passing, style fixes per request by Serhiy. modified: test_inspect.py --- Lib/test/test_inspect.py | 58 +++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 797b259df4ca4d3..1320f9a44a7f171 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1080,46 +1080,56 @@ class C(metaclass=M): class TestIsDataDescriptor(unittest.TestCase): def test_custom_descriptors(self): - class NonDataDescriptor(object): - def __get__(self, value, type=None): "" - class DataDescriptor0(object): - def __set__(self, name, value): "" - class DataDescriptor1(object): - def __delete__(self, name): "" - class DataDescriptor2(object): + class NonDataDescriptor: + def __get__(self, value, type=None): pass + class DataDescriptor0: + def __set__(self, name, value): pass + class DataDescriptor1: + def __delete__(self, name): pass + class DataDescriptor2: __set__ = None - self.assertFalse(inspect.isdatadescriptor(NonDataDescriptor), + self.assertFalse(inspect.isdatadescriptor(NonDataDescriptor()), 'class with only __get__ not a data descriptor') - self.assertTrue(inspect.isdatadescriptor(DataDescriptor0), + self.assertTrue(inspect.isdatadescriptor(DataDescriptor0()), 'class with __set__ is a data descriptor') - self.assertTrue(inspect.isdatadescriptor(DataDescriptor1), + self.assertTrue(inspect.isdatadescriptor(DataDescriptor1()), 'class with __delete__ is a data descriptor') - self.assertTrue(inspect.isdatadescriptor(DataDescriptor2), + self.assertTrue(inspect.isdatadescriptor(DataDescriptor2()), 'class with __set__ = None is a data descriptor') def test_slot(self): - class Slotted(object): __slots__ = 'foo', - self.assertTrue(inspect.isdatadescriptor(type(Slotted.foo)), + class Slotted: + __slots__ = 'foo', + self.assertTrue(inspect.isdatadescriptor(Slotted.foo), 'a slot is a data descriptor') def test_property(self): - self.assertTrue(inspect.isdatadescriptor(property), - 'property is a data descriptor') + class Propertied: + @property + def a_property(self): + pass + self.assertTrue(inspect.isdatadescriptor(Propertied.a_property), + 'a property is a data descriptor') def test_functions(self): class Test(object): - def instance_method(self): '' - def function(): '' - a_lambda = lambda: '' - self.assertFalse(inspect.isdatadescriptor(type(Test().instance_method)), + def instance_method(self): pass + @classmethod + def class_method(cls): pass + @staticmethod + def static_method(): pass + def function(): + pass + a_lambda = lambda: None + self.assertFalse(inspect.isdatadescriptor(Test().instance_method), 'a instance method is not a data descriptor') - self.assertFalse(inspect.isdatadescriptor(classmethod), + self.assertFalse(inspect.isdatadescriptor(Test().class_method), 'a class method is not a data descriptor') - self.assertFalse(inspect.isdatadescriptor(staticmethod), + self.assertFalse(inspect.isdatadescriptor(Test().static_method), 'a static method is not a data descriptor') - self.assertFalse(inspect.isdatadescriptor(type(function)), + self.assertFalse(inspect.isdatadescriptor(function), 'a function is not a data descriptor') - self.assertFalse(inspect.isdatadescriptor(type(a_lambda)), + self.assertFalse(inspect.isdatadescriptor(a_lambda), 'a lambda is not a data descriptor') @@ -3758,7 +3768,7 @@ def test_main(): TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState, TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject, TestBoundArguments, TestSignaturePrivateHelpers, - TestSignatureDefinitions, + TestSignatureDefinitions, TestIsDataDescriptor, TestGetClosureVars, TestUnwrap, TestMain, TestReload, TestGetCoroutineState ) From 2b0beab2c7248c4dad7e9bd7994fecf54edd506b Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Tue, 19 Sep 2017 11:05:49 -0400 Subject: [PATCH 12/17] Resolving conflict - learned updates go on top --- Misc/NEWS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS index 71f20af510bdc21..53380b1cf88071f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.7.0 alpha 1? Core and Builtins ----------------- +- Issue #26103: Correct ``inspect.isdatadescriptor`` to look for + ``__set__`` or ``__delete__``. Patch by Aaron Hall. + - bpo-25324: Tokens needed for parsing in Python moved to C. ``COMMENT``, ``NL`` and ``ENCODING``. This way the tokens and tok_names in the token module don't get changed when you import the tokenize module. @@ -1028,9 +1031,6 @@ Library - Issue #29581: ABCMeta.__new__ now accepts ``**kwargs``, allowing abstract base classes to use keyword parameters in __init_subclass__. Patch by Nate Soares. -- Issue #26103: Correct ``inspect.isdatadescriptor`` to look for - ``__set__`` or ``__delete__``. Patch by Aaron Hall. - - Issue #25532: inspect.unwrap() will now only try to unwrap an object sys.getrecursionlimit() times, to protect against objects which create a new object on every attribute access. From 2f3ef812461e886bb072b47d7f964d6af3a5b7b1 Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Tue, 19 Sep 2017 11:14:33 -0400 Subject: [PATCH 13/17] when in Rome (change from issue to bpo-) --- Misc/NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS b/Misc/NEWS index 53380b1cf88071f..775c2d685731335 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,7 +10,7 @@ What's New in Python 3.7.0 alpha 1? Core and Builtins ----------------- -- Issue #26103: Correct ``inspect.isdatadescriptor`` to look for +- bpo-26103: Correct ``inspect.isdatadescriptor`` to look for ``__set__`` or ``__delete__``. Patch by Aaron Hall. - bpo-25324: Tokens needed for parsing in Python moved to C. ``COMMENT``, From ed7caa7ecaed46109c55f4aa73c66f32f6652c63 Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Thu, 21 Sep 2017 22:53:13 -0400 Subject: [PATCH 14/17] remove entry - such entries go in News.d now --- Misc/NEWS | 3 --- 1 file changed, 3 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS index 775c2d685731335..4b0879bee296eca 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,9 +10,6 @@ What's New in Python 3.7.0 alpha 1? Core and Builtins ----------------- -- bpo-26103: Correct ``inspect.isdatadescriptor`` to look for - ``__set__`` or ``__delete__``. Patch by Aaron Hall. - - bpo-25324: Tokens needed for parsing in Python moved to C. ``COMMENT``, ``NL`` and ``ENCODING``. This way the tokens and tok_names in the token module don't get changed when you import the tokenize module. From eb882ce705372914261db1f6af5584815667c8cd Mon Sep 17 00:00:00 2001 From: Aaron Hall Date: Mon, 14 May 2018 09:08:03 -0400 Subject: [PATCH 15/17] add blurb --- .../next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst diff --git a/Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst b/Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst new file mode 100644 index 000000000000000..cb4c41ba5db9280 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-14-09-07-14.bpo-26103._zU8E2.rst @@ -0,0 +1,2 @@ +Correct ``inspect.isdatadescriptor`` to look for ``__set__`` or +``__delete__``. Patch by Aaron Hall. From a69a525c3183841734856167b83b98910cc1c6a7 Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Sun, 20 May 2018 12:20:23 -0400 Subject: [PATCH 16/17] change "and/or" to "or" --- Doc/howto/descriptor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index e392f60ec603468..5e11a44724ff422 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -58,7 +58,7 @@ That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up as an attribute. -If an object defines :meth:`__set__` and/or :meth:`__delete__`, it is considered +If an object defines :meth:`__set__` or :meth:`__delete__`, it is considered a data descriptor. Descriptors that only define :meth:`__get__` are called non-data descriptors (they are typically used for methods but other uses are possible). From a7af9f6bdcbaa0cd395510026cb54cab62055037 Mon Sep 17 00:00:00 2001 From: "Aaron Hall, MBA" Date: Sun, 20 May 2018 12:23:32 -0400 Subject: [PATCH 17/17] docstring - change "and/or" to "or" --- Lib/inspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 40feca3bf1abe92..37ba084c2cf1906 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -111,7 +111,7 @@ def ismethoddescriptor(object): def isdatadescriptor(object): """Return true if the object is a data descriptor. - Data descriptors have a __set__ and/or a __delete__ attribute. Examples are + Data descriptors have a __set__ or a __delete__ attribute. Examples are properties (defined in Python) and getsets and members (defined in C). Typically, data descriptors will also have __name__ and __doc__ attributes (properties, getsets, and members have both of these attributes), but this