Skip to content

Commit af8c8ca

Browse files
hongweipengitamaroDinoV
authored
bpo-30533:Add function inspect.getmembers_static that does not call properties or dynamic properties. (#20911)
* 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 <[email protected]> * Update Lib/inspect.py Co-authored-by: Itamar Ostricher <[email protected]> * Removes the copy pasted doc string Co-authored-by: Itamar Ostricher <[email protected]> Co-authored-by: Dino Viehland <[email protected]>
1 parent 4b97d97 commit af8c8ca

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

Lib/inspect.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,7 @@ def isabstract(object):
440440
return True
441441
return False
442442

443-
def getmembers(object, predicate=None):
444-
"""Return all members of an object as (name, value) pairs sorted by name.
445-
Optionally, only return members that satisfy a given predicate."""
443+
def _getmembers(object, predicate, getter):
446444
if isclass(object):
447445
mro = (object,) + getmro(object)
448446
else:
@@ -465,7 +463,7 @@ def getmembers(object, predicate=None):
465463
# like calling their __get__ (see bug #1785), so fall back to
466464
# looking in the __dict__.
467465
try:
468-
value = getattr(object, key)
466+
value = getter(object, key)
469467
# handle the duplicate key
470468
if key in processed:
471469
raise AttributeError
@@ -484,6 +482,25 @@ def getmembers(object, predicate=None):
484482
results.sort(key=lambda pair: pair[0])
485483
return results
486484

485+
def getmembers(object, predicate=None):
486+
"""Return all members of an object as (name, value) pairs sorted by name.
487+
Optionally, only return members that satisfy a given predicate."""
488+
return _getmembers(object, predicate, getattr)
489+
490+
def getmembers_static(object, predicate=None):
491+
"""Return all members of an object as (name, value) pairs sorted by name
492+
without triggering dynamic lookup via the descriptor protocol,
493+
__getattr__ or __getattribute__. Optionally, only return members that
494+
satisfy a given predicate.
495+
496+
Note: this function may not be able to retrieve all members
497+
that getmembers can fetch (like dynamically created attributes)
498+
and may find members that getmembers can't (like descriptors
499+
that raise AttributeError). It can also return descriptor objects
500+
instead of instance members in some cases.
501+
"""
502+
return _getmembers(object, predicate, getattr_static)
503+
487504
Attribute = namedtuple('Attribute', 'name kind defining_class object')
488505

489506
def classify_class_attrs(cls):

Lib/test/test_inspect.py

+17
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,23 @@ def eggs(self):
12151215
self.assertIn(('eggs', 'scrambled'), inspect.getmembers(A))
12161216
self.assertIn(('eggs', 'spam'), inspect.getmembers(A()))
12171217

1218+
def test_getmembers_static(self):
1219+
class A:
1220+
@property
1221+
def name(self):
1222+
raise NotImplementedError
1223+
@types.DynamicClassAttribute
1224+
def eggs(self):
1225+
raise NotImplementedError
1226+
1227+
a = A()
1228+
instance_members = inspect.getmembers_static(a)
1229+
class_members = inspect.getmembers_static(A)
1230+
self.assertIn(('name', inspect.getattr_static(a, 'name')), instance_members)
1231+
self.assertIn(('eggs', inspect.getattr_static(a, 'eggs')), instance_members)
1232+
self.assertIn(('name', inspect.getattr_static(A, 'name')), class_members)
1233+
self.assertIn(('eggs', inspect.getattr_static(A, 'eggs')), class_members)
1234+
12181235
def test_getmembers_with_buggy_dir(self):
12191236
class M(type):
12201237
def __dir__(cls):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :func:`inspect.getmembers_static` , it return all members without
2+
triggering dynamic lookup via the descriptor protocol. Patch by Weipeng Hong.

0 commit comments

Comments
 (0)