# HG changeset patch # User Franz Glasner # Date 1521913674 -3600 # Node ID d51a18e5b0e3bc780a81419af4699dcad8fe5907 # Parent 29cf359ddf4d87e1206d8c5c806a0ed451fd67e5 Reimplement configmix.safe_merge() do to a deepcopy of all source configurations when merging. configmix.safe_merge() does now a deepcopy of all source configurations when merging. Changes in configuration instances after will not be reflected in the merged configuration any more. diff -r 29cf359ddf4d -r d51a18e5b0e3 CHANGES.txt --- a/CHANGES.txt Sat Mar 24 16:06:08 2018 +0100 +++ b/CHANGES.txt Sat Mar 24 18:47:54 2018 +0100 @@ -1,7 +1,7 @@ .. -*- coding: utf-8; mode: rst; indent-tabs-mode: nil; -*- .. -.. Valid tags: doc, feature, bugfix, test +.. Valid tags: doc, feature, bugfix, test, breaking .. .. _changelog: @@ -16,6 +16,12 @@ :version: 0.6 .. change:: + :tags: breaking, feature + + Reimplement :py:func:`configmix.safe_merge` do to a deepcopy of all + source configurations when merging + + .. change:: :tags: doc Begin the documentation with `Sphinx `_ @@ -30,6 +36,7 @@ Build a tree of configuration settings from INI files + .. changelog:: :version: 0.5 :released: 2016-04-19 diff -r 29cf359ddf4d -r d51a18e5b0e3 configmix/__init__.py --- a/configmix/__init__.py Sat Mar 24 16:06:08 2018 +0100 +++ b/configmix/__init__.py Sat Mar 24 18:47:54 2018 +0100 @@ -124,17 +124,34 @@ def safe_merge(user, default): - """A more safe version of :func:`merge` that makes shallow copies of + """A more safe version of :func:`merge` that makes deep copies of the returned container objects. + No given argument is ever changed inplace. Every object from `default` + is decoupled from the result -- so changing the `default` configuration + lates does not yield into a merged configuration later. + """ if user is None: - return copy.copy(default) - user = copy.copy(user) + return copy.deepcopy(default) + user = copy.deepcopy(user) if isinstance(user, dict) and isinstance(default, dict): for k, v in default.items(): if k not in user: - user[k] = copy.copy(v) + user[k] = copy.deepcopy(v) else: - user[k] = _merge(user[k], v) + user[k] = _safe_merge(user[k], v) return user + + +def _safe_merge(user, default): + """Recursion helper for :meth:`safe_merge` + + """ + if isinstance(user, dict) and isinstance(default, dict): + for k, v in default.items(): + if k not in user: + user[k] = copy.deepcopy(v) + else: + user[k] = _safe_merge(user[k], v) + return user diff -r 29cf359ddf4d -r d51a18e5b0e3 doc/changes.rst --- a/doc/changes.rst Sat Mar 24 16:06:08 2018 +0100 +++ b/doc/changes.rst Sat Mar 24 18:47:54 2018 +0100 @@ -4,7 +4,7 @@ Changes ========= -All major changes over the versions are listed here. For API breaking +All major changes over the versions are listed here. For breaking changes have a look at :ref:`api-changes`, they are listed there in detail. @@ -13,7 +13,15 @@ .. _api-changes: -API Breaking Changes -==================== +Breaking Changes +================ + +0.6 +--- -No incompatible changes yet. +- :py:func:`configmix.safe_merge` does now a deepcopy of all source + configurations when merging. Changes in configuration instances after + will not be reflected in the merged configuration any more. + + The public signature of :py:func:`configmix.safe_merge` has *not* + changed. diff -r 29cf359ddf4d -r d51a18e5b0e3 doc/conf.py --- a/doc/conf.py Sat Mar 24 16:06:08 2018 +0100 +++ b/doc/conf.py Sat Mar 24 18:47:54 2018 +0100 @@ -211,4 +211,4 @@ # -- Options for changelog --------------------------------------------------- -changelog_inner_tag_sort = ['feature', 'bugfix', 'test', 'doc'] +changelog_inner_tag_sort = ['breaking', 'feature', 'bugfix', 'test', 'doc']