Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions reframe/utility/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ def repr(obj, htchar=' ', lfchar='\n', indent=4, basic_offset=0):
def attrs(obj):
'''Inspect object and return its attributes and their values.

This function returns also any descriptors found at the owner class.
This function returns also any descriptors found at the owner class,
with the exception of descriptors without an assigned value, which are
expected to raise an ``AttributeError``.

:arg obj: The object to inspect.
:returns: an iterator over ``(attr_name, value)`` tuples
Expand All @@ -287,9 +289,13 @@ def attrs(obj):

# Look for descriptors
for cls in type(obj).mro():
for attr, value in cls.__dict__.items():
if inspect.isdatadescriptor(value):
ret[attr] = getattr(obj, attr)
for attr in cls.__dict__:
if inspect.isdatadescriptor(cls.__dict__[attr]):
try:
ret[attr] = getattr(obj, attr)
except AttributeError:
# Pass if the descriptor does not have an assigned value
pass

return ret

Expand Down
14 changes: 13 additions & 1 deletion unittests/test_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,9 +658,14 @@ def test_repr_default():


def test_attrs():
class C:
class B:
z = fields.TypedField(int)

def __init__(self, x, y):
self.x = x
self.y = y

class C(B):
def __init__(self, x, y):
self._x = x
self.y = y
Expand All @@ -676,6 +681,13 @@ def x(self):
class D(C):
pass

# Test undefined descriptors are not returned
b = B(-1, 0)
b_attrs = util.attrs(b)
assert b_attrs['x'] == -1
assert b_attrs['y'] == 0
assert 'z' not in b_attrs

c = C(1, 2)
c_attrs = util.attrs(c)
assert c_attrs['x'] == 1
Expand Down