summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Magnusson <m@jacobian.se>2016-03-04 16:06:12 +0100
committerJacob Magnusson <m@jacobian.se>2016-03-04 16:20:33 +0100
commit4e55ffae0710741e158ba97e7a27ce2f44bd9f16 (patch)
treeeba643462146762db5ace138d4d9b59eb3309432
parent0e7904e730c3d2b0d3a394ad60010158ee29050c (diff)
downloadsqlalchemy-pr/245.tar.gz
Support recursive operation on _asdict and allow passing in dictionary implementationpr/245
-rw-r--r--lib/sqlalchemy/util/_collections.py66
-rw-r--r--test/base/test_utils.py29
2 files changed, 86 insertions, 9 deletions
diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py
index c29b81f6a..ffd3107fa 100644
--- a/lib/sqlalchemy/util/_collections.py
+++ b/lib/sqlalchemy/util/_collections.py
@@ -98,16 +98,42 @@ class KeyedTuple(AbstractKeyedTuple):
def __setattr__(self, key, value):
raise AttributeError("Can't set attribute: %s" % key)
- def _asdict(self):
+ def _asdict(self, impl=dict, recursive=False):
"""Return the contents of this :class:`.KeyedTuple` as a dictionary.
This method provides compatibility with ``collections.namedtuple()``,
- with the exception that the dictionary returned is **not** ordered.
+ with the exception that the dictionary returned is **not** ordered by
+ default.
+
+ :param impl:
+ The implementation to use. Defaults to :class:`dict`.
+
+ :param recursive:
+ Recursively call :meth:`._asdict` on values found that inherit from
+ AbstractKeyedTuple. Defaults to False.
+
.. versionadded:: 0.8
"""
- return dict((key, self.__dict__[key]) for key in self.keys())
+
+ if recursive:
+ return impl(
+ (
+ key,
+ (
+ self.__dict__[key]._asdict(
+ impl=impl,
+ recursive=recursive
+ )
+ if isinstance(self.__dict__[key], AbstractKeyedTuple)
+ else self.__dict__[key]
+ )
+ )
+ for key in self.keys()
+ )
+ else:
+ return impl((key, self.__dict__[key]) for key in self.keys())
class _LW(AbstractKeyedTuple):
@@ -122,10 +148,34 @@ class _LW(AbstractKeyedTuple):
# difficulties
return KeyedTuple, (list(self), self._real_fields)
- def _asdict(self):
- """Return the contents of this :class:`.KeyedTuple` as a dictionary."""
+ def _asdict(self, impl=dict, recursive=False):
+ """Return the contents of this :class:`.KeyedTuple` as a dictionary.
- d = dict(zip(self._real_fields, self))
+ :param impl:
+ The implementation to use. Defaults to :class:`dict`.
+
+ :param recursive:
+ Recursively call :meth:`._asdict` on values found that inherit from
+ AbstractKeyedTuple. Defaults to False.
+
+ """
+
+ if recursive:
+ d = impl(
+ zip(
+ self._real_fields,
+ (
+ (
+ v._asdict(impl=impl, recursive=recursive)
+ if isinstance(v, AbstractKeyedTuple)
+ else v
+ )
+ for v in self
+ )
+ )
+ )
+ else:
+ d = impl(zip(self._real_fields, self))
d.pop(None, None)
return d
@@ -931,7 +981,7 @@ class LRUCache(dict):
_lw_tuples = LRUCache(100)
-def lightweight_named_tuple(name, fields):
+def lightweight_named_tuple(name, fields, **kw):
hash_ = (name, ) + tuple(fields)
tp_cls = _lw_tuples.get(hash_)
if tp_cls:
@@ -942,7 +992,7 @@ def lightweight_named_tuple(name, fields):
dict([
(field, _property_getters[idx])
for idx, field in enumerate(fields) if field is not None
- ] + [('__slots__', ())])
+ ] + [('__slots__', ())], **kw)
)
tp_cls._real_fields = fields
diff --git a/test/base/test_utils.py b/test/base/test_utils.py
index fcb9a59a3..0dafdb63a 100644
--- a/test/base/test_utils.py
+++ b/test/base/test_utils.py
@@ -7,7 +7,7 @@ from sqlalchemy.testing import eq_, is_, ne_, fails_if, mock, expect_warnings
from sqlalchemy.testing.util import picklers, gc_collect
from sqlalchemy.util import classproperty, WeakSequence, get_callable_argspec
from sqlalchemy.sql import column
-from sqlalchemy.util import langhelpers, compat
+from sqlalchemy.util import OrderedDict, langhelpers, compat
import inspect
@@ -121,6 +121,33 @@ class _KeyedTupleTest(object):
eq_(kt._fields, ('a', 'b'))
eq_(kt._asdict(), {'a': 1, 'b': 3})
+ def test_asdict_impl(self):
+ keyed_tuple = self._fixture([1, 2, 3, 4], ['a', 'b', 'c', 'd'])
+ from sqlalchemy.util import OrderedDict
+ assert keyed_tuple._asdict(impl=OrderedDict) == OrderedDict(
+ [['a', 1], ['b', 2], ['c', 3], ['d', 4]]
+ )
+
+ def test_asdict_recursive(self):
+ keyed_tuple = self._fixture(
+ [1, 2, self._fixture([3, 4], ['d', 'e'])],
+ ['a', 'b', 'c']
+ )
+ eq_(
+ keyed_tuple._asdict(recursive=True),
+ {'a': 1, 'b': 2, 'c': {'d': 3, 'e': 4}}
+ )
+
+ def test_asdict_impl_recursive(self):
+ keyed_tuple = self._fixture(
+ [1, 2, self._fixture([3, 4], ['d', 'e'])],
+ ['a', 'b', 'c']
+ )
+ dct = keyed_tuple._asdict(impl=OrderedDict, recursive=True)
+ assert dct == OrderedDict(
+ [['a', 1], ['b', 2], ['c', OrderedDict([['d', 3], ['e', 4]])]]
+ )
+
class KeyedTupleTest(_KeyedTupleTest, fixtures.TestBase):
def _fixture(self, values, labels):