-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmodule_parameters.py
337 lines (283 loc) · 12.9 KB
/
module_parameters.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
"""
.. todo:: This is a copy from mermaid of the same file. Link this later to the actual mermaid install and do not copy this file.
This package implements a simple way of dealing with parameters, ofproviding
default parameters and comments, and to keep track of used parameters for
registration runs. See the corresponding note for a brief description on how to use it.
"""
from __future__ import print_function
# from builtins import str
# from builtins import object
import json
class ParameterDict(object):
def __init__(self, initDict=None, printSettings=True):
if initDict is not None:
if type(initDict) == type(self):
self.ext = initDict.ext
else:
print('WARNING: Cannot initialize from non ParameterDict object. Ignoring initialization.')
self.ext = {}
else:
self.ext = {}
self.int = {}
self.com = {}
self.currentCategoryName = 'root'
self.printSettings = printSettings
def __str__(self):
return 'ext = ' + "\n" + json.dumps(self.ext, indent=4, sort_keys=True) + "\n" + \
'int = ' + "\n" + json.dumps(self.int, indent=4, sort_keys=True) + "\n" + \
'com = ' + "\n" + json.dumps(self.com, indent=4, sort_keys=True) + "\n" + \
'currentCategoryName = ' + str(self.currentCategoryName) + "\n"
def isempty(self):
return self.int == {}
def load_JSON(self, fileName):
"""
Loads a JSON configuration file
:param fileName: filename of the configuration to be loaded
"""
try:
with open(fileName) as data_file:
if self.printSettings:
print('Loading parameter file = ' + fileName)
self.ext = json.load(data_file)
except IOError as e:
print('Could not open file = ' + fileName + '; ignoring request.')
def write_JSON_and_JSON_comments(self, fileNames):
"""
Writes the JSON configuration to a file
:param fileNames: filename tuple; first entry is filename for configuration, second for comments
"""
self.write_JSON(fileNames[0])
self.write_JSON_comments(fileNames[1])
def write_JSON(self, fileName):
"""
Writes the JSON configuration to a file
:param fileName: filename to write the configuration to
"""
with open(fileName, 'w') as outfile:
if self.printSettings:
print('Writing parameter file = ' + fileName)
json.dump(self.int, outfile, indent=4, sort_keys=True)
def write_JSON_comments(self, fileNameComments):
"""
Writes the JSON commments file. This file will not contain any actual values, but
instead descriptions of the settings (if they have been provided). The goal is to
provide self-documenting configuration files.
:param fileNameComments: filename to write the commented JSON configuration to
"""
with open(fileNameComments, 'w') as outfile:
if self.printSettings:
print('Writing parameter file = ' + fileNameComments)
json.dump(self.com, outfile, indent=4, sort_keys=True)
def print_settings_on(self):
"""
Enable screen output as configurations are read and set.
"""
self.printSettings = True
def print_settings_off(self):
"""
Disable screen output as configurations are read and set.
"""
self.printSettings = False
def get_print_settings(self):
"""
Current print settings
:return: Returns if screen printing is on (True) or off (False)
"""
return self.printSettings
def set_print_settings(self, val):
"""
Sets Current print settings
:param val: value to set the print settings to, needs to be boolean
:return: n/a
"""
self.printSettings = val
def _set_value_of_instance(self, ext, int, com, currentCategoryName):
self.ext = ext
self.int = int
self.com = com
self.currentCategoryName = currentCategoryName
def __missing__(self, key):
# if key cannot be found
raise ValueError('Could not find key = ' + str(key))
def __getitem__(self, key_or_keyTuple):
# getting an item based on key
# here the key can be three different things
# 1) simply a text key (then returns the current value)
# 2) A 2-tuple (keyname,defaultvalue)
# 3) A 3-tuple (keyname,defaultvalue,comment)
# returns a ParDicts object if we are accessing a category (i.e., a dictionary)
# returns just the value if it is a regular value
if type(key_or_keyTuple) == tuple:
# here, we need to distinguish cases 2) and 3)
lT = len(key_or_keyTuple)
if lT == 1:
# treat this as if it would only be the keyword
return self._get_current_key(key_or_keyTuple[0])
elif lT == 2:
# treat this as keyword + default value
return self._get_current_key(key_or_keyTuple[0],
key_or_keyTuple[1])
elif lT == 3:
# treat this as keyword + default value + comment
return self._get_current_key(key_or_keyTuple[0],
key_or_keyTuple[1],
key_or_keyTuple[2])
else:
raise ValueError('Tuple of incorrect size')
else:
# now we just want to return it (there is no default value or comment)
return self._get_current_key(key_or_keyTuple)
def __setitem__(self, key, valueTuple):
# to set an item
# valueTuple is either a 2-tuple (actual value, comment)
# or it is simply a comment, then this key becomes a category
if type(valueTuple) == tuple:
if len(valueTuple) == 2:
value = valueTuple[0]
comment = valueTuple[1]
elif len(valueTuple) == 1:
value = {}
comment = valueTuple[0]
else:
raise ValueError('Expected a 2-tuple as input')
else: # not a tuple
value = valueTuple
comment = None
if type(value) == dict:
# only add if this is an empty dictionary
if len(value) == 0:
self._set_current_category(key, comment)
else:
raise ValueError('Can only add empty dictionaries')
# we are assigning a category
else:
# now we have to set an actual value (not a category)
if type(value) == type(self):
# Here we are trying to assign a full parameter object
# We want to add the content and not the object itself
self.ext[key] = value.ext
self.int[key] = {}
self.com[key] = {}
else:
# this is just a normal value
self._set_current_key(key, value, comment)
def _set_current_category(self, key, comment):
currentCategoryName = self.currentCategoryName + '.' + str(key)
if key not in self.ext or (key in self.ext and type(self.ext[key]) != dict):
# we do not want to over-write any settings here
if self.printSettings:
print('Creating new category: ' + currentCategoryName)
self.ext[key] = {}
self.int[key] = {}
self.com[key] = {}
if comment is not None:
if len(comment) > 0:
self.com[key]['__doc__'] = comment
def _set_current_key(self, key, value, comment=None):
if self.printSettings:
if key in self.ext:
print('Overwriting key = ' + str(key) + '; category = ' + self.currentCategoryName + '; value = ' +
str(self.ext[key]) + ' -> ' + str(value))
else:
print('Creating key = ' + str(key) + '; category = ' + self.currentCategoryName + '; value = ' + str(
value))
self.ext[key] = value
self.int[key] = value
if comment is not None:
if len(comment) > 0:
self.com[key] = comment
def _recursive_has_key(self, current_key_list, current_dict):
if len(current_key_list) == 1:
if current_key_list[0] in current_dict:
return True
else:
return False
else:
if current_key_list[0] in current_dict:
return self._recursive_has_key(current_key_list[1:], current_dict[current_key_list[0]])
else:
return False
def has_key(self, key_list):
if len(key_list) < 1:
raise ValueError('At least one key expected in key list')
else:
return self._recursive_has_key(key_list, self.int)
def _get_current_key(self, key, defaultValue=None, comment=None):
# returns a ParDicts object if we are accessing a category (i.e., a dictionary)
# returns just the value if it is a regular value
if key in self.ext:
value = self.ext[key]
if type(value) == dict:
# this is a category, need to create a ParDicts object to return
# if the key already exists in int and com keep it otherwise initialize it to empty
if key not in self.int:
self.int[key] = {}
if key not in self.com:
self.com[key] = {}
if comment is not None:
if len(comment) > 0:
self.com[key]['__doc__'] = comment
newpar = ParameterDict(printSettings=self.printSettings)
currentCategoryName = self.currentCategoryName + '.' + str(key)
newpar._set_value_of_instance(self.ext[key], self.int[key], self.com[key], currentCategoryName)
return newpar
else:
# just a regular value which we can return
self.int[key] = value
if comment is not None:
if len(comment) > 0:
self.com[key] = comment
return value
else:
# does not have the key, create it via the default value
if defaultValue is None:
# then make it a dictionary
defaultValue = {}
# if defaultValue is not None:
if type(defaultValue) == dict:
# make sure it is empty and if it is create a category
if len(defaultValue) == 0:
self._set_current_category(key, comment)
# and now we need to return it
newpar = ParameterDict(printSettings=self.printSettings)
currentCategoryName = self.currentCategoryName + '.' + str(key)
newpar._set_value_of_instance(self.ext[key], self.int[key], self.com[key], currentCategoryName)
return newpar
else:
raise ValueError('Cannot create a default key of type dict()')
else:
# now we can create it and return it
self.ext[key] = defaultValue
self.int[key] = defaultValue
if comment is not None:
if len(comment) > 0:
self.com[key] = comment
if self.printSettings:
print('Using default value = ' + str(defaultValue) + ' for key = ' + str(
key) + ' of category = ' + self.currentCategoryName)
return defaultValue
# else:
# raise ValueError('Cannot create key = ' + str(key) + ' without a default value')
# test it
def test_parameter_dict():
"""
Convenience testing script (to be converted to an actual test)
"""
p = ParameterDict()
# we can directly assign
p['registration_model'] = ({}, 'general settings for registration models')
p['registration_model']['similarity_measure'] = ({}, 'settings for the similarity measures')
p['registration_model']['similarity_measure']['type'] = ('ssd', 'similarity measure type')
# we can also ask for a parameter and use a default parameter if it does not exist
p['registration_model'][('nrOfIterations', 10, 'number of iterations')]
# we can also create a new category with default values if it does not exist yet
p[('new_category', {}, 'this is a new category')]
p[('registration_model', {}, 'this category already existed')]
# check if a key exists
print(p.has_key(['registration_model']))
print(p.has_key(['registration_model', 'bla']))
# and we can print everything of course
print(p)
# lastly we can write it all out as json
p.write_JSON('test_pars.json')
p.write_JSON_comments('test_pars_comments.json')