h5rdmtoolbox 2.5.3__py3-none-any.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.
Files changed (144) hide show
  1. h5rdmtoolbox/__init__.py +424 -0
  2. h5rdmtoolbox/_cfg.py +104 -0
  3. h5rdmtoolbox/_repr.py +747 -0
  4. h5rdmtoolbox/_version.py +6 -0
  5. h5rdmtoolbox/cli.py +61 -0
  6. h5rdmtoolbox/consts.py +3 -0
  7. h5rdmtoolbox/convention/__init__.py +41 -0
  8. h5rdmtoolbox/convention/cfg.py +2 -0
  9. h5rdmtoolbox/convention/consts.py +28 -0
  10. h5rdmtoolbox/convention/core.py +817 -0
  11. h5rdmtoolbox/convention/errors.py +34 -0
  12. h5rdmtoolbox/convention/generate.py +385 -0
  13. h5rdmtoolbox/convention/ontology/__init__.py +3 -0
  14. h5rdmtoolbox/convention/ontology/h5ontocls.py +133 -0
  15. h5rdmtoolbox/convention/ontology/hdf_datatypes.py +64 -0
  16. h5rdmtoolbox/convention/references.py +23 -0
  17. h5rdmtoolbox/convention/standard_attributes.py +383 -0
  18. h5rdmtoolbox/convention/toolbox_validators.py +175 -0
  19. h5rdmtoolbox/convention/utils.py +216 -0
  20. h5rdmtoolbox/data/def_icon.png +0 -0
  21. h5rdmtoolbox/data/fluid-v1.yml +36 -0
  22. h5rdmtoolbox/data/h5tbx.yaml +28 -0
  23. h5rdmtoolbox/data/id_icon.png +0 -0
  24. h5rdmtoolbox/data/iri_icon.png +0 -0
  25. h5rdmtoolbox/data/ld_icon.png +0 -0
  26. h5rdmtoolbox/data/piv-v1.yml +35 -0
  27. h5rdmtoolbox/data/style.css +216 -0
  28. h5rdmtoolbox/data/tutorial_convention.yaml +91 -0
  29. h5rdmtoolbox/data/tutorial_standard_name_table.yaml +390 -0
  30. h5rdmtoolbox/data/type_icon.png +0 -0
  31. h5rdmtoolbox/database/__init__.py +75 -0
  32. h5rdmtoolbox/database/hdfdb/__init__.py +4 -0
  33. h5rdmtoolbox/database/hdfdb/filedb.py +129 -0
  34. h5rdmtoolbox/database/hdfdb/objdb.py +520 -0
  35. h5rdmtoolbox/database/hdfdb/query.py +133 -0
  36. h5rdmtoolbox/database/hdfdb/utils.py +51 -0
  37. h5rdmtoolbox/database/interface.py +59 -0
  38. h5rdmtoolbox/database/lazy.py +183 -0
  39. h5rdmtoolbox/database/mongo.py +374 -0
  40. h5rdmtoolbox/errors.py +4 -0
  41. h5rdmtoolbox/extensions/__init__.py +0 -0
  42. h5rdmtoolbox/extensions/magnitude.py +67 -0
  43. h5rdmtoolbox/extensions/normalize.py +103 -0
  44. h5rdmtoolbox/extensions/onto.py +52 -0
  45. h5rdmtoolbox/extensions/units.py +48 -0
  46. h5rdmtoolbox/extensions/vector.py +138 -0
  47. h5rdmtoolbox/gui/__init__.py +0 -0
  48. h5rdmtoolbox/gui/fairify.py +446 -0
  49. h5rdmtoolbox/gui/src/__init__.py +0 -0
  50. h5rdmtoolbox/gui/src/main.py +131 -0
  51. h5rdmtoolbox/identifiers.py +257 -0
  52. h5rdmtoolbox/layout/__init__.py +4 -0
  53. h5rdmtoolbox/layout/core.py +624 -0
  54. h5rdmtoolbox/ld/__init__.py +129 -0
  55. h5rdmtoolbox/ld/globals.py +3 -0
  56. h5rdmtoolbox/ld/hdf/__init__.py +0 -0
  57. h5rdmtoolbox/ld/hdf/attributes.py +36 -0
  58. h5rdmtoolbox/ld/hdf/datasets.py +160 -0
  59. h5rdmtoolbox/ld/hdf/file.py +54 -0
  60. h5rdmtoolbox/ld/hdf/groups.py +47 -0
  61. h5rdmtoolbox/ld/rdf.py +883 -0
  62. h5rdmtoolbox/ld/shacl.py +221 -0
  63. h5rdmtoolbox/ld/user/__init__.py +0 -0
  64. h5rdmtoolbox/ld/user/attributes.py +241 -0
  65. h5rdmtoolbox/ld/user/datasets.py +22 -0
  66. h5rdmtoolbox/ld/user/file.py +58 -0
  67. h5rdmtoolbox/ld/user/groups.py +58 -0
  68. h5rdmtoolbox/ld/user/utils.py +8 -0
  69. h5rdmtoolbox/ld/utils.py +133 -0
  70. h5rdmtoolbox/plotting.py +168 -0
  71. h5rdmtoolbox/protected_attributes.py +9 -0
  72. h5rdmtoolbox/protocols.py +268 -0
  73. h5rdmtoolbox/repository/__init__.py +11 -0
  74. h5rdmtoolbox/repository/default_metamapper.py +55 -0
  75. h5rdmtoolbox/repository/interface.py +214 -0
  76. h5rdmtoolbox/repository/utils.py +131 -0
  77. h5rdmtoolbox/repository/zenodo/__init__.py +5 -0
  78. h5rdmtoolbox/repository/zenodo/core.py +616 -0
  79. h5rdmtoolbox/repository/zenodo/metadata.py +276 -0
  80. h5rdmtoolbox/repository/zenodo/tokens.py +118 -0
  81. h5rdmtoolbox/repository/zenodo/utils.py +16 -0
  82. h5rdmtoolbox/tutorial.py +374 -0
  83. h5rdmtoolbox/user.py +158 -0
  84. h5rdmtoolbox/utils.py +743 -0
  85. h5rdmtoolbox/wrapper/__init__.py +8 -0
  86. h5rdmtoolbox/wrapper/accessor.py +97 -0
  87. h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
  88. h5rdmtoolbox/wrapper/accessors/provenance.py +36 -0
  89. h5rdmtoolbox/wrapper/core.py +2528 -0
  90. h5rdmtoolbox/wrapper/ds_decoder.py +113 -0
  91. h5rdmtoolbox/wrapper/h5attr.py +519 -0
  92. h5rdmtoolbox/wrapper/h5ext.py +20 -0
  93. h5rdmtoolbox/wrapper/h5utils.py +32 -0
  94. h5rdmtoolbox/wrapper/h5yaml.py +116 -0
  95. h5rdmtoolbox/wrapper/jsonld.py +1189 -0
  96. h5rdmtoolbox/wrapper/xr2hdf.py +106 -0
  97. h5rdmtoolbox-2.5.3.dist-info/METADATA +379 -0
  98. h5rdmtoolbox-2.5.3.dist-info/RECORD +144 -0
  99. h5rdmtoolbox-2.5.3.dist-info/WHEEL +5 -0
  100. h5rdmtoolbox-2.5.3.dist-info/entry_points.txt +2 -0
  101. h5rdmtoolbox-2.5.3.dist-info/licenses/LICENSE +20 -0
  102. h5rdmtoolbox-2.5.3.dist-info/top_level.txt +2 -0
  103. tests/__init__.py +0 -0
  104. tests/clean_zenodo_sandbox.py +23 -0
  105. tests/conventions/__init__.py +0 -0
  106. tests/conventions/test_convention_vfuncs.py +98 -0
  107. tests/conventions/test_conventions.py +697 -0
  108. tests/conventions/test_engmeta.py +55 -0
  109. tests/conventions/test_references.py +22 -0
  110. tests/conventions/test_toolbox_validators.py +138 -0
  111. tests/conventions/test_utils.py +94 -0
  112. tests/database/__init__.py +0 -0
  113. tests/database/test_find_rdf.py +36 -0
  114. tests/database/test_hdfDB.py +619 -0
  115. tests/database/test_mongo.py +229 -0
  116. tests/layouts/__init__.py +0 -0
  117. tests/layouts/test_core.py +362 -0
  118. tests/layouts/test_docs.py +66 -0
  119. tests/ld/__init__.py +0 -0
  120. tests/ld/test_ld.py +904 -0
  121. tests/ld/test_rdf.py +688 -0
  122. tests/ld/test_shacl.py +223 -0
  123. tests/ld/test_user_graph.py +278 -0
  124. tests/ld/test_utils.py +13 -0
  125. tests/repository/__init__.py +0 -0
  126. tests/repository/test_zenodo.py +728 -0
  127. tests/test_cli.py +69 -0
  128. tests/test_config.py +80 -0
  129. tests/test_extensions.py +148 -0
  130. tests/test_identifiers.py +85 -0
  131. tests/test_plotting.py +155 -0
  132. tests/test_repr.py +114 -0
  133. tests/test_user.py +100 -0
  134. tests/test_utils.py +143 -0
  135. tests/test_version.py +72 -0
  136. tests/test_xarray_export.py +47 -0
  137. tests/wrapper/__init__.py +0 -0
  138. tests/wrapper/test_core.py +994 -0
  139. tests/wrapper/test_dump.py +77 -0
  140. tests/wrapper/test_file.py +411 -0
  141. tests/wrapper/test_h5ext.py +22 -0
  142. tests/wrapper/test_lazy.py +62 -0
  143. tests/wrapper/test_namespaces.py +46 -0
  144. tests/wrapper/test_xr2df.py +41 -0
@@ -0,0 +1,424 @@
1
+ """h5rdmtoolbox repository"""
2
+
3
+ import logging
4
+ import pathlib
5
+ import warnings
6
+ from logging.handlers import RotatingFileHandler
7
+ from typing import Optional, Dict
8
+
9
+ import rdflib
10
+
11
+ from ._version import __version__
12
+ from .user import USER_LOG_DIR, USER_DATA_DIR
13
+
14
+ DEFAULT_LOGGING_LEVEL = logging.WARNING
15
+ _formatter = logging.Formatter(
16
+ '%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
17
+ datefmt='%Y-%m-%d_%H:%M:%S')
18
+
19
+ _stream_handler = logging.StreamHandler()
20
+ _stream_handler.setLevel(DEFAULT_LOGGING_LEVEL)
21
+ _stream_handler.setFormatter(_formatter)
22
+
23
+ _file_handler = RotatingFileHandler(USER_LOG_DIR / 'h5rdmtoolbox.log')
24
+ _file_handler.setLevel(logging.DEBUG) # log everything to file!
25
+ _file_handler.setFormatter(_formatter)
26
+
27
+ logger = logging.getLogger(__package__)
28
+ logger.addHandler(_stream_handler)
29
+ logger.addHandler(_file_handler)
30
+
31
+ import atexit
32
+ # noinspection PyUnresolvedReferences
33
+ import pint_xarray
34
+ import shutil
35
+ import xarray as xr
36
+ from typing import Union, Callable
37
+ from pydantic import HttpUrl
38
+
39
+ from h5rdmtoolbox._cfg import set_config, get_config, get_ureg
40
+
41
+ pint_xarray.unit_registry = get_ureg()
42
+
43
+ from . import convention
44
+ from .convention.core import Convention
45
+ from . import wrapper
46
+ from .user import UserDir
47
+ from ._version import __version__
48
+ from . import utils
49
+ from .wrapper.core import lower, Lower, File, Group, Dataset
50
+ from . import errors
51
+ from .wrapper import jsonld
52
+ from .database.lazy import lazy
53
+ from .wrapper.h5attr import Attribute
54
+ import json
55
+ from .wrapper.accessor import register_accessor
56
+
57
+ from .ld.shacl import validate_hdf, ValidationResult
58
+ # noinspection PyUnresolvedReferences
59
+ from .utils import DownloadFileManager
60
+
61
+ name = 'h5rdmtoolbox'
62
+ __this_dir__ = pathlib.Path(__file__).parent
63
+ __author__ = 'Matthias Probst'
64
+ __author_orcid__ = 'https://orcid.org/0000-0001-8729-0482'
65
+
66
+
67
+ def get_package_meta() -> Dict:
68
+ """Reads codemeta.json and returns it as dict"""
69
+ with open(__this_dir__ / '../codemeta.json', 'r') as f:
70
+ codemeta = json.loads(f.read())
71
+ return codemeta
72
+
73
+
74
+ cv_h5py = convention.Convention(name='h5py',
75
+ contact=__author_orcid__)
76
+ cv_h5py.register()
77
+
78
+ use = convention.core.use
79
+ use(None)
80
+
81
+
82
+ def dump(src: Union[str, File, pathlib.Path],
83
+ *args, **kwargs) -> None:
84
+ """Call h5.dump() on the provided HDF5 file
85
+
86
+ Parameters
87
+ ----------
88
+ src : str, File, pathlib.Path
89
+ the HDF5 file or filename to dump. An object which has a hdf_filename attribute can also be provided.
90
+ kwargs
91
+ kwargs passed to h5.dump(). See h5.dump() for more information.
92
+ """
93
+ if isinstance(src, File):
94
+ with File(src.hdf_filename) as h5:
95
+ return h5.dump(*args, **kwargs)
96
+
97
+ if isinstance(src, (str, pathlib.Path)):
98
+ pass
99
+ else:
100
+ if hasattr(src, 'hdf_filename'):
101
+ src = src.hdf_filename
102
+
103
+ with File(src) as h5:
104
+ return h5.dump(**kwargs)
105
+
106
+
107
+ def dumps(src: Union[str, File, pathlib.Path]):
108
+ """Call h5.dumps() on the provided HDF5 file
109
+
110
+ Parameters
111
+ ----------
112
+ src : str, File, pathlib.Path
113
+ the HDF5 file or filename to dump. An object which has a hdf_filename attribute can also be provided.
114
+ """
115
+ if isinstance(src, File):
116
+ with File(src.hdf_filename) as h5:
117
+ return h5.dumps()
118
+
119
+ if isinstance(src, (str, pathlib.Path)):
120
+ pass
121
+ else:
122
+ if hasattr(src, 'hdf_filename'):
123
+ src = src.hdf_filename
124
+
125
+ with File(src) as h5:
126
+ return h5.dumps()
127
+
128
+
129
+ def get_ld(
130
+ hdf_filename: Union[str, pathlib.Path],
131
+ structural: bool = True,
132
+ contextual: bool = True,
133
+ skipND: int = 1,
134
+ file_uri: Optional[str] = None,
135
+ context: Optional[Dict] = None
136
+ ) -> rdflib.Graph:
137
+ """Return the HDF file content as a rdflib.Graph object."""
138
+ from h5rdmtoolbox.ld import get_ld
139
+ return get_ld(hdf_filename,
140
+ structural=structural,
141
+ contextual=contextual,
142
+ skipND=skipND,
143
+ file_uri=file_uri,
144
+ context=context)
145
+
146
+
147
+ def dump_jsonld(
148
+ hdf_filename: Union[str, pathlib.Path],
149
+ skipND: int = 1,
150
+ indent: int = 2,
151
+ structural: bool = True,
152
+ contextual: bool = True,
153
+ context: Optional[Dict] = None,
154
+ file_uri: Optional[str] = None
155
+ ):
156
+ """Return the file content as a JSON-LD string."""
157
+ from .ld import optimize_context
158
+ context = context or {}
159
+ graph = get_ld(hdf_filename,
160
+ structural=structural,
161
+ contextual=contextual,
162
+ file_uri=file_uri,
163
+ skipND=skipND)
164
+ context = optimize_context(graph, context)
165
+ return graph.serialize(
166
+ format="json-ld",
167
+ indent=indent,
168
+ auto_compact=True,
169
+ context=context
170
+ )
171
+
172
+
173
+ # def shacl_validate(
174
+ # *,
175
+ # hdf_data: Union[str, rdflib.Graph] = None,
176
+ # hdf_source: Union[h5py.File, pathlib.Path] = None,
177
+ # shacl_data: Union[str, rdflib.Graph] = None,
178
+ # shacl_source: Union[str, pathlib.Path] = None,
179
+ # hdf_file_uri="https://example.org/hdf5file#",
180
+ # shacl_format: str = 'turtle',
181
+ # hdf_data_format: str = 'turtle',
182
+ # **pyshacl_kwargs
183
+ # ) -> ValidationResult:
184
+ # """Validate HDF5 file content against SHACL shapes.
185
+ #
186
+ # Parameters
187
+ # ----------
188
+ # hdf_data : Union[str, rdflib.Graph], optional
189
+ # RDF data of the HDF5 file as string or rdflib.Graph. If not
190
+ # provided, `hdf_source` must be provided.
191
+ # hdf_source : Union[h5py.File, pathlib.Path], optional
192
+ # HDF5 file or h5py.File object to extract RDF data from. If not
193
+ # provided, `hdf_data` must be provided.
194
+ # shacl_data : Union[str, rdflib.Graph], optional
195
+ # SHACL shapes as string or rdflib.Graph. If not provided,
196
+ # `shacl_source` must be provided.
197
+ # shacl_source : Union[str, pathlib.Path], optional
198
+ # File path to SHACL shapes. If not provided, `shacl_data`
199
+ # must be provided.
200
+ # hdf_file_uri : str, optional
201
+ # The file URI to use for the HDF5 file when extracting RDF data.
202
+ # Default is "https://example.org/hdf5file#".
203
+ # shacl_format : str, optional
204
+ # The format of the SHACL shapes if `shacl_data` is provided as
205
+ # string. Default is 'turtle'.
206
+ # hdf_data_format : str, optional
207
+ # The format of the HDF5 RDF data if `hdf_data` is provided as
208
+ # string. Default is 'turtle'.
209
+ # **pyshacl_kwargs
210
+ # Additional keyword arguments passed to pyshacl.validate().
211
+ #
212
+ # Returns
213
+ # -------
214
+ # ValidationResult
215
+ # The result of the SHACL validation.
216
+ # """
217
+ # return validate_hdf(
218
+ # hdf_data=hdf_data,
219
+ # hdf_source=hdf_source,
220
+ # shacl_data=shacl_data,
221
+ # shacl_source=shacl_source,
222
+ # hdf_file_uri=hdf_file_uri,
223
+ # shacl_format=shacl_format,
224
+ # hdf_data_format=hdf_data_format,
225
+ # **pyshacl_kwargs
226
+ # )
227
+
228
+
229
+ def dump_jsonld_depr(hdf_filename: Union[str, pathlib.Path],
230
+ skipND: int = 1,
231
+ structural: bool = True,
232
+ contextual: bool = True,
233
+ context: Optional[Dict] = None,
234
+ blank_node_iri_base: Optional[str] = None
235
+ ) -> str:
236
+ """Dump the JSON-LD representation of the file. With semantic=True and structural=False, the JSON-LD
237
+ represents the semantic content only. To get a pure structural representation, set semantic=False, which
238
+ will ignore any RDF content. If both are set to True, the JSON-LD will contain both structural and semantic.
239
+
240
+ Parameters
241
+ ----------
242
+ hdf_filename : str, pathlib.Path
243
+ the HDF5 file to dump.
244
+ skipND : int=1
245
+ Skip writing data of datasets with more than `skipND` dimensions. Only
246
+ considered if structural=True.
247
+ structural : bool=True
248
+ Include structural information in the JSON-LD output.
249
+ contextual : bool=True
250
+ Include contextual information in the JSON-LD output.
251
+ context: Optional[Dict]
252
+ context in form of {prefix: IRI}, e.g. "ssno": "https://matthiasprobst.github.io/ssno#"
253
+ blank_node_iri_base: Optional[str]
254
+ IRI base used for blank nodes
255
+ """
256
+ if blank_node_iri_base is not None:
257
+ if not isinstance(blank_node_iri_base, (str, HttpUrl)):
258
+ raise ValueError('blank_node_iri_base must be a valid URL')
259
+ from .wrapper import jsonld
260
+ if not structural and not contextual:
261
+ raise ValueError('At least one of structural or contextual must be True.')
262
+
263
+ from h5rdmtoolbox.ld.hdf.file import get_serialized_ld
264
+ if structural and not contextual:
265
+ return get_serialized_ld(
266
+ hdf_filename,
267
+ blank_node_iri_base,
268
+ format="json-ld",
269
+ context=context,
270
+ skipND=skipND)
271
+ return get_serialized_ld(
272
+ hdf_filename,
273
+ blank_node_iri_base,
274
+ format="json-ld",
275
+ context=context,
276
+ skipND=skipND)
277
+
278
+
279
+ def serialize(hdf_filename,
280
+ fmt: str = "ttl",
281
+ skipND: int = 1,
282
+ structural: bool = True,
283
+ contextual: bool = True,
284
+ file_uri: Optional[str] = None,
285
+ **kwargs):
286
+ """Alternative to json-ld but allows multiple serialization options"""
287
+ fmt = kwargs.pop("format", fmt)
288
+ with File(hdf_filename) as h5:
289
+ return h5.serialize(fmt=fmt,
290
+ skipND=skipND,
291
+ structural=structural,
292
+ contextual=contextual,
293
+ file_uri=file_uri)
294
+
295
+
296
+ def build_pyvis_graph(hdf_filename, output_filename="kg-graph.html", notebook=False,
297
+ structural: bool = True,
298
+ contextual: bool = True,
299
+ style: Dict = None):
300
+ """Calls `build_pyvis_graph` of kglab library. Requires kglab and pyvis"""
301
+ try:
302
+ import kglab
303
+ except ImportError:
304
+ raise ImportError('kglab is required for this function. Install it using: pip install kglab')
305
+ kg = kglab.KnowledgeGraph().load_rdf_text(
306
+ serialize(hdf_filename, fmt="ttl", structural=structural, contextual=contextual)
307
+ )
308
+ vis_style = style or {
309
+ "hdf": {
310
+ "color": "orange",
311
+ "size": 40,
312
+ },
313
+ "ind": {
314
+ "color": "blue",
315
+ "size": 30,
316
+ },
317
+ }
318
+ subgraph = kglab.SubgraphTensor(kg)
319
+ pyvis_graph = subgraph.build_pyvis_graph(notebook=notebook, style=vis_style)
320
+ pyvis_graph.force_atlas_2based()
321
+ pyvis_graph.show(output_filename)
322
+ return pyvis_graph
323
+
324
+
325
+ def get_filesize(hdf_filename: Union[str, pathlib.Path]) -> int:
326
+ """Get the size of the HDF5 file in bytes"""
327
+ return utils.get_filesize(hdf_filename)
328
+
329
+
330
+ def get_checksum(hdf_filename: Union[str, pathlib.Path]) -> str:
331
+ """Get the checksum of the HDF5 file"""
332
+ return utils.get_checksum(hdf_filename)
333
+
334
+
335
+ def register_dataset_decoder(decoder: Callable, decoder_name: str = None, overwrite: bool = False):
336
+ """A decoder function takes a xarray.DataArray and a dataset as input and returns a xarray.DataArray
337
+ It is called after the dataset is loaded into memory and before being returned to the user. Be careful:
338
+ Multiple decoders can be registered, and they are called in the order of registration. Hence, your decoder
339
+ may behave unexpectedly!
340
+ """
341
+ from .wrapper import ds_decoder
342
+ if decoder_name is None:
343
+ decoder_name = decoder.__name__
344
+ registered_decorators = ds_decoder.registered_dataset_decoders
345
+ if decoder_name in registered_decorators or decoder in registered_decorators.values():
346
+ if not overwrite:
347
+ raise ValueError(f'decoder "{decoder_name}" already registered. Name and function must be unique.')
348
+ ds_decoder.registered_dataset_decoders[decoder_name] = decoder
349
+
350
+
351
+ _ATEXIT_VERBOSE = False
352
+
353
+
354
+ def set_loglevel(level: Union[int, str]):
355
+ """Set the logging level of the h5rdmtoolbox logger"""
356
+ import logging
357
+ _logger = logging.getLogger('h5rdmtoolbox')
358
+ _logger.setLevel(level)
359
+ for h in _logger.handlers:
360
+ h.setLevel(level)
361
+
362
+
363
+ @atexit.register
364
+ def clean_temp_data(full: bool = False):
365
+ """cleaning up the tmp directory"""
366
+
367
+ failed_dirs = []
368
+ failed_dirs_file = UserDir['tmp'] / 'failed.txt'
369
+ if full:
370
+ root_tmp = USER_DATA_DIR / 'tmp'
371
+ if root_tmp.exists():
372
+ try:
373
+ shutil.rmtree(root_tmp)
374
+ root_tmp.mkdir(exist_ok=True, parents=True)
375
+ except PermissionError as e:
376
+ print(f'removing tmp folder "{root_tmp}" failed due to "{e}".')
377
+ return
378
+
379
+ for _tmp_session_dir in [UserDir['tmp'], ]:
380
+ if _ATEXIT_VERBOSE:
381
+ print(f'cleaning up tmp directory "{_tmp_session_dir}"')
382
+ if _tmp_session_dir.exists():
383
+ try:
384
+ if _ATEXIT_VERBOSE:
385
+ print(f'try deleting tmp in session dir: {_tmp_session_dir}')
386
+ shutil.rmtree(_tmp_session_dir)
387
+ except PermissionError as e:
388
+ if _ATEXIT_VERBOSE:
389
+ print(f'[!] failed deleting tmp session dir: {_tmp_session_dir}')
390
+ failed_dirs.append(UserDir['tmp'])
391
+ if _ATEXIT_VERBOSE:
392
+ print(f'removing tmp folder "{_tmp_session_dir}" failed due to "{e}". Best is you '
393
+ f'manually delete the directory.')
394
+ finally:
395
+ lines = []
396
+ if failed_dirs_file.exists():
397
+ with open(failed_dirs_file, 'r') as f:
398
+ lines = f.readlines()
399
+ for line in lines:
400
+ try:
401
+ shutil.rmtree(line.strip())
402
+ except Exception:
403
+ if pathlib.Path(line).exists():
404
+ failed_dirs.append(line)
405
+
406
+ if lines or failed_dirs:
407
+ with open(failed_dirs_file, 'w') as f:
408
+ for fd in failed_dirs:
409
+ f.writelines(f'{fd}\n')
410
+ else:
411
+ failed_dirs_file.unlink(missing_ok=True)
412
+ else:
413
+ logger.debug(f'No user tmp dir not found: {_tmp_session_dir}')
414
+
415
+
416
+ xr.set_options(display_expand_data=False)
417
+
418
+ __all__ = ('__version__', '__author__', '__author_orcid__',
419
+ 'UserDir', 'use',
420
+ 'File', 'Group', 'Dataset', 'Attribute',
421
+ 'dump', 'dumps', 'cv_h5py', 'lower', 'Lower',
422
+ 'set_config', 'get_config', 'get_ureg',
423
+ 'Convention', 'jsonld', 'lazy', 'DownloadFileManager',
424
+ 'clean_temp_data')
h5rdmtoolbox/_cfg.py ADDED
@@ -0,0 +1,104 @@
1
+ """package configuration. largely based on xarray's configuration concept. no credits taken!"""
2
+ from pint import UnitRegistry
3
+ from typing import Dict, Union
4
+
5
+ ureg = UnitRegistry(force_ndarray_like=True)
6
+
7
+
8
+ def is_valid_logger_level(level: Union[str, int]):
9
+ """Check if the logger level is valid."""
10
+ if not isinstance(level, (str, int)):
11
+ # raise TypeError(f'Invalid type for the logger: {type(logger_value)}')
12
+ return False
13
+ if isinstance(level, str):
14
+ return level.lower() in ('error', 'debug', 'critical', 'warning', 'info', 'fatal', 'warning', 'warn')
15
+ # 0: NOTSET, 10: DEBUG, 20: INFO, 30: WARNING, 40: ERROR, 50: CRITICAL
16
+ return level in (0, 10, 20, 30, 40, 50)
17
+
18
+
19
+ CONFIG = {
20
+ 'return_xarray': True,
21
+ 'advanced_shape_repr': True,
22
+ 'natural_naming': True,
23
+ 'hdf_compression': None, # 'gzip',
24
+ 'hdf_compression_opts': None, # 5,
25
+ 'adjusting_plotting_labels': True,
26
+ 'xarray_unit_repr_in_plots': 'in',
27
+ 'plotting_name_order': ('plot_name', 'long_name', 'standard_name'),
28
+ 'require_unit': True, # datasets require units
29
+ 'ureg_format': 'C~',
30
+ 'init_logger_level': 'ERROR',
31
+ 'dtime_fmt': '%Y-%m-%dT%H:%M:%S%f',
32
+ 'expose_user_prop_to_attrs': True,
33
+ 'add_provenance': False,
34
+ 'ignore_set_std_attr_err': False,
35
+ 'auto_create_h5tbx_version': False, # automatically creates the group h5rdmtoolbox with the version attribute
36
+ 'uuid_name': 'uuid', # attribute name used for UUIDs
37
+ # if a standard attribute is defined and cannot be retrieved because the value is invalid, ignore it:
38
+ 'ignore_get_std_attr_err': False,
39
+ 'allow_deleting_standard_attributes': False,
40
+ 'ignore_none': False
41
+ }
42
+
43
+ _VALIDATORS = {
44
+ 'return_xarray': lambda x: isinstance(x, bool),
45
+ 'advanced_shape_repr': lambda x: isinstance(x, bool),
46
+ 'natural_naming': lambda x: isinstance(x, bool),
47
+ 'hdf_compression': lambda x: isinstance(x, str),
48
+ 'hdf_compression_opts': lambda x: isinstance(x, int),
49
+ 'xarray_unit_repr_in_plots': lambda x: x in ('/', '()', '(', '[]', '[', '//', 'in'),
50
+ 'plotting_name_order': lambda x: isinstance(x, (tuple, list)) and [xx in ('plot_name', 'long_name', 'standard_name')
51
+ for xx in x],
52
+ 'require_unit': lambda x: isinstance(x, bool),
53
+ 'ureg_format': lambda x: isinstance(x, str),
54
+ 'init_logger_level': lambda x: is_valid_logger_level(x),
55
+ 'dtime_fmt': lambda x: isinstance(x, str),
56
+ 'expose_user_prop_to_attrs': lambda x: isinstance(x, bool),
57
+ 'add_provenance': lambda x: isinstance(x, bool),
58
+ 'ignore_set_std_attr_err': lambda x: isinstance(x, bool),
59
+ 'ignore_get_std_attr_err': lambda x: isinstance(x, bool),
60
+ 'ignore_none': lambda x: isinstance(x, bool)
61
+ }
62
+
63
+
64
+ class set_config:
65
+ """Set the configuration parameters."""
66
+
67
+ def __init__(self, **kwargs):
68
+ self.old = {}
69
+ for k, v in kwargs.items():
70
+ if k in _VALIDATORS and not _VALIDATORS[k](v):
71
+ raise ValueError(f'Config parameter "{k}" has invalid value: "{v}"')
72
+ if k not in CONFIG:
73
+ raise KeyError(f'Not a configuration key: "{k}"')
74
+ self.old[k] = CONFIG[k]
75
+ if k == 'ureg_format':
76
+ get_ureg().formatter.default_format = str(v)
77
+ self._update(kwargs)
78
+
79
+ def __enter__(self):
80
+ return
81
+
82
+ def __exit__(self, *args, **kwargs):
83
+ self._update(self.old)
84
+
85
+ def _update(self, options_dict: Dict):
86
+ CONFIG.update(options_dict)
87
+
88
+
89
+ # set_config = ConfigSetter()
90
+
91
+
92
+ def get_config(key=None):
93
+ """Return the configuration parameters."""
94
+ if key is None:
95
+ return CONFIG
96
+ return CONFIG[key]
97
+
98
+
99
+ def get_ureg() -> UnitRegistry:
100
+ """get unit registry"""
101
+ return ureg
102
+
103
+
104
+ set_config(ureg_format=CONFIG['ureg_format'])