pax_global_header00006660000000000000000000000064136563313000014512gustar00rootroot0000000000000052 comment=7e9d6db2d0b8538f1752ec851bf37d23acc44dfa python-mergedict-1.0.0/000077500000000000000000000000001365633130000147725ustar00rootroot00000000000000python-mergedict-1.0.0/.gitignore000066400000000000000000000001061365633130000167570ustar00rootroot00000000000000*.pyc __pycache__ .doit.db* .coverage dist mergedict.egg-info MANIFESTpython-mergedict-1.0.0/.travis.yml000066400000000000000000000007751365633130000171140ustar00rootroot00000000000000language: python python: - "3.5" - "3.6" - "3.7" - "3.8" - "3.9-dev" - "pypy3" branches: only: - master - test install: - pip install . - pip install -r dev_requirements.txt - pip install python-coveralls script: - doit --continue - if [[ $TRAVIS_PYTHON_VERSION == '3.8' ]]; then doit coverage; fi after_success: - if [[ $TRAVIS_PYTHON_VERSION == '3.8' ]]; then coveralls; fi notifications: email: on_success: change on_failure: change python-mergedict-1.0.0/CHANGES000066400000000000000000000012371365633130000157700ustar00rootroot00000000000000======= Changes ======= 1.0.0 (2020-01-28) ===================== - Drop support for Python 2.7 - Drop support for Python 3.3 - Drop support for Python 3.4 0.3.0 (2020-01-28) ===================== - No longer depend on singledispatch for Python 3.4 and after - Add support for Python 3.5, 3.6, 3.7 and 3.8 0.2.0 (2014-07-21) ===================== - Merge() implements same signature as dict.update() - Fix for dispatch methods defined in a hierarchy - Drop support for Python 2.6 - Drop support for Python 3.2 0.1.2 (2014-07-07) ====================== - Fix "pip install mergedict" (#1) 0.1.1 (2013-12-15) ====================== - Initial release python-mergedict-1.0.0/LICENSE000066400000000000000000000021611365633130000157770ustar00rootroot00000000000000mergedict - A Python `dict` with a merge() method The MIT License Copyright (c) 2013 Eduardo Naufel Schettino Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-mergedict-1.0.0/README.rst000066400000000000000000000034201365633130000164600ustar00rootroot00000000000000mergedict - A Python `dict` with a merge() method =================================================== .. display some badges .. image:: https://travis-ci.org/schettino72/mergedict.png?branch=master :target: https://travis-ci.org/schettino72/mergedict .. image:: https://coveralls.io/repos/schettino72/mergedict/badge.png :target: https://coveralls.io/r/schettino72/mergedict A MergeDict is a `dict` with a `merge()` method. `merge()` is like `dict.update()`... :: from mergedict import MergeDict d1 = MergeDict({'a': 1, 'b': 'one'}) d1.merge({'a':2, 'c': [2]}) assert d1 == {'a': 2, 'c': [2], 'b': 'one'} A MergeDict can be subclassed to create custom "merge" operations based on the type of an item value. :: from mergedict import MergeDict class SumDict(MergeDict): @MergeDict.dispatch(int) def merge_int(this, other): return this + other d2 = SumDict({'a': 1, 'b': 'one'}) d2.merge({'a':2, 'b': 'two'}) assert d2 == {'a': 3, 'b': 'two'} `mergedict` module comes with a `ConfigDict` that will extend/update lists/sets/dicts. :: from mergedict import ConfigDict d3 = ConfigDict({'a': 1, 'my_list': [1, 2]}) d3.merge({'a':2, 'my_list': [3, 4]}) assert d3 == {'a': 2, 'my_list': [1, 2, 3, 4]} Project Details =============== - Project management on github - https://github.com/schettino72/mergedict/ license ======= The MIT License Copyright (c) 2013 Eduardo Naufel Schettino see LICENSE file developers / contributors ========================== - Eduardo Naufel Schettino — main author - Sebastian Pipping — build system fixes install ======= :: $ pip install mergedict or download and:: $ python setup.py install tests ======= To run the tests:: $ py.test python-mergedict-1.0.0/dev_requirements.txt000066400000000000000000000003061365633130000211130ustar00rootroot00000000000000# modules required for development only # $ pip install --requirement dev_requirements.txt pyflakes pytest coverage<5 # due to https://github.com/z4r/python-coveralls/issues/73 doit>=0.30 doit-py python-mergedict-1.0.0/dodo.py000066400000000000000000000014611365633130000162730ustar00rootroot00000000000000from doitpy.pyflakes import Pyflakes DOIT_CONFIG = {'default_tasks': ['pyflakes', 'test']} def task_pyflakes(): yield Pyflakes().tasks('*.py') def task_test(): return { 'actions': ['py.test'], 'file_dep': ['mergedict.py', 'test_mergedict.py'], } def task_coverage(): return { 'actions': [ 'coverage run --source=mergedict,test_mergedict `which py.test`', 'coverage report --show-missing'], 'verbosity': 2, } def task_manifest(): """create manifest file for distutils """ cmd = "git ls-tree --name-only -r HEAD > MANIFEST" return {'actions': [cmd]} def task_pypi(): """upload package to pypi""" return { 'actions': ["python setup.py sdist upload"], 'task_dep': ['manifest'], } python-mergedict-1.0.0/mergedict.py000066400000000000000000000071551365633130000173170ustar00rootroot00000000000000"""mergedict - A Python `dict` with a merge() method.""" # The MIT License # Copyright (c) 2013 Eduardo Naufel Schettino # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. __version__ = (1, 0, 0) import sys import inspect from functools import singledispatch class MergeDict(dict): """Base class for a dict that implements a merge() method. What exactly merge() depends on the subclass... """ def __init__(self, *args, **kwargs): super(MergeDict, self).__init__(*args, **kwargs) # register singlesingle dispatch methods self.merge_value = singledispatch(self.merge_value) # python 2 version if sys.version_info[0] < 3: # pragma: no cover for name, val in inspect.getmembers(self.__class__): _type = getattr(val, 'merge_dispatch', None) if _type: self.merge_value.register(_type, val.__func__) return # python 3 version for name, val in inspect.getmembers(self.__class__, inspect.isfunction): _type = getattr(val, 'merge_dispatch', None) if _type: self.merge_value.register(_type, val) def merge(self, *args, **kwargs): """merge other dict into self""" class Sentinel: pass if args: if len(args) > 1: msg = "merge() expected at most 1 arguments, got {}." raise TypeError(msg.format(len(args))) other = args[0] else: other = kwargs for key, other_value in other.items(): this_value = self.get(key, Sentinel) if this_value is Sentinel: self[key] = other_value else: self[key] = self.merge_value(this_value, other_value) class dispatch: """decorator to mark methods as single dispatch functions.""" def __init__(self, _type): self._type = _type def __call__(self, func): func.merge_dispatch = self._type return func @staticmethod def merge_value(this, other): """default merge operation, just replace the value""" return other class ConfigDict(MergeDict): """A MergeDict that on merge() extend/update lists/sets/dicts. Other values are over-written as in dict.update() """ @MergeDict.dispatch(list) def merge_list(this, other): this.extend(other) return this @MergeDict.dispatch(set) def merge_set(this, other): this.update(other) return this @MergeDict.dispatch(dict) def merge_dict(this, other): this.update(other) return this python-mergedict-1.0.0/setup.py000066400000000000000000000026031365633130000165050ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- import os import codecs from setuptools import setup with codecs.open( os.path.join(os.path.dirname(__file__), 'README.rst'), 'r', 'utf8', ) as ld_file: long_description = ld_file.read() setup ( name = 'mergedict', version = '1.0.0', author = 'Eduardo Naufel Schettino', author_email = 'schettino72@gmail.com', description = 'A Python `dict` with a merge() method.', long_description = long_description, url = 'https://github.com/schettino72/mergedict/', keywords = ['dict', 'singledispatch', 'config'], platforms = ['any'], license = 'MIT', py_modules = ['mergedict'], python_requires='>=3.4', classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Operating System :: OS Independent', '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 :: Only', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', ] ) python-mergedict-1.0.0/test_mergedict.py000066400000000000000000000041721365633130000203520ustar00rootroot00000000000000from mergedict import MergeDict, ConfigDict import pytest class TestMergeDict(): def test_default_merge_is_like_update(self): d1 = MergeDict({'a': 1, 'b': 'one', 'c': [1]}) d1.merge({'a':2, 'c': [2]}) assert d1 == {'a': 2, 'b': 'one', 'c': [2]} def test_custom_merge(self): class SumDict(MergeDict): @MergeDict.dispatch(int) def merge_int(this, other): return this + other d1 = SumDict({'a': 1, 'b': 'one', 'c': [1]}) d1.merge({'a':2, 'c': [2]}) assert d1 == {'a': 3, 'b': 'one', 'c': [2]} # make sure subclass doesnt mess up with MergeDict d2 = MergeDict({'a': 1, 'b': 'one', 'c': [1]}) d2.merge({'a':2, 'c': [2]}) assert d2 == {'a': 2, 'b': 'one', 'c': [2]} def test_hierarchy(self): class SumDict(MergeDict): @MergeDict.dispatch(int) def merge_int(this, other): return this + other class ConcatDict(SumDict): @MergeDict.dispatch(str) def merge_str(this, other): return this + other d1 = ConcatDict({'a': 1, 'b': 'one', 'c': [1]}) d1.merge({'a':2, 'b': '-two'}) assert d1 == {'a': 3, 'b': 'one-two', 'c': [1]} def test_update_error_two_args(self): config = MergeDict({'foo': 'bar'}) pytest.raises(TypeError, config.merge, {'foo': 'baz'}, {'foo': 'baz2'}) def test_merge_keyword(self): config = MergeDict({'foo': 'bar'}) config.merge(foo='baz') assert config['foo'] == 'baz' def test_ConfigDict(): d1 = ConfigDict({ 'a': 1, 'b': 'one', 'c': [1], 'd': {'foo': 'x1', 'bar': 'x2'}, 'e': set((3, 5)), }) d1.merge({ 'a': 2, 'c': [1, 2], 'd': {'bar': 'y2', 'baz': 'y3'}, 'e': set((3, 6)), 'f': 'func', }) assert d1 == { 'a': 2, 'b': 'one', 'c': [1, 1, 2], 'd': {'foo': 'x1', 'bar': 'y2', 'baz': 'y3'}, 'e': set((3, 5, 6)), 'f': 'func', }