]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | # -*- coding: utf-8 -*- |
2 | from __future__ import absolute_import | |
3 | ||
4 | try: | |
5 | from typing import Dict, Any # pylint: disable=unused-import | |
6 | except ImportError: | |
7 | pass | |
8 | ||
9 | ||
10 | def update_dict(data, update_data): | |
11 | # type: (Dict[Any, Any], Dict[Any, Any]) -> Dict[Any] | |
12 | """ Update a dictionary recursively. | |
13 | ||
14 | Eases doing so by providing the option to separate the key to be updated by dot characters. If | |
15 | a key provided does not exist, it will raise an KeyError instead of just updating the | |
16 | dictionary. | |
17 | ||
18 | Limitations | |
19 | ||
20 | Please note that the functionality provided by this method can only be used if the dictionary to | |
21 | be updated (`data`) does not contain dot characters in its keys. | |
22 | ||
23 | :raises KeyError: | |
24 | ||
25 | >>> update_dict({'foo': {'bar': 5}}, {'foo.bar': 10}) | |
26 | {'foo': {'bar': 10}} | |
27 | ||
28 | >>> update_dict({'foo': {'bar': 5}}, {'xyz': 10}) | |
29 | Traceback (most recent call last): | |
30 | ... | |
31 | KeyError: 'xyz' | |
32 | ||
33 | >>> update_dict({'foo': {'bar': 5}}, {'foo.xyz': 10}) | |
34 | Traceback (most recent call last): | |
35 | ... | |
36 | KeyError: 'xyz' | |
37 | """ | |
38 | for k, v in update_data.items(): | |
39 | keys = k.split('.') | |
40 | element = None | |
41 | for i, key in enumerate(keys): | |
42 | last = False | |
43 | if len(keys) == i + 1: | |
44 | last = True | |
45 | ||
46 | if not element: | |
47 | element = data[key] | |
48 | elif not last: | |
49 | element = element[key] # pylint: disable=unsubscriptable-object | |
50 | ||
51 | if last: | |
52 | if key not in element: | |
53 | raise KeyError(key) | |
54 | ||
55 | element[key] = v | |
56 | return data |