blob: 322ac71abef5834604ba106fef85243d869eb17a [file] [log] [blame]
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +03001import os
2import json
3import yaml
4
Dennis Dmitrievde847d92017-06-26 18:58:05 +03005
6def get_nested_key(data, path=None):
7 if type(path) is not list:
8 raise("Use 'list' object with key names for 'path'")
9 for key in path:
10 value = data.get(key, None)
11 if value:
12 data = value
13 else:
14 return None
15 return data
16
17
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030018def create_nested_key(data, path=None, value=None):
19 if type(data) is not dict:
20 raise("Use 'dict' object for 'data'")
21 if type(path) is not list:
22 raise("Use 'list' object with key names for 'path'")
23 for key in path[:-1]:
24 if key not in data:
25 data[key] = {}
26 data = data[key]
27 data[path[-1]] = value
28
29
Dennis Dmitrievde847d92017-06-26 18:58:05 +030030def remove_nested_key(data, path=None):
31 if type(path) is not list:
32 raise("Use 'list' object with key names for 'path'")
33
34 # Remove the value from the specified key
35 val = get_nested_key(data, path[:-1])
36 val[path[-1]] = None
37
38 # Clear parent keys if empty
39 while path:
40 val = get_nested_key(data, path)
41 if val:
42 # Non-empty value, nothing to do
43 return
44 else:
45 get_nested_key(data, path[:-1]).pop(path[-1])
46 path = path[:-1]
47
48
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030049def yaml_read(yaml_file):
50 if os.path.isfile(yaml_file):
51 with open(yaml_file, 'r') as f:
52 return yaml.load(f)
53 else:
54 print("\'{}\' is not a file!".format(yaml_file))
55
56
57def json_read(yaml_file):
58 if os.path.isfile(yaml_file):
59 with open(yaml_file, 'r') as f:
60 return json.load(f)
61 else:
62 print("\'{}\' is not a file!".format(yaml_file))
63
64
65def merge_nested_objects(obj_1, obj_2):
66 """Merge two objects with optional key overwrites
67
68 Original : https://stackoverflow.com/a/17860173
69 - Merges dicts and lists
70 - If a dict key has the suffix '__overwrite__' and boolean value,
71 then the key is assumed as a special keyword for merging:
72 <key>__overwrite__: True # Overwrite the existing <key> content with <key> from obj_2
73 <key>__overwrite__: False # Keep the existing <key> content from obj_1
74
75
76 Case #1: Merge dicts and lists, overwrite other types with latest value
77
78 dict_a = {
79 'host': '1.1.1.1',
80 'ssh': {
81 'login': 'user'
82 }
83 }
84
85 dict_b = {
86 'host': '2.2.2.2',
87 'ssh': {
88 'password': 'pass'
89 }
90 }
91
92 print(merge_nested_objects(dict_a, dict_b))
93 {
94 'host': '2.2.2.2',
95 'ssh': {
96 'login': 'user',
97 'password': 'pass',
98 }
99 }
100
101 Case #2: Use <key>__overwrite__: True to remove previous key content
102
103 dict_a = {
104 'host': '1.1.1.1'
105 'ssh': {
106 'login': 'user'
107 }
108 }
109
110 dict_b = {
111 'ssh__overwrite__': True
112 'ssh': {
113 'password': 'pass'
114 }
115 }
116
117 print(merge_nested_objects(dict_a, dict_b))
118 {
119 'host': '1.1.1.1',
120 'ssh': {
121 'password': 'pass',
122 }
123 }
124
125 Case #3: Use <key>__overwrite__: False to skip merging key if already exists
126
127 dict_a = {
128 'host': '1.1.1.1'
129 'ssh': {
130 'login': 'user'
131 }
132 }
133
134 dict_b = {
135 'host__overwrite__': False
136 'host': '2.2.2.2'
137 'ssh': {
138 'login__overwrite__': False
139 'login': 'new_user'
140 'password': 'pass'
141 }
142 }
143
144 print(merge_nested_objects(dict_a, dict_b))
145 {
146 'host': '1.1.1.1',
147 'ssh': {
148 'login': 'user',
149 'password': 'pass'
150 }
151 }
152
153
154 """
155 # Merge two dicts
156 if isinstance(obj_1, dict) and isinstance(obj_2, dict):
157 result = {}
158 for key, value in obj_1.iteritems():
159 if key not in obj_2:
160 result[key] = value
161 else:
162 overwrite_key = key + '__overwrite__'
163 if overwrite_key in obj_2 and obj_2[overwrite_key] == True:
164 result[key] = obj_2[key]
165 elif overwrite_key in obj_2 and obj_2[overwrite_key] == False:
166 result[key] = value
167 else:
168 result[key] = merge_nested_objects(value, obj_2[key])
169 for key, value in obj_2.iteritems():
170 if key not in obj_1:
171 result[key] = value
172 return result
173
174 # Add two lists
175 if isinstance(obj_1, list) and isinstance(obj_2, list):
176 return obj_1 + obj_2
177
178 # Overwrite a value with new one
179 return obj_2