dclab 0.67.0__cp314-cp314-macosx_10_13_x86_64.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.

Files changed (142) hide show
  1. dclab/__init__.py +41 -0
  2. dclab/_version.py +34 -0
  3. dclab/cached.py +97 -0
  4. dclab/cli/__init__.py +10 -0
  5. dclab/cli/common.py +237 -0
  6. dclab/cli/task_compress.py +126 -0
  7. dclab/cli/task_condense.py +223 -0
  8. dclab/cli/task_join.py +229 -0
  9. dclab/cli/task_repack.py +98 -0
  10. dclab/cli/task_split.py +154 -0
  11. dclab/cli/task_tdms2rtdc.py +186 -0
  12. dclab/cli/task_verify_dataset.py +75 -0
  13. dclab/definitions/__init__.py +79 -0
  14. dclab/definitions/feat_const.py +202 -0
  15. dclab/definitions/feat_logic.py +182 -0
  16. dclab/definitions/meta_const.py +252 -0
  17. dclab/definitions/meta_logic.py +111 -0
  18. dclab/definitions/meta_parse.py +94 -0
  19. dclab/downsampling.cpython-314-darwin.so +0 -0
  20. dclab/downsampling.pyx +230 -0
  21. dclab/external/__init__.py +4 -0
  22. dclab/external/packaging/LICENSE +3 -0
  23. dclab/external/packaging/LICENSE.APACHE +177 -0
  24. dclab/external/packaging/LICENSE.BSD +23 -0
  25. dclab/external/packaging/__init__.py +6 -0
  26. dclab/external/packaging/_structures.py +61 -0
  27. dclab/external/packaging/version.py +505 -0
  28. dclab/external/skimage/LICENSE +28 -0
  29. dclab/external/skimage/__init__.py +2 -0
  30. dclab/external/skimage/_find_contours.py +216 -0
  31. dclab/external/skimage/_find_contours_cy.cpython-314-darwin.so +0 -0
  32. dclab/external/skimage/_find_contours_cy.pyx +188 -0
  33. dclab/external/skimage/_pnpoly.cpython-314-darwin.so +0 -0
  34. dclab/external/skimage/_pnpoly.pyx +99 -0
  35. dclab/external/skimage/_shared/__init__.py +1 -0
  36. dclab/external/skimage/_shared/geometry.cpython-314-darwin.so +0 -0
  37. dclab/external/skimage/_shared/geometry.pxd +6 -0
  38. dclab/external/skimage/_shared/geometry.pyx +55 -0
  39. dclab/external/skimage/measure.py +7 -0
  40. dclab/external/skimage/pnpoly.py +53 -0
  41. dclab/external/statsmodels/LICENSE +35 -0
  42. dclab/external/statsmodels/__init__.py +6 -0
  43. dclab/external/statsmodels/nonparametric/__init__.py +1 -0
  44. dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
  45. dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
  46. dclab/external/statsmodels/nonparametric/kernels.py +36 -0
  47. dclab/features/__init__.py +9 -0
  48. dclab/features/bright.py +81 -0
  49. dclab/features/bright_bc.py +93 -0
  50. dclab/features/bright_perc.py +63 -0
  51. dclab/features/contour.py +161 -0
  52. dclab/features/emodulus/__init__.py +339 -0
  53. dclab/features/emodulus/load.py +252 -0
  54. dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
  55. dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
  56. dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
  57. dclab/features/emodulus/pxcorr.py +135 -0
  58. dclab/features/emodulus/scale_linear.py +247 -0
  59. dclab/features/emodulus/viscosity.py +260 -0
  60. dclab/features/fl_crosstalk.py +95 -0
  61. dclab/features/inert_ratio.py +377 -0
  62. dclab/features/volume.py +242 -0
  63. dclab/http_utils.py +322 -0
  64. dclab/isoelastics/__init__.py +468 -0
  65. dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
  66. dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
  67. dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
  68. dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
  69. dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
  70. dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
  71. dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
  72. dclab/kde/__init__.py +1 -0
  73. dclab/kde/base.py +459 -0
  74. dclab/kde/contours.py +222 -0
  75. dclab/kde/methods.py +313 -0
  76. dclab/kde_contours.py +10 -0
  77. dclab/kde_methods.py +11 -0
  78. dclab/lme4/__init__.py +5 -0
  79. dclab/lme4/lme4_template.R +94 -0
  80. dclab/lme4/rsetup.py +204 -0
  81. dclab/lme4/wrapr.py +386 -0
  82. dclab/polygon_filter.py +398 -0
  83. dclab/rtdc_dataset/__init__.py +15 -0
  84. dclab/rtdc_dataset/check.py +902 -0
  85. dclab/rtdc_dataset/config.py +533 -0
  86. dclab/rtdc_dataset/copier.py +353 -0
  87. dclab/rtdc_dataset/core.py +896 -0
  88. dclab/rtdc_dataset/export.py +867 -0
  89. dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
  90. dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
  91. dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
  92. dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
  93. dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
  94. dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
  95. dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
  96. dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
  97. dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
  98. dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
  99. dclab/rtdc_dataset/feat_basin.py +762 -0
  100. dclab/rtdc_dataset/feat_temp.py +102 -0
  101. dclab/rtdc_dataset/filter.py +263 -0
  102. dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
  103. dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
  104. dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
  105. dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
  106. dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
  107. dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
  108. dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
  109. dclab/rtdc_dataset/fmt_dict.py +103 -0
  110. dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
  111. dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
  112. dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
  113. dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
  114. dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
  115. dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
  116. dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
  117. dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
  118. dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
  119. dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
  120. dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
  121. dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
  122. dclab/rtdc_dataset/fmt_http.py +102 -0
  123. dclab/rtdc_dataset/fmt_s3.py +354 -0
  124. dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
  125. dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
  126. dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
  127. dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
  128. dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
  129. dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
  130. dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
  131. dclab/rtdc_dataset/load.py +77 -0
  132. dclab/rtdc_dataset/meta_table.py +25 -0
  133. dclab/rtdc_dataset/writer.py +1019 -0
  134. dclab/statistics.py +226 -0
  135. dclab/util.py +176 -0
  136. dclab/warn.py +15 -0
  137. dclab-0.67.0.dist-info/METADATA +153 -0
  138. dclab-0.67.0.dist-info/RECORD +142 -0
  139. dclab-0.67.0.dist-info/WHEEL +6 -0
  140. dclab-0.67.0.dist-info/entry_points.txt +8 -0
  141. dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
  142. 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