junifer 0.0.5.dev242__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.
Files changed (279) hide show
  1. junifer/__init__.py +2 -31
  2. junifer/__init__.pyi +37 -0
  3. junifer/_version.py +9 -4
  4. junifer/api/__init__.py +3 -5
  5. junifer/api/__init__.pyi +4 -0
  6. junifer/api/decorators.py +14 -19
  7. junifer/api/functions.py +165 -109
  8. junifer/api/py.typed +0 -0
  9. junifer/api/queue_context/__init__.py +2 -4
  10. junifer/api/queue_context/__init__.pyi +5 -0
  11. junifer/api/queue_context/gnu_parallel_local_adapter.py +22 -6
  12. junifer/api/queue_context/htcondor_adapter.py +23 -6
  13. junifer/api/queue_context/py.typed +0 -0
  14. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +3 -3
  15. junifer/api/queue_context/tests/test_htcondor_adapter.py +3 -3
  16. junifer/api/tests/test_functions.py +168 -74
  17. junifer/cli/__init__.py +24 -0
  18. junifer/cli/__init__.pyi +3 -0
  19. junifer/{api → cli}/cli.py +141 -125
  20. junifer/cli/parser.py +235 -0
  21. junifer/cli/py.typed +0 -0
  22. junifer/{api → cli}/tests/test_cli.py +8 -8
  23. junifer/{api/tests/test_api_utils.py → cli/tests/test_cli_utils.py} +5 -4
  24. junifer/{api → cli}/tests/test_parser.py +2 -2
  25. junifer/{api → cli}/utils.py +6 -16
  26. junifer/configs/juseless/__init__.py +2 -2
  27. junifer/configs/juseless/__init__.pyi +3 -0
  28. junifer/configs/juseless/datagrabbers/__init__.py +2 -12
  29. junifer/configs/juseless/datagrabbers/__init__.pyi +13 -0
  30. junifer/configs/juseless/datagrabbers/ixi_vbm.py +2 -2
  31. junifer/configs/juseless/datagrabbers/py.typed +0 -0
  32. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -2
  33. junifer/configs/juseless/datagrabbers/ucla.py +4 -4
  34. junifer/configs/juseless/py.typed +0 -0
  35. junifer/conftest.py +25 -0
  36. junifer/data/__init__.py +2 -42
  37. junifer/data/__init__.pyi +29 -0
  38. junifer/data/_dispatch.py +248 -0
  39. junifer/data/coordinates/__init__.py +9 -0
  40. junifer/data/coordinates/__init__.pyi +5 -0
  41. junifer/data/coordinates/_ants_coordinates_warper.py +104 -0
  42. junifer/data/coordinates/_coordinates.py +385 -0
  43. junifer/data/coordinates/_fsl_coordinates_warper.py +81 -0
  44. junifer/data/{tests → coordinates/tests}/test_coordinates.py +26 -33
  45. junifer/data/masks/__init__.py +9 -0
  46. junifer/data/masks/__init__.pyi +6 -0
  47. junifer/data/masks/_ants_mask_warper.py +177 -0
  48. junifer/data/masks/_fsl_mask_warper.py +106 -0
  49. junifer/data/masks/_masks.py +802 -0
  50. junifer/data/{tests → masks/tests}/test_masks.py +67 -63
  51. junifer/data/parcellations/__init__.py +9 -0
  52. junifer/data/parcellations/__init__.pyi +6 -0
  53. junifer/data/parcellations/_ants_parcellation_warper.py +166 -0
  54. junifer/data/parcellations/_fsl_parcellation_warper.py +89 -0
  55. junifer/data/parcellations/_parcellations.py +1388 -0
  56. junifer/data/{tests → parcellations/tests}/test_parcellations.py +165 -295
  57. junifer/data/pipeline_data_registry_base.py +76 -0
  58. junifer/data/py.typed +0 -0
  59. junifer/data/template_spaces.py +44 -79
  60. junifer/data/tests/test_data_utils.py +1 -2
  61. junifer/data/tests/test_template_spaces.py +8 -4
  62. junifer/data/utils.py +109 -4
  63. junifer/datagrabber/__init__.py +2 -26
  64. junifer/datagrabber/__init__.pyi +27 -0
  65. junifer/datagrabber/aomic/__init__.py +2 -4
  66. junifer/datagrabber/aomic/__init__.pyi +5 -0
  67. junifer/datagrabber/aomic/id1000.py +81 -52
  68. junifer/datagrabber/aomic/piop1.py +83 -55
  69. junifer/datagrabber/aomic/piop2.py +85 -56
  70. junifer/datagrabber/aomic/py.typed +0 -0
  71. junifer/datagrabber/aomic/tests/test_id1000.py +19 -12
  72. junifer/datagrabber/aomic/tests/test_piop1.py +52 -18
  73. junifer/datagrabber/aomic/tests/test_piop2.py +50 -17
  74. junifer/datagrabber/base.py +22 -18
  75. junifer/datagrabber/datalad_base.py +71 -34
  76. junifer/datagrabber/dmcc13_benchmark.py +31 -18
  77. junifer/datagrabber/hcp1200/__init__.py +2 -3
  78. junifer/datagrabber/hcp1200/__init__.pyi +4 -0
  79. junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -3
  80. junifer/datagrabber/hcp1200/hcp1200.py +26 -15
  81. junifer/datagrabber/hcp1200/py.typed +0 -0
  82. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +8 -2
  83. junifer/datagrabber/multiple.py +14 -9
  84. junifer/datagrabber/pattern.py +132 -96
  85. junifer/datagrabber/pattern_validation_mixin.py +206 -94
  86. junifer/datagrabber/py.typed +0 -0
  87. junifer/datagrabber/tests/test_datalad_base.py +27 -12
  88. junifer/datagrabber/tests/test_dmcc13_benchmark.py +28 -11
  89. junifer/datagrabber/tests/test_multiple.py +48 -2
  90. junifer/datagrabber/tests/test_pattern_datalad.py +1 -1
  91. junifer/datagrabber/tests/test_pattern_validation_mixin.py +6 -6
  92. junifer/datareader/__init__.py +2 -2
  93. junifer/datareader/__init__.pyi +3 -0
  94. junifer/datareader/default.py +6 -6
  95. junifer/datareader/py.typed +0 -0
  96. junifer/external/nilearn/__init__.py +2 -3
  97. junifer/external/nilearn/__init__.pyi +4 -0
  98. junifer/external/nilearn/junifer_connectivity_measure.py +25 -17
  99. junifer/external/nilearn/junifer_nifti_spheres_masker.py +4 -4
  100. junifer/external/nilearn/py.typed +0 -0
  101. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +17 -16
  102. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +2 -3
  103. junifer/markers/__init__.py +2 -38
  104. junifer/markers/__init__.pyi +37 -0
  105. junifer/markers/base.py +11 -14
  106. junifer/markers/brainprint.py +12 -14
  107. junifer/markers/complexity/__init__.py +2 -18
  108. junifer/markers/complexity/__init__.pyi +17 -0
  109. junifer/markers/complexity/complexity_base.py +9 -11
  110. junifer/markers/complexity/hurst_exponent.py +7 -7
  111. junifer/markers/complexity/multiscale_entropy_auc.py +7 -7
  112. junifer/markers/complexity/perm_entropy.py +7 -7
  113. junifer/markers/complexity/py.typed +0 -0
  114. junifer/markers/complexity/range_entropy.py +7 -7
  115. junifer/markers/complexity/range_entropy_auc.py +7 -7
  116. junifer/markers/complexity/sample_entropy.py +7 -7
  117. junifer/markers/complexity/tests/test_complexity_base.py +1 -1
  118. junifer/markers/complexity/tests/test_hurst_exponent.py +5 -5
  119. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +5 -5
  120. junifer/markers/complexity/tests/test_perm_entropy.py +5 -5
  121. junifer/markers/complexity/tests/test_range_entropy.py +5 -5
  122. junifer/markers/complexity/tests/test_range_entropy_auc.py +5 -5
  123. junifer/markers/complexity/tests/test_sample_entropy.py +5 -5
  124. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +5 -5
  125. junifer/markers/complexity/weighted_perm_entropy.py +7 -7
  126. junifer/markers/ets_rss.py +12 -11
  127. junifer/markers/falff/__init__.py +2 -3
  128. junifer/markers/falff/__init__.pyi +4 -0
  129. junifer/markers/falff/_afni_falff.py +38 -45
  130. junifer/markers/falff/_junifer_falff.py +16 -19
  131. junifer/markers/falff/falff_base.py +7 -11
  132. junifer/markers/falff/falff_parcels.py +9 -9
  133. junifer/markers/falff/falff_spheres.py +8 -8
  134. junifer/markers/falff/py.typed +0 -0
  135. junifer/markers/falff/tests/test_falff_spheres.py +3 -1
  136. junifer/markers/functional_connectivity/__init__.py +2 -12
  137. junifer/markers/functional_connectivity/__init__.pyi +13 -0
  138. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +9 -8
  139. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +8 -8
  140. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +7 -7
  141. junifer/markers/functional_connectivity/functional_connectivity_base.py +13 -12
  142. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +8 -8
  143. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +7 -7
  144. junifer/markers/functional_connectivity/py.typed +0 -0
  145. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +1 -2
  146. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +1 -2
  147. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +6 -6
  148. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +5 -5
  149. junifer/markers/parcel_aggregation.py +22 -17
  150. junifer/markers/py.typed +0 -0
  151. junifer/markers/reho/__init__.py +2 -3
  152. junifer/markers/reho/__init__.pyi +4 -0
  153. junifer/markers/reho/_afni_reho.py +29 -35
  154. junifer/markers/reho/_junifer_reho.py +13 -14
  155. junifer/markers/reho/py.typed +0 -0
  156. junifer/markers/reho/reho_base.py +7 -11
  157. junifer/markers/reho/reho_parcels.py +10 -10
  158. junifer/markers/reho/reho_spheres.py +9 -9
  159. junifer/markers/sphere_aggregation.py +22 -17
  160. junifer/markers/temporal_snr/__init__.py +2 -3
  161. junifer/markers/temporal_snr/__init__.pyi +4 -0
  162. junifer/markers/temporal_snr/py.typed +0 -0
  163. junifer/markers/temporal_snr/temporal_snr_base.py +11 -10
  164. junifer/markers/temporal_snr/temporal_snr_parcels.py +8 -8
  165. junifer/markers/temporal_snr/temporal_snr_spheres.py +7 -7
  166. junifer/markers/tests/test_ets_rss.py +3 -3
  167. junifer/markers/tests/test_parcel_aggregation.py +24 -24
  168. junifer/markers/tests/test_sphere_aggregation.py +6 -6
  169. junifer/markers/utils.py +3 -3
  170. junifer/onthefly/__init__.py +2 -1
  171. junifer/onthefly/_brainprint.py +138 -0
  172. junifer/onthefly/read_transform.py +5 -8
  173. junifer/pipeline/__init__.py +2 -10
  174. junifer/pipeline/__init__.pyi +13 -0
  175. junifer/{markers/collection.py → pipeline/marker_collection.py} +8 -14
  176. junifer/pipeline/pipeline_component_registry.py +294 -0
  177. junifer/pipeline/pipeline_step_mixin.py +15 -11
  178. junifer/pipeline/py.typed +0 -0
  179. junifer/{markers/tests/test_collection.py → pipeline/tests/test_marker_collection.py} +2 -3
  180. junifer/pipeline/tests/test_pipeline_component_registry.py +200 -0
  181. junifer/pipeline/tests/test_pipeline_step_mixin.py +36 -37
  182. junifer/pipeline/tests/test_update_meta_mixin.py +4 -4
  183. junifer/pipeline/tests/test_workdir_manager.py +43 -0
  184. junifer/pipeline/update_meta_mixin.py +21 -17
  185. junifer/pipeline/utils.py +6 -6
  186. junifer/pipeline/workdir_manager.py +19 -5
  187. junifer/preprocess/__init__.py +2 -10
  188. junifer/preprocess/__init__.pyi +11 -0
  189. junifer/preprocess/base.py +10 -10
  190. junifer/preprocess/confounds/__init__.py +2 -2
  191. junifer/preprocess/confounds/__init__.pyi +3 -0
  192. junifer/preprocess/confounds/fmriprep_confound_remover.py +243 -64
  193. junifer/preprocess/confounds/py.typed +0 -0
  194. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +121 -14
  195. junifer/preprocess/py.typed +0 -0
  196. junifer/preprocess/smoothing/__init__.py +2 -2
  197. junifer/preprocess/smoothing/__init__.pyi +3 -0
  198. junifer/preprocess/smoothing/_afni_smoothing.py +40 -40
  199. junifer/preprocess/smoothing/_fsl_smoothing.py +22 -32
  200. junifer/preprocess/smoothing/_nilearn_smoothing.py +35 -14
  201. junifer/preprocess/smoothing/py.typed +0 -0
  202. junifer/preprocess/smoothing/smoothing.py +11 -13
  203. junifer/preprocess/warping/__init__.py +2 -2
  204. junifer/preprocess/warping/__init__.pyi +3 -0
  205. junifer/preprocess/warping/_ants_warper.py +136 -32
  206. junifer/preprocess/warping/_fsl_warper.py +73 -22
  207. junifer/preprocess/warping/py.typed +0 -0
  208. junifer/preprocess/warping/space_warper.py +39 -11
  209. junifer/preprocess/warping/tests/test_space_warper.py +5 -9
  210. junifer/py.typed +0 -0
  211. junifer/stats.py +5 -5
  212. junifer/storage/__init__.py +2 -10
  213. junifer/storage/__init__.pyi +11 -0
  214. junifer/storage/base.py +47 -13
  215. junifer/storage/hdf5.py +95 -33
  216. junifer/storage/pandas_base.py +12 -11
  217. junifer/storage/py.typed +0 -0
  218. junifer/storage/sqlite.py +11 -11
  219. junifer/storage/tests/test_hdf5.py +86 -4
  220. junifer/storage/tests/test_sqlite.py +2 -2
  221. junifer/storage/tests/test_storage_base.py +5 -2
  222. junifer/storage/tests/test_utils.py +33 -7
  223. junifer/storage/utils.py +95 -9
  224. junifer/testing/__init__.py +2 -3
  225. junifer/testing/__init__.pyi +4 -0
  226. junifer/testing/datagrabbers.py +10 -11
  227. junifer/testing/py.typed +0 -0
  228. junifer/testing/registry.py +4 -7
  229. junifer/testing/tests/test_testing_registry.py +9 -17
  230. junifer/tests/test_stats.py +2 -2
  231. junifer/typing/__init__.py +9 -0
  232. junifer/typing/__init__.pyi +31 -0
  233. junifer/typing/_typing.py +68 -0
  234. junifer/utils/__init__.py +2 -12
  235. junifer/utils/__init__.pyi +18 -0
  236. junifer/utils/_config.py +110 -0
  237. junifer/utils/_yaml.py +16 -0
  238. junifer/utils/helpers.py +6 -6
  239. junifer/utils/logging.py +117 -8
  240. junifer/utils/py.typed +0 -0
  241. junifer/{pipeline → utils}/singleton.py +19 -14
  242. junifer/utils/tests/test_config.py +59 -0
  243. {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info}/METADATA +43 -38
  244. junifer-0.0.6.dist-info/RECORD +350 -0
  245. {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info}/WHEEL +1 -1
  246. junifer-0.0.6.dist-info/entry_points.txt +2 -0
  247. junifer/api/parser.py +0 -118
  248. junifer/data/coordinates.py +0 -408
  249. junifer/data/masks.py +0 -670
  250. junifer/data/parcellations.py +0 -1828
  251. junifer/pipeline/registry.py +0 -177
  252. junifer/pipeline/tests/test_registry.py +0 -150
  253. junifer-0.0.5.dev242.dist-info/RECORD +0 -275
  254. junifer-0.0.5.dev242.dist-info/entry_points.txt +0 -2
  255. /junifer/{api → cli}/tests/data/gmd_mean.yaml +0 -0
  256. /junifer/{api → cli}/tests/data/gmd_mean_htcondor.yaml +0 -0
  257. /junifer/{api → cli}/tests/data/partly_cloudy_agg_mean_tian.yml +0 -0
  258. /junifer/data/{VOIs → coordinates/VOIs}/meta/AutobiographicalMemory_VOIs.txt +0 -0
  259. /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAC_VOIs.txt +0 -0
  260. /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAR_VOIs.txt +0 -0
  261. /junifer/data/{VOIs → coordinates/VOIs}/meta/DMNBuckner_VOIs.txt +0 -0
  262. /junifer/data/{VOIs → coordinates/VOIs}/meta/Dosenbach2010_MNI_VOIs.txt +0 -0
  263. /junifer/data/{VOIs → coordinates/VOIs}/meta/Empathy_VOIs.txt +0 -0
  264. /junifer/data/{VOIs → coordinates/VOIs}/meta/Motor_VOIs.txt +0 -0
  265. /junifer/data/{VOIs → coordinates/VOIs}/meta/MultiTask_VOIs.txt +0 -0
  266. /junifer/data/{VOIs → coordinates/VOIs}/meta/PhysioStress_VOIs.txt +0 -0
  267. /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2011_MNI_VOIs.txt +0 -0
  268. /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2013_MNI_VOIs.tsv +0 -0
  269. /junifer/data/{VOIs → coordinates/VOIs}/meta/Rew_VOIs.txt +0 -0
  270. /junifer/data/{VOIs → coordinates/VOIs}/meta/Somatosensory_VOIs.txt +0 -0
  271. /junifer/data/{VOIs → coordinates/VOIs}/meta/ToM_VOIs.txt +0 -0
  272. /junifer/data/{VOIs → coordinates/VOIs}/meta/VigAtt_VOIs.txt +0 -0
  273. /junifer/data/{VOIs → coordinates/VOIs}/meta/WM_VOIs.txt +0 -0
  274. /junifer/data/{VOIs → coordinates/VOIs}/meta/eMDN_VOIs.txt +0 -0
  275. /junifer/data/{VOIs → coordinates/VOIs}/meta/eSAD_VOIs.txt +0 -0
  276. /junifer/data/{VOIs → coordinates/VOIs}/meta/extDMN_VOIs.txt +0 -0
  277. {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
  278. {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
  279. {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
junifer/cli/parser.py ADDED
@@ -0,0 +1,235 @@
1
+ """Provide functions for CLI parser."""
2
+
3
+ # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
+ # Synchon Mandal <s.mandal@fz-juelich.de>
5
+ # License: AGPL
6
+
7
+ import importlib
8
+ import importlib.util
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Union
12
+
13
+ import pandas as pd
14
+
15
+ from ..typing import Elements
16
+ from ..utils import logger, raise_error, warn_with_log, yaml
17
+
18
+
19
+ __all__ = ["parse_elements", "parse_yaml"]
20
+
21
+
22
+ def parse_yaml(filepath: Union[str, Path]) -> dict: # noqa: C901
23
+ """Parse YAML.
24
+
25
+ Parameters
26
+ ----------
27
+ filepath : str or pathlib.Path
28
+ The filepath to read from.
29
+
30
+ Returns
31
+ -------
32
+ dict
33
+ The contents represented as dictionary.
34
+
35
+ """
36
+ # Convert str to Path
37
+ if not isinstance(filepath, Path):
38
+ filepath = Path(filepath)
39
+
40
+ logger.info(f"Parsing yaml file: {filepath.absolute()!s}")
41
+ # Filepath existence check
42
+ if not filepath.exists():
43
+ raise_error(f"File does not exist: {filepath.absolute()!s}")
44
+ # Filepath reading
45
+ contents = yaml.load(filepath)
46
+ if "elements" in contents:
47
+ if contents["elements"] is None:
48
+ raise_error(
49
+ "The elements key was defined but its content is empty. "
50
+ "Please define the elements to operate on or remove the key."
51
+ )
52
+ # load modules
53
+ if "with" in contents:
54
+ to_load = contents["with"]
55
+ # Convert load modules to list
56
+ if not isinstance(to_load, list):
57
+ to_load = [to_load]
58
+ # Initialize list to have absolute paths for custom modules
59
+ final_to_load = []
60
+ for t_module in to_load:
61
+ if t_module.endswith(".py"):
62
+ logger.debug(f"Importing file: {t_module}")
63
+ # This resolves both absolute and relative paths
64
+ file_path = filepath.parent / t_module
65
+ if not file_path.exists():
66
+ raise_error(
67
+ f"File in 'with' section does not exist: {file_path}"
68
+ )
69
+ # Add the parent directory to the sys.path so that the
70
+ # any imports from this module work correctly
71
+ t_path = str(file_path.parent)
72
+ if t_path not in sys.path:
73
+ sys.path.append(str(file_path.parent))
74
+
75
+ spec = importlib.util.spec_from_file_location(
76
+ t_module, file_path
77
+ )
78
+ module = importlib.util.module_from_spec(spec) # type: ignore
79
+ sys.modules[t_module] = module
80
+ spec.loader.exec_module(module) # type: ignore
81
+
82
+ # Add absolute path to final list
83
+ final_to_load.append(str(file_path.resolve()))
84
+
85
+ # Check if the module has junifer_module_deps function
86
+ if hasattr(module, "junifer_module_deps"):
87
+ logger.debug(
88
+ f"Module {t_module} has junifer_module_deps function"
89
+ )
90
+ # Get the dependencies
91
+ deps = module.junifer_module_deps()
92
+ # Add the dependencies to the final list
93
+ for dep in deps:
94
+ if dep not in final_to_load:
95
+ final_to_load.append(
96
+ str((file_path.parent / dep).resolve())
97
+ )
98
+ else:
99
+ logger.info(f"Importing module: {t_module}")
100
+ importlib.import_module(t_module)
101
+ # Add module to final list
102
+ final_to_load.append(t_module)
103
+
104
+ # Replace modules to be loaded so that custom modules will take the
105
+ # absolute path. This was not the case as found in #224. Similar thing
106
+ # is done with the storage URI below.
107
+ contents["with"] = final_to_load
108
+
109
+ # Compute path for the URI parameter in storage files that are relative
110
+ # This is a tricky thing that appeared in #127. The problem is that
111
+ # the path in the URI parameter is relative to YAML file, not to the
112
+ # current working directory. If we leave it as is in the contents
113
+ # dict, then it will be used later in the ``build`` function as is,
114
+ # which will be computed relative to the current working directory.
115
+ # The solution is to compute the absolute path and replace the
116
+ # relative path in the contents dict with the absolute path.
117
+
118
+ # Check if the storage file is defined
119
+ if "storage" in contents:
120
+ if "uri" in contents["storage"]:
121
+ # Check if the storage file is relative
122
+ uri_path = Path(contents["storage"]["uri"])
123
+ if not uri_path.is_absolute():
124
+ # Compute the absolute path
125
+ contents["storage"]["uri"] = str(
126
+ (filepath.parent / uri_path).resolve()
127
+ )
128
+
129
+ # Allow relative path if queue env kind is venv; same motivation as above
130
+ if "queue" in contents:
131
+ if "env" in contents["queue"]:
132
+ if "venv" == contents["queue"]["env"]["kind"]:
133
+ # Check if the env name is relative
134
+ venv_path = Path(contents["queue"]["env"]["name"])
135
+ if not venv_path.is_absolute():
136
+ # Compute the absolute path
137
+ contents["queue"]["env"]["name"] = str(
138
+ (filepath.parent / venv_path).resolve()
139
+ )
140
+
141
+ return contents
142
+
143
+
144
+ def parse_elements(
145
+ element: tuple[str, ...], config: dict
146
+ ) -> Union[Elements, None]:
147
+ """Parse elements from cli.
148
+
149
+ Parameters
150
+ ----------
151
+ element : tuple of str
152
+ The element(s) to operate on.
153
+ config : dict
154
+ The configuration to operate using.
155
+
156
+ Returns
157
+ -------
158
+ list or None
159
+ The element(s) as list or None.
160
+
161
+ Raises
162
+ ------
163
+ ValueError
164
+ If no element is found either in the command-line options or
165
+ the configuration file.
166
+
167
+ Warns
168
+ -----
169
+ RuntimeWarning
170
+ If elements are specified both via the command-line options and
171
+ the configuration file.
172
+
173
+ """
174
+ logger.debug(f"Parsing elements: {element}")
175
+ # Early return None to continue with all elements
176
+ if not element:
177
+ return None
178
+ # Check if the element is a file for single element;
179
+ # if yes, then parse elements from it
180
+ if len(element) == 1 and Path(element[0]).resolve().is_file():
181
+ elements = _parse_elements_file(Path(element[0]).resolve())
182
+ else:
183
+ # Process multi-keyed elements
184
+ elements = [tuple(x.split(",")) if "," in x else x for x in element]
185
+ logger.debug(f"Parsed elements: {elements}")
186
+ if elements is not None and "elements" in config:
187
+ warn_with_log(
188
+ "One or more elements have been specified in both the command "
189
+ "line and in the config file. The command line has precedence "
190
+ "over the configuration file. That is, the elements specified "
191
+ "in the command line will be used. The elements specified in "
192
+ "the configuration file will be ignored. To remove this warning, "
193
+ "please remove the `elements` item from the configuration file."
194
+ )
195
+ elif elements is None:
196
+ # Check in config
197
+ elements = config.get("elements", None)
198
+ if elements is None:
199
+ raise_error(
200
+ "The `elements` key is set in the configuration, but its value"
201
+ " is `None`. It is likely that there is an empty `elements` "
202
+ "section in the yaml configuration file."
203
+ )
204
+ return elements
205
+
206
+
207
+ def _parse_elements_file(filepath: Path) -> Elements:
208
+ """Parse elements from file.
209
+
210
+ Parameters
211
+ ----------
212
+ filepath : pathlib.Path
213
+ The path to the element file.
214
+
215
+ Returns
216
+ -------
217
+ list
218
+ The element(s) as list.
219
+
220
+ """
221
+ # Read CSV into dataframe
222
+ csv_df = pd.read_csv(
223
+ filepath,
224
+ header=None, # no header # type: ignore
225
+ index_col=False, # no index column
226
+ dtype=str,
227
+ skipinitialspace=True, # no leading space after delimiter
228
+ )
229
+ # Remove trailing whitespace in cell entries
230
+ csv_df_trimmed = csv_df.apply(lambda x: x.str.strip())
231
+ # Convert to list of tuple of str if more than one column else flatten
232
+ if len(csv_df_trimmed.columns) == 1:
233
+ return csv_df_trimmed.to_numpy().flatten().tolist()
234
+ else:
235
+ return list(map(tuple, csv_df_trimmed.to_numpy()))
junifer/cli/py.typed ADDED
File without changes
@@ -1,18 +1,17 @@
1
- """Provide tests for cli."""
1
+ """Provide tests for CLI."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
7
  from pathlib import Path
8
- from typing import Callable, List, Tuple
8
+ from typing import Callable
9
9
 
10
10
  import pytest
11
11
  from click.testing import CliRunner
12
12
  from ruamel.yaml import YAML
13
13
 
14
- from junifer.api.cli import (
15
- _parse_elements_file,
14
+ from junifer.cli.cli import (
16
15
  collect,
17
16
  list_elements,
18
17
  queue,
@@ -21,6 +20,7 @@ from junifer.api.cli import (
21
20
  selftest,
22
21
  wtf,
23
22
  )
23
+ from junifer.cli.parser import _parse_elements_file
24
24
 
25
25
 
26
26
  # Configure YAML class
@@ -42,7 +42,7 @@ runner = CliRunner()
42
42
  ],
43
43
  )
44
44
  def test_run_and_collect_commands(
45
- tmp_path: Path, elements: Tuple[str, ...]
45
+ tmp_path: Path, elements: tuple[str, ...]
46
46
  ) -> None:
47
47
  """Test run and collect commands.
48
48
 
@@ -175,7 +175,7 @@ def test_run_using_element_file(tmp_path: Path, elements: str) -> None:
175
175
  ],
176
176
  )
177
177
  def test_multi_element_access(
178
- tmp_path: Path, elements: str, expected_list: List[Tuple[str, ...]]
178
+ tmp_path: Path, elements: str, expected_list: list[tuple[str, ...]]
179
179
  ) -> None:
180
180
  """Test mulit-element parsing.
181
181
 
@@ -306,7 +306,7 @@ def test_reset(
306
306
  ],
307
307
  )
308
308
  def test_list_elements_stdout(
309
- elements: Tuple[str, ...],
309
+ elements: tuple[str, ...],
310
310
  ) -> None:
311
311
  """Test elements listing to stdout.
312
312
 
@@ -344,7 +344,7 @@ def test_list_elements_stdout(
344
344
  )
345
345
  def test_list_elements_output_file(
346
346
  tmp_path: Path,
347
- elements: Tuple[str, ...],
347
+ elements: tuple[str, ...],
348
348
  ) -> None:
349
349
  """Test elements listing to output file.
350
350
 
@@ -1,4 +1,4 @@
1
- """Provide tests for utils."""
1
+ """Provide tests for CLI utils."""
2
2
 
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
@@ -9,7 +9,7 @@ import sys
9
9
  import pytest
10
10
 
11
11
  from junifer._version import __version__
12
- from junifer.api.utils import (
12
+ from junifer.cli.utils import (
13
13
  _get_dependency_information,
14
14
  _get_environment_information,
15
15
  _get_junifer_version,
@@ -43,11 +43,12 @@ def test_get_dependency_information_short() -> None:
43
43
  "nilearn",
44
44
  "sqlalchemy",
45
45
  "ruamel.yaml",
46
- "httpx",
47
46
  "tqdm",
48
47
  "templateflow",
49
48
  "lapy",
49
+ "lazy_loader",
50
50
  "looseversion",
51
+ "junifer_data",
51
52
  ]
52
53
 
53
54
  if sys.version_info < (3, 11):
@@ -72,10 +73,10 @@ def test_get_dependency_information_long() -> None:
72
73
  "nilearn",
73
74
  "sqlalchemy",
74
75
  "ruamel.yaml",
75
- "httpx",
76
76
  "tqdm",
77
77
  "templateflow",
78
78
  "lapy",
79
+ "lazy_loader",
79
80
  ]
80
81
  for key in dependency_list:
81
82
  assert key in dependency_information_keys
@@ -1,4 +1,4 @@
1
- """Provide tests for parser."""
1
+ """Provide tests for CLI parser."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
@@ -9,7 +9,7 @@ from pathlib import Path
9
9
 
10
10
  import pytest
11
11
 
12
- from junifer.api.parser import parse_yaml
12
+ from junifer.cli.parser import parse_yaml
13
13
 
14
14
 
15
15
  def test_parse_yaml_failure() -> None:
@@ -1,4 +1,4 @@
1
- """Provide utility functions for the api sub-package."""
1
+ """Provide utility functions for the cli sub-package."""
2
2
 
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
@@ -7,22 +7,12 @@ import os
7
7
  import platform as pl
8
8
  import re
9
9
  from importlib.metadata import distribution
10
- from typing import Dict
11
-
12
- from ruamel.yaml import YAML
13
10
 
14
11
  from .._version import __version__
15
12
  from ..utils.logging import get_versions
16
13
 
17
14
 
18
- # Configure YAML class once for further use
19
- yaml = YAML()
20
- yaml.default_flow_style = False
21
- yaml.allow_unicode = True
22
- yaml.indent(mapping=2, sequence=4, offset=2)
23
-
24
-
25
- def _get_junifer_version() -> Dict[str, str]:
15
+ def _get_junifer_version() -> dict[str, str]:
26
16
  """Get junifer version information.
27
17
 
28
18
  Returns
@@ -36,7 +26,7 @@ def _get_junifer_version() -> Dict[str, str]:
36
26
  }
37
27
 
38
28
 
39
- def _get_python_information() -> Dict[str, str]:
29
+ def _get_python_information() -> dict[str, str]:
40
30
  """Get installed Python information.
41
31
 
42
32
  Parameters
@@ -51,7 +41,7 @@ def _get_python_information() -> Dict[str, str]:
51
41
  }
52
42
 
53
43
 
54
- def _get_dependency_information(long_: bool) -> Dict[str, str]:
44
+ def _get_dependency_information(long_: bool) -> dict[str, str]:
55
45
  """Get Python environment dependency information.
56
46
 
57
47
  Parameters
@@ -96,7 +86,7 @@ def _get_dependency_information(long_: bool) -> Dict[str, str]:
96
86
  return pruned_dependency_versions
97
87
 
98
88
 
99
- def _get_system_information() -> Dict[str, str]:
89
+ def _get_system_information() -> dict[str, str]:
100
90
  """Get system information.
101
91
 
102
92
  Returns
@@ -110,7 +100,7 @@ def _get_system_information() -> Dict[str, str]:
110
100
  }
111
101
 
112
102
 
113
- def _get_environment_information(long_: bool) -> Dict[str, str]:
103
+ def _get_environment_information(long_: bool) -> dict[str, str]:
114
104
  """Get system environment information.
115
105
 
116
106
  Parameters
@@ -3,7 +3,7 @@
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from . import datagrabbers
6
+ import lazy_loader as lazy
7
7
 
8
8
 
9
- __all__ = ["datagrabbers"]
9
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,3 @@
1
+ __all__ = ["datagrabbers"]
2
+
3
+ from . import datagrabbers
@@ -5,17 +5,7 @@
5
5
  # Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  # License: AGPL
7
7
 
8
- from .aomic_id1000_vbm import JuselessDataladAOMICID1000VBM
9
- from .camcan_vbm import JuselessDataladCamCANVBM
10
- from .ixi_vbm import JuselessDataladIXIVBM
11
- from .ucla import JuselessUCLA
12
- from .ukb_vbm import JuselessDataladUKBVBM
8
+ import lazy_loader as lazy
13
9
 
14
10
 
15
- __all__ = [
16
- "JuselessDataladAOMICID1000VBM",
17
- "JuselessDataladCamCANVBM",
18
- "JuselessDataladIXIVBM",
19
- "JuselessUCLA",
20
- "JuselessDataladUKBVBM",
21
- ]
11
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,13 @@
1
+ __all__ = [
2
+ "JuselessDataladAOMICID1000VBM",
3
+ "JuselessDataladCamCANVBM",
4
+ "JuselessDataladIXIVBM",
5
+ "JuselessUCLA",
6
+ "JuselessDataladUKBVBM",
7
+ ]
8
+
9
+ from .aomic_id1000_vbm import JuselessDataladAOMICID1000VBM
10
+ from .camcan_vbm import JuselessDataladCamCANVBM
11
+ from .ixi_vbm import JuselessDataladIXIVBM
12
+ from .ucla import JuselessUCLA
13
+ from .ukb_vbm import JuselessDataladUKBVBM
@@ -6,7 +6,7 @@
6
6
  # License: AGPL
7
7
 
8
8
  from pathlib import Path
9
- from typing import List, Union
9
+ from typing import Union
10
10
 
11
11
  from ....api.decorators import register_datagrabber
12
12
  from ....datagrabber import PatternDataladDataGrabber
@@ -37,7 +37,7 @@ class JuselessDataladIXIVBM(PatternDataladDataGrabber):
37
37
  def __init__(
38
38
  self,
39
39
  datadir: Union[str, Path, None] = None,
40
- sites: Union[str, List[str], None] = None,
40
+ sites: Union[str, list[str], None] = None,
41
41
  ) -> None:
42
42
  uri = (
43
43
  "ria+http://cat_12.5.ds.inm7.de"
File without changes
@@ -6,7 +6,7 @@
6
6
  # License: AGPL
7
7
 
8
8
  import socket
9
- from typing import List, Optional, Union
9
+ from typing import Optional, Union
10
10
 
11
11
  import pytest
12
12
 
@@ -53,7 +53,7 @@ def test_JuselessUCLA() -> None:
53
53
  ],
54
54
  )
55
55
  def test_JuselessUCLA_partial_data_access(
56
- types: Union[str, List[str]],
56
+ types: Union[str, list[str]],
57
57
  ) -> None:
58
58
  """Test JuselessUCLA DataGrabber partial data access.
59
59
 
@@ -5,7 +5,7 @@
5
5
  # License: AGPL
6
6
 
7
7
  from pathlib import Path
8
- from typing import List, Union
8
+ from typing import Union
9
9
 
10
10
  from ....api.decorators import register_datagrabber
11
11
  from ....datagrabber import PatternDataGrabber
@@ -43,8 +43,8 @@ class JuselessUCLA(PatternDataGrabber):
43
43
  datadir: Union[
44
44
  str, Path
45
45
  ] = "/data/project/psychosis_thalamus/data/fmriprep",
46
- types: Union[str, List[str], None] = None,
47
- tasks: Union[str, List[str], None] = None,
46
+ types: Union[str, list[str], None] = None,
47
+ tasks: Union[str, list[str], None] = None,
48
48
  ) -> None:
49
49
  # Declare all tasks
50
50
  all_tasks = [
@@ -136,7 +136,7 @@ class JuselessUCLA(PatternDataGrabber):
136
136
  confounds_format="fmriprep",
137
137
  )
138
138
 
139
- def get_elements(self) -> List:
139
+ def get_elements(self) -> list:
140
140
  """Implement fetching list of elements in the dataset.
141
141
 
142
142
  Returns
File without changes
junifer/conftest.py ADDED
@@ -0,0 +1,25 @@
1
+ """Provide conftest for pytest."""
2
+
3
+ # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import pytest
7
+
8
+ from junifer.utils.singleton import Singleton
9
+
10
+
11
+ @pytest.fixture(autouse=True)
12
+ def reset_singletons() -> None:
13
+ """Reset all singletons."""
14
+ to_clean = ["WorkDirManager"]
15
+ to_remove = [
16
+ v for k, v in Singleton.instances.items() if k.__name__ in to_clean
17
+ ]
18
+ Singleton.instances = {
19
+ k: v
20
+ for k, v in Singleton.instances.items()
21
+ if k.__name__ not in to_clean
22
+ }
23
+ # Force deleting the singletons
24
+ for elem in to_remove:
25
+ del elem
junifer/data/__init__.py CHANGED
@@ -4,47 +4,7 @@
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
- from .coordinates import (
8
- list_coordinates,
9
- load_coordinates,
10
- register_coordinates,
11
- get_coordinates,
12
- )
13
- from .parcellations import (
14
- list_parcellations,
15
- load_parcellation,
16
- register_parcellation,
17
- merge_parcellations,
18
- get_parcellation,
19
- )
7
+ import lazy_loader as lazy
20
8
 
21
- from .masks import (
22
- list_masks,
23
- load_mask,
24
- register_mask,
25
- get_mask,
26
- )
27
9
 
28
- from .template_spaces import get_template, get_xfm
29
-
30
- from . import utils
31
-
32
-
33
- __all__ = [
34
- "list_coordinates",
35
- "load_coordinates",
36
- "register_coordinates",
37
- "get_coordinates",
38
- "list_parcellations",
39
- "load_parcellation",
40
- "register_parcellation",
41
- "merge_parcellations",
42
- "get_parcellation",
43
- "list_masks",
44
- "load_mask",
45
- "register_mask",
46
- "get_mask",
47
- "get_template",
48
- "get_xfm",
49
- "utils",
50
- ]
10
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,29 @@
1
+ __all__ = [
2
+ "CoordinatesRegistry",
3
+ "ParcellationRegistry",
4
+ "MaskRegistry",
5
+ "get_data",
6
+ "list_data",
7
+ "load_data",
8
+ "register_data",
9
+ "deregister_data",
10
+ "get_template",
11
+ "get_xfm",
12
+ "utils",
13
+ ]
14
+
15
+ from .coordinates import CoordinatesRegistry
16
+ from .parcellations import ParcellationRegistry
17
+ from .masks import MaskRegistry
18
+
19
+ from ._dispatch import (
20
+ get_data,
21
+ list_data,
22
+ load_data,
23
+ register_data,
24
+ deregister_data,
25
+ )
26
+
27
+ from .template_spaces import get_template, get_xfm
28
+
29
+ from . import utils