././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581621372.8737347 flatdict-4.0.1/0000755000000000000000000000000000000000000013245 5ustar00rootroot00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621371.0 flatdict-4.0.1/CHANGELOG.md0000644000000000000000000000643400000000000015065 0ustar00rootroot00000000000000# Changelog ## 4.0.1 (2020-02-13) - Gracefully fail to install if setuptools is too old ## 4.0.0 (2020-02-12) - FIXED deprecation warning from Python 3.9 (#40 [nugend](https://github.com/nugend)) - FIXED keep order of received dict and it's nested objects (#38 [wsantos](https://github.com/wsantos)) - Drops Python 2 support and Python 3.4 ## 3.4.0 (2019-07-24) - FIXED sort order with regard to a nested list of dictionaries (#33 [wsantos](https://github.com/wsantos)) ## 3.3.0 (2019-07-17) - FIXED FlatDict.setdefault() to match dict behavior (#32 [abmyii](https://github.com/abmyii)) - FIXED empty nested Flatterdict (#30 [wsantos](https://github.com/wsantos)) - CHANGED functionality to allow setting and updating nests within iterables (#29 [mileslucas](https://github.com/mileslucas)) ## 3.2.1 (2019-06-10) - FIXED docs generation for readthedocs.io ## 3.2.0 (2019-06-10) - FIXED List Flattening does not return list when an odd number of depth in the dictionary (#27 [mileslucas](https://github.com/mileslucas)) - CHANGED FlatterDict to allow for deeply nested dicts and lists when invoking `FlatterDict.as_dict()` (#28 [mileslucas](https://github.com/mileslucas)) - Flake8 cleanup/improvements - Distribution/packaging updates to put metadata into setup.cfg ## 3.1.0 (2018-10-30) - FIXED `FlatDict` behavior with empty iteratable values - CHANGED behavior when casting to str or repr (#23) ## 3.0.1 (2018-07-01) - Add 3.7 to Trove Classifiers - Add Python 2.7 unicode string compatibility (#22 [nvllsvm](https://github.com/nvllsvm)) ## 3.0.0 (2018-03-06) - CHANGED `FlatDict.as_dict` to return the nested data structure based upon delimiters, coercing `FlatDict` objects to `dict`. - CHANGED `FlatDict` to extend `collections.MutableMapping` instead of dict - CHANGED `dict(FlatDict())` to return a shallow `dict` instance with the delimited keys as strings - CHANGED `FlatDict.__eq__` to only evaluate against dict or the same class - FIXED `FlatterDict` behavior to match expectations from pre-2.0 releases. ## 2.0.1 (2018-01-18) - FIXED metadata for pypi upload ## 2.0.0 (2018-01-18) - Code efficiency refactoring and cleanup - Rewrote a majority of the tests, now at 100% coverage - ADDED `FlatDict.__eq__` and `FlatDict.__ne__` (#13 - [arm77](https://github.com/arm77)) - ADDED `FlatterDict` class that performs the list, set, and tuple coercion that was added in v1.20 - REMOVED coercion of lists and tuples from `FlatDict` that was added in 1.2.0. Alternative to (#12 - [rj-jesus](https://github.com/rj-jesus)) - REMOVED `FlatDict.has_key()` as it duplicates of `FlatDict.__contains__` - ADDED Python 3.5 and 3.6 to support matrix - REMOVED support for Python 2.6 and Python 3.2, 3.3 - CHANGED `FlatDict.set_delimiter` to raise a `ValueError` if a key already exists with the delimiter value in it. (#8) ## 1.2.0 (2015-06-25) - ADDED Support lists and tuples as well as dicts. (#4 - [alex-hutton](https://github.com/alex-hutton)) ## 1.1.3 (2015-01-04) - ADDED Python wheel support ## 1.1.2 (2013-10-09) - Documentation and CI updates - CHANGED use of `dict()` to a dict literal `{}` ## 1.1.1 (2012-08-17) - ADDED `FlatDict.as_dict()` - ADDED Python 3 support - ADDED `FlatDict.set_delimiter()` - Bugfixes and improvements from [naiquevin](https://github.com/naiquevin) ## 1.0.0 (2012-08-10) - Initial release ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621371.0 flatdict-4.0.1/LICENSE0000644000000000000000000000273400000000000014260 0ustar00rootroot00000000000000Copyright (c) 2013-2020 Gavin M. Roy All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621371.0 flatdict-4.0.1/MANIFEST.in0000644000000000000000000000005000000000000014776 0ustar00rootroot00000000000000include CHANGELOG.md README.rst LICENSE ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581621372.8737347 flatdict-4.0.1/PKG-INFO0000644000000000000000000000731400000000000014347 0ustar00rootroot00000000000000Metadata-Version: 1.1 Name: flatdict Version: 4.0.1 Summary: Python module for interacting with nested dicts as a single level dict with delimited keys. Home-page: https://github.com/gmr/flatdict Author: Gavin M. Roy Author-email: gavinmroy@gmail.com License: BSD 3-Clause License Description: FlatDict ======== |Version| |Status| |Coverage| |License| ``FlatDict`` and ``FlatterDict`` are a dict classes that allows for single level, delimited key/value pair mapping of nested dictionaries. You can interact with ``FlatDict`` and ``FlatterDict`` like a normal dictionary and access child dictionaries as you normally would or with the composite key. *For example:* .. code-block:: python value = flatdict.FlatDict({'foo': {'bar': 'baz', 'qux': 'corge'}}) *would be the same as:* .. code-block:: python value == {'foo:bar': 'baz', 'foo:qux': 'corge'} *values can be accessed as:* .. code-block:: python print(foo['foo:bar']) # or print(foo['foo']['bar']) Additionally, lists and tuples are also converted into dicts using ``enumerate()``, using the ``FlatterDict`` class. *For example:* .. code-block:: python value = flatdict.FlatterDict({'list': ['a', 'b', 'c']}) *will be the same as:* .. code-block:: python value == {'list:0': 'a', 'list:1': 'b', 'list:2': 'c'} API --- Documentation is available at https://flatdict.readthedocs.io Versioning ---------- This package attempts to use semantic versioning. API changes are indicated by the major version, non-breaking improvements by the minor, and bug fixes in the revision. It is recommended that you pin your targets to greater or equal to the current version and less than the next major version. Installation ------------ .. code-block:: bash $ pip install flatdict Note that as of 4.0, setuptools 39.2 or higher is required for installation. .. |Version| image:: https://img.shields.io/pypi/v/flatdict.svg? :target: https://pypi.python.org/pypi/flatdict .. |Status| image:: https://github.com/gmr/flatdict/workflows/Testing/badge.svg :target: https://github.com/gmr/flatdict/actions :alt: Build Status .. |Coverage| image:: https://img.shields.io/codecov/c/github/gmr/flatdict.svg? :target: https://codecov.io/github/gmr/flatdict?branch=master .. |License| image:: https://img.shields.io/pypi/l/flatdict.svg? :target: https://flatdict.readthedocs.org Platform: UNKNOWN Classifier: Topic :: Software Development :: Libraries Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621371.0 flatdict-4.0.1/README.rst0000644000000000000000000000405500000000000014740 0ustar00rootroot00000000000000FlatDict ======== |Version| |Status| |Coverage| |License| ``FlatDict`` and ``FlatterDict`` are a dict classes that allows for single level, delimited key/value pair mapping of nested dictionaries. You can interact with ``FlatDict`` and ``FlatterDict`` like a normal dictionary and access child dictionaries as you normally would or with the composite key. *For example:* .. code-block:: python value = flatdict.FlatDict({'foo': {'bar': 'baz', 'qux': 'corge'}}) *would be the same as:* .. code-block:: python value == {'foo:bar': 'baz', 'foo:qux': 'corge'} *values can be accessed as:* .. code-block:: python print(foo['foo:bar']) # or print(foo['foo']['bar']) Additionally, lists and tuples are also converted into dicts using ``enumerate()``, using the ``FlatterDict`` class. *For example:* .. code-block:: python value = flatdict.FlatterDict({'list': ['a', 'b', 'c']}) *will be the same as:* .. code-block:: python value == {'list:0': 'a', 'list:1': 'b', 'list:2': 'c'} API --- Documentation is available at https://flatdict.readthedocs.io Versioning ---------- This package attempts to use semantic versioning. API changes are indicated by the major version, non-breaking improvements by the minor, and bug fixes in the revision. It is recommended that you pin your targets to greater or equal to the current version and less than the next major version. Installation ------------ .. code-block:: bash $ pip install flatdict Note that as of 4.0, setuptools 39.2 or higher is required for installation. .. |Version| image:: https://img.shields.io/pypi/v/flatdict.svg? :target: https://pypi.python.org/pypi/flatdict .. |Status| image:: https://github.com/gmr/flatdict/workflows/Testing/badge.svg :target: https://github.com/gmr/flatdict/actions :alt: Build Status .. |Coverage| image:: https://img.shields.io/codecov/c/github/gmr/flatdict.svg? :target: https://codecov.io/github/gmr/flatdict?branch=master .. |License| image:: https://img.shields.io/pypi/l/flatdict.svg? :target: https://flatdict.readthedocs.org ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581621372.8737347 flatdict-4.0.1/flatdict.egg-info/0000755000000000000000000000000000000000000016531 5ustar00rootroot00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621372.0 flatdict-4.0.1/flatdict.egg-info/PKG-INFO0000644000000000000000000000731400000000000017633 0ustar00rootroot00000000000000Metadata-Version: 1.1 Name: flatdict Version: 4.0.1 Summary: Python module for interacting with nested dicts as a single level dict with delimited keys. Home-page: https://github.com/gmr/flatdict Author: Gavin M. Roy Author-email: gavinmroy@gmail.com License: BSD 3-Clause License Description: FlatDict ======== |Version| |Status| |Coverage| |License| ``FlatDict`` and ``FlatterDict`` are a dict classes that allows for single level, delimited key/value pair mapping of nested dictionaries. You can interact with ``FlatDict`` and ``FlatterDict`` like a normal dictionary and access child dictionaries as you normally would or with the composite key. *For example:* .. code-block:: python value = flatdict.FlatDict({'foo': {'bar': 'baz', 'qux': 'corge'}}) *would be the same as:* .. code-block:: python value == {'foo:bar': 'baz', 'foo:qux': 'corge'} *values can be accessed as:* .. code-block:: python print(foo['foo:bar']) # or print(foo['foo']['bar']) Additionally, lists and tuples are also converted into dicts using ``enumerate()``, using the ``FlatterDict`` class. *For example:* .. code-block:: python value = flatdict.FlatterDict({'list': ['a', 'b', 'c']}) *will be the same as:* .. code-block:: python value == {'list:0': 'a', 'list:1': 'b', 'list:2': 'c'} API --- Documentation is available at https://flatdict.readthedocs.io Versioning ---------- This package attempts to use semantic versioning. API changes are indicated by the major version, non-breaking improvements by the minor, and bug fixes in the revision. It is recommended that you pin your targets to greater or equal to the current version and less than the next major version. Installation ------------ .. code-block:: bash $ pip install flatdict Note that as of 4.0, setuptools 39.2 or higher is required for installation. .. |Version| image:: https://img.shields.io/pypi/v/flatdict.svg? :target: https://pypi.python.org/pypi/flatdict .. |Status| image:: https://github.com/gmr/flatdict/workflows/Testing/badge.svg :target: https://github.com/gmr/flatdict/actions :alt: Build Status .. |Coverage| image:: https://img.shields.io/codecov/c/github/gmr/flatdict.svg? :target: https://codecov.io/github/gmr/flatdict?branch=master .. |License| image:: https://img.shields.io/pypi/l/flatdict.svg? :target: https://flatdict.readthedocs.org Platform: UNKNOWN Classifier: Topic :: Software Development :: Libraries Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621372.0 flatdict-4.0.1/flatdict.egg-info/SOURCES.txt0000644000000000000000000000034500000000000020417 0ustar00rootroot00000000000000CHANGELOG.md LICENSE MANIFEST.in README.rst flatdict.py setup.cfg setup.py flatdict.egg-info/PKG-INFO flatdict.egg-info/SOURCES.txt flatdict.egg-info/dependency_links.txt flatdict.egg-info/top_level.txt flatdict.egg-info/zip-safe././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621372.0 flatdict-4.0.1/flatdict.egg-info/dependency_links.txt0000644000000000000000000000000100000000000022577 0ustar00rootroot00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621372.0 flatdict-4.0.1/flatdict.egg-info/top_level.txt0000644000000000000000000000001100000000000021253 0ustar00rootroot00000000000000flatdict ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621372.0 flatdict-4.0.1/flatdict.egg-info/zip-safe0000644000000000000000000000000100000000000020161 0ustar00rootroot00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621371.0 flatdict-4.0.1/flatdict.py0000644000000000000000000003732100000000000015417 0ustar00rootroot00000000000000"""FlatDict is a dict object that allows for single level, delimited key/value pair mapping of nested dictionaries. """ try: from collections.abc import MutableMapping except ImportError: # pragma: nocover from collections import MutableMapping import sys __version__ = '4.0.1' NO_DEFAULT = object() class FlatDict(MutableMapping): """:class:`~flatdict.FlatDict` is a dictionary object that allows for single level, delimited key/value pair mapping of nested dictionaries. The default delimiter value is ``:`` but can be changed in the constructor or by calling :meth:`FlatDict.set_delimiter`. """ _COERCE = dict def __init__(self, value=None, delimiter=':', dict_class=dict): super(FlatDict, self).__init__() self._values = dict_class() self._delimiter = delimiter self.update(value) def __contains__(self, key): """Check to see if the key exists, checking for both delimited and not delimited key values. :param mixed key: The key to check for """ if self._has_delimiter(key): pk, ck = key.split(self._delimiter, 1) return pk in self._values and ck in self._values[pk] return key in self._values def __delitem__(self, key): """Delete the item for the specified key, automatically dealing with nested children. :param mixed key: The key to use :raises: KeyError """ if key not in self: raise KeyError if self._has_delimiter(key): pk, ck = key.split(self._delimiter, 1) del self._values[pk][ck] if not self._values[pk]: del self._values[pk] else: del self._values[key] def __eq__(self, other): """Check for equality against the other value :param other: The value to compare :type other: FlatDict :rtype: bool :raises: TypeError """ if isinstance(other, dict): return self.as_dict() == other elif not isinstance(other, self.__class__): raise TypeError return self.as_dict() == other.as_dict() def __ne__(self, other): """Check for inequality against the other value :param other: The value to compare :type other: dict or FlatDict :rtype: bool """ return not self.__eq__(other) def __getitem__(self, key): """Get an item for the specified key, automatically dealing with nested children. :param mixed key: The key to use :rtype: mixed :raises: KeyError """ values = self._values key = [key] if isinstance(key, int) else key.split(self._delimiter) for part in key: values = values[part] return values def __iter__(self): """Iterate over the flat dictionary key and values :rtype: Iterator :raises: RuntimeError """ return iter(self.keys()) def __len__(self): """Return the number of items. :rtype: int """ return len(self.keys()) def __reduce__(self): """Return state information for pickling :rtype: tuple """ return type(self), (self.as_dict(), self._delimiter) def __repr__(self): """Return the string representation of the instance. :rtype: str """ return '<{} id={} {}>"'.format(self.__class__.__name__, id(self), str(self)) def __setitem__(self, key, value): """Assign the value to the key, dynamically building nested FlatDict items where appropriate. :param mixed key: The key for the item :param mixed value: The value for the item :raises: TypeError """ if isinstance(value, self._COERCE) and not isinstance(value, FlatDict): value = self.__class__(value, self._delimiter) if self._has_delimiter(key): pk, ck = key.split(self._delimiter, 1) if pk not in self._values: self._values[pk] = self.__class__({ck: value}, self._delimiter) return elif not isinstance(self._values[pk], FlatDict): raise TypeError( 'Assignment to invalid type for key {}'.format(pk)) self._values[pk][ck] = value else: self._values[key] = value def __str__(self): """Return the string value of the instance. :rtype: str """ return '{{{}}}'.format(', '.join( ['{!r}: {!r}'.format(k, self[k]) for k in self.keys()])) def as_dict(self): """Return the :class:`~flatdict.FlatDict` as a :class:`dict` :rtype: dict """ out = dict({}) for key in self.keys(): if self._has_delimiter(key): pk, ck = key.split(self._delimiter, 1) if self._has_delimiter(ck): ck = ck.split(self._delimiter, 1)[0] if isinstance(self._values[pk], FlatDict) and pk not in out: out[pk] = {} if isinstance(self._values[pk][ck], FlatDict): out[pk][ck] = self._values[pk][ck].as_dict() else: out[pk][ck] = self._values[pk][ck] else: out[key] = self._values[key] return out def clear(self): """Remove all items from the flat dictionary.""" self._values.clear() def copy(self): """Return a shallow copy of the flat dictionary. :rtype: flatdict.FlatDict """ return self.__class__(self.as_dict(), delimiter=self._delimiter) def get(self, key, d=None): """Return the value for key if key is in the flat dictionary, else default. If default is not given, it defaults to ``None``, so that this method never raises :exc:`KeyError`. :param mixed key: The key to get :param mixed d: The default value :rtype: mixed """ try: return self.__getitem__(key) except KeyError: return d def items(self): """Return a copy of the flat dictionary's list of ``(key, value)`` pairs. .. note:: CPython implementation detail: Keys and values are listed in an arbitrary order which is non-random, varies across Python implementations, and depends on the flat dictionary's history of insertions and deletions. :rtype: list """ return [(k, self.__getitem__(k)) for k in self.keys()] def iteritems(self): """Return an iterator over the flat dictionary's (key, value) pairs. See the note for :meth:`flatdict.FlatDict.items`. Using ``iteritems()`` while adding or deleting entries in the flat dictionary may raise :exc:`RuntimeError` or fail to iterate over all entries. :rtype: Iterator :raises: RuntimeError """ for item in self.items(): yield item def iterkeys(self): """Iterate over the flat dictionary's keys. See the note for :meth:`flatdict.FlatDict.items`. Using ``iterkeys()`` while adding or deleting entries in the flat dictionary may raise :exc:`RuntimeError` or fail to iterate over all entries. :rtype: Iterator :raises: RuntimeError """ for key in self.keys(): yield key def itervalues(self): """Return an iterator over the flat dictionary's values. See the note :meth:`flatdict.FlatDict.items`. Using ``itervalues()`` while adding or deleting entries in the flat dictionary may raise a :exc:`RuntimeError` or fail to iterate over all entries. :rtype: Iterator :raises: RuntimeError """ for value in self.values(): yield value def keys(self): """Return a copy of the flat dictionary's list of keys. See the note for :meth:`flatdict.FlatDict.items`. :rtype: list """ keys = [] for key, value in self._values.items(): if isinstance(value, (FlatDict, dict)): nested = [ self._delimiter.join([str(key), str(k)]) for k in value.keys()] keys += nested if nested else [key] else: keys.append(key) return keys def pop(self, key, default=NO_DEFAULT): """If key is in the flat dictionary, remove it and return its value, else return default. If default is not given and key is not in the dictionary, :exc:`KeyError` is raised. :param mixed key: The key name :param mixed default: The default value :rtype: mixed """ if key not in self and default != NO_DEFAULT: return default value = self[key] self.__delitem__(key) return value def setdefault(self, key, default): """If key is in the flat dictionary, return its value. If not, insert key with a value of default and return default. default defaults to ``None``. :param mixed key: The key name :param mixed default: The default value :rtype: mixed """ if key not in self: self.__setitem__(key, default) return self.__getitem__(key) def set_delimiter(self, delimiter): """Override the default or passed in delimiter with a new value. If the requested delimiter already exists in a key, a :exc:`ValueError` will be raised. :param str delimiter: The delimiter to use :raises: ValueError """ for key in self.keys(): if delimiter in key: raise ValueError('Key {!r} collides with delimiter {!r}', key, delimiter) self._delimiter = delimiter for key in self._values.keys(): if isinstance(self._values[key], FlatDict): self._values[key].set_delimiter(delimiter) def update(self, other=None, **kwargs): """Update the flat dictionary with the key/value pairs from other, overwriting existing keys. ``update()`` accepts either another flat dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, the flat dictionary is then updated with those key/value pairs: ``d.update(red=1, blue=2)``. :param iterable other: Iterable of key, value pairs :rtype: None """ [self.__setitem__(k, v) for k, v in dict(other or kwargs).items()] def values(self): """Return a copy of the flat dictionary's list of values. See the note for :meth:`flatdict.FlatDict.items`. :rtype: list """ return [self.__getitem__(k) for k in self.keys()] def _has_delimiter(self, key): """Checks to see if the key contains the delimiter. :rtype: bool """ return isinstance(key, str) and self._delimiter in key class FlatterDict(FlatDict): """Like :class:`~flatdict.FlatDict` but also coerces lists and sets to child-dict instances with the offset as the key. Alternative to the implementation added in v1.2 of FlatDict. """ _COERCE = list, tuple, set, dict, FlatDict _ARRAYS = list, set, tuple def __init__(self, value=None, delimiter=':', dict_class=dict): self.original_type = type(value) if self.original_type in self._ARRAYS: value = {str(i): v for i, v in enumerate(value)} super(FlatterDict, self).__init__(value, delimiter, dict_class) def __setitem__(self, key, value): """Assign the value to the key, dynamically building nested FlatDict items where appropriate. :param mixed key: The key for the item :param mixed value: The value for the item :raises: TypeError """ if isinstance(value, self._COERCE) and \ not isinstance(value, FlatterDict): value = self.__class__(value, self._delimiter) if self._has_delimiter(key): pk, ck = key.split(self._delimiter, 1) if pk not in self._values: self._values[pk] = self.__class__({ck: value}, self._delimiter) return if getattr(self._values[pk], 'original_type', None) in self._ARRAYS: try: k, cck = ck.split(self._delimiter, 1) int(k) except ValueError: raise TypeError( 'Assignment to invalid type for key {}{}{}'.format( pk, self._delimiter, ck)) self._values[pk][k][cck] = value return elif not isinstance(self._values[pk], FlatterDict): raise TypeError( 'Assignment to invalid type for key {}'.format(pk)) self._values[pk][ck] = value else: self._values[key] = value def as_dict(self): """Return the :class:`~flatdict.FlatterDict` as a nested :class:`dict`. :rtype: dict """ out = {} for key in self.keys(): if self._has_delimiter(key): pk, ck = key.split(self._delimiter, 1) if self._has_delimiter(ck): ck = ck.split(self._delimiter, 1)[0] if isinstance(self._values[pk], FlatterDict) and pk not in out: if self._values[pk].original_type == tuple: out[pk] = tuple(self._child_as_list(pk)) elif self._values[pk].original_type == list: out[pk] = self._child_as_list(pk) elif self._values[pk].original_type == set: out[pk] = set(self._child_as_list(pk)) elif self._values[pk].original_type == dict: out[pk] = self._values[pk].as_dict() else: if isinstance(self._values[key], FlatterDict): out[key] = self._values[key].original_type() else: out[key] = self._values[key] return out def _child_as_list(self, pk, ck=None): """Returns a list of values from the child FlatterDict instance with string based integer keys. :param str pk: The parent key :param str ck: The child key, optional :rtype: list """ if ck is None: subset = self._values[pk] else: subset = self._values[pk][ck] # Check if keys has delimiter, which implies deeply nested dict keys = subset.keys() if any(self._has_delimiter(k) for k in keys): out = [] split_keys = {k.split(self._delimiter)[0] for k in keys} for k in sorted(split_keys, key=lambda x: int(x)): if subset[k].original_type == tuple: out.append(tuple(self._child_as_list(pk, k))) elif subset[k].original_type == list: out.append(self._child_as_list(pk, k)) elif subset[k].original_type == set: out.append(set(self._child_as_list(pk, k))) elif subset[k].original_type == dict: out.append(subset[k].as_dict()) return out # Python prior 3.6 does not guarantee insertion order, remove it after # EOL python 3.5 - 2020-09-13 if sys.version_info[0:2] < (3, 6): # pragma: nocover return [subset[k] for k in sorted(keys, key=lambda x: int(x))] else: return [subset[k] for k in keys] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1581621372.8737347 flatdict-4.0.1/setup.cfg0000644000000000000000000000263300000000000015072 0ustar00rootroot00000000000000[metadata] name = flatdict version = attr: flatdict.__version__ description = Python module for interacting with nested dicts as a single level dict with delimited keys. long_description = file: README.rst, LICENSE.rst url = https://github.com/gmr/flatdict author = Gavin M. Roy author_email = gavinmroy@gmail.com license = BSD 3-Clause License classifiers = Topic :: Software Development :: Libraries Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: POSIX Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy [options] include_package_data = True py_modules = flatdict zip_safe = True [options.package_data] * = CHANGELOG.md, README.rst, LICENSE [flake8] application-import-names = flatdict exclude = ci, docs, env ignore = RST304 import-order-style = google [coverage:run] branch = True command_line = -m unittest discover [coverage:report] show_missing = True omit = tests.py [coverage:html] directory = build/coverage [coverage:xml] output = build/coverage.xml [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1581621371.0 flatdict-4.0.1/setup.py0000644000000000000000000000041600000000000014760 0ustar00rootroot00000000000000import pkg_resources import setuptools setuptools_version = pkg_resources.parse_version(setuptools.__version__) if setuptools_version < pkg_resources.parse_version('39.2'): raise SystemExit('setuptools 39.2 or greater required for installation') setuptools.setup()