]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-30533:Add function inspect.getmembers_static that does not call properties or...
authorWeipeng Hong <hongweichen8888@sina.com>
Tue, 30 Nov 2021 18:23:13 +0000 (02:23 +0800)
committerGitHub <noreply@github.com>
Tue, 30 Nov 2021 18:23:13 +0000 (10:23 -0800)
* Add function inspect.getmembers_static that does not call properties or dynamic
properties.

* update _getmembers args

* Update Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst

Co-authored-by: Itamar Ostricher <itamarost@gmail.com>
* Update Lib/inspect.py

Co-authored-by: Itamar Ostricher <itamarost@gmail.com>
* Removes the copy pasted doc string

Co-authored-by: Itamar Ostricher <itamarost@gmail.com>
Co-authored-by: Dino Viehland <dinoviehland@gmail.com>
Lib/inspect.py
Lib/test/test_inspect.py
Misc/NEWS.d/next/Library/2020-06-16-18-00-56.bpo-30533.StL57t.rst [new file with mode: 0644]

index dd891112570c6a9eda1bdecfae9f836a51eb25cc..1eb2f2b575ef454dcc7e0226d1343a35a84457c6 100644 (file)
@@ -440,9 +440,7 @@ def isabstract(object):
                 return True
     return False
 
-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."""
+def _getmembers(object, predicate, getter):
     if isclass(object):
         mro = (object,) + getmro(object)
     else:
@@ -465,7 +463,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
@@ -484,6 +482,25 @@ 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 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')
 
 def classify_class_attrs(cls):
index d43486a219158be1bc0ebc52f8483e26ee6c752c..33665cf3c79ebf59d4b604ea78471f5d82c0edd1 100644 (file)
@@ -1215,6 +1215,23 @@ class TestClassesAndFunctions(unittest.TestCase):
         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 (file)
index 0000000..bc4523f
--- /dev/null
@@ -0,0 +1,2 @@
+Add :func:`inspect.getmembers_static` , it return all members without
+triggering dynamic lookup via the descriptor protocol. Patch by Weipeng Hong.