junifer 0.0.5.dev240__py3-none-any.whl → 0.0.6__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.
- junifer/__init__.py +2 -31
- junifer/__init__.pyi +37 -0
- junifer/_version.py +9 -4
- junifer/api/__init__.py +3 -5
- junifer/api/__init__.pyi +4 -0
- junifer/api/decorators.py +14 -19
- junifer/api/functions.py +165 -109
- junifer/api/py.typed +0 -0
- junifer/api/queue_context/__init__.py +2 -4
- junifer/api/queue_context/__init__.pyi +5 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +22 -6
- junifer/api/queue_context/htcondor_adapter.py +23 -6
- junifer/api/queue_context/py.typed +0 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +3 -3
- junifer/api/queue_context/tests/test_htcondor_adapter.py +3 -3
- junifer/api/tests/test_functions.py +168 -74
- junifer/cli/__init__.py +24 -0
- junifer/cli/__init__.pyi +3 -0
- junifer/{api → cli}/cli.py +141 -125
- junifer/cli/parser.py +235 -0
- junifer/cli/py.typed +0 -0
- junifer/{api → cli}/tests/test_cli.py +8 -8
- junifer/{api/tests/test_api_utils.py → cli/tests/test_cli_utils.py} +5 -4
- junifer/{api → cli}/tests/test_parser.py +2 -2
- junifer/{api → cli}/utils.py +6 -16
- junifer/configs/juseless/__init__.py +2 -2
- junifer/configs/juseless/__init__.pyi +3 -0
- junifer/configs/juseless/datagrabbers/__init__.py +2 -12
- junifer/configs/juseless/datagrabbers/__init__.pyi +13 -0
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +2 -2
- junifer/configs/juseless/datagrabbers/py.typed +0 -0
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -2
- junifer/configs/juseless/datagrabbers/ucla.py +4 -4
- junifer/configs/juseless/py.typed +0 -0
- junifer/conftest.py +25 -0
- junifer/data/__init__.py +2 -42
- junifer/data/__init__.pyi +29 -0
- junifer/data/_dispatch.py +248 -0
- junifer/data/coordinates/__init__.py +9 -0
- junifer/data/coordinates/__init__.pyi +5 -0
- junifer/data/coordinates/_ants_coordinates_warper.py +104 -0
- junifer/data/coordinates/_coordinates.py +385 -0
- junifer/data/coordinates/_fsl_coordinates_warper.py +81 -0
- junifer/data/{tests → coordinates/tests}/test_coordinates.py +26 -33
- junifer/data/masks/__init__.py +9 -0
- junifer/data/masks/__init__.pyi +6 -0
- junifer/data/masks/_ants_mask_warper.py +177 -0
- junifer/data/masks/_fsl_mask_warper.py +106 -0
- junifer/data/masks/_masks.py +802 -0
- junifer/data/{tests → masks/tests}/test_masks.py +67 -63
- junifer/data/parcellations/__init__.py +9 -0
- junifer/data/parcellations/__init__.pyi +6 -0
- junifer/data/parcellations/_ants_parcellation_warper.py +166 -0
- junifer/data/parcellations/_fsl_parcellation_warper.py +89 -0
- junifer/data/parcellations/_parcellations.py +1388 -0
- junifer/data/{tests → parcellations/tests}/test_parcellations.py +165 -295
- junifer/data/pipeline_data_registry_base.py +76 -0
- junifer/data/py.typed +0 -0
- junifer/data/template_spaces.py +44 -79
- junifer/data/tests/test_data_utils.py +1 -2
- junifer/data/tests/test_template_spaces.py +8 -4
- junifer/data/utils.py +109 -4
- junifer/datagrabber/__init__.py +2 -26
- junifer/datagrabber/__init__.pyi +27 -0
- junifer/datagrabber/aomic/__init__.py +2 -4
- junifer/datagrabber/aomic/__init__.pyi +5 -0
- junifer/datagrabber/aomic/id1000.py +81 -52
- junifer/datagrabber/aomic/piop1.py +83 -55
- junifer/datagrabber/aomic/piop2.py +85 -56
- junifer/datagrabber/aomic/py.typed +0 -0
- junifer/datagrabber/aomic/tests/test_id1000.py +19 -12
- junifer/datagrabber/aomic/tests/test_piop1.py +52 -18
- junifer/datagrabber/aomic/tests/test_piop2.py +50 -17
- junifer/datagrabber/base.py +22 -18
- junifer/datagrabber/datalad_base.py +71 -34
- junifer/datagrabber/dmcc13_benchmark.py +31 -18
- junifer/datagrabber/hcp1200/__init__.py +2 -3
- junifer/datagrabber/hcp1200/__init__.pyi +4 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -3
- junifer/datagrabber/hcp1200/hcp1200.py +26 -15
- junifer/datagrabber/hcp1200/py.typed +0 -0
- junifer/datagrabber/hcp1200/tests/test_hcp1200.py +8 -2
- junifer/datagrabber/multiple.py +14 -9
- junifer/datagrabber/pattern.py +132 -96
- junifer/datagrabber/pattern_validation_mixin.py +206 -94
- junifer/datagrabber/py.typed +0 -0
- junifer/datagrabber/tests/test_datalad_base.py +27 -12
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +28 -11
- junifer/datagrabber/tests/test_multiple.py +48 -2
- junifer/datagrabber/tests/test_pattern_datalad.py +1 -1
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +6 -6
- junifer/datareader/__init__.py +2 -2
- junifer/datareader/__init__.pyi +3 -0
- junifer/datareader/default.py +6 -6
- junifer/datareader/py.typed +0 -0
- junifer/external/nilearn/__init__.py +2 -3
- junifer/external/nilearn/__init__.pyi +4 -0
- junifer/external/nilearn/junifer_connectivity_measure.py +25 -17
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +4 -4
- junifer/external/nilearn/py.typed +0 -0
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +17 -16
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +2 -3
- junifer/markers/__init__.py +2 -38
- junifer/markers/__init__.pyi +37 -0
- junifer/markers/base.py +11 -14
- junifer/markers/brainprint.py +12 -14
- junifer/markers/complexity/__init__.py +2 -18
- junifer/markers/complexity/__init__.pyi +17 -0
- junifer/markers/complexity/complexity_base.py +9 -11
- junifer/markers/complexity/hurst_exponent.py +7 -7
- junifer/markers/complexity/multiscale_entropy_auc.py +7 -7
- junifer/markers/complexity/perm_entropy.py +7 -7
- junifer/markers/complexity/py.typed +0 -0
- junifer/markers/complexity/range_entropy.py +7 -7
- junifer/markers/complexity/range_entropy_auc.py +7 -7
- junifer/markers/complexity/sample_entropy.py +7 -7
- junifer/markers/complexity/tests/test_complexity_base.py +1 -1
- junifer/markers/complexity/tests/test_hurst_exponent.py +5 -5
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +5 -5
- junifer/markers/complexity/tests/test_perm_entropy.py +5 -5
- junifer/markers/complexity/tests/test_range_entropy.py +5 -5
- junifer/markers/complexity/tests/test_range_entropy_auc.py +5 -5
- junifer/markers/complexity/tests/test_sample_entropy.py +5 -5
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +5 -5
- junifer/markers/complexity/weighted_perm_entropy.py +7 -7
- junifer/markers/ets_rss.py +12 -11
- junifer/markers/falff/__init__.py +2 -3
- junifer/markers/falff/__init__.pyi +4 -0
- junifer/markers/falff/_afni_falff.py +38 -45
- junifer/markers/falff/_junifer_falff.py +16 -19
- junifer/markers/falff/falff_base.py +7 -11
- junifer/markers/falff/falff_parcels.py +9 -9
- junifer/markers/falff/falff_spheres.py +8 -8
- junifer/markers/falff/py.typed +0 -0
- junifer/markers/falff/tests/test_falff_spheres.py +3 -1
- junifer/markers/functional_connectivity/__init__.py +2 -12
- junifer/markers/functional_connectivity/__init__.pyi +13 -0
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +9 -8
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +8 -8
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +7 -7
- junifer/markers/functional_connectivity/functional_connectivity_base.py +13 -12
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +8 -8
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +7 -7
- junifer/markers/functional_connectivity/py.typed +0 -0
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +1 -2
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +1 -2
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +6 -6
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +5 -5
- junifer/markers/parcel_aggregation.py +22 -17
- junifer/markers/py.typed +0 -0
- junifer/markers/reho/__init__.py +2 -3
- junifer/markers/reho/__init__.pyi +4 -0
- junifer/markers/reho/_afni_reho.py +29 -35
- junifer/markers/reho/_junifer_reho.py +13 -14
- junifer/markers/reho/py.typed +0 -0
- junifer/markers/reho/reho_base.py +7 -11
- junifer/markers/reho/reho_parcels.py +10 -10
- junifer/markers/reho/reho_spheres.py +9 -9
- junifer/markers/sphere_aggregation.py +22 -17
- junifer/markers/temporal_snr/__init__.py +2 -3
- junifer/markers/temporal_snr/__init__.pyi +4 -0
- junifer/markers/temporal_snr/py.typed +0 -0
- junifer/markers/temporal_snr/temporal_snr_base.py +11 -10
- junifer/markers/temporal_snr/temporal_snr_parcels.py +8 -8
- junifer/markers/temporal_snr/temporal_snr_spheres.py +7 -7
- junifer/markers/tests/test_ets_rss.py +3 -3
- junifer/markers/tests/test_parcel_aggregation.py +24 -24
- junifer/markers/tests/test_sphere_aggregation.py +6 -6
- junifer/markers/utils.py +3 -3
- junifer/onthefly/__init__.py +2 -1
- junifer/onthefly/_brainprint.py +138 -0
- junifer/onthefly/read_transform.py +5 -8
- junifer/pipeline/__init__.py +2 -10
- junifer/pipeline/__init__.pyi +13 -0
- junifer/{markers/collection.py → pipeline/marker_collection.py} +8 -14
- junifer/pipeline/pipeline_component_registry.py +294 -0
- junifer/pipeline/pipeline_step_mixin.py +15 -11
- junifer/pipeline/py.typed +0 -0
- junifer/{markers/tests/test_collection.py → pipeline/tests/test_marker_collection.py} +2 -3
- junifer/pipeline/tests/test_pipeline_component_registry.py +200 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +36 -37
- junifer/pipeline/tests/test_update_meta_mixin.py +4 -4
- junifer/pipeline/tests/test_workdir_manager.py +43 -0
- junifer/pipeline/update_meta_mixin.py +21 -17
- junifer/pipeline/utils.py +6 -6
- junifer/pipeline/workdir_manager.py +19 -5
- junifer/preprocess/__init__.py +2 -10
- junifer/preprocess/__init__.pyi +11 -0
- junifer/preprocess/base.py +10 -10
- junifer/preprocess/confounds/__init__.py +2 -2
- junifer/preprocess/confounds/__init__.pyi +3 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +243 -64
- junifer/preprocess/confounds/py.typed +0 -0
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +121 -14
- junifer/preprocess/py.typed +0 -0
- junifer/preprocess/smoothing/__init__.py +2 -2
- junifer/preprocess/smoothing/__init__.pyi +3 -0
- junifer/preprocess/smoothing/_afni_smoothing.py +40 -40
- junifer/preprocess/smoothing/_fsl_smoothing.py +22 -32
- junifer/preprocess/smoothing/_nilearn_smoothing.py +35 -14
- junifer/preprocess/smoothing/py.typed +0 -0
- junifer/preprocess/smoothing/smoothing.py +11 -13
- junifer/preprocess/warping/__init__.py +2 -2
- junifer/preprocess/warping/__init__.pyi +3 -0
- junifer/preprocess/warping/_ants_warper.py +136 -32
- junifer/preprocess/warping/_fsl_warper.py +73 -22
- junifer/preprocess/warping/py.typed +0 -0
- junifer/preprocess/warping/space_warper.py +39 -11
- junifer/preprocess/warping/tests/test_space_warper.py +5 -9
- junifer/py.typed +0 -0
- junifer/stats.py +5 -5
- junifer/storage/__init__.py +2 -10
- junifer/storage/__init__.pyi +11 -0
- junifer/storage/base.py +47 -13
- junifer/storage/hdf5.py +95 -33
- junifer/storage/pandas_base.py +12 -11
- junifer/storage/py.typed +0 -0
- junifer/storage/sqlite.py +11 -11
- junifer/storage/tests/test_hdf5.py +86 -4
- junifer/storage/tests/test_sqlite.py +2 -2
- junifer/storage/tests/test_storage_base.py +5 -2
- junifer/storage/tests/test_utils.py +33 -7
- junifer/storage/utils.py +95 -9
- junifer/testing/__init__.py +2 -3
- junifer/testing/__init__.pyi +4 -0
- junifer/testing/datagrabbers.py +10 -11
- junifer/testing/py.typed +0 -0
- junifer/testing/registry.py +4 -7
- junifer/testing/tests/test_testing_registry.py +9 -17
- junifer/tests/test_stats.py +2 -2
- junifer/typing/__init__.py +9 -0
- junifer/typing/__init__.pyi +31 -0
- junifer/typing/_typing.py +68 -0
- junifer/utils/__init__.py +2 -12
- junifer/utils/__init__.pyi +18 -0
- junifer/utils/_config.py +110 -0
- junifer/utils/_yaml.py +16 -0
- junifer/utils/helpers.py +6 -6
- junifer/utils/logging.py +117 -8
- junifer/utils/py.typed +0 -0
- junifer/{pipeline → utils}/singleton.py +19 -14
- junifer/utils/tests/test_config.py +59 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/METADATA +43 -38
- junifer-0.0.6.dist-info/RECORD +350 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/WHEEL +1 -1
- junifer-0.0.6.dist-info/entry_points.txt +2 -0
- junifer/api/parser.py +0 -118
- junifer/data/coordinates.py +0 -408
- junifer/data/masks.py +0 -670
- junifer/data/parcellations.py +0 -1828
- junifer/pipeline/registry.py +0 -177
- junifer/pipeline/tests/test_registry.py +0 -150
- junifer-0.0.5.dev240.dist-info/RECORD +0 -275
- junifer-0.0.5.dev240.dist-info/entry_points.txt +0 -2
- /junifer/{api → cli}/tests/data/gmd_mean.yaml +0 -0
- /junifer/{api → cli}/tests/data/gmd_mean_htcondor.yaml +0 -0
- /junifer/{api → cli}/tests/data/partly_cloudy_agg_mean_tian.yml +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/AutobiographicalMemory_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAC_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAR_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/DMNBuckner_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Dosenbach2010_MNI_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Empathy_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Motor_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/MultiTask_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/PhysioStress_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2011_MNI_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2013_MNI_VOIs.tsv +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Rew_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Somatosensory_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/ToM_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/VigAtt_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/WM_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/eMDN_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/eSAD_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/extDMN_VOIs.txt +0 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
__all__ = [
|
2
|
+
"DataGrabberLike",
|
3
|
+
"PreprocessorLike",
|
4
|
+
"MarkerLike",
|
5
|
+
"StorageLike",
|
6
|
+
"PipelineComponent",
|
7
|
+
"Dependencies",
|
8
|
+
"ConditionalDependencies",
|
9
|
+
"ExternalDependencies",
|
10
|
+
"MarkerInOutMappings",
|
11
|
+
"DataGrabberPatterns",
|
12
|
+
"ConfigVal",
|
13
|
+
"Element",
|
14
|
+
"Elements",
|
15
|
+
]
|
16
|
+
|
17
|
+
from ._typing import (
|
18
|
+
DataGrabberLike,
|
19
|
+
PreprocessorLike,
|
20
|
+
MarkerLike,
|
21
|
+
StorageLike,
|
22
|
+
PipelineComponent,
|
23
|
+
Dependencies,
|
24
|
+
ConditionalDependencies,
|
25
|
+
ExternalDependencies,
|
26
|
+
MarkerInOutMappings,
|
27
|
+
DataGrabberPatterns,
|
28
|
+
ConfigVal,
|
29
|
+
Element,
|
30
|
+
Elements,
|
31
|
+
)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
"""Provide type hints for internal and external use."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from collections.abc import MutableMapping, Sequence
|
7
|
+
from typing import (
|
8
|
+
TYPE_CHECKING,
|
9
|
+
Union,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from ..datagrabber import BaseDataGrabber
|
15
|
+
from ..datareader import DefaultDataReader
|
16
|
+
from ..markers import BaseMarker
|
17
|
+
from ..preprocess import BasePreprocessor
|
18
|
+
from ..storage import BaseFeatureStorage
|
19
|
+
|
20
|
+
|
21
|
+
__all__ = [
|
22
|
+
"ConditionalDependencies",
|
23
|
+
"ConfigVal",
|
24
|
+
"DataGrabberLike",
|
25
|
+
"DataGrabberPatterns",
|
26
|
+
"Dependencies",
|
27
|
+
"Element",
|
28
|
+
"Elements",
|
29
|
+
"ExternalDependencies",
|
30
|
+
"MarkerInOutMappings",
|
31
|
+
"MarkerLike",
|
32
|
+
"PipelineComponent",
|
33
|
+
"PreprocessorLike",
|
34
|
+
"StorageLike",
|
35
|
+
]
|
36
|
+
|
37
|
+
|
38
|
+
DataGrabberLike = type["BaseDataGrabber"]
|
39
|
+
PreprocessorLike = type["BasePreprocessor"]
|
40
|
+
MarkerLike = type["BaseMarker"]
|
41
|
+
StorageLike = type["BaseFeatureStorage"]
|
42
|
+
PipelineComponent = Union[
|
43
|
+
"DataGrabberLike",
|
44
|
+
"DefaultDataReader",
|
45
|
+
"PreprocessorLike",
|
46
|
+
"MarkerLike",
|
47
|
+
"StorageLike",
|
48
|
+
]
|
49
|
+
Dependencies = set[str]
|
50
|
+
ConditionalDependencies = Sequence[
|
51
|
+
MutableMapping[
|
52
|
+
str,
|
53
|
+
Union[
|
54
|
+
str,
|
55
|
+
PipelineComponent,
|
56
|
+
Sequence[str],
|
57
|
+
Sequence[PipelineComponent],
|
58
|
+
],
|
59
|
+
]
|
60
|
+
]
|
61
|
+
ExternalDependencies = Sequence[MutableMapping[str, Union[str, Sequence[str]]]]
|
62
|
+
MarkerInOutMappings = MutableMapping[str, MutableMapping[str, str]]
|
63
|
+
DataGrabberPatterns = dict[
|
64
|
+
str, Union[dict[str, str], Sequence[dict[str, str]]]
|
65
|
+
]
|
66
|
+
ConfigVal = Union[bool, int, float, str]
|
67
|
+
Element = Union[str, tuple[str, ...]]
|
68
|
+
Elements = Sequence[Element]
|
junifer/utils/__init__.py
CHANGED
@@ -4,17 +4,7 @@
|
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
-
|
8
|
-
from .logging import configure_logging, logger, raise_error, warn_with_log
|
9
|
-
from .helpers import run_ext_cmd, deep_update
|
7
|
+
import lazy_loader as lazy
|
10
8
|
|
11
9
|
|
12
|
-
__all__ =
|
13
|
-
"make_executable",
|
14
|
-
"configure_logging",
|
15
|
-
"logger",
|
16
|
-
"raise_error",
|
17
|
-
"warn_with_log",
|
18
|
-
"run_ext_cmd",
|
19
|
-
"deep_update",
|
20
|
-
]
|
10
|
+
__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
__all__ = [
|
2
|
+
"make_executable",
|
3
|
+
"configure_logging",
|
4
|
+
"config",
|
5
|
+
"logger",
|
6
|
+
"raise_error",
|
7
|
+
"warn_with_log",
|
8
|
+
"run_ext_cmd",
|
9
|
+
"deep_update",
|
10
|
+
"yaml",
|
11
|
+
"ConfigManager",
|
12
|
+
]
|
13
|
+
|
14
|
+
from .fs import make_executable
|
15
|
+
from .logging import configure_logging, logger, raise_error, warn_with_log
|
16
|
+
from ._config import config, ConfigManager
|
17
|
+
from .helpers import run_ext_cmd, deep_update
|
18
|
+
from ._yaml import yaml
|
junifer/utils/_config.py
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
"""Provide junifer global configuration."""
|
2
|
+
|
3
|
+
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
|
+
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
|
+
# License: AGPL
|
6
|
+
|
7
|
+
import os
|
8
|
+
from typing import Optional
|
9
|
+
|
10
|
+
from ..typing import ConfigVal
|
11
|
+
from .logging import logger
|
12
|
+
from .singleton import Singleton
|
13
|
+
|
14
|
+
|
15
|
+
__all__ = ["ConfigManager", "config"]
|
16
|
+
|
17
|
+
|
18
|
+
class ConfigManager(metaclass=Singleton):
|
19
|
+
"""Manage configuration parameters.
|
20
|
+
|
21
|
+
Attributes
|
22
|
+
----------
|
23
|
+
_config : dict
|
24
|
+
Configuration parameters.
|
25
|
+
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self) -> None:
|
29
|
+
"""Initialize the class."""
|
30
|
+
self._config = {}
|
31
|
+
# Initial setup from process env
|
32
|
+
self._reload()
|
33
|
+
|
34
|
+
def _reload(self) -> None:
|
35
|
+
"""Reload env vars."""
|
36
|
+
for t_var in os.environ:
|
37
|
+
if t_var.startswith("JUNIFER_"):
|
38
|
+
# Set correct type
|
39
|
+
var_value = os.environ[t_var]
|
40
|
+
# bool
|
41
|
+
if var_value.lower() == "true":
|
42
|
+
var_value = True
|
43
|
+
elif var_value.lower() == "false":
|
44
|
+
var_value = False
|
45
|
+
# numeric
|
46
|
+
else:
|
47
|
+
try:
|
48
|
+
var_value = int(var_value)
|
49
|
+
except ValueError:
|
50
|
+
try:
|
51
|
+
var_value = float(var_value)
|
52
|
+
except ValueError:
|
53
|
+
pass
|
54
|
+
# Set value
|
55
|
+
var_name = (
|
56
|
+
t_var.replace("JUNIFER_", "").lower().replace("_", ".")
|
57
|
+
)
|
58
|
+
logger.debug(
|
59
|
+
f"Setting `{var_name}` from environment to "
|
60
|
+
f"`{var_value}` (type: {type(var_value)})"
|
61
|
+
)
|
62
|
+
self._config[var_name] = var_value
|
63
|
+
|
64
|
+
def get(self, key: str, default: Optional[ConfigVal] = None) -> ConfigVal:
|
65
|
+
"""Get configuration parameter.
|
66
|
+
|
67
|
+
Parameters
|
68
|
+
----------
|
69
|
+
key : str
|
70
|
+
The configuration key to get.
|
71
|
+
default : bool or int or float or None, optional
|
72
|
+
The default value to return if the key is not found (default None).
|
73
|
+
|
74
|
+
Returns
|
75
|
+
-------
|
76
|
+
bool or int or float
|
77
|
+
The configuration value.
|
78
|
+
|
79
|
+
"""
|
80
|
+
return self._config.get(key, default)
|
81
|
+
|
82
|
+
def set(self, key: str, val: ConfigVal) -> None:
|
83
|
+
"""Set configuration parameter.
|
84
|
+
|
85
|
+
Parameters
|
86
|
+
----------
|
87
|
+
key : str
|
88
|
+
The configuration key to set.
|
89
|
+
val : bool or int or float
|
90
|
+
The value to set ``key`` to.
|
91
|
+
|
92
|
+
"""
|
93
|
+
logger.debug(f"Setting `{key}` to `{val}` (type: {type(val)})")
|
94
|
+
self._config[key] = val
|
95
|
+
|
96
|
+
def delete(self, key: str) -> None:
|
97
|
+
"""Delete configuration parameter.
|
98
|
+
|
99
|
+
Parameters
|
100
|
+
----------
|
101
|
+
key : str
|
102
|
+
The configuration key to delete.
|
103
|
+
|
104
|
+
"""
|
105
|
+
logger.debug(f"Deleting `{key}` from config")
|
106
|
+
_ = self._config.pop(key)
|
107
|
+
|
108
|
+
|
109
|
+
# Initialize here to access from anywhere
|
110
|
+
config = ConfigManager()
|
junifer/utils/_yaml.py
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
"""Provide YAML config definition for junifer use."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from ruamel.yaml import YAML
|
7
|
+
|
8
|
+
|
9
|
+
__all__ = ["yaml"]
|
10
|
+
|
11
|
+
|
12
|
+
# Configure YAML class once for further use
|
13
|
+
yaml = YAML()
|
14
|
+
yaml.default_flow_style = False
|
15
|
+
yaml.allow_unicode = True
|
16
|
+
yaml.indent(mapping=2, sequence=4, offset=2)
|
junifer/utils/helpers.py
CHANGED
@@ -5,15 +5,15 @@
|
|
5
5
|
|
6
6
|
import collections.abc
|
7
7
|
import subprocess
|
8
|
-
|
8
|
+
import sys
|
9
9
|
|
10
10
|
from .logging import logger, raise_error
|
11
11
|
|
12
12
|
|
13
|
-
__all__ = ["
|
13
|
+
__all__ = ["deep_update", "run_ext_cmd"]
|
14
14
|
|
15
15
|
|
16
|
-
def run_ext_cmd(name: str, cmd:
|
16
|
+
def run_ext_cmd(name: str, cmd: list[str]) -> None:
|
17
17
|
"""Run external command via subprocess.
|
18
18
|
|
19
19
|
Parameters
|
@@ -45,19 +45,19 @@ def run_ext_cmd(name: str, cmd: List[str]) -> None:
|
|
45
45
|
if process.returncode == 0:
|
46
46
|
logger.info(
|
47
47
|
f"{name} command succeeded with the following output:\n"
|
48
|
-
f"{process.stdout}"
|
48
|
+
f"{process.stdout.decode(sys.stdout.encoding)}"
|
49
49
|
)
|
50
50
|
else:
|
51
51
|
raise_error(
|
52
52
|
msg=(
|
53
53
|
f"{name} command failed with the following error:\n"
|
54
|
-
f"{process.stdout}"
|
54
|
+
f"{process.stdout.decode(sys.stdout.encoding)}"
|
55
55
|
),
|
56
56
|
klass=RuntimeError,
|
57
57
|
)
|
58
58
|
|
59
59
|
|
60
|
-
def deep_update(d:
|
60
|
+
def deep_update(d: dict, u: dict) -> dict:
|
61
61
|
"""Deep update `d` with `u`.
|
62
62
|
|
63
63
|
From: "https://stackoverflow.com/questions/3232943/update-value-of-a-nested
|
junifer/utils/logging.py
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
+
import os
|
7
8
|
import sys
|
8
9
|
|
9
10
|
|
@@ -16,7 +17,7 @@ import logging
|
|
16
17
|
import warnings
|
17
18
|
from pathlib import Path
|
18
19
|
from subprocess import PIPE, Popen, TimeoutExpired
|
19
|
-
from typing import
|
20
|
+
from typing import ClassVar, NoReturn, Optional, Union
|
20
21
|
from warnings import warn
|
21
22
|
|
22
23
|
import datalad
|
@@ -24,9 +25,9 @@ import datalad
|
|
24
25
|
|
25
26
|
__all__ = [
|
26
27
|
"WrapStdOut",
|
28
|
+
"configure_logging",
|
27
29
|
"get_versions",
|
28
30
|
"log_versions",
|
29
|
-
"configure_logging",
|
30
31
|
"raise_error",
|
31
32
|
"warn_with_log",
|
32
33
|
]
|
@@ -80,6 +81,59 @@ class WrapStdOut(logging.StreamHandler):
|
|
80
81
|
raise AttributeError(f"'file' object has not attribute '{name}'")
|
81
82
|
|
82
83
|
|
84
|
+
class ColorFormatter(logging.Formatter):
|
85
|
+
"""Color formatter for logging messages.
|
86
|
+
|
87
|
+
Parameters
|
88
|
+
----------
|
89
|
+
fmt : str
|
90
|
+
The format string for the logging message.
|
91
|
+
datefmt : str, optional
|
92
|
+
The format string for the date.
|
93
|
+
|
94
|
+
"""
|
95
|
+
|
96
|
+
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
|
97
|
+
|
98
|
+
COLORS: ClassVar[dict[str, int]] = {
|
99
|
+
"WARNING": YELLOW,
|
100
|
+
"INFO": GREEN,
|
101
|
+
"DEBUG": BLUE,
|
102
|
+
"CRITICAL": MAGENTA,
|
103
|
+
"ERROR": RED,
|
104
|
+
}
|
105
|
+
|
106
|
+
RESET_SEQ: str = "\033[0m"
|
107
|
+
COLOR_SEQ: str = "\033[1;%dm"
|
108
|
+
BOLD_SEQ: str = "\033[1m"
|
109
|
+
|
110
|
+
def __init__(self, fmt: str, datefmt: Optional[str] = None) -> None:
|
111
|
+
"""Initialize the ColorFormatter."""
|
112
|
+
logging.Formatter.__init__(self, fmt, datefmt)
|
113
|
+
|
114
|
+
def format(self, record: logging.LogRecord) -> str:
|
115
|
+
"""Format the log record.
|
116
|
+
|
117
|
+
Parameters
|
118
|
+
----------
|
119
|
+
record : logging.LogRecord
|
120
|
+
The log record to format.
|
121
|
+
|
122
|
+
Returns
|
123
|
+
-------
|
124
|
+
str
|
125
|
+
The formatted log record.
|
126
|
+
|
127
|
+
"""
|
128
|
+
levelname = record.levelname
|
129
|
+
if levelname in self.COLORS:
|
130
|
+
levelname_color = (
|
131
|
+
self.COLOR_SEQ % (30 + self.COLORS[levelname]) + levelname
|
132
|
+
)
|
133
|
+
record.levelname = levelname_color + self.RESET_SEQ
|
134
|
+
return logging.Formatter.format(self, record)
|
135
|
+
|
136
|
+
|
83
137
|
def _get_git_head(path: Path) -> str:
|
84
138
|
"""Aux function to read HEAD from git.
|
85
139
|
|
@@ -119,7 +173,7 @@ def _get_git_head(path: Path) -> str:
|
|
119
173
|
return proc_stdout
|
120
174
|
|
121
175
|
|
122
|
-
def get_versions() ->
|
176
|
+
def get_versions() -> dict:
|
123
177
|
"""Import stuff and get versions if module.
|
124
178
|
|
125
179
|
Returns
|
@@ -189,7 +243,7 @@ def _close_handlers(logger: logging.Logger) -> None:
|
|
189
243
|
logger.removeHandler(handler)
|
190
244
|
|
191
245
|
|
192
|
-
def _safe_log(versions:
|
246
|
+
def _safe_log(versions: dict, name: str) -> None:
|
193
247
|
"""Log with safety.
|
194
248
|
|
195
249
|
Parameters
|
@@ -237,11 +291,51 @@ def log_versions(tbox_path: Optional[Path] = None) -> None:
|
|
237
291
|
pass
|
238
292
|
|
239
293
|
|
294
|
+
def _can_use_color(handler: logging.Handler) -> bool:
|
295
|
+
"""Check if color can be used in the logging output.
|
296
|
+
|
297
|
+
Parameters
|
298
|
+
----------
|
299
|
+
handler : logging.Handler
|
300
|
+
The logging handler to check for color support.
|
301
|
+
|
302
|
+
Returns
|
303
|
+
-------
|
304
|
+
bool
|
305
|
+
Whether color can be used in the logging output.
|
306
|
+
|
307
|
+
"""
|
308
|
+
if isinstance(handler, logging.FileHandler):
|
309
|
+
# Do not use colors in file handlers
|
310
|
+
return False
|
311
|
+
else:
|
312
|
+
stream = handler.stream
|
313
|
+
if hasattr(stream, "isatty") and stream.isatty():
|
314
|
+
valid_terms = [
|
315
|
+
"xterm-256color",
|
316
|
+
"xterm-kitty",
|
317
|
+
"xterm-color",
|
318
|
+
]
|
319
|
+
this_term = os.getenv("TERM", None)
|
320
|
+
if this_term is not None:
|
321
|
+
if this_term in valid_terms:
|
322
|
+
return True
|
323
|
+
if this_term.endswith("256color") or this_term.endswith("256"):
|
324
|
+
return True
|
325
|
+
if this_term == "dumb" and os.getenv("CI", False):
|
326
|
+
return True
|
327
|
+
if os.getenv("COLORTERM", False):
|
328
|
+
return True
|
329
|
+
# No TTY, no color
|
330
|
+
return False
|
331
|
+
|
332
|
+
|
240
333
|
def configure_logging(
|
241
334
|
level: Union[int, str] = "WARNING",
|
242
335
|
fname: Optional[Union[str, Path]] = None,
|
243
336
|
overwrite: Optional[bool] = None,
|
244
337
|
output_format=None,
|
338
|
+
level_datalad: Union[int, str, None] = None,
|
245
339
|
) -> None:
|
246
340
|
"""Configure the logging functionality.
|
247
341
|
|
@@ -264,6 +358,10 @@ def configure_logging(
|
|
264
358
|
e.g., ``"%(asctime)s - %(levelname)s - %(message)s"``.
|
265
359
|
If None, default string format is used
|
266
360
|
(default ``"%(asctime)s - %(name)s - %(levelname)s - %(message)s"``).
|
361
|
+
level_datalad : int or {"DEBUG", "INFO", "WARNING", "ERROR"}, optional
|
362
|
+
The level of the messages to print for datalad. If string, it will be
|
363
|
+
interpreted as elements of logging. If None, it will be set as the
|
364
|
+
``level`` parameter (default None).
|
267
365
|
|
268
366
|
"""
|
269
367
|
_close_handlers(logger) # close relevant logger handlers
|
@@ -297,18 +395,29 @@ def configure_logging(
|
|
297
395
|
# "%(asctime)s [%(levelname)s] %(message)s "
|
298
396
|
# "(%(filename)s:%(lineno)s)"
|
299
397
|
# )
|
300
|
-
|
398
|
+
if _can_use_color(lh):
|
399
|
+
formatter = ColorFormatter(fmt=output_format)
|
400
|
+
else:
|
401
|
+
formatter = logging.Formatter(fmt=output_format)
|
301
402
|
|
302
403
|
lh.setFormatter(formatter) # set formatter
|
303
404
|
logger.setLevel(level) # set level
|
304
|
-
|
405
|
+
|
406
|
+
# Set datalad logging level accordingly
|
407
|
+
if level_datalad is not None:
|
408
|
+
if isinstance(level_datalad, str):
|
409
|
+
level_datalad = _logging_types[level_datalad]
|
410
|
+
else:
|
411
|
+
level_datalad = level
|
412
|
+
datalad.log.lgr.setLevel(level_datalad) # set level for datalad
|
413
|
+
|
305
414
|
logger.addHandler(lh) # set handler
|
306
415
|
log_versions() # log versions of installed packages
|
307
416
|
|
308
417
|
|
309
418
|
def raise_error(
|
310
419
|
msg: str,
|
311
|
-
klass:
|
420
|
+
klass: type[Exception] = ValueError,
|
312
421
|
exception: Optional[Exception] = None,
|
313
422
|
) -> NoReturn:
|
314
423
|
"""Raise error, but first log it.
|
@@ -331,7 +440,7 @@ def raise_error(
|
|
331
440
|
|
332
441
|
|
333
442
|
def warn_with_log(
|
334
|
-
msg: str, category: Optional[
|
443
|
+
msg: str, category: Optional[type[Warning]] = RuntimeWarning
|
335
444
|
) -> None:
|
336
445
|
"""Warn, but first log it.
|
337
446
|
|
junifer/utils/py.typed
ADDED
File without changes
|
@@ -1,15 +1,17 @@
|
|
1
1
|
"""Provide a singleton class to be used by pipeline components."""
|
2
2
|
|
3
3
|
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
5
|
# License: AGPL
|
5
6
|
|
6
|
-
from
|
7
|
+
from abc import ABCMeta
|
8
|
+
from typing import Any, ClassVar
|
7
9
|
|
8
10
|
|
9
|
-
__all__ = ["
|
11
|
+
__all__ = ["ABCSingleton", "Singleton"]
|
10
12
|
|
11
13
|
|
12
|
-
|
14
|
+
class Singleton(type):
|
13
15
|
"""Make a class singleton.
|
14
16
|
|
15
17
|
Parameters
|
@@ -17,15 +19,11 @@ def singleton(cls: Type) -> Type:
|
|
17
19
|
cls : class
|
18
20
|
The class to designate as singleton.
|
19
21
|
|
20
|
-
Returns
|
21
|
-
-------
|
22
|
-
class
|
23
|
-
The only instance of the class.
|
24
|
-
|
25
22
|
"""
|
26
|
-
instances: Dict = {}
|
27
23
|
|
28
|
-
|
24
|
+
instances: ClassVar[dict] = {}
|
25
|
+
|
26
|
+
def __call__(cls, *args: Any, **kwargs: Any) -> type:
|
29
27
|
"""Get the only instance for a class.
|
30
28
|
|
31
29
|
Parameters
|
@@ -41,8 +39,15 @@ def singleton(cls: Type) -> Type:
|
|
41
39
|
The only instance of the class.
|
42
40
|
|
43
41
|
"""
|
44
|
-
if cls not in instances:
|
45
|
-
instances[cls] =
|
46
|
-
|
42
|
+
if cls not in cls.instances:
|
43
|
+
cls.instances[cls] = super(Singleton, cls).__call__( # noqa: UP008
|
44
|
+
*args, **kwargs
|
45
|
+
)
|
46
|
+
|
47
|
+
return cls.instances[cls]
|
48
|
+
|
49
|
+
|
50
|
+
class ABCSingleton(ABCMeta, Singleton):
|
51
|
+
"""Make an abstract class a singleton."""
|
47
52
|
|
48
|
-
|
53
|
+
pass
|
@@ -0,0 +1,59 @@
|
|
1
|
+
"""Provide tests for ConfigManager."""
|
2
|
+
|
3
|
+
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
|
+
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
|
+
# License: AGPL
|
6
|
+
|
7
|
+
import os
|
8
|
+
|
9
|
+
import pytest
|
10
|
+
|
11
|
+
from junifer.typing import ConfigVal
|
12
|
+
from junifer.utils import config
|
13
|
+
from junifer.utils._config import ConfigManager
|
14
|
+
|
15
|
+
|
16
|
+
def test_config_manager_singleton() -> None:
|
17
|
+
"""Test that ConfigManager is a singleton."""
|
18
|
+
config_mgr_1 = ConfigManager()
|
19
|
+
config_mgr_2 = ConfigManager()
|
20
|
+
assert id(config_mgr_1) == id(config_mgr_2)
|
21
|
+
|
22
|
+
|
23
|
+
def test_config_manager() -> None:
|
24
|
+
"""Test config operations for ConfigManager."""
|
25
|
+
# Get non-existing with default
|
26
|
+
assert config.get(key="scooby") is None
|
27
|
+
# Set
|
28
|
+
config.set(key="scooby", val=True)
|
29
|
+
# Get existing
|
30
|
+
assert config.get("scooby")
|
31
|
+
# Delete
|
32
|
+
config.delete("scooby")
|
33
|
+
# Get non-existing with default
|
34
|
+
assert config.get(key="scooby") is None
|
35
|
+
|
36
|
+
|
37
|
+
@pytest.mark.parametrize(
|
38
|
+
"val, expected_val",
|
39
|
+
[("TRUE", True), ("FALSE", False), ("1", 1), ("0.0", 0.0)],
|
40
|
+
)
|
41
|
+
def test_config_manager_env_reload(val: str, expected_val: ConfigVal) -> None:
|
42
|
+
"""Test config parsing from env reload.
|
43
|
+
|
44
|
+
Parameters
|
45
|
+
----------
|
46
|
+
val : str
|
47
|
+
The parametrized values.
|
48
|
+
expected_val : bool or int or float
|
49
|
+
The parametrized expected value.
|
50
|
+
|
51
|
+
"""
|
52
|
+
# Set env var
|
53
|
+
os.environ["JUNIFER_TESTME"] = val
|
54
|
+
# Check
|
55
|
+
config._reload()
|
56
|
+
assert config.get("testme") == expected_val
|
57
|
+
# Cleanup
|
58
|
+
del os.environ["JUNIFER_TESTME"]
|
59
|
+
config.delete("testme")
|