|
6 | 6 | Metadata class of OneLogin's Python Toolkit. |
7 | 7 | """ |
8 | 8 |
|
| 9 | + |
| 10 | +from copy import deepcopy |
| 11 | + |
| 12 | + |
9 | 13 | try: |
10 | 14 | import urllib.request as urllib2 |
11 | 15 | except ImportError: |
@@ -174,6 +178,35 @@ def merge_settings(settings, new_metadata_settings): |
174 | 178 | :returns: merged settings |
175 | 179 | :rtype: dict |
176 | 180 | """ |
177 | | - result_settings = settings.copy() |
178 | | - result_settings.update(new_metadata_settings) |
| 181 | + for d in (settings, new_metadata_settings): |
| 182 | + if not isinstance(d, dict): |
| 183 | + raise TypeError('Both arguments must be dictionaries.') |
| 184 | + |
| 185 | + # Guarantee to not modify original data (`settings.copy()` would not |
| 186 | + # be sufficient, as it's just a shallow copy). |
| 187 | + result_settings = deepcopy(settings) |
| 188 | + # Merge `new_metadata_settings` into `result_settings`. |
| 189 | + dict_deep_merge(result_settings, new_metadata_settings) |
179 | 190 | return result_settings |
| 191 | + |
| 192 | + |
| 193 | +def dict_deep_merge(a, b, path=None): |
| 194 | + """Deep-merge dictionary `b` into dictionary `a`. |
| 195 | +
|
| 196 | + Kudos to http://stackoverflow.com/a/7205107/145400 |
| 197 | + """ |
| 198 | + if path is None: |
| 199 | + path = [] |
| 200 | + for key in b: |
| 201 | + if key in a: |
| 202 | + if isinstance(a[key], dict) and isinstance(b[key], dict): |
| 203 | + dict_deep_merge(a[key], b[key], path + [str(key)]) |
| 204 | + elif a[key] == b[key]: |
| 205 | + # Key conflict, but equal value. |
| 206 | + pass |
| 207 | + else: |
| 208 | + # Key/value conflict. Prioritize b over a. |
| 209 | + a[key] = b[key] |
| 210 | + else: |
| 211 | + a[key] = b[key] |
| 212 | + return a |
0 commit comments