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.
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.dev240.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.dev240.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.dev240.dist-info/RECORD +0 -275
  254. junifer-0.0.5.dev240.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.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
  278. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
  279. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
@@ -7,17 +7,18 @@
7
7
 
8
8
  import atexit
9
9
  import os
10
- import shutil
11
10
  import tempfile
12
11
  from pathlib import Path
13
- from typing import Dict, Optional, Tuple, Union
12
+ from typing import Optional, Union
14
13
 
15
14
  import datalad
16
15
  import datalad.api as dl
17
16
  from datalad.support.exceptions import IncompleteResultsError
18
17
  from datalad.support.gitrepo import GitRepo
19
18
 
20
- from ..utils import logger, raise_error, warn_with_log
19
+ from ..pipeline import WorkDirManager
20
+ from ..typing import Element
21
+ from ..utils import config, logger, raise_error, warn_with_log
21
22
  from .base import BaseDataGrabber
22
23
 
23
24
 
@@ -78,7 +79,8 @@ class DataladDataGrabber(BaseDataGrabber):
78
79
  if datadir is None:
79
80
  logger.info("`datadir` is None, creating a temporary directory")
80
81
  # Create temporary directory
81
- tmpdir = Path(tempfile.mkdtemp())
82
+ tmpdir = WorkDirManager().get_tempdir(prefix="datalad")
83
+ self._tmpdir = tmpdir
82
84
  datadir = tmpdir / "datadir"
83
85
  datadir.mkdir(parents=True, exist_ok=False)
84
86
  logger.info(f"`datadir` set to {datadir}")
@@ -104,7 +106,6 @@ class DataladDataGrabber(BaseDataGrabber):
104
106
  "Datalad locks set to "
105
107
  f"{datalad.cfg.get('datalad.locations.locks')}"
106
108
  )
107
- self._tmpdir = tmpdir
108
109
  atexit.register(self._rmtmpdir)
109
110
  # TODO: uri can be converted to a positional argument
110
111
  if uri is None:
@@ -129,7 +130,7 @@ class DataladDataGrabber(BaseDataGrabber):
129
130
  """Remove temporary directory if it exists."""
130
131
  if self._tmpdir.exists():
131
132
  logger.debug("Removing temporary directory")
132
- shutil.rmtree(self._tmpdir)
133
+ WorkDirManager().delete_tempdir(self._tmpdir)
133
134
 
134
135
  @property
135
136
  def datadir(self) -> Path:
@@ -143,29 +144,51 @@ class DataladDataGrabber(BaseDataGrabber):
143
144
  """
144
145
  return super().datadir / self._rootdir
145
146
 
146
- def _get_dataset_id_remote(self) -> str:
147
+ def _get_dataset_id_remote(self) -> tuple[str, bool]:
147
148
  """Get the dataset ID from the remote.
148
149
 
149
150
  Returns
150
151
  -------
151
152
  str
152
153
  The dataset ID.
154
+ bool
155
+ Whether the dataset is dirty.
156
+
157
+ Raises
158
+ ------
159
+ ValueError
160
+ If the dataset ID cannot be obtained from the remote.
153
161
 
154
162
  """
155
163
  remote_id = None
164
+ is_dirty = False
156
165
  with tempfile.TemporaryDirectory() as tmpdir:
157
- logger.debug(f"Querying {self.uri} for dataset ID")
158
- repo = GitRepo.clone(
159
- self.uri, path=tmpdir, clone_options=["-n", "--depth=1"]
166
+ if not config.get("datagrabber.skipidcheck", False):
167
+ logger.debug(f"Querying {self.uri} for dataset ID")
168
+ repo = GitRepo.clone(
169
+ self.uri, path=tmpdir, clone_options=["-n", "--depth=1"]
170
+ )
171
+ repo.checkout(name=".datalad/config", options=["HEAD"])
172
+ remote_id = repo.config.get("datalad.dataset.id", None)
173
+ logger.debug(f"Got remote dataset ID = {remote_id}")
174
+
175
+ if not config.get("datagrabber.skipdirtycheck", False):
176
+ is_dirty = repo.dirty
177
+ else:
178
+ logger.debug("Skipping dirty check")
179
+ is_dirty = False
180
+ else:
181
+ logger.debug("Skipping dataset ID check")
182
+ remote_id = self._dataset.id
183
+ is_dirty = False
184
+ logger.debug(
185
+ f"Remote dataset is {'' if is_dirty else 'not'} dirty"
160
186
  )
161
- repo.checkout(name=".datalad/config", options=["HEAD"])
162
- remote_id = repo.config.get("datalad.dataset.id", None)
163
- logger.debug(f"Got remote dataset ID = {remote_id}")
164
187
  if remote_id is None:
165
188
  raise_error("Could not get dataset ID from remote")
166
- return remote_id
189
+ return remote_id, is_dirty
167
190
 
168
- def _dataset_get(self, out: Dict) -> Dict:
191
+ def _dataset_get(self, out: dict) -> dict:
169
192
  """Get the dataset found from the path in ``out``.
170
193
 
171
194
  Parameters
@@ -178,17 +201,29 @@ class DataladDataGrabber(BaseDataGrabber):
178
201
  dict
179
202
  The unmodified input dictionary.
180
203
 
204
+ Raises
205
+ ------
206
+ datalad.support.exceptions.IncompleteResultsError
207
+ If there is a datalad-related problem while fetching data.
208
+
181
209
  """
182
210
  to_get = []
183
211
  for type_val in out.values():
184
- # Iterate to check for nested "types" like mask
185
- for k, v in type_val.items():
186
- # Add base data type path
187
- if k == "path":
188
- to_get.append(v)
189
- # Add nested data type path
190
- if isinstance(v, dict) and "path" in v:
191
- to_get.append(v["path"])
212
+ # Conditional for list dtype vals like Warp
213
+ if isinstance(type_val, list):
214
+ for entry in type_val:
215
+ for k, v in entry.items():
216
+ if k == "path":
217
+ to_get.append(v)
218
+ else:
219
+ # Iterate to check for nested "types" like mask
220
+ for k, v in type_val.items():
221
+ # Add base data type path
222
+ if k == "path":
223
+ to_get.append(v)
224
+ # Add nested data type path
225
+ if isinstance(v, dict) and "path" in v:
226
+ to_get.append(v["path"])
192
227
 
193
228
  if len(to_get) > 0:
194
229
  logger.debug(f"Getting {len(to_get)} files using datalad:")
@@ -223,6 +258,8 @@ class DataladDataGrabber(BaseDataGrabber):
223
258
  ------
224
259
  ValueError
225
260
  If the dataset is already installed but with a different ID.
261
+ datalad.support.exceptions.IncompleteResultsError
262
+ If there is a datalad-related problem while cloning dataset.
226
263
 
227
264
  """
228
265
  isinstalled = dl.Dataset(self._datadir).is_installed()
@@ -231,23 +268,23 @@ class DataladDataGrabber(BaseDataGrabber):
231
268
  self._got_files = []
232
269
  self._dataset: dl.Dataset = dl.Dataset(self._datadir)
233
270
 
234
- remote_id = self._get_dataset_id_remote()
271
+ # Check if dataset is already installed with a different ID
272
+ remote_id, is_dirty = self._get_dataset_id_remote()
235
273
  if remote_id != self._dataset.id:
236
274
  raise_error(
237
275
  "Dataset already installed but with a different "
238
276
  f"ID: {self._dataset.id} (local) != {remote_id} (remote)"
239
277
  )
240
278
 
241
- # Check for dirty datasets:
242
- status = self._dataset.status()
243
- if any(x["state"] != "clean" for x in status):
244
- self.datalad_dirty = True
279
+ # Conditional reporting on dataset dirtiness
280
+ self.datalad_dirty = is_dirty
281
+ if self.datalad_dirty:
245
282
  warn_with_log(
246
- "At least one file is not clean, Junifer will "
247
- "consider this dataset as dirty."
283
+ "At least one file is not clean, "
284
+ f"marking dataset (id: {self._dataset.id}) as dirty."
248
285
  )
249
286
  else:
250
- logger.debug("Dataset is clean")
287
+ logger.debug(f"Dataset (id: {self._dataset.id}) is clean")
251
288
 
252
289
  else:
253
290
  logger.debug(f"Installing dataset {self.uri} to {self._datadir}")
@@ -276,7 +313,7 @@ class DataladDataGrabber(BaseDataGrabber):
276
313
  logger.debug(f"Dropping {f}")
277
314
  self._dataset.drop(f, result_renderer="disabled")
278
315
 
279
- def __getitem__(self, element: Union[str, Tuple]) -> Dict:
316
+ def __getitem__(self, element: Element) -> dict:
280
317
  """Implement single element indexing in the Datalad database.
281
318
 
282
319
  It will first obtain the paths from the parent class and then
@@ -284,11 +321,11 @@ class DataladDataGrabber(BaseDataGrabber):
284
321
 
285
322
  Parameters
286
323
  ----------
287
- element : str or tuple
324
+ element : str or tuple of str
288
325
  The element to be indexed. If one string is provided, it is
289
326
  assumed to be a tuple with only one item. If a tuple is provided,
290
327
  each item in the tuple is the value for the replacement string
291
- specified in "replacements".
328
+ specified in ``"replacements"``.
292
329
 
293
330
  Returns
294
331
  -------
@@ -5,7 +5,7 @@
5
5
 
6
6
  from itertools import product
7
7
  from pathlib import Path
8
- from typing import Dict, List, Union
8
+ from typing import Union
9
9
 
10
10
  from ..api.decorators import register_datagrabber
11
11
  from ..utils import raise_error
@@ -59,11 +59,11 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
59
59
  def __init__(
60
60
  self,
61
61
  datadir: Union[str, Path, None] = None,
62
- types: Union[str, List[str], None] = None,
63
- sessions: Union[str, List[str], None] = None,
64
- tasks: Union[str, List[str], None] = None,
65
- phase_encodings: Union[str, List[str], None] = None,
66
- runs: Union[str, List[str], None] = None,
62
+ types: Union[str, list[str], None] = None,
63
+ sessions: Union[str, list[str], None] = None,
64
+ tasks: Union[str, list[str], None] = None,
65
+ phase_encodings: Union[str, list[str], None] = None,
66
+ runs: Union[str, list[str], None] = None,
67
67
  native_t1w: bool = False,
68
68
  ) -> None:
69
69
  # Declare all sessions
@@ -150,7 +150,7 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
150
150
  "mask": {
151
151
  "pattern": (
152
152
  "derivatives/fmriprep-1.3.2/{subject}/{session}/"
153
- "/func/{subject}_{session}_task-{task}_acq-mb4"
153
+ "func/{subject}_{session}_task-{task}_acq-mb4"
154
154
  "{phase_encoding}_run-{run}_"
155
155
  "space-MNI152NLin2009cAsym_desc-brain_mask.nii.gz"
156
156
  ),
@@ -221,15 +221,28 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
221
221
  "space": "native",
222
222
  },
223
223
  },
224
- "Warp": {
225
- "pattern": (
226
- "derivatives/fmriprep-1.3.2/{subject}/anat/"
227
- "{subject}_from-MNI152NLin2009cAsym_to-T1w_"
228
- "mode-image_xfm.h5"
229
- ),
230
- "src": "MNI152NLin2009cAsym",
231
- "dst": "native",
232
- },
224
+ "Warp": [
225
+ {
226
+ "pattern": (
227
+ "derivatives/fmriprep-1.3.2/{subject}/anat/"
228
+ "{subject}_from-MNI152NLin2009cAsym_to-T1w_"
229
+ "mode-image_xfm.h5"
230
+ ),
231
+ "src": "MNI152NLin2009cAsym",
232
+ "dst": "native",
233
+ "warper": "ants",
234
+ },
235
+ {
236
+ "pattern": (
237
+ "derivatives/fmriprep-1.3.2/{subject}/anat/"
238
+ "{subject}_from-T1w_to-MNI152NLin2009cAsym_"
239
+ "mode-image_xfm.h5"
240
+ ),
241
+ "src": "native",
242
+ "dst": "MNI152NLin2009cAsym",
243
+ "warper": "ants",
244
+ },
245
+ ],
233
246
  }
234
247
  )
235
248
  # Set default types
@@ -258,7 +271,7 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
258
271
  task: str,
259
272
  phase_encoding: str,
260
273
  run: str,
261
- ) -> Dict:
274
+ ) -> dict:
262
275
  """Index one element in the dataset.
263
276
 
264
277
  Parameters
@@ -296,7 +309,7 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
296
309
  )
297
310
  return out
298
311
 
299
- def get_elements(self) -> List:
312
+ def get_elements(self) -> list:
300
313
  """Implement fetching list of subjects in the dataset.
301
314
 
302
315
  Returns
@@ -3,8 +3,7 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from .hcp1200 import HCP1200
7
- from .datalad_hcp1200 import DataladHCP1200
6
+ import lazy_loader as lazy
8
7
 
9
8
 
10
- __all__ = ["HCP1200", "DataladHCP1200"]
9
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,4 @@
1
+ __all__ = ["HCP1200", "DataladHCP1200"]
2
+
3
+ from .hcp1200 import HCP1200
4
+ from .datalad_hcp1200 import DataladHCP1200
@@ -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 junifer.datagrabber.datalad_base import DataladDataGrabber
12
12
 
@@ -50,8 +50,8 @@ class DataladHCP1200(DataladDataGrabber, HCP1200):
50
50
  def __init__(
51
51
  self,
52
52
  datadir: Union[str, Path, None] = None,
53
- tasks: Union[str, List[str], None] = None,
54
- phase_encodings: Union[str, List[str], None] = None,
53
+ tasks: Union[str, list[str], None] = None,
54
+ phase_encodings: Union[str, list[str], None] = None,
55
55
  ica_fix: bool = False,
56
56
  ) -> None:
57
57
  uri = (
@@ -7,7 +7,7 @@
7
7
 
8
8
  from itertools import product
9
9
  from pathlib import Path
10
- from typing import Dict, List, Union
10
+ from typing import Union
11
11
 
12
12
  from ...api.decorators import register_datagrabber
13
13
  from ...utils import raise_error
@@ -48,8 +48,8 @@ class HCP1200(PatternDataGrabber):
48
48
  def __init__(
49
49
  self,
50
50
  datadir: Union[str, Path],
51
- tasks: Union[str, List[str], None] = None,
52
- phase_encodings: Union[str, List[str], None] = None,
51
+ tasks: Union[str, list[str], None] = None,
52
+ phase_encodings: Union[str, list[str], None] = None,
53
53
  ica_fix: bool = False,
54
54
  ) -> None:
55
55
  # All tasks
@@ -66,10 +66,10 @@ class HCP1200(PatternDataGrabber):
66
66
  ]
67
67
  # Set default tasks
68
68
  if tasks is None:
69
- self.tasks: List[str] = all_tasks
69
+ self.tasks: list[str] = all_tasks
70
70
  # Convert single task into list
71
71
  else:
72
- if not isinstance(tasks, List):
72
+ if not isinstance(tasks, list):
73
73
  tasks = [tasks]
74
74
  # Check for invalid task(s)
75
75
  for task in tasks:
@@ -78,7 +78,7 @@ class HCP1200(PatternDataGrabber):
78
78
  f"'{task}' is not a valid HCP-YA fMRI task input. "
79
79
  f"Valid task values can be any or all of {all_tasks}."
80
80
  )
81
- self.tasks: List[str] = tasks
81
+ self.tasks: list[str] = tasks
82
82
 
83
83
  # All phase encodings
84
84
  all_phase_encodings = ["LR", "RL"]
@@ -122,13 +122,24 @@ class HCP1200(PatternDataGrabber):
122
122
  "pattern": "{subject}/T1w/T1w_acpc_dc_restore.nii.gz",
123
123
  "space": "native",
124
124
  },
125
- "Warp": {
126
- "pattern": (
127
- "{subject}/MNINonLinear/xfms/standard2acpc_dc.nii.gz"
128
- ),
129
- "src": "MNI152NLin6Asym",
130
- "dst": "native",
131
- },
125
+ "Warp": [
126
+ {
127
+ "pattern": (
128
+ "{subject}/MNINonLinear/xfms/standard2acpc_dc.nii.gz"
129
+ ),
130
+ "src": "MNI152NLin6Asym",
131
+ "dst": "native",
132
+ "warper": "fsl",
133
+ },
134
+ {
135
+ "pattern": (
136
+ "{subject}/MNINonLinear/xfms/acpc_dc2standard.nii.gz"
137
+ ),
138
+ "src": "native",
139
+ "dst": "MNI152NLin6Asym",
140
+ "warper": "fsl",
141
+ },
142
+ ],
132
143
  }
133
144
  # The replacements
134
145
  replacements = ["subject", "task", "phase_encoding"]
@@ -139,7 +150,7 @@ class HCP1200(PatternDataGrabber):
139
150
  replacements=replacements,
140
151
  )
141
152
 
142
- def get_item(self, subject: str, task: str, phase_encoding: str) -> Dict:
153
+ def get_item(self, subject: str, task: str, phase_encoding: str) -> dict:
143
154
  """Implement single element indexing in the database.
144
155
 
145
156
  Parameters
@@ -169,7 +180,7 @@ class HCP1200(PatternDataGrabber):
169
180
  subject=subject, task=new_task, phase_encoding=phase_encoding
170
181
  )
171
182
 
172
- def get_elements(self) -> List:
183
+ def get_elements(self) -> list:
173
184
  """Implement fetching list of elements in the dataset.
174
185
 
175
186
  Returns
File without changes
@@ -3,7 +3,11 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from typing import Iterable, Optional
6
+ import shutil
7
+ import tempfile
8
+ from collections.abc import Iterable
9
+ from pathlib import Path
10
+ from typing import Optional
7
11
 
8
12
  import pytest
9
13
 
@@ -17,7 +21,8 @@ URI = "https://gin.g-node.org/juaml/datalad-example-hcp1200"
17
21
  @pytest.fixture(scope="module")
18
22
  def hcpdg() -> Iterable[DataladHCP1200]:
19
23
  """Return a HCP1200 DataGrabber."""
20
- dg = DataladHCP1200()
24
+ tmpdir = Path(tempfile.gettempdir())
25
+ dg = DataladHCP1200(datadir=tmpdir / "datadir")
21
26
  # Set URI to Gin
22
27
  dg.uri = URI
23
28
  # Set correct root directory
@@ -26,6 +31,7 @@ def hcpdg() -> Iterable[DataladHCP1200]:
26
31
  for t_elem in dg.get_elements():
27
32
  dg[t_elem]
28
33
  yield dg
34
+ shutil.rmtree(tmpdir / "datadir", ignore_errors=True)
29
35
 
30
36
 
31
37
  @pytest.mark.parametrize(
@@ -5,9 +5,10 @@
5
5
  # Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  # License: AGPL
7
7
 
8
- from typing import Dict, List, Tuple, Union
8
+ from typing import Union
9
9
 
10
10
  from ..api.decorators import register_datagrabber
11
+ from ..typing import DataGrabberLike
11
12
  from ..utils import deep_update, raise_error
12
13
  from .base import BaseDataGrabber
13
14
 
@@ -37,7 +38,7 @@ class MultipleDataGrabber(BaseDataGrabber):
37
38
 
38
39
  """
39
40
 
40
- def __init__(self, datagrabbers: List[BaseDataGrabber], **kwargs) -> None:
41
+ def __init__(self, datagrabbers: list[DataGrabberLike], **kwargs) -> None:
41
42
  # Check datagrabbers consistency
42
43
  # Check for same element keys
43
44
  first_keys = datagrabbers[0].get_element_keys()
@@ -78,7 +79,7 @@ class MultipleDataGrabber(BaseDataGrabber):
78
79
  )
79
80
  self._datagrabbers = datagrabbers
80
81
 
81
- def __getitem__(self, element: Union[str, Tuple]) -> Dict:
82
+ def __getitem__(self, element: Union[str, tuple]) -> dict:
82
83
  """Implement indexing.
83
84
 
84
85
  Parameters
@@ -110,8 +111,12 @@ class MultipleDataGrabber(BaseDataGrabber):
110
111
 
111
112
  # Update all the metas again
112
113
  for kind in out:
113
- self.update_meta(out[kind], "datagrabber")
114
- out[kind]["meta"]["datagrabber"]["datagrabbers"] = metas
114
+ to_update = out[kind]
115
+ if not isinstance(to_update, list):
116
+ to_update = [to_update]
117
+ for t_kind in to_update:
118
+ self.update_meta(t_kind, "datagrabber")
119
+ t_kind["meta"]["datagrabber"]["datagrabbers"] = metas
115
120
  return out
116
121
 
117
122
  def __enter__(self) -> "MultipleDataGrabber":
@@ -126,7 +131,7 @@ class MultipleDataGrabber(BaseDataGrabber):
126
131
  dg.__exit__(exc_type, exc_value, exc_traceback)
127
132
 
128
133
  # TODO: return type should be List[List[str]], but base type is List[str]
129
- def get_types(self) -> List[str]:
134
+ def get_types(self) -> list[str]:
130
135
  """Get types.
131
136
 
132
137
  Returns
@@ -138,7 +143,7 @@ class MultipleDataGrabber(BaseDataGrabber):
138
143
  types = [x for dg in self._datagrabbers for x in dg.get_types()]
139
144
  return types
140
145
 
141
- def get_element_keys(self) -> List[str]:
146
+ def get_element_keys(self) -> list[str]:
142
147
  """Get element keys.
143
148
 
144
149
  For each item in the ``element`` tuple passed to ``__getitem__()``,
@@ -152,7 +157,7 @@ class MultipleDataGrabber(BaseDataGrabber):
152
157
  """
153
158
  return self._datagrabbers[0].get_element_keys()
154
159
 
155
- def get_elements(self) -> List:
160
+ def get_elements(self) -> list:
156
161
  """Get elements.
157
162
 
158
163
  Returns
@@ -170,7 +175,7 @@ class MultipleDataGrabber(BaseDataGrabber):
170
175
  elements.intersection_update(s)
171
176
  return list(elements)
172
177
 
173
- def get_item(self, **_: Dict) -> Dict[str, Dict]:
178
+ def get_item(self, **_: dict) -> dict[str, dict]:
174
179
  """Get the specified item from the dataset.
175
180
 
176
181
  Parameters