dclab 0.67.0__cp314-cp314t-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dclab might be problematic. Click here for more details.
- dclab/__init__.py +41 -0
- dclab/_version.py +34 -0
- dclab/cached.py +97 -0
- dclab/cli/__init__.py +10 -0
- dclab/cli/common.py +237 -0
- dclab/cli/task_compress.py +126 -0
- dclab/cli/task_condense.py +223 -0
- dclab/cli/task_join.py +229 -0
- dclab/cli/task_repack.py +98 -0
- dclab/cli/task_split.py +154 -0
- dclab/cli/task_tdms2rtdc.py +186 -0
- dclab/cli/task_verify_dataset.py +75 -0
- dclab/definitions/__init__.py +79 -0
- dclab/definitions/feat_const.py +202 -0
- dclab/definitions/feat_logic.py +182 -0
- dclab/definitions/meta_const.py +252 -0
- dclab/definitions/meta_logic.py +111 -0
- dclab/definitions/meta_parse.py +94 -0
- dclab/downsampling.cpython-314t-darwin.so +0 -0
- dclab/downsampling.pyx +230 -0
- dclab/external/__init__.py +4 -0
- dclab/external/packaging/LICENSE +3 -0
- dclab/external/packaging/LICENSE.APACHE +177 -0
- dclab/external/packaging/LICENSE.BSD +23 -0
- dclab/external/packaging/__init__.py +6 -0
- dclab/external/packaging/_structures.py +61 -0
- dclab/external/packaging/version.py +505 -0
- dclab/external/skimage/LICENSE +28 -0
- dclab/external/skimage/__init__.py +2 -0
- dclab/external/skimage/_find_contours.py +216 -0
- dclab/external/skimage/_find_contours_cy.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.pyx +99 -0
- dclab/external/skimage/_shared/__init__.py +1 -0
- dclab/external/skimage/_shared/geometry.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_shared/geometry.pxd +6 -0
- dclab/external/skimage/_shared/geometry.pyx +55 -0
- dclab/external/skimage/measure.py +7 -0
- dclab/external/skimage/pnpoly.py +53 -0
- dclab/external/statsmodels/LICENSE +35 -0
- dclab/external/statsmodels/__init__.py +6 -0
- dclab/external/statsmodels/nonparametric/__init__.py +1 -0
- dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
- dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
- dclab/external/statsmodels/nonparametric/kernels.py +36 -0
- dclab/features/__init__.py +9 -0
- dclab/features/bright.py +81 -0
- dclab/features/bright_bc.py +93 -0
- dclab/features/bright_perc.py +63 -0
- dclab/features/contour.py +161 -0
- dclab/features/emodulus/__init__.py +339 -0
- dclab/features/emodulus/load.py +252 -0
- dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
- dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
- dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
- dclab/features/emodulus/pxcorr.py +135 -0
- dclab/features/emodulus/scale_linear.py +247 -0
- dclab/features/emodulus/viscosity.py +260 -0
- dclab/features/fl_crosstalk.py +95 -0
- dclab/features/inert_ratio.py +377 -0
- dclab/features/volume.py +242 -0
- dclab/http_utils.py +322 -0
- dclab/isoelastics/__init__.py +468 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
- dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
- dclab/kde/__init__.py +1 -0
- dclab/kde/base.py +459 -0
- dclab/kde/contours.py +222 -0
- dclab/kde/methods.py +313 -0
- dclab/kde_contours.py +10 -0
- dclab/kde_methods.py +11 -0
- dclab/lme4/__init__.py +5 -0
- dclab/lme4/lme4_template.R +94 -0
- dclab/lme4/rsetup.py +204 -0
- dclab/lme4/wrapr.py +386 -0
- dclab/polygon_filter.py +398 -0
- dclab/rtdc_dataset/__init__.py +15 -0
- dclab/rtdc_dataset/check.py +902 -0
- dclab/rtdc_dataset/config.py +533 -0
- dclab/rtdc_dataset/copier.py +353 -0
- dclab/rtdc_dataset/core.py +896 -0
- dclab/rtdc_dataset/export.py +867 -0
- dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
- dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
- dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
- dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
- dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
- dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
- dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
- dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
- dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
- dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
- dclab/rtdc_dataset/feat_basin.py +762 -0
- dclab/rtdc_dataset/feat_temp.py +102 -0
- dclab/rtdc_dataset/filter.py +263 -0
- dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
- dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
- dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
- dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
- dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
- dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
- dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
- dclab/rtdc_dataset/fmt_dict.py +103 -0
- dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
- dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
- dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
- dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
- dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
- dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
- dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
- dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
- dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
- dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
- dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
- dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
- dclab/rtdc_dataset/fmt_http.py +102 -0
- dclab/rtdc_dataset/fmt_s3.py +354 -0
- dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
- dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
- dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
- dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
- dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
- dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
- dclab/rtdc_dataset/load.py +77 -0
- dclab/rtdc_dataset/meta_table.py +25 -0
- dclab/rtdc_dataset/writer.py +1019 -0
- dclab/statistics.py +226 -0
- dclab/util.py +176 -0
- dclab/warn.py +15 -0
- dclab-0.67.0.dist-info/METADATA +153 -0
- dclab-0.67.0.dist-info/RECORD +142 -0
- dclab-0.67.0.dist-info/WHEEL +6 -0
- dclab-0.67.0.dist-info/entry_points.txt +8 -0
- dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
- dclab-0.67.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"""RT-DC dataset configuration"""
|
|
2
|
+
import copy
|
|
3
|
+
from collections import UserDict
|
|
4
|
+
import json
|
|
5
|
+
import numbers
|
|
6
|
+
import pathlib
|
|
7
|
+
import sys
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from .. import definitions as dfn
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class WrongConfigurationTypeWarning(UserWarning):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EmptyConfigurationKeyWarning(UserWarning):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class UnknownConfigurationKeyWarning(UserWarning):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BadUserConfigurationKeyWarning(UserWarning):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BadUserConfigurationValueWarning(UserWarning):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ConfigurationDict(UserDict):
|
|
36
|
+
def __init__(self, section=None, *args, **kwargs):
|
|
37
|
+
"""A case-insensitive dict that is section-aware
|
|
38
|
+
|
|
39
|
+
Instantiate this dictionary like any other dictionary.
|
|
40
|
+
All keys will be treated as lower-case keys. If `section`
|
|
41
|
+
is given, new entries will be checked using the function
|
|
42
|
+
:func:`verify_section_key`.
|
|
43
|
+
"""
|
|
44
|
+
self.section = section
|
|
45
|
+
super(ConfigurationDict, self).__init__(*args, **kwargs)
|
|
46
|
+
self._convert_keys()
|
|
47
|
+
|
|
48
|
+
def __getitem__(self, key):
|
|
49
|
+
return super(ConfigurationDict,
|
|
50
|
+
self).__getitem__(self.__class__._k(key))
|
|
51
|
+
|
|
52
|
+
def __setitem__(self, key, value):
|
|
53
|
+
key = self.__class__._k(key)
|
|
54
|
+
# make sure "section: key" exists
|
|
55
|
+
if self.section:
|
|
56
|
+
valid = verify_section_key(self.section, key)
|
|
57
|
+
# check for empty string values
|
|
58
|
+
if valid and isinstance(value, str) and len(value) == 0:
|
|
59
|
+
warnings.warn(
|
|
60
|
+
"Empty value for [{}]: '{}'!".format(self.section, key),
|
|
61
|
+
EmptyConfigurationKeyWarning,
|
|
62
|
+
)
|
|
63
|
+
valid = False
|
|
64
|
+
else:
|
|
65
|
+
valid = True
|
|
66
|
+
if value is None:
|
|
67
|
+
warnings.warn(
|
|
68
|
+
f"Bad value '{value}' for [{self.section}]: '{key}'!",
|
|
69
|
+
BadUserConfigurationValueWarning,
|
|
70
|
+
)
|
|
71
|
+
valid = False
|
|
72
|
+
if valid:
|
|
73
|
+
# only set valid keys
|
|
74
|
+
if self.section:
|
|
75
|
+
typ = dfn.get_config_value_type(self.section, key)
|
|
76
|
+
if typ is not None and not isinstance(value, typ):
|
|
77
|
+
warnings.warn(
|
|
78
|
+
f"Type of configuration key [{self.section}]: {key} "
|
|
79
|
+
f"should be {typ}, got {type(value)}!",
|
|
80
|
+
WrongConfigurationTypeWarning)
|
|
81
|
+
# convert value to its correct type (independent of case above)
|
|
82
|
+
convfunc = dfn.get_config_value_func(self.section, key)
|
|
83
|
+
value = convfunc(value)
|
|
84
|
+
|
|
85
|
+
super(ConfigurationDict, self).__setitem__(key, value)
|
|
86
|
+
|
|
87
|
+
def __delitem__(self, key):
|
|
88
|
+
return super(ConfigurationDict,
|
|
89
|
+
self).__delitem__(self.__class__._k(key))
|
|
90
|
+
|
|
91
|
+
def __contains__(self, key):
|
|
92
|
+
return super(ConfigurationDict,
|
|
93
|
+
self).__contains__(self.__class__._k(key))
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def _k(cls, key):
|
|
97
|
+
"""Convert a key to lower case"""
|
|
98
|
+
return key.lower() if isinstance(key, str) else key
|
|
99
|
+
|
|
100
|
+
def _convert_keys(self):
|
|
101
|
+
for k in list(self.keys()):
|
|
102
|
+
v = super(ConfigurationDict, self).pop(k)
|
|
103
|
+
self.__setitem__(k, v)
|
|
104
|
+
|
|
105
|
+
def get(self, key, *args, **kwargs):
|
|
106
|
+
return super(ConfigurationDict,
|
|
107
|
+
self).get(self.__class__._k(key), *args, **kwargs)
|
|
108
|
+
|
|
109
|
+
def items(self):
|
|
110
|
+
keys = list(self.keys())
|
|
111
|
+
keys.sort()
|
|
112
|
+
out = [(k, self[k]) for k in keys]
|
|
113
|
+
return out
|
|
114
|
+
|
|
115
|
+
def pop(self, key, *args, **kwargs):
|
|
116
|
+
return super(ConfigurationDict,
|
|
117
|
+
self).pop(self.__class__._k(key), *args, **kwargs)
|
|
118
|
+
|
|
119
|
+
def setdefault(self, key, *args, **kwargs):
|
|
120
|
+
return super(ConfigurationDict,
|
|
121
|
+
self).setdefault(self.__class__._k(key), *args, **kwargs)
|
|
122
|
+
|
|
123
|
+
def update(self, E=None, **F):
|
|
124
|
+
if E is None:
|
|
125
|
+
E = {}
|
|
126
|
+
for key in E:
|
|
127
|
+
self.__setitem__(key, E[key])
|
|
128
|
+
for key in F:
|
|
129
|
+
self.__setitem__(key, F[key])
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Configuration(object):
|
|
133
|
+
def __init__(self, files=None, cfg=None, disable_checks=False):
|
|
134
|
+
"""Configuration class for RT-DC datasets
|
|
135
|
+
|
|
136
|
+
This class has a dictionary-like interface to access
|
|
137
|
+
and set configuration values, e.g.
|
|
138
|
+
|
|
139
|
+
.. code::
|
|
140
|
+
|
|
141
|
+
cfg = load_from_file("/path/to/config.txt")
|
|
142
|
+
# access the channel width
|
|
143
|
+
cfg["setup"]["channel width"]
|
|
144
|
+
# modify the channel width
|
|
145
|
+
cfg["setup"]["channel width"] = 30
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
files: list of files
|
|
150
|
+
The config files with which to initialize the configuration
|
|
151
|
+
cfg: dict-like
|
|
152
|
+
The dictionary with which to initialize the configuration
|
|
153
|
+
disable_checks: bool
|
|
154
|
+
Set this to True if you want to avoid checking against
|
|
155
|
+
section and key names defined in `dclab.definitions`
|
|
156
|
+
using :func:`verify_section_key`. This avoids excess
|
|
157
|
+
warning messages when loading data from configuration
|
|
158
|
+
files not generated by dclab.
|
|
159
|
+
"""
|
|
160
|
+
if cfg is None:
|
|
161
|
+
cfg = {}
|
|
162
|
+
if files is None:
|
|
163
|
+
files = []
|
|
164
|
+
self.disable_checks = disable_checks
|
|
165
|
+
|
|
166
|
+
self._cfg = ConfigurationDict()
|
|
167
|
+
|
|
168
|
+
# set initial default values
|
|
169
|
+
self._init_default_filter_values()
|
|
170
|
+
|
|
171
|
+
# Update with additional dictionary
|
|
172
|
+
self.update(cfg)
|
|
173
|
+
|
|
174
|
+
# Load configuration files
|
|
175
|
+
for f in files:
|
|
176
|
+
self.update(load_from_file(f))
|
|
177
|
+
|
|
178
|
+
def __contains__(self, key):
|
|
179
|
+
return self._cfg.__contains__(key)
|
|
180
|
+
|
|
181
|
+
def __getitem__(self, sec):
|
|
182
|
+
if sec not in self and (sec in dfn.config_keys or sec == "user"):
|
|
183
|
+
# create an empty section for user-convenience
|
|
184
|
+
section = None if self.disable_checks else sec
|
|
185
|
+
self._cfg[sec] = ConfigurationDict(section=section)
|
|
186
|
+
item = self._cfg.__getitem__(sec)
|
|
187
|
+
return item
|
|
188
|
+
|
|
189
|
+
def __iter__(self):
|
|
190
|
+
return self._cfg.__iter__()
|
|
191
|
+
|
|
192
|
+
def __len__(self):
|
|
193
|
+
return len(self._cfg)
|
|
194
|
+
|
|
195
|
+
def __repr__(self):
|
|
196
|
+
rep = ""
|
|
197
|
+
keys = sorted(list(self.keys()))
|
|
198
|
+
for key in keys:
|
|
199
|
+
rep += "- {}\n".format(key)
|
|
200
|
+
subkeys = sorted(list(self[key].keys()))
|
|
201
|
+
for subkey in subkeys:
|
|
202
|
+
rep += " {}: {}\n".format(subkey, self[key][subkey])
|
|
203
|
+
return rep
|
|
204
|
+
|
|
205
|
+
def __setitem__(self, *args):
|
|
206
|
+
self._cfg.__setitem__(*args)
|
|
207
|
+
|
|
208
|
+
def _init_default_filter_values(self):
|
|
209
|
+
"""Set default initial values
|
|
210
|
+
|
|
211
|
+
The default values are hard-coded for backwards compatibility
|
|
212
|
+
and for several functionalities in dclab.
|
|
213
|
+
"""
|
|
214
|
+
# Do not filter out invalid event values
|
|
215
|
+
self["filtering"]["remove invalid events"] = False
|
|
216
|
+
# Enable filters switch is mandatory
|
|
217
|
+
self["filtering"]["enable filters"] = True
|
|
218
|
+
# Limit events integer to downsample output data
|
|
219
|
+
self["filtering"]["limit events"] = 0
|
|
220
|
+
# Polygon filter list
|
|
221
|
+
self["filtering"]["polygon filters"] = []
|
|
222
|
+
# Defaults to no hierarchy parent
|
|
223
|
+
self["filtering"]["hierarchy parent"] = "none"
|
|
224
|
+
|
|
225
|
+
# Make sure that all filtering values have a default value
|
|
226
|
+
# (otherwise we will get problems with resetting filters)
|
|
227
|
+
for item in dfn.CFG_ANALYSIS["filtering"]:
|
|
228
|
+
if item[0] not in self["filtering"]:
|
|
229
|
+
raise KeyError(
|
|
230
|
+
"No default value set for [filtering]:{}".format(item[0]))
|
|
231
|
+
|
|
232
|
+
def as_dict(self, pop_filtering=False):
|
|
233
|
+
data = json.loads(self.tojson())
|
|
234
|
+
if pop_filtering:
|
|
235
|
+
data.pop("filtering", None)
|
|
236
|
+
return data
|
|
237
|
+
|
|
238
|
+
def copy(self):
|
|
239
|
+
"""Return copy of current configuration"""
|
|
240
|
+
return Configuration(cfg=copy.deepcopy(self._cfg))
|
|
241
|
+
|
|
242
|
+
def get(self, key, other):
|
|
243
|
+
"""Famous `dict.get` function
|
|
244
|
+
|
|
245
|
+
.. versionadded:: 0.29.1
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
if key in self:
|
|
249
|
+
return self[key]
|
|
250
|
+
else:
|
|
251
|
+
return other
|
|
252
|
+
|
|
253
|
+
def tojson(self):
|
|
254
|
+
"""Convert the configuration to a JSON string
|
|
255
|
+
|
|
256
|
+
Note that the data type of some configuration options
|
|
257
|
+
will likely be lost.
|
|
258
|
+
"""
|
|
259
|
+
# Dear future person,
|
|
260
|
+
# if you would like to implement `fromjson`, you will have
|
|
261
|
+
# to set the `section` properly in the ConfigurationDict.
|
|
262
|
+
# Besides the data types, there might be other things to
|
|
263
|
+
# look out for. ~paulmueller
|
|
264
|
+
return json.dumps(dict(self),
|
|
265
|
+
cls=ConfigurationJSONEncode,
|
|
266
|
+
sort_keys=True)
|
|
267
|
+
|
|
268
|
+
def keys(self):
|
|
269
|
+
"""Return the configuration keys (sections)"""
|
|
270
|
+
return self._cfg.keys()
|
|
271
|
+
|
|
272
|
+
def save(self, filename):
|
|
273
|
+
"""Save the configuration to a file"""
|
|
274
|
+
filename = pathlib.Path(filename)
|
|
275
|
+
out_str = self.tostring()
|
|
276
|
+
with filename.open("w") as f:
|
|
277
|
+
f.write(out_str)
|
|
278
|
+
|
|
279
|
+
def tostring(self, sections=None):
|
|
280
|
+
"""Convert the configuration to its string representation
|
|
281
|
+
|
|
282
|
+
The optional argument `sections` allows to export only
|
|
283
|
+
specific sections of the configuration, i.e.
|
|
284
|
+
`sections=dclab.dfn.CFG_METADATA` will only export
|
|
285
|
+
configuration data from the original measurement and no
|
|
286
|
+
filtering data.
|
|
287
|
+
"""
|
|
288
|
+
out = []
|
|
289
|
+
if sections is None:
|
|
290
|
+
keys = sorted(list(self.keys()))
|
|
291
|
+
else:
|
|
292
|
+
keys = sorted([k for k in sections if k in self.keys()])
|
|
293
|
+
for key in keys:
|
|
294
|
+
out.append("[{}]".format(key))
|
|
295
|
+
section = self[key]
|
|
296
|
+
ikeys = list(section.keys())
|
|
297
|
+
ikeys.sort()
|
|
298
|
+
for ikey in ikeys:
|
|
299
|
+
var, val = keyval_typ2str(ikey, section[ikey])
|
|
300
|
+
out.append("{} = {}".format(var, val))
|
|
301
|
+
out.append("")
|
|
302
|
+
|
|
303
|
+
out_str = ""
|
|
304
|
+
for i in range(len(out)):
|
|
305
|
+
# win-like line endings
|
|
306
|
+
out_str += out[i]+"\n"
|
|
307
|
+
return out_str
|
|
308
|
+
|
|
309
|
+
def update(self, newcfg):
|
|
310
|
+
"""Update current config with a dictionary"""
|
|
311
|
+
for sec in newcfg.keys():
|
|
312
|
+
if sec not in self._cfg:
|
|
313
|
+
section = None if self.disable_checks else sec
|
|
314
|
+
self._cfg[sec] = ConfigurationDict(section=section)
|
|
315
|
+
self._cfg[sec].update(newcfg[sec])
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class CaseInsensitiveDict(ConfigurationDict):
|
|
319
|
+
def __init__(self, *args, **kwargs):
|
|
320
|
+
warnings.warn("CaseInsensitiveDict is deprecated, use "
|
|
321
|
+
+ "ConfigurationDict instead.",
|
|
322
|
+
DeprecationWarning)
|
|
323
|
+
super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class ConfigurationJSONEncode(json.JSONEncoder):
|
|
327
|
+
def default(self, obj):
|
|
328
|
+
if isinstance(obj, ConfigurationDict):
|
|
329
|
+
return dict(obj)
|
|
330
|
+
elif isinstance(obj, np.ndarray):
|
|
331
|
+
return obj.tolist()
|
|
332
|
+
elif isinstance(obj, numbers.Integral):
|
|
333
|
+
return int(obj)
|
|
334
|
+
elif isinstance(obj, numbers.Number):
|
|
335
|
+
return float(obj)
|
|
336
|
+
elif isinstance(obj, (bool, np.bool_)):
|
|
337
|
+
return bool(obj)
|
|
338
|
+
return json.JSONEncoder.default(self, obj)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def verify_section_key(section, key):
|
|
342
|
+
"""Return True if the section-key combination exists"""
|
|
343
|
+
wcount = 0
|
|
344
|
+
if dfn.config_key_exists(section, key):
|
|
345
|
+
pass
|
|
346
|
+
elif section in ["plotting", "analysis"]:
|
|
347
|
+
# hijacked by Shape-Out 1
|
|
348
|
+
warnings.warn("The '{}' configuration key is deprecated!".format(
|
|
349
|
+
section), UnknownConfigurationKeyWarning)
|
|
350
|
+
wcount += 1
|
|
351
|
+
elif section == "filtering":
|
|
352
|
+
if key.endswith(" min") or key.endswith(" max"):
|
|
353
|
+
feat = key[:-4]
|
|
354
|
+
if not dfn.scalar_feature_exists(feat):
|
|
355
|
+
warnings.warn(
|
|
356
|
+
"A range has been specified for an unknown feature "
|
|
357
|
+
+ "'{}' in the 'filtering' section!".format(feat),
|
|
358
|
+
UnknownConfigurationKeyWarning)
|
|
359
|
+
wcount += 1
|
|
360
|
+
elif key == "limit events auto":
|
|
361
|
+
# Shape-Out 1 used this to limit the number of events to
|
|
362
|
+
# a common minimum.
|
|
363
|
+
if sys.version_info[0] != 2:
|
|
364
|
+
warnings.warn("The 'limit events auto' configuration key "
|
|
365
|
+
" in the 'filtering' section is deprecated!")
|
|
366
|
+
wcount += 1
|
|
367
|
+
else:
|
|
368
|
+
warnings.warn(
|
|
369
|
+
"Unknown key '{}' in the 'filtering' section!".format(key),
|
|
370
|
+
UnknownConfigurationKeyWarning)
|
|
371
|
+
wcount += 1
|
|
372
|
+
elif section == "user":
|
|
373
|
+
# the keys must be strings for clarity
|
|
374
|
+
if not isinstance(key, str):
|
|
375
|
+
warnings.warn("The 'user' section keys must be strings, "
|
|
376
|
+
f"got '{key}' of type '{type(key)}'!",
|
|
377
|
+
BadUserConfigurationKeyWarning)
|
|
378
|
+
wcount += 1
|
|
379
|
+
elif len(key.strip()) == 0:
|
|
380
|
+
warnings.warn("The 'user' section keys must not be empty strings "
|
|
381
|
+
"or consist of whitespace characters only!",
|
|
382
|
+
BadUserConfigurationKeyWarning)
|
|
383
|
+
wcount += 1
|
|
384
|
+
elif section not in dfn.config_keys:
|
|
385
|
+
warnings.warn("Unknown section '{}'!".format(section),
|
|
386
|
+
UnknownConfigurationKeyWarning)
|
|
387
|
+
wcount += 1
|
|
388
|
+
else:
|
|
389
|
+
warnings.warn(
|
|
390
|
+
"Unknown key '{}' in the '{}' section!".format(key, section),
|
|
391
|
+
UnknownConfigurationKeyWarning)
|
|
392
|
+
wcount += 1
|
|
393
|
+
|
|
394
|
+
return wcount == 0
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def load_from_file(cfg_file):
|
|
398
|
+
"""Load the configuration from a file
|
|
399
|
+
|
|
400
|
+
Parameters
|
|
401
|
+
----------
|
|
402
|
+
cfg_file: str
|
|
403
|
+
Path to configuration file
|
|
404
|
+
|
|
405
|
+
Returns
|
|
406
|
+
-------
|
|
407
|
+
cfg : ConfigurationDict
|
|
408
|
+
Dictionary with configuration parameters
|
|
409
|
+
"""
|
|
410
|
+
path = pathlib.Path(cfg_file).resolve()
|
|
411
|
+
with path.open("r", errors="replace") as f:
|
|
412
|
+
code = f.readlines()
|
|
413
|
+
|
|
414
|
+
cfg = ConfigurationDict()
|
|
415
|
+
for line in code:
|
|
416
|
+
# We deal with comments and empty lines
|
|
417
|
+
# We need to check line length first and then we look for
|
|
418
|
+
# a hash.
|
|
419
|
+
line = line.split("#")[0].strip()
|
|
420
|
+
if len(line) != 0:
|
|
421
|
+
if line.startswith("[") and line.endswith("]"):
|
|
422
|
+
sec = line[1:-1].lower()
|
|
423
|
+
if sec not in cfg:
|
|
424
|
+
cfg[sec] = ConfigurationDict()
|
|
425
|
+
continue
|
|
426
|
+
elif not line.count("="):
|
|
427
|
+
# ignore invalid lines
|
|
428
|
+
continue
|
|
429
|
+
var, val = line.split("=", 1)
|
|
430
|
+
var = var.strip().lower()
|
|
431
|
+
val = val.strip("' ").strip('" ').strip()
|
|
432
|
+
if len(val) == 0:
|
|
433
|
+
# skip invalid values
|
|
434
|
+
continue
|
|
435
|
+
# convert parameter value to correct type
|
|
436
|
+
if dfn.config_key_exists(sec, var):
|
|
437
|
+
convfunc = dfn.get_config_value_func(sec, var)
|
|
438
|
+
val = convfunc(val)
|
|
439
|
+
else:
|
|
440
|
+
# unknown parameter (e.g. plotting in DCscope), guess type
|
|
441
|
+
var, val = keyval_str2typ(var, val)
|
|
442
|
+
if len(var) != 0 and len(str(val)) != 0:
|
|
443
|
+
cfg[sec][var] = val
|
|
444
|
+
return cfg
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def keyval_str2typ(var, val):
|
|
448
|
+
"""Convert a variable from a string to its correct type
|
|
449
|
+
|
|
450
|
+
Parameters
|
|
451
|
+
----------
|
|
452
|
+
var: str
|
|
453
|
+
The variable name
|
|
454
|
+
val: str
|
|
455
|
+
The value of the variable represented as a string
|
|
456
|
+
|
|
457
|
+
Returns
|
|
458
|
+
-------
|
|
459
|
+
varout: str
|
|
460
|
+
Stripped lowercase `var`
|
|
461
|
+
valout: any type
|
|
462
|
+
The value converted from string to its presumed type
|
|
463
|
+
|
|
464
|
+
Notes
|
|
465
|
+
-----
|
|
466
|
+
This method is heuristic and is only intended for usage in
|
|
467
|
+
dclab.
|
|
468
|
+
|
|
469
|
+
See Also
|
|
470
|
+
--------
|
|
471
|
+
keyval_typ2str: the opposite
|
|
472
|
+
"""
|
|
473
|
+
if not (isinstance(val, str)):
|
|
474
|
+
# already a type:
|
|
475
|
+
return var.strip(), val
|
|
476
|
+
var = var.strip().lower()
|
|
477
|
+
val = val.strip()
|
|
478
|
+
# Find values
|
|
479
|
+
if len(var) != 0 and len(val) != 0:
|
|
480
|
+
# check for float
|
|
481
|
+
if val.startswith("[") and val.endswith("]"):
|
|
482
|
+
if len(val.strip("[],")) == 0:
|
|
483
|
+
# empty list
|
|
484
|
+
values = []
|
|
485
|
+
else:
|
|
486
|
+
values = val.strip("[],").split(",")
|
|
487
|
+
values = [float(v) for v in values]
|
|
488
|
+
return var, values
|
|
489
|
+
elif val.lower() in ["true", "y"]:
|
|
490
|
+
return var, True
|
|
491
|
+
elif val.lower() in ["false", "n"]:
|
|
492
|
+
return var, False
|
|
493
|
+
elif val[0] in ["'", '"'] and val[-1] in ["'", '"']:
|
|
494
|
+
return var, val.strip("'").strip('"').strip()
|
|
495
|
+
elif dfn.scalar_feature_exists(val):
|
|
496
|
+
return var, val
|
|
497
|
+
else:
|
|
498
|
+
try:
|
|
499
|
+
return var, float(val.replace(",", "."))
|
|
500
|
+
except ValueError:
|
|
501
|
+
return var, val
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def keyval_typ2str(var, val):
|
|
505
|
+
"""Convert a variable to a string
|
|
506
|
+
|
|
507
|
+
Parameters
|
|
508
|
+
----------
|
|
509
|
+
var: str
|
|
510
|
+
The variable name
|
|
511
|
+
val: any type
|
|
512
|
+
The value of the variable
|
|
513
|
+
|
|
514
|
+
Returns
|
|
515
|
+
-------
|
|
516
|
+
varout: str
|
|
517
|
+
Stripped lowercase `var`
|
|
518
|
+
valout: any type
|
|
519
|
+
The value converted to a useful string representation
|
|
520
|
+
|
|
521
|
+
See Also
|
|
522
|
+
--------
|
|
523
|
+
keyval_str2typ: the opposite
|
|
524
|
+
"""
|
|
525
|
+
varout = var.strip()
|
|
526
|
+
if isinstance(val, list):
|
|
527
|
+
data = ", ".join([keyval_typ2str(var, it)[1] for it in val])
|
|
528
|
+
valout = "["+data+"]"
|
|
529
|
+
elif isinstance(val, float):
|
|
530
|
+
valout = "{:.12f}".format(val)
|
|
531
|
+
else:
|
|
532
|
+
valout = "{}".format(val)
|
|
533
|
+
return varout, valout
|