junifer 0.0.7.dev130__py3-none-any.whl → 0.0.7.dev153__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/_version.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '0.0.7.dev130'
21
- __version_tuple__ = version_tuple = (0, 0, 7, 'dev130')
31
+ __version__ = version = '0.0.7.dev153'
32
+ __version_tuple__ = version_tuple = (0, 0, 7, 'dev153')
33
+
34
+ __commit_id__ = commit_id = None
junifer/data/__init__.pyi CHANGED
@@ -3,6 +3,7 @@ __all__ = [
3
3
  "CoordinatesRegistry",
4
4
  "DataDispatcher",
5
5
  "ParcellationRegistry",
6
+ "MapsRegistry",
6
7
  "MaskRegistry",
7
8
  "get_data",
8
9
  "list_data",
@@ -17,6 +18,7 @@ __all__ = [
17
18
  from .pipeline_data_registry_base import BasePipelineDataRegistry
18
19
  from .coordinates import CoordinatesRegistry
19
20
  from .parcellations import ParcellationRegistry
21
+ from .maps import MapsRegistry
20
22
  from .masks import MaskRegistry
21
23
 
22
24
  from ._dispatch import (
junifer/data/_dispatch.py CHANGED
@@ -17,6 +17,7 @@ from numpy.typing import ArrayLike
17
17
 
18
18
  from ..utils import raise_error
19
19
  from .coordinates import CoordinatesRegistry
20
+ from .maps import MapsRegistry
20
21
  from .masks import MaskRegistry
21
22
  from .parcellations import ParcellationRegistry
22
23
  from .pipeline_data_registry_base import BasePipelineDataRegistry
@@ -52,6 +53,7 @@ class DataDispatcher(MutableMapping):
52
53
  {
53
54
  "coordinates": CoordinatesRegistry,
54
55
  "parcellation": ParcellationRegistry,
56
+ "maps": MapsRegistry,
55
57
  "mask": MaskRegistry,
56
58
  }
57
59
  )
@@ -118,14 +120,14 @@ def get_data(
118
120
  extra_input: Optional[dict[str, Any]] = None,
119
121
  ) -> Union[
120
122
  tuple[ArrayLike, list[str]], # coordinates
121
- tuple["Nifti1Image", list[str]], # parcellation
123
+ tuple["Nifti1Image", list[str]], # parcellation / maps
122
124
  "Nifti1Image", # mask
123
125
  ]:
124
126
  """Get tailored ``kind`` for ``target_data``.
125
127
 
126
128
  Parameters
127
129
  ----------
128
- kind : {"coordinates", "parcellation", "mask"}
130
+ kind : {"coordinates", "parcellation", "mask", "maps"}
129
131
  Kind of data to fetch and apply.
130
132
  names : str or dict or list of str / dict
131
133
  The registered name(s) of the data.
@@ -166,7 +168,7 @@ def list_data(kind: str) -> list[str]:
166
168
 
167
169
  Parameters
168
170
  ----------
169
- kind : {"coordinates", "parcellation", "mask"}
171
+ kind : {"coordinates", "parcellation", "mask", "maps"}
170
172
  Kind of data registry to list.
171
173
 
172
174
  Returns
@@ -195,7 +197,9 @@ def load_data(
195
197
  **kwargs,
196
198
  ) -> Union[
197
199
  tuple[ArrayLike, list[str], str], # coordinates
198
- tuple[Optional["Nifti1Image"], list[str], Path, str], # parcellation
200
+ tuple[
201
+ Optional["Nifti1Image"], list[str], Path, str
202
+ ], # parcellation / maps
199
203
  tuple[
200
204
  Optional[Union["Nifti1Image", Callable]], Optional[Path], str
201
205
  ], # mask
@@ -204,7 +208,7 @@ def load_data(
204
208
 
205
209
  Parameters
206
210
  ----------
207
- kind : {"coordinates", "parcellation", "mask"}
211
+ kind : {"coordinates", "parcellation", "mask", "maps"}
208
212
  Kind of data to load.
209
213
  name : str
210
214
  The registered name of the data.
@@ -244,7 +248,7 @@ def register_data(
244
248
 
245
249
  Parameters
246
250
  ----------
247
- kind : {"coordinates", "parcellation", "mask"}
251
+ kind : {"coordinates", "parcellation", "mask", "maps"}
248
252
  Kind of data to register.
249
253
  name : str
250
254
  The name to register.
@@ -277,7 +281,7 @@ def deregister_data(kind: str, name: str) -> None:
277
281
 
278
282
  Parameters
279
283
  ----------
280
- kind : {"coordinates", "parcellation", "mask"}
284
+ kind : {"coordinates", "parcellation", "mask", "maps"}
281
285
  Kind of data to register.
282
286
  name : str
283
287
  The name to de-register.
@@ -0,0 +1,9 @@
1
+ """Maps."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import lazy_loader as lazy
7
+
8
+
9
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,5 @@
1
+ __all__ = [
2
+ "MapsRegistry",
3
+ ]
4
+
5
+ from ._maps import MapsRegistry
@@ -0,0 +1,156 @@
1
+ """Provide class for maps space warping via ANTs."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import uuid
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ import nibabel as nib
10
+
11
+ from ...pipeline import WorkDirManager
12
+ from ...utils import logger, raise_error, run_ext_cmd
13
+ from ..template_spaces import get_template, get_xfm
14
+
15
+
16
+ if TYPE_CHECKING:
17
+ from nibabel.nifti1 import Nifti1Image
18
+
19
+
20
+ __all__ = ["ANTsMapsWarper"]
21
+
22
+
23
+ class ANTsMapsWarper:
24
+ """Class for maps space warping via ANTs.
25
+
26
+ This class uses ANTs ``antsApplyTransforms`` for transformation.
27
+
28
+ """
29
+
30
+ def warp(
31
+ self,
32
+ maps_name: str,
33
+ maps_img: "Nifti1Image",
34
+ src: str,
35
+ dst: str,
36
+ target_data: dict[str, Any],
37
+ warp_data: Optional[dict[str, Any]],
38
+ ) -> "Nifti1Image":
39
+ """Warp ``maps_img`` to correct space.
40
+
41
+ Parameters
42
+ ----------
43
+ maps_name : str
44
+ The name of the maps.
45
+ maps_img : nibabel.nifti1.Nifti1Image
46
+ The maps image to transform.
47
+ src : str
48
+ The data type or template space to warp from.
49
+ It should be empty string if ``dst="T1w"``.
50
+ dst : str
51
+ The data type or template space to warp to.
52
+ `"T1w"` is the only allowed data type and it uses the resampled T1w
53
+ found in ``target_data.reference``. The ``"reference"``
54
+ key is added if the :class:`.SpaceWarper` is used or if the
55
+ data is provided in native space.
56
+ target_data : dict
57
+ The corresponding item of the data object to which the maps
58
+ will be applied.
59
+ warp_data : dict or None
60
+ The warp data item of the data object. The value is unused if
61
+ ``dst!="T1w"``.
62
+
63
+ Returns
64
+ -------
65
+ nibabel.nifti1.Nifti1Image
66
+ The transformed maps image.
67
+
68
+ Raises
69
+ ------
70
+ ValueError
71
+ If ``warp_data`` is None when ``dst="T1w"``.
72
+
73
+ """
74
+ # Create element-scoped tempdir so that warped maps is
75
+ # available later as nibabel stores file path reference for
76
+ # loading on computation
77
+ prefix = (
78
+ f"ants_maps_warper_{maps_name}"
79
+ f"{'' if not src else f'_from_{src}'}_to_{dst}_"
80
+ f"{uuid.uuid1()}"
81
+ )
82
+ element_tempdir = WorkDirManager().get_element_tempdir(
83
+ prefix=prefix,
84
+ )
85
+
86
+ # Native space warping
87
+ if dst == "native": # pragma: no cover
88
+ # Warp data check
89
+ if warp_data is None:
90
+ raise_error("No `warp_data` provided")
91
+ if "reference" not in target_data:
92
+ raise_error("No `reference` provided")
93
+ if "path" not in target_data["reference"]:
94
+ raise_error("No `path` provided in `reference`")
95
+
96
+ logger.debug("Using ANTs for maps transformation")
97
+
98
+ # Save existing maps image to a tempfile
99
+ prewarp_maps_path = element_tempdir / "prewarp_maps.nii.gz"
100
+ nib.save(maps_img, prewarp_maps_path)
101
+
102
+ # Create a tempfile for warped output
103
+ warped_maps_path = element_tempdir / "maps_warped.nii.gz"
104
+ # Set antsApplyTransforms command
105
+ apply_transforms_cmd = [
106
+ "antsApplyTransforms",
107
+ "-d 3",
108
+ "-e 3",
109
+ "-n LanczosWindowedSinc",
110
+ f"-i {prewarp_maps_path.resolve()}",
111
+ # use resampled reference
112
+ f"-r {target_data['reference']['path'].resolve()}",
113
+ f"-t {warp_data['path'].resolve()}",
114
+ f"-o {warped_maps_path.resolve()}",
115
+ ]
116
+ # Call antsApplyTransforms
117
+ run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
118
+
119
+ # Template space warping
120
+ else:
121
+ logger.debug(f"Using ANTs to warp maps from {src} to {dst}")
122
+
123
+ # Get xfm file
124
+ xfm_file_path = get_xfm(src=src, dst=dst)
125
+ # Get template space image
126
+ template_space_img = get_template(
127
+ space=dst,
128
+ target_img=maps_img,
129
+ extra_input=None,
130
+ )
131
+ # Save template to a tempfile
132
+ template_space_img_path = element_tempdir / f"{dst}_T1w.nii.gz"
133
+ nib.save(template_space_img, template_space_img_path)
134
+
135
+ # Save existing maps image to a tempfile
136
+ prewarp_maps_path = element_tempdir / "prewarp_maps.nii.gz"
137
+ nib.save(maps_img, prewarp_maps_path)
138
+
139
+ # Create a tempfile for warped output
140
+ warped_maps_path = element_tempdir / "maps_warped.nii.gz"
141
+ # Set antsApplyTransforms command
142
+ apply_transforms_cmd = [
143
+ "antsApplyTransforms",
144
+ "-d 3",
145
+ "-e 3",
146
+ "-n LanczosWindowedSinc",
147
+ f"-i {prewarp_maps_path.resolve()}",
148
+ f"-r {template_space_img_path.resolve()}",
149
+ f"-t {xfm_file_path.resolve()}",
150
+ f"-o {warped_maps_path.resolve()}",
151
+ ]
152
+ # Call antsApplyTransforms
153
+ run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
154
+
155
+ # Load nifti
156
+ return nib.load(warped_maps_path)
@@ -0,0 +1,85 @@
1
+ """Provide class for maps space warping via FSL FLIRT."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import uuid
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ import nibabel as nib
10
+
11
+ from ...pipeline import WorkDirManager
12
+ from ...utils import logger, run_ext_cmd
13
+
14
+
15
+ if TYPE_CHECKING:
16
+ from nibabel.nifti1 import Nifti1Image
17
+
18
+
19
+ __all__ = ["FSLMapsWarper"]
20
+
21
+
22
+ class FSLMapsWarper:
23
+ """Class for maps space warping via FSL FLIRT.
24
+
25
+ This class uses FSL FLIRT's ``applywarp`` for transformation.
26
+
27
+ """
28
+
29
+ def warp(
30
+ self,
31
+ maps_name: str,
32
+ maps_img: "Nifti1Image",
33
+ target_data: dict[str, Any],
34
+ warp_data: dict[str, Any],
35
+ ) -> "Nifti1Image": # pragma: no cover
36
+ """Warp ``maps_img`` to correct space.
37
+
38
+ Parameters
39
+ ----------
40
+ maps_name : str
41
+ The name of the maps.
42
+ maps_img : nibabel.nifti1.Nifti1Image
43
+ The maps image to transform.
44
+ target_data : dict
45
+ The corresponding item of the data object to which the maps
46
+ will be applied.
47
+ warp_data : dict
48
+ The warp data item of the data object.
49
+
50
+ Returns
51
+ -------
52
+ nibabel.nifti1.Nifti1Image
53
+ The transformed maps image.
54
+
55
+ """
56
+ logger.debug("Using FSL for maps transformation")
57
+
58
+ # Create element-scoped tempdir so that warped maps is
59
+ # available later as nibabel stores file path reference for
60
+ # loading on computation
61
+ element_tempdir = WorkDirManager().get_element_tempdir(
62
+ prefix=f"fsl_maps_warper_{maps_name}_{uuid.uuid1()}"
63
+ )
64
+
65
+ # Save existing maps image to a tempfile
66
+ prewarp_maps_path = element_tempdir / "prewarp_maps.nii.gz"
67
+ nib.save(maps_img, prewarp_maps_path)
68
+
69
+ # Create a tempfile for warped output
70
+ warped_maps_path = element_tempdir / "maps_warped.nii.gz"
71
+ # Set applywarp command
72
+ applywarp_cmd = [
73
+ "applywarp",
74
+ "--interp=spline",
75
+ f"-i {prewarp_maps_path.resolve()}",
76
+ # use resampled reference
77
+ f"-r {target_data['reference']['path'].resolve()}",
78
+ f"-w {warp_data['path'].resolve()}",
79
+ f"-o {warped_maps_path.resolve()}",
80
+ ]
81
+ # Call applywarp
82
+ run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
83
+
84
+ # Load nifti
85
+ return nib.load(warped_maps_path)
@@ -0,0 +1,446 @@
1
+ """Provide a class for centralized maps data registry."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING, Any, Optional, Union
8
+
9
+ import nibabel as nib
10
+ import nilearn.image as nimg
11
+ import numpy as np
12
+ from junifer_data import get
13
+
14
+ from ...utils import logger, raise_error
15
+ from ..pipeline_data_registry_base import BasePipelineDataRegistry
16
+ from ..utils import (
17
+ JUNIFER_DATA_PARAMS,
18
+ closest_resolution,
19
+ get_dataset_path,
20
+ get_native_warper,
21
+ )
22
+ from ._ants_maps_warper import ANTsMapsWarper
23
+ from ._fsl_maps_warper import FSLMapsWarper
24
+
25
+
26
+ if TYPE_CHECKING:
27
+ from nibabel.nifti1 import Nifti1Image
28
+
29
+
30
+ __all__ = ["MapsRegistry"]
31
+
32
+
33
+ class MapsRegistry(BasePipelineDataRegistry):
34
+ """Class for maps data registry.
35
+
36
+ This class is a singleton and is used for managing available maps
37
+ data in a centralized manner.
38
+
39
+ """
40
+
41
+ def __init__(self) -> None:
42
+ """Initialize the class."""
43
+ super().__init__()
44
+ # Each entry in registry is a dictionary that must contain at least
45
+ # the following keys:
46
+ # * 'family': the maps' family name (e.g., 'Smith')
47
+ # * 'space': the maps' space (e.g., 'MNI')
48
+ # and can also have optional key(s):
49
+ # * 'valid_resolutions': a list of valid resolutions for the
50
+ # maps (e.g., [1, 2])
51
+ # The built-in maps are files that are shipped with the
52
+ # junifer-data dataset.
53
+ # Make built-in and external dictionaries for validation later
54
+ self._builtin = {}
55
+ self._external = {}
56
+
57
+ # Add Smith
58
+ for comp in ["rsn", "bm"]:
59
+ for dim in [10, 20, 70]:
60
+ self._builtin.update(
61
+ {
62
+ f"Smith_{comp}_{dim}": {
63
+ "family": "Smith2009",
64
+ "components": comp,
65
+ "dimension": dim,
66
+ "space": "MNI152NLin6Asym",
67
+ }
68
+ }
69
+ )
70
+
71
+ # Update registry with built-in ones
72
+ self._registry.update(self._builtin)
73
+
74
+ def register(
75
+ self,
76
+ name: str,
77
+ maps_path: Union[str, Path],
78
+ maps_labels: list[str],
79
+ space: str,
80
+ overwrite: bool = False,
81
+ ) -> None:
82
+ """Register a custom user map(s).
83
+
84
+ Parameters
85
+ ----------
86
+ name : str
87
+ The name of the map(s).
88
+ maps_path : str or pathlib.Path
89
+ The path to the map(s) file.
90
+ maps_labels : list of str
91
+ The list of labels for the map(s).
92
+ space : str
93
+ The template space of the map(s), e.g., "MNI152NLin6Asym".
94
+ overwrite : bool, optional
95
+ If True, overwrite an existing maps with the same name.
96
+ Does not apply to built-in maps (default False).
97
+
98
+ Raises
99
+ ------
100
+ ValueError
101
+ If the map(s) ``name`` is a built-in map(s) or
102
+ if the map(s) ``name`` is already registered and
103
+ ``overwrite=False``.
104
+
105
+ """
106
+ # Check for attempt of overwriting built-in maps
107
+ if name in self._builtin:
108
+ raise_error(
109
+ f"Map(s): {name} already registered as built-in map(s)."
110
+ )
111
+ # Check for attempt of overwriting external maps
112
+ if name in self._external:
113
+ if overwrite:
114
+ logger.info(f"Overwriting map(s): {name}")
115
+ else:
116
+ raise_error(
117
+ f"Map(s): {name} already registered. Set "
118
+ "`overwrite=True` to update its value."
119
+ )
120
+ # Convert str to Path
121
+ if not isinstance(maps_path, Path):
122
+ maps_path = Path(maps_path)
123
+ # Registration
124
+ logger.info(f"Registering map(s): {name}")
125
+ # Add user maps info
126
+ self._external[name] = {
127
+ "path": maps_path,
128
+ "labels": maps_labels,
129
+ "family": "CustomUserMaps",
130
+ "space": space,
131
+ }
132
+ # Update registry
133
+ self._registry[name] = {
134
+ "path": maps_path,
135
+ "labels": maps_labels,
136
+ "family": "CustomUserMaps",
137
+ "space": space,
138
+ }
139
+
140
+ def deregister(self, name: str) -> None:
141
+ """De-register a custom user map(s).
142
+
143
+ Parameters
144
+ ----------
145
+ name : str
146
+ The name of the map(s).
147
+
148
+ """
149
+ logger.info(f"De-registering map(s): {name}")
150
+ # Remove maps info
151
+ _ = self._external.pop(name)
152
+ # Update registry
153
+ _ = self._registry.pop(name)
154
+
155
+ def load(
156
+ self,
157
+ name: str,
158
+ target_space: str,
159
+ resolution: Optional[float] = None,
160
+ path_only: bool = False,
161
+ ) -> tuple[Optional["Nifti1Image"], list[str], Path, str]:
162
+ """Load map(s) and labels.
163
+
164
+ Parameters
165
+ ----------
166
+ name : str
167
+ The name of the map(s).
168
+ target_space : str
169
+ The desired space of the map(s).
170
+ resolution : float, optional
171
+ The desired resolution of the map(s) to load. If it is not
172
+ available, the closest resolution will be loaded. Preferably, use a
173
+ resolution higher than the desired one. By default, will load the
174
+ highest one (default None).
175
+ path_only : bool, optional
176
+ If True, the map(s) image will not be loaded (default False).
177
+
178
+ Returns
179
+ -------
180
+ Nifti1Image or None
181
+ Loaded map(s) image.
182
+ list of str
183
+ Map(s) labels.
184
+ pathlib.Path
185
+ File path to the map(s) image.
186
+ str
187
+ The space of the map(s).
188
+
189
+ Raises
190
+ ------
191
+ ValueError
192
+ If ``name`` is invalid or
193
+ if the map(s) family is invalid or
194
+ if the map(s) values and labels
195
+ don't have equal dimension or if the value range is invalid.
196
+
197
+ """
198
+ # Check for valid maps name
199
+ if name not in self._registry:
200
+ raise_error(
201
+ f"Map(s): {name} not found. Valid options are: {self.list}"
202
+ )
203
+
204
+ # Copy maps definition to avoid edits in original object
205
+ maps_def = self._registry[name].copy()
206
+ t_family = maps_def.pop("family")
207
+ space = maps_def.pop("space")
208
+
209
+ # Check and get highest resolution
210
+ if space != target_space:
211
+ logger.info(
212
+ f"Map(s) will be warped from {space} to {target_space} "
213
+ "using highest resolution"
214
+ )
215
+ resolution = None
216
+
217
+ # Check if the maps family is custom or built-in
218
+ if t_family == "CustomUserMaps":
219
+ maps_fname = maps_def["path"]
220
+ maps_labels = maps_def["labels"]
221
+ elif t_family in [
222
+ "Smith2009",
223
+ ]:
224
+ # Load maps and labels
225
+ if t_family == "Smith2009":
226
+ maps_fname, maps_labels = _retrieve_smith(
227
+ resolution=resolution,
228
+ **maps_def,
229
+ )
230
+ else: # pragma: no cover
231
+ raise_error(f"Unknown map(s) family: {t_family}")
232
+
233
+ # Load maps image and values
234
+ logger.info(f"Loading map(s): {maps_fname.absolute()!s}")
235
+ maps_img = None
236
+ if not path_only:
237
+ # Load image via nibabel
238
+ maps_img = nib.load(maps_fname)
239
+ # Get regions
240
+ maps_regions = maps_img.get_fdata().shape[-1]
241
+ # Check for dimension
242
+ if maps_regions != len(maps_labels):
243
+ raise_error(
244
+ f"Map(s) {name} has {maps_regions} "
245
+ f"regions but {len(maps_labels)} labels."
246
+ )
247
+
248
+ return maps_img, maps_labels, maps_fname, space
249
+
250
+ def get(
251
+ self,
252
+ maps: str,
253
+ target_data: dict[str, Any],
254
+ extra_input: Optional[dict[str, Any]] = None,
255
+ ) -> tuple["Nifti1Image", list[str]]:
256
+ """Get map(s), tailored for the target image.
257
+
258
+ Parameters
259
+ ----------
260
+ maps : str
261
+ The name of the map(s).
262
+ target_data : dict
263
+ The corresponding item of the data object to which the map(s)
264
+ will be applied.
265
+ extra_input : dict, optional
266
+ The other fields in the data object. Useful for accessing other
267
+ data kinds that needs to be used in the computation of
268
+ map(s) (default None).
269
+
270
+ Returns
271
+ -------
272
+ Nifti1Image
273
+ The map(s) image.
274
+ list of str
275
+ Map(s) labels.
276
+
277
+ Raises
278
+ ------
279
+ ValueError
280
+ If ``extra_input`` is None when ``target_data``'s space is native.
281
+
282
+ """
283
+ # Check pre-requirements for space manipulation
284
+ target_space = target_data["space"]
285
+ logger.debug(f"Getting {maps} in {target_space} space.")
286
+ # Extra data type requirement check if target space is native
287
+ if target_space == "native": # pragma: no cover
288
+ # Check for extra inputs
289
+ if extra_input is None:
290
+ raise_error(
291
+ "No extra input provided, requires `Warp` and `T1w` "
292
+ "data types in particular for transformation to "
293
+ f"{target_data['space']} space for further computation."
294
+ )
295
+ # Get native space warper spec
296
+ warper_spec = get_native_warper(
297
+ target_data=target_data,
298
+ other_data=extra_input,
299
+ )
300
+ # Set target standard space to warp file space source
301
+ target_std_space = warper_spec["src"]
302
+ logger.debug(
303
+ f"Target space is native. Will warp from {target_std_space}"
304
+ )
305
+ else:
306
+ # Set target standard space to target space
307
+ target_std_space = target_space
308
+
309
+ # Get the min of the voxels sizes and use it as the resolution
310
+ target_img = target_data["data"]
311
+ resolution = np.min(target_img.header.get_zooms()[:3])
312
+
313
+ # Load maps
314
+ logger.debug(f"Loading map(s) {maps}")
315
+ img, labels, _, space = self.load(
316
+ name=maps,
317
+ resolution=resolution,
318
+ target_space=target_space,
319
+ )
320
+
321
+ # Convert maps spaces if required;
322
+ # cannot be "native" due to earlier check
323
+ if space != target_std_space:
324
+ logger.debug(
325
+ f"Warping {maps} to {target_std_space} space using ANTs."
326
+ )
327
+ raw_img = ANTsMapsWarper().warp(
328
+ maps_name=maps,
329
+ maps_img=img,
330
+ src=space,
331
+ dst=target_std_space,
332
+ target_data=target_data,
333
+ warp_data=None,
334
+ )
335
+ # Remove extra dimension added by ANTs
336
+ img = nimg.math_img("np.squeeze(img)", img=raw_img)
337
+
338
+ if target_space != "native":
339
+ # No warping is going to happen, just resampling, because
340
+ # we are in the correct space
341
+ logger.debug(f"Resampling {maps} to target image.")
342
+ # Resample maps to target image
343
+ img = nimg.resample_to_img(
344
+ source_img=img,
345
+ target_img=target_img,
346
+ interpolation="continuous",
347
+ copy=True,
348
+ )
349
+ else: # pragma: no cover
350
+ # Warp maps if target space is native as either
351
+ # the image is in the right non-native space or it's
352
+ # warped from one non-native space to another non-native space
353
+ logger.debug(
354
+ "Warping map(s) to native space using "
355
+ f"{warper_spec['warper']}."
356
+ )
357
+ # extra_input check done earlier and warper_spec exists
358
+ if warper_spec["warper"] == "fsl":
359
+ img = FSLMapsWarper().warp(
360
+ maps_name="native",
361
+ maps_img=img,
362
+ target_data=target_data,
363
+ warp_data=warper_spec,
364
+ )
365
+ elif warper_spec["warper"] == "ants":
366
+ img = ANTsMapsWarper().warp(
367
+ maps_name="native",
368
+ maps_img=img,
369
+ src="",
370
+ dst="native",
371
+ target_data=target_data,
372
+ warp_data=warper_spec,
373
+ )
374
+
375
+ return img, labels
376
+
377
+
378
+ def _retrieve_smith(
379
+ resolution: Optional[float] = None,
380
+ components: Optional[str] = None,
381
+ dimension: Optional[int] = None,
382
+ ) -> tuple[Path, list[str]]:
383
+ """Retrieve Smith maps.
384
+
385
+ Parameters
386
+ ----------
387
+ resolution : 2.0, optional
388
+ The desired resolution of the maps to load. If it is not
389
+ available, the closest resolution will be loaded. Preferably, use a
390
+ resolution higher than the desired one. By default, will load the
391
+ highest one (default None). Available resolution for these
392
+ maps are 2mm.
393
+ components : {"rsn", "bm"}, optional
394
+ The components to load. "rsn" loads the resting-fMRI components and
395
+ "bm" loads the BrainMap components (default None).
396
+ dimension : {10, 20, 70}, optional
397
+ The number of dimensions to load (default None).
398
+
399
+ Returns
400
+ -------
401
+ pathlib.Path
402
+ File path to the maps image.
403
+ list of str
404
+ Maps labels.
405
+
406
+ Raises
407
+ ------
408
+ ValueError
409
+ If invalid value is provided for ``components`` or ``dimension``.
410
+
411
+ """
412
+ logger.info("Maps parameters:")
413
+ logger.info(f"\tresolution: {resolution}")
414
+ logger.info(f"\tcomponents: {components}")
415
+ logger.info(f"\tdimension: {dimension}")
416
+
417
+ # Check resolution
418
+ _valid_resolutions = [2.0]
419
+ resolution = closest_resolution(resolution, _valid_resolutions)
420
+
421
+ # Check components value
422
+ _valid_components = ["rsn", "bm"]
423
+ if components not in _valid_components:
424
+ raise_error(
425
+ f"The parameter `components` ({components}) needs to be one of "
426
+ f"the following: {_valid_components}"
427
+ )
428
+
429
+ # Check dimension value
430
+ _valid_dimension = [10, 20, 70]
431
+ if dimension not in _valid_dimension:
432
+ raise_error(
433
+ f"The parameter `dimension` ({dimension}) needs to be one of the "
434
+ f"following: {_valid_dimension}"
435
+ )
436
+
437
+ # Fetch file path
438
+ maps_img_path = get(
439
+ file_path=Path(
440
+ f"parcellations/Smith2009/{components}{dimension}.nii.gz"
441
+ ),
442
+ dataset_path=get_dataset_path(),
443
+ **JUNIFER_DATA_PARAMS,
444
+ )
445
+
446
+ return maps_img_path, [f"Map_{i}" for i in range(dimension)]
@@ -0,0 +1,255 @@
1
+ """Provide tests for maps."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import pytest
7
+ from numpy.testing import assert_array_equal
8
+
9
+ from junifer.data import (
10
+ deregister_data,
11
+ get_data,
12
+ list_data,
13
+ load_data,
14
+ register_data,
15
+ )
16
+ from junifer.data.maps._maps import _retrieve_smith
17
+ from junifer.datagrabber import PatternDataladDataGrabber
18
+ from junifer.datareader import DefaultDataReader
19
+ from junifer.pipeline.utils import _check_ants
20
+ from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
21
+
22
+
23
+ def test_register_built_in_check() -> None:
24
+ """Test maps registration check for built-in maps."""
25
+ with pytest.raises(ValueError, match=r"built-in"):
26
+ register_data(
27
+ kind="maps",
28
+ name="Smith_rsn_10",
29
+ maps_path="testmaps.nii.gz",
30
+ maps_labels=["1", "2"],
31
+ space="MNI",
32
+ )
33
+
34
+
35
+ def test_list_incorrect() -> None:
36
+ """Test incorrect information check for list mapss."""
37
+ assert "testmaps" not in list_data(kind="maps")
38
+
39
+
40
+ def test_register_overwrite() -> None:
41
+ """Test maps registration check for overwriting."""
42
+ register_data(
43
+ kind="maps",
44
+ name="testmaps",
45
+ maps_path="testmaps.nii.gz",
46
+ maps_labels=["1", "2"],
47
+ space="MNI152NLin6Sym",
48
+ )
49
+ with pytest.raises(ValueError, match=r"already registered"):
50
+ register_data(
51
+ kind="maps",
52
+ name="testmaps",
53
+ maps_path="testmaps.nii.gz",
54
+ maps_labels=["1", "2"],
55
+ space="MNI152NLin6Sym",
56
+ overwrite=False,
57
+ )
58
+
59
+ register_data(
60
+ kind="maps",
61
+ name="testmaps",
62
+ maps_path="testmaps.nii.gz",
63
+ maps_labels=["1", "2"],
64
+ space="MNI152NLin6Sym",
65
+ overwrite=True,
66
+ )
67
+
68
+ assert (
69
+ load_data(
70
+ kind="maps",
71
+ name="testmaps",
72
+ target_space="MNI152NLin6Sym",
73
+ path_only=True,
74
+ )[2].name
75
+ == "testmaps.nii.gz"
76
+ )
77
+
78
+
79
+ def test_register_valid_input() -> None:
80
+ """Test maps registration check for valid input."""
81
+ maps, labels, maps_path, _ = load_data(
82
+ kind="maps",
83
+ name="Smith_rsn_10",
84
+ target_space="MNI152NLin6Asym",
85
+ )
86
+ assert maps is not None
87
+
88
+ # Test wrong number of labels
89
+ register_data(
90
+ kind="maps",
91
+ name="WrongLabels",
92
+ maps_path=maps_path,
93
+ maps_labels=labels[:5],
94
+ space="MNI152NLin6Asym",
95
+ )
96
+ with pytest.raises(ValueError, match=r"has 10 regions but 5"):
97
+ load_data(
98
+ kind="maps",
99
+ name="WrongLabels",
100
+ target_space="MNI152NLin6Asym",
101
+ )
102
+
103
+
104
+ def test_list() -> None:
105
+ """Test listing of available coordinates."""
106
+ assert {"Smith_rsn_10", "Smith_bm_70"}.issubset(
107
+ set(list_data(kind="maps"))
108
+ )
109
+
110
+
111
+ def test_load_nonexisting() -> None:
112
+ """Test loading maps that not exist."""
113
+ with pytest.raises(ValueError, match=r"not found"):
114
+ load_data(kind="maps", name="nomaps", target_space="MNI152NLin6Sym")
115
+
116
+
117
+ def test_get() -> None:
118
+ """Test tailored maps fetch."""
119
+ with PatternDataladDataGrabber(
120
+ uri="https://github.com/OpenNeuroDatasets/ds005226.git",
121
+ types=["BOLD"],
122
+ patterns={
123
+ "BOLD": {
124
+ "pattern": (
125
+ "derivatives/pre-processed_data/space-MNI/{subject}/"
126
+ "{subject-padded}_task-{task}_run-{run}_space-MNI152NLin6Asym"
127
+ "_res-2_desc-preproc_bold.nii.gz"
128
+ ),
129
+ "space": "MNI152NLin6Asym",
130
+ },
131
+ },
132
+ replacements=["subject", "subject-padded", "task", "run"],
133
+ ) as dg:
134
+ element = dg[("sub-01", "sub-001", "rest", "1")]
135
+ element_data = DefaultDataReader().fit_transform(element)
136
+ bold = element_data["BOLD"]
137
+ bold_img = bold["data"]
138
+ # Get tailored coordinates
139
+ tailored_maps, tailored_labels = get_data(
140
+ kind="maps", names="Smith_rsn_10", target_data=bold
141
+ )
142
+
143
+ # Check shape with original element data
144
+ assert tailored_maps.shape[:3] == bold_img.shape[:3]
145
+
146
+ # Get raw maps
147
+ raw_maps, raw_labels, _, _ = load_data(
148
+ kind="maps", name="Smith_rsn_10", target_space="MNI152NLin6Asym"
149
+ )
150
+ # Tailored and raw shape should be same
151
+ assert tailored_maps.shape[:3] == raw_maps.shape[:3]
152
+ assert tailored_labels == raw_labels
153
+
154
+
155
+ @pytest.mark.skipif(
156
+ _check_ants() is False, reason="requires ANTs to be in PATH"
157
+ )
158
+ def test_get_different_space() -> None:
159
+ """Test tailored maps fetch in different space."""
160
+ with PartlyCloudyTestingDataGrabber() as dg:
161
+ element = dg["sub-01"]
162
+ element_data = DefaultDataReader().fit_transform(element)
163
+ bold = element_data["BOLD"]
164
+ bold_img = bold["data"]
165
+ # Get tailored coordinates
166
+ tailored_maps, tailored_labels = get_data(
167
+ kind="maps", names="Smith_rsn_10", target_data=bold
168
+ )
169
+
170
+ # Check shape with original element data
171
+ assert tailored_maps.shape[:3] == bold_img.shape[:3]
172
+
173
+ # Get raw maps
174
+ raw_maps, raw_labels, _, _ = load_data(
175
+ kind="maps",
176
+ name="Smith_rsn_10",
177
+ target_space="MNI152NLin2009cAsym",
178
+ )
179
+ # Tailored and raw should not be same
180
+ assert tailored_maps.shape[:3] != raw_maps.shape[:3]
181
+ assert tailored_labels == raw_labels
182
+
183
+
184
+ def test_deregister() -> None:
185
+ """Test maps deregistration."""
186
+ deregister_data(kind="maps", name="testmaps")
187
+ assert "testmaps" not in list_data(kind="maps")
188
+
189
+
190
+ @pytest.mark.parametrize(
191
+ "resolution, components, dimension",
192
+ [
193
+ (2.0, "rsn", 10),
194
+ (2.0, "rsn", 20),
195
+ (2.0, "rsn", 70),
196
+ (2.0, "bm", 10),
197
+ (2.0, "bm", 20),
198
+ (2.0, "bm", 70),
199
+ ],
200
+ )
201
+ def test_smith(
202
+ resolution: float,
203
+ components: str,
204
+ dimension: int,
205
+ ) -> None:
206
+ """Test Smith maps.
207
+
208
+ Parameters
209
+ ----------
210
+ resolution : float
211
+ The parametrized resolution values.
212
+ components : str
213
+ The parametrized components values.
214
+ dimension : int
215
+ The parametrized dimension values.
216
+
217
+ """
218
+ maps = list_data(kind="maps")
219
+ maps_name = f"Smith_{components}_{dimension}"
220
+ assert maps_name in maps
221
+
222
+ maps_file = f"{components}{dimension}.nii.gz"
223
+ # Load maps
224
+ img, label, img_path, space = load_data(
225
+ kind="maps",
226
+ name=maps_name,
227
+ target_space="MNI152NLin6Asym",
228
+ resolution=resolution,
229
+ )
230
+ assert img is not None
231
+ assert img_path.name == maps_file
232
+ assert space == "MNI152NLin6Asym"
233
+ assert len(label) == dimension
234
+ assert_array_equal(
235
+ img.header["pixdim"][1:4],
236
+ 3 * [2.0],
237
+ )
238
+
239
+
240
+ def test_retrieve_smith_incorrect_components() -> None:
241
+ """Test retrieve Smith with incorrect components."""
242
+ with pytest.raises(ValueError, match="The parameter `components`"):
243
+ _retrieve_smith(
244
+ components="abc",
245
+ dimension=10,
246
+ )
247
+
248
+
249
+ def test_retrieve_smith_incorrect_dimension() -> None:
250
+ """Test retrieve Smith with incorrect dimension."""
251
+ with pytest.raises(ValueError, match="The parameter `dimension`"):
252
+ _retrieve_smith(
253
+ components="rsn",
254
+ dimension=100,
255
+ )
@@ -231,8 +231,9 @@ class MaskRegistry(BasePipelineDataRegistry):
231
231
  # * 'family': the mask's family name
232
232
  # (e.g., 'Vickery-Patil', 'Callable')
233
233
  # * 'space': the mask's space (e.g., 'MNI', 'inherit')
234
- # The built-in masks are files that are shipped with the package in the
235
- # data/masks directory. The user can also register their own masks.
234
+ # The built-in masks are files that are shipped either with the
235
+ # junifer-data dataset or computed on-demand. The user can also
236
+ # register their own masks.
236
237
  # Callable masks should be functions that take at least one parameter:
237
238
  # * `target_img`: the image to which the mask will be applied.
238
239
  # and should be included in the registry as a value to a key: `func`.
@@ -55,7 +55,7 @@ class ParcellationRegistry(BasePipelineDataRegistry):
55
55
  # and can also have optional key(s):
56
56
  # * 'valid_resolutions': a list of valid resolutions for the
57
57
  # parcellation (e.g., [1, 2])
58
- # The built-in coordinates are files that are shipped with the
58
+ # The built-in parcellations are files that are shipped with the
59
59
  # junifer-data dataset.
60
60
  # Make built-in and external dictionaries for validation later
61
61
  self._builtin = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: junifer
3
- Version: 0.0.7.dev130
3
+ Version: 0.0.7.dev153
4
4
  Summary: JUelich NeuroImaging FEature extractoR
5
5
  Author-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  Maintainer-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
@@ -39,7 +39,7 @@ Requires-Dist: sqlalchemy<=2.1.0,>=2.0.25
39
39
  Requires-Dist: ruamel.yaml<0.19,>=0.17
40
40
  Requires-Dist: h5py>=3.10
41
41
  Requires-Dist: tqdm<4.67.0,>=4.66.1
42
- Requires-Dist: templateflow>=23.0.0
42
+ Requires-Dist: templateflow<25.0.0,>=23.0.0
43
43
  Requires-Dist: lapy<2.0.0,>=1.0.0
44
44
  Requires-Dist: lazy_loader==0.4
45
45
  Requires-Dist: importlib_metadata; python_version < "3.9"
@@ -1,6 +1,6 @@
1
1
  junifer/__init__.py,sha256=2McgH1yNue6Z1V26-uN_mfMjbTcx4CLhym-DMBl5xA4,266
2
2
  junifer/__init__.pyi,sha256=SsTvgq2Dod6UqJN96GH1lCphH6hJQQurEJHGNhHjGUI,508
3
- junifer/_version.py,sha256=RFvEsWQ6YmMabVuSiWCanoOnrIrskMWcAk8aE6y_LG0,528
3
+ junifer/_version.py,sha256=nGBDjmgSsVIBdNcUeVNDrvwixSrlmh0dkwRYtw7_Yeg,721
4
4
  junifer/conftest.py,sha256=PWYkkRDU8ly2lYwv7VBKMHje4et6HX7Yey3Md_I2KbA,613
5
5
  junifer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  junifer/stats.py,sha256=e9aaagMGtgpRfW3Wdpz9ocpnYld1IWylCDcjFUgX9Mk,6225
@@ -72,8 +72,8 @@ junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py,sha256=8jxpNZelXwpJG
72
72
  junifer/configs/juseless/datagrabbers/tests/test_ucla.py,sha256=l-1y_m6NJo7JExhyIzp-vajUfiqiofX69YUOrRHIFKw,3246
73
73
  junifer/configs/juseless/datagrabbers/tests/test_ukb_vbm.py,sha256=b9hjc1mgO--PSRC3id2EzzfE2yWNsuZ2UI47a6sfGZU,1025
74
74
  junifer/data/__init__.py,sha256=xJDI2QKtdjcNzpd1oVFM3guh1SFHM6jKstl7pFmzOuk,267
75
- junifer/data/__init__.pyi,sha256=yt6gNTY8fgv9O5HjUqEHdKkO7Oo3q4dVp-aeqP4q5PY,682
76
- junifer/data/_dispatch.py,sha256=l1UbmSQyrAVzfYopaijUFIkbHntFdIxsA8RpRHjvcf0,7519
75
+ junifer/data/__init__.pyi,sha256=3zKWlcp0JXFrZ4Pzfehha8QzG8sNSwvEKObdCfiNYuc,733
76
+ junifer/data/_dispatch.py,sha256=4T5cf6Ph6CgPFtDioM-eIMidh-IYVTKTWWJ6d2YI5UE,7660
77
77
  junifer/data/pipeline_data_registry_base.py,sha256=G8bE3WTj4D_rKC4ZKZe6E48Sd96CGea1PS3SxmTgGK4,2010
78
78
  junifer/data/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  junifer/data/template_spaces.py,sha256=7LcQbUV_SWCic-TtETCejgat-BxxFDxZFm5z80rRZ-M,4661
@@ -84,17 +84,23 @@ junifer/data/coordinates/_ants_coordinates_warper.py,sha256=9nIdg4B5EMje8s30n_8D
84
84
  junifer/data/coordinates/_coordinates.py,sha256=l-nu-eGdW2Cp4eDmnrSsuWE-zcj55apPab5pTt07SwU,13303
85
85
  junifer/data/coordinates/_fsl_coordinates_warper.py,sha256=fxxvsiq-hSeP_PDokMHJSxlyffMugzSDUHhKzuU7Vg4,2429
86
86
  junifer/data/coordinates/tests/test_coordinates.py,sha256=JG045I7l5zcxGRQMbZZTrJY0TI5ZKaJ_szGMB6j1ZRI,4623
87
+ junifer/data/maps/__init__.py,sha256=t0J3N7RemjbCFaZfRmec0-af5lzH9Tsml5UBB9-e99M,179
88
+ junifer/data/maps/__init__.pyi,sha256=ev1ZNCXMzpw83BpWQZT-UXvR80YCuhdrKpb2VF5cmuE,67
89
+ junifer/data/maps/_ants_maps_warper.py,sha256=1gyVG89TcLYnaVyKfp4ZQYb3niV4P7U_ev1HjEVV3WQ,5340
90
+ junifer/data/maps/_fsl_maps_warper.py,sha256=OhLk3aLd6BOHBEQp88Hx-b2GwPR3ubfThgUXEJQxFEk,2439
91
+ junifer/data/maps/_maps.py,sha256=2rFixyEoKSQPW7Ztcu8hRmzlbXCuPkKQTwRTxyDIrAY,14700
92
+ junifer/data/maps/tests/test_maps.py,sha256=ZktNGd8coAbOGn95sYHAPEAhJodr6pUqVKbuGtBRenU,7378
87
93
  junifer/data/masks/__init__.py,sha256=eEEhHglyVEx1LrqwXjq3cOmjf4sTsgBstRx5-k7zIQU,180
88
94
  junifer/data/masks/__init__.pyi,sha256=lcgr8gmWDPibC4RxnWBXb8DDpIkO73Aax09u6VXiJJI,114
89
95
  junifer/data/masks/_ants_mask_warper.py,sha256=YJYVuD4QkgFwUSs1edt2rrqfHYHaIR1u85HSeQE4GUk,5825
90
96
  junifer/data/masks/_fsl_mask_warper.py,sha256=7-Aw4m9GA7ZLqB26jjuKYJUJSv9V11DfPFNBbb5caSE,2877
91
- junifer/data/masks/_masks.py,sha256=hOQ_VpXW8HvOfpd7-bxxvPClEn5WT3WQHro2Ofrtna8,28602
97
+ junifer/data/masks/_masks.py,sha256=oWArr4cgM5nvi8uzBrpEFmnxgnxqMYv-e4QiqBEp5ug,28626
92
98
  junifer/data/masks/tests/test_masks.py,sha256=VK3IenIwhaYpbESe0pOjb40OKE6rcRlw8ddsBxSRfyU,16688
93
99
  junifer/data/parcellations/__init__.py,sha256=6-Ysil3NyZ69V6rWx4RO15_d-iDKizfbHuxSjsHNt24,188
94
100
  junifer/data/parcellations/__init__.pyi,sha256=lhBHTbMDizzqUqVHrx2eyfPFodrTBgMFeTgxfESSkQ8,140
95
101
  junifer/data/parcellations/_ants_parcellation_warper.py,sha256=LIfeIAv3bFQbIrl6Cr7RU2RdkA-c2G6qURIBUe5MJCQ,5826
96
102
  junifer/data/parcellations/_fsl_parcellation_warper.py,sha256=lmZDPv2fMjOnbJ0z2d3K9S7QH2bgYd5bXbzxNDUR5NY,2699
97
- junifer/data/parcellations/_parcellations.py,sha256=e5Cd6VptKNZaqmAnfuhfMPeb48utimAkq5h4JioaB5w,48428
103
+ junifer/data/parcellations/_parcellations.py,sha256=eG6i_UgvhR0XwIdVhRqhiwunn7fO_-3AFlKAk8ayXHc,48430
98
104
  junifer/data/parcellations/tests/test_parcellations.py,sha256=s3TtsXTw7wEb-FvW0pFX2QcIXjp9IwlgjtM8X95rz_8,37544
99
105
  junifer/data/tests/test_data_utils.py,sha256=6-UQ7HDZ7_zA7iQalzk29KJBdftQMVyqKsQ0tx1URkE,1062
100
106
  junifer/data/tests/test_dispatch.py,sha256=bm4R0E8gs_XpJ6B5lfWFXjle7PmDjaX7Wu0L6mEU33w,2315
@@ -326,10 +332,10 @@ junifer/utils/tests/test_config.py,sha256=7ltIXuwb_W4Mv_1dxQWyiyM10XgUAfsWKV6D_i
326
332
  junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
327
333
  junifer/utils/tests/test_helpers.py,sha256=k5qqfxK8dFyuewTJyR1Qn6-nFaYNuVr0ysc18bfPjyU,929
328
334
  junifer/utils/tests/test_logging.py,sha256=W4tFKmaf8_CxnWZ-o_-XxM7DQbhGG18RsLZJk8bZelI,8163
329
- junifer-0.0.7.dev130.dist-info/licenses/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
330
- junifer-0.0.7.dev130.dist-info/licenses/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
331
- junifer-0.0.7.dev130.dist-info/METADATA,sha256=eylDK-maUxA0kRXTH3np3CK44BidjITRPDUNMlsjzRk,8388
332
- junifer-0.0.7.dev130.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
333
- junifer-0.0.7.dev130.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
334
- junifer-0.0.7.dev130.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
335
- junifer-0.0.7.dev130.dist-info/RECORD,,
335
+ junifer-0.0.7.dev153.dist-info/licenses/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
336
+ junifer-0.0.7.dev153.dist-info/licenses/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
337
+ junifer-0.0.7.dev153.dist-info/METADATA,sha256=re2C1YH9-idiVNi-SmZ4C3dzzfNqaqLiqU0uJPATGv4,8396
338
+ junifer-0.0.7.dev153.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
339
+ junifer-0.0.7.dev153.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
340
+ junifer-0.0.7.dev153.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
341
+ junifer-0.0.7.dev153.dist-info/RECORD,,