(to, fro, to_file, fro_file)
| 2270 | |
| 2271 | |
| 2272 | def MergeDicts(to, fro, to_file, fro_file): |
| 2273 | # I wanted to name the parameter "from" but it's a Python keyword... |
| 2274 | for k, v in fro.items(): |
| 2275 | # It would be nice to do "if not k in to: to[k] = v" but that wouldn't give |
| 2276 | # copy semantics. Something else may want to merge from the |fro| dict |
| 2277 | # later, and having the same dict ref pointed to twice in the tree isn't |
| 2278 | # what anyone wants considering that the dicts may subsequently be |
| 2279 | # modified. |
| 2280 | if k in to: |
| 2281 | bad_merge = False |
| 2282 | if type(v) in (str, int): |
| 2283 | if type(to[k]) not in (str, int): |
| 2284 | bad_merge = True |
| 2285 | elif not isinstance(v, type(to[k])): |
| 2286 | bad_merge = True |
| 2287 | |
| 2288 | if bad_merge: |
| 2289 | raise TypeError( |
| 2290 | "Attempt to merge dict value of type " |
| 2291 | + v.__class__.__name__ |
| 2292 | + " into incompatible type " |
| 2293 | + to[k].__class__.__name__ |
| 2294 | + " for key " |
| 2295 | + k |
| 2296 | ) |
| 2297 | if type(v) in (str, int): |
| 2298 | # Overwrite the existing value, if any. Cheap and easy. |
| 2299 | is_path = IsPathSection(k) |
| 2300 | if is_path: |
| 2301 | to[k] = MakePathRelative(to_file, fro_file, v) |
| 2302 | else: |
| 2303 | to[k] = v |
| 2304 | elif isinstance(v, dict): |
| 2305 | # Recurse, guaranteeing copies will be made of objects that require it. |
| 2306 | if k not in to: |
| 2307 | to[k] = {} |
| 2308 | MergeDicts(to[k], v, to_file, fro_file) |
| 2309 | elif isinstance(v, list): |
| 2310 | # Lists in dicts can be merged with different policies, depending on |
| 2311 | # how the key in the "from" dict (k, the from-key) is written. |
| 2312 | # |
| 2313 | # If the from-key has ...the to-list will have this action |
| 2314 | # this character appended:... applied when receiving the from-list: |
| 2315 | # = replace |
| 2316 | # + prepend |
| 2317 | # ? set, only if to-list does not yet exist |
| 2318 | # (none) append |
| 2319 | # |
| 2320 | # This logic is list-specific, but since it relies on the associated |
| 2321 | # dict key, it's checked in this dict-oriented function. |
| 2322 | ext = k[-1] |
| 2323 | append = True |
| 2324 | if ext == "=": |
| 2325 | list_base = k[:-1] |
| 2326 | lists_incompatible = [list_base, list_base + "?"] |
| 2327 | to[list_base] = [] |
| 2328 | elif ext == "+": |
| 2329 | list_base = k[:-1] |
no test coverage detected