Recurse into containers too, add skip in asdict
This commit is contained in:
parent
d28336c28a
commit
2321fc202a
|
@ -30,7 +30,7 @@ def fields(cl):
|
||||||
return copy.deepcopy(attrs)
|
return copy.deepcopy(attrs)
|
||||||
|
|
||||||
|
|
||||||
def asdict(inst, recurse=True):
|
def asdict(inst, recurse=True, skip=None):
|
||||||
"""
|
"""
|
||||||
Return the ``attrs`` attribute values of *i* as a dict. Optionally recurse
|
Return the ``attrs`` attribute values of *i* as a dict. Optionally recurse
|
||||||
into other ``attrs``-decorated classes.
|
into other ``attrs``-decorated classes.
|
||||||
|
@ -40,14 +40,30 @@ def asdict(inst, recurse=True):
|
||||||
:param recurse: Recurse into classes that are also ``attrs``-decorated.
|
:param recurse: Recurse into classes that are also ``attrs``-decorated.
|
||||||
:type recurse: bool
|
:type recurse: bool
|
||||||
|
|
||||||
|
:param skip: A filter function that causes elements to be left out if it
|
||||||
|
returns ``True``. Is called with the :class:`attr.Attribute` as the
|
||||||
|
first argument and the value as the second argument.
|
||||||
|
:type skip: callable
|
||||||
|
|
||||||
:rtype: :class:`dict`
|
:rtype: :class:`dict`
|
||||||
"""
|
"""
|
||||||
attrs = fields(inst.__class__)
|
attrs = fields(inst.__class__)
|
||||||
rv = {}
|
rv = {}
|
||||||
for a in attrs:
|
for a in attrs:
|
||||||
v = getattr(inst, a.name)
|
v = getattr(inst, a.name)
|
||||||
if recurse is True and has(v.__class__):
|
if skip is not None and skip(a, v):
|
||||||
rv[a.name] = asdict(v, recurse=True)
|
continue
|
||||||
|
if recurse is True:
|
||||||
|
if has(v.__class__):
|
||||||
|
rv[a.name] = asdict(v, recurse=True, skip=skip)
|
||||||
|
elif isinstance(v, (tuple, list, set)):
|
||||||
|
rv[a.name] = [asdict(i, recurse=True, skip=skip) for i in v]
|
||||||
|
elif isinstance(v, dict):
|
||||||
|
rv[a.name] = dict((asdict(kk) if has(kk.__class__) else kk,
|
||||||
|
asdict(vv) if has(vv.__class__) else vv)
|
||||||
|
for kk, vv in iteritems(v))
|
||||||
|
else:
|
||||||
|
rv[a.name] = v
|
||||||
else:
|
else:
|
||||||
rv[a.name] = v
|
rv[a.name] = v
|
||||||
return rv
|
return rv
|
||||||
|
|
|
@ -92,6 +92,39 @@ class TestAsDict(object):
|
||||||
C(3, 4),
|
C(3, 4),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def test_skip(self):
|
||||||
|
"""
|
||||||
|
Attributes that are supposed to be skipped are skipped.
|
||||||
|
"""
|
||||||
|
assert {
|
||||||
|
"x": {"x": 1},
|
||||||
|
} == asdict(C(
|
||||||
|
C(1, 2),
|
||||||
|
C(3, 4),
|
||||||
|
), skip=lambda a, v: a.name == "y")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("container", [
|
||||||
|
list,
|
||||||
|
tuple,
|
||||||
|
])
|
||||||
|
def test_lists_tuples(self, container):
|
||||||
|
"""
|
||||||
|
If recurse is True, also recurse into lists.
|
||||||
|
"""
|
||||||
|
assert {
|
||||||
|
"x": 1,
|
||||||
|
"y": [{"x": 2, "y": 3}, {"x": 4, "y": 5}],
|
||||||
|
} == asdict(C(1, container([C(2, 3), C(4, 5)])))
|
||||||
|
|
||||||
|
def test_dicts(self):
|
||||||
|
"""
|
||||||
|
If recurse is True, also recurse into dicts.
|
||||||
|
"""
|
||||||
|
assert {
|
||||||
|
"x": 1,
|
||||||
|
"y": {"a": {"x": 4, "y": 5}},
|
||||||
|
} == asdict(C(1, {"a": C(4, 5)}))
|
||||||
|
|
||||||
|
|
||||||
class TestHas(object):
|
class TestHas(object):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue