From 8ba3a08d790ef5eeb49b2ccc968f04622d927c2b Mon Sep 17 00:00:00 2001 From: hongweipeng Date: Tue, 16 Jun 2020 18:05:58 +0800 Subject: [PATCH 1/5] Add function inspect.getmembers_static that does not call properties or dynamic properties. --- Lib/inspect.py | 15 +++++++++++++-- Lib/test/test_inspect.py | 17 +++++++++++++++++ .../2020-06-16-18-00-56.bpo-30533.StL57t.rst | 2 ++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index 7a2eefec5a80d4..136701c7d62b9d 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -441,7 +441,7 @@ def isabstract(object): return True return False -def getmembers(object, predicate=None): +def _getmembers(object, predicate=None, getter=None): """Return all members of an object as (name, value) pairs sorted by name. Optionally, only return members that satisfy a given predicate.""" if isclass(object): @@ -466,7 +466,7 @@ def getmembers(object, predicate=None): # like calling their __get__ (see bug #1785), so fall back to # looking in the __dict__. try: - value = getattr(object, key) + value = getter(object, key) # handle the duplicate key if key in processed: raise AttributeError @@ -485,6 +485,17 @@ def getmembers(object, predicate=None): results.sort(key=lambda pair: pair[0]) return results +def getmembers(object, predicate=None): + """Return all members of an object as (name, value) pairs sorted by name. + Optionally, only return members that satisfy a given predicate.""" + return _getmembers(object, predicate, getattr) + +def getmembers_static(object, predicate=None): + """Return all members of an object as (name, value) pairs sorted by name + without calling properties and other dynamic. Optionally, only return + members that satisfy a given predicate.""" + return _getmembers(object, predicate, getattr_static) + Attribute = namedtuple('Attribute', 'name kind defining_class object') def classify_class_attrs(cls): diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 69f17f2477a2f2..c45e52152362fb 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1275,6 +1275,23 @@ def eggs(self): self.assertIn(('eggs', 'scrambled'), inspect.getmembers(A)) self.assertIn(('eggs', 'spam'), inspect.getmembers(A())) + def test_getmembers_static(self): + class A: + @property + def name(self): + raise NotImplementedError + @types.DynamicClassAttribute + def eggs(self): + raise NotImplementedError + + a = A() + instance_members = inspect.getmembers_static(a) + class_members = inspect.getmembers_static(A) + self.assertIn(('name', inspect.getattr_static(a, 'name')), instance_members) + self.assertIn(('eggs', inspect.getattr_static(a, 'eggs')), instance_members) + self.assertIn(('name', inspect.getattr_static(A, 'name')), class_members) + self.assertIn(('eggs', inspect.getattr_static(A, 'eggs')), class_members) + def test_getmembers_with_buggy_dir(self): class M(type): def __dir__(cls): diff --git a/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst b/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst new file mode 100644 index 00000000000000..0acab2e8869a83 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst @@ -0,0 +1,2 @@ +Add :func:`inspect.getmembers_static` , it return all members without +calling properties and other dynamic. Patch by Weipeng Hong. From a708ace2b45c3cc9cc14a30f3c1f46dc747283ba Mon Sep 17 00:00:00 2001 From: HongWeipeng Date: Mon, 7 Sep 2020 18:52:16 +0800 Subject: [PATCH 2/5] udpate _getmembers args --- Lib/inspect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 136701c7d62b9d..969e625f715acc 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -441,7 +441,7 @@ def isabstract(object): return True return False -def _getmembers(object, predicate=None, getter=None): +def _getmembers(object, predicate, getter): """Return all members of an object as (name, value) pairs sorted by name. Optionally, only return members that satisfy a given predicate.""" if isclass(object): From 77408b9ee16b9aff3634225b82750f26c8a2c780 Mon Sep 17 00:00:00 2001 From: Weipeng Hong Date: Wed, 10 Nov 2021 20:00:18 +0800 Subject: [PATCH 3/5] Update Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst Co-authored-by: Itamar Ostricher --- .../next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst b/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst index 0acab2e8869a83..bc4523f626666f 100644 --- a/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst +++ b/Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst @@ -1,2 +1,2 @@ Add :func:`inspect.getmembers_static` , it return all members without -calling properties and other dynamic. Patch by Weipeng Hong. +triggering dynamic lookup via the descriptor protocol. Patch by Weipeng Hong. From f13421af3fc20dd6f0fea5616e752c1003ae3c94 Mon Sep 17 00:00:00 2001 From: Weipeng Hong Date: Wed, 10 Nov 2021 20:01:00 +0800 Subject: [PATCH 4/5] Update Lib/inspect.py Co-authored-by: Itamar Ostricher --- Lib/inspect.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 969e625f715acc..84095790a1f306 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -492,8 +492,16 @@ def getmembers(object, predicate=None): def getmembers_static(object, predicate=None): """Return all members of an object as (name, value) pairs sorted by name - without calling properties and other dynamic. Optionally, only return - members that satisfy a given predicate.""" + without triggering dynamic lookup via the descriptor protocol, + __getattr__ or __getattribute__. Optionally, only return members that + satisfy a given predicate. + + Note: this function may not be able to retrieve all members + that getmembers can fetch (like dynamically created attributes) + and may find members that getmembers can't (like descriptors + that raise AttributeError). It can also return descriptor objects + instead of instance members in some cases. + """ return _getmembers(object, predicate, getattr_static) Attribute = namedtuple('Attribute', 'name kind defining_class object') From 206ea77bfdb3e12234510a8fa258441122ace608 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Mon, 29 Nov 2021 17:16:35 -0800 Subject: [PATCH 5/5] Removes the copy pasted doc string --- Lib/inspect.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 84095790a1f306..b99f5dddbfd3c6 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -442,8 +442,6 @@ def isabstract(object): return False def _getmembers(object, predicate, getter): - """Return all members of an object as (name, value) pairs sorted by name. - Optionally, only return members that satisfy a given predicate.""" if isclass(object): mro = (object,) + getmro(object) else: