Optionally retain collection types (fixes #69)
This commit is contained in:
parent
dd4140ebcd
commit
c6fbec9637
|
@ -41,6 +41,9 @@ Changes:
|
|||
- Add ``attr.attrs`` and ``attr.attrib`` as a more consistent aliases for ``attr.s`` and ``attr.ib``.
|
||||
- Add ``frozen`` option to ``attr.s`` that will make instances best-effort immutable.
|
||||
`#60 <https://github.com/hynek/attrs/issues/60>`_
|
||||
- ``attr.asdict`` now takes ``retain_collection_types`` as an argument.
|
||||
If ``True``, it does not convert attributes of type ``tuple`` or ``set`` to ``list``.
|
||||
`#69 <https://github.com/hynek/attrs/issues/69>`_
|
||||
|
||||
|
||||
----
|
||||
|
|
|
@ -6,29 +6,31 @@ from ._compat import iteritems
|
|||
from ._make import Attribute, NOTHING, fields
|
||||
|
||||
|
||||
def asdict(inst, recurse=True, filter=None, dict_factory=dict):
|
||||
def asdict(inst, recurse=True, filter=None, dict_factory=dict,
|
||||
retain_collection_types=False):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a dict. Optionally
|
||||
recurse into other ``attrs``-decorated classes.
|
||||
Return the ``attrs`` attribute values of *inst* as a dict.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
|
||||
:param callable filter: A callable whose return code deteremines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the :class:`attr.Attribute` as the first argument and the
|
||||
value as the second argument.
|
||||
|
||||
:param callable dict_factory: A callable to produce dictionaries from. For
|
||||
:param callable dict_factory: A callable to produce dictionaries from. For
|
||||
example, to produce ordered dictionaries instead of normal Python
|
||||
dictionaries, pass in ``collections.OrderedDict``.
|
||||
:param bool retain_collection_types: Do not convert to ``list`` when
|
||||
encountering an attribute which is type ``tuple`` or ``set``. Only
|
||||
meaningful if ``recurse`` is ``True``.
|
||||
|
||||
:rtype: :class:`dict`
|
||||
:rtype: return type of *dict_factory*
|
||||
|
||||
.. versionadded:: 16.0.0
|
||||
*dict_factory*
|
||||
.. versionadded:: 16.0.0 *dict_factory*
|
||||
.. versionadded:: 16.1.0 *retain_collection_types*
|
||||
"""
|
||||
attrs = fields(inst.__class__)
|
||||
rv = dict_factory()
|
||||
|
@ -41,12 +43,13 @@ def asdict(inst, recurse=True, filter=None, dict_factory=dict):
|
|||
rv[a.name] = asdict(v, recurse=True, filter=filter,
|
||||
dict_factory=dict_factory)
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
rv[a.name] = [
|
||||
cf = v.__class__ if retain_collection_types is True else list
|
||||
rv[a.name] = cf([
|
||||
asdict(i, recurse=True, filter=filter,
|
||||
dict_factory=dict_factory)
|
||||
if has(i.__class__) else i
|
||||
for i in v
|
||||
]
|
||||
])
|
||||
elif isinstance(v, dict):
|
||||
df = dict_factory
|
||||
rv[a.name] = df((
|
||||
|
|
|
@ -105,6 +105,18 @@ class TestAsDict(object):
|
|||
"y": [{"x": 2, "y": 3}, {"x": 4, "y": 5}, "a"],
|
||||
} == asdict(C(1, container([C(2, 3), C(4, 5), "a"])))
|
||||
|
||||
@given(container=st.sampled_from(SEQUENCE_TYPES))
|
||||
def test_lists_tuples_retain_type(self, container, C):
|
||||
"""
|
||||
If recurse and retain_collection_types are True, also recurse
|
||||
into lists and do not convert them into list.
|
||||
"""
|
||||
assert {
|
||||
"x": 1,
|
||||
"y": container([{"x": 2, "y": 3}, {"x": 4, "y": 5}, "a"]),
|
||||
} == asdict(C(1, container([C(2, 3), C(4, 5), "a"])),
|
||||
retain_collection_types=True)
|
||||
|
||||
@given(st.sampled_from(MAPPING_TYPES))
|
||||
def test_dicts(self, C, dict_factory):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue