junifer 0.0.7.dev43__py3-none-any.whl → 0.0.7.dev65__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
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.7.dev43'
21
- __version_tuple__ = version_tuple = (0, 0, 7, 'dev43')
20
+ __version__ = version = '0.0.7.dev65'
21
+ __version_tuple__ = version_tuple = (0, 0, 7, 'dev65')
@@ -75,6 +75,7 @@ class PipelineComponentRegistry(metaclass=Singleton):
75
75
  "Smoothing": "Smoothing",
76
76
  "SpaceWarper": "SpaceWarper",
77
77
  "fMRIPrepConfoundRemover": "fMRIPrepConfoundRemover",
78
+ "TemporalSlicer": "TemporalSlicer",
78
79
  },
79
80
  "marker": {
80
81
  "ALFFParcels": "ALFFParcels",
@@ -3,9 +3,11 @@ __all__ = [
3
3
  "fMRIPrepConfoundRemover",
4
4
  "SpaceWarper",
5
5
  "Smoothing",
6
+ "TemporalSlicer",
6
7
  ]
7
8
 
8
9
  from .base import BasePreprocessor
9
10
  from .confounds import fMRIPrepConfoundRemover
10
11
  from .warping import SpaceWarper
11
12
  from .smoothing import Smoothing
13
+ from ._temporal_slicer import TemporalSlicer
@@ -0,0 +1,236 @@
1
+ """Provide class for temporal slicing."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from typing import Any, ClassVar, Optional
7
+
8
+ import nibabel as nib
9
+ import nilearn.image as nimg
10
+
11
+ from ..api.decorators import register_preprocessor
12
+ from ..pipeline import WorkDirManager
13
+ from ..typing import Dependencies
14
+ from ..utils import logger, raise_error
15
+ from .base import BasePreprocessor
16
+
17
+
18
+ __all__ = ["TemporalSlicer"]
19
+
20
+
21
+ @register_preprocessor
22
+ class TemporalSlicer(BasePreprocessor):
23
+ """Class for temporal slicing.
24
+
25
+ Parameters
26
+ ----------
27
+ start : float
28
+ Starting time point, in second.
29
+ stop : float or None
30
+ Ending time point, in second. If None, stops at the last time point.
31
+ Can also do negative indexing and has the same meaning as standard
32
+ Python slicing except it represents time points.
33
+ duration : float or None, optional
34
+ Time duration to add to ``start``, in second. If None, ``stop`` is
35
+ respected, else error is raised (default None).
36
+ t_r : float or None, optional
37
+ Repetition time, in second (sampling period).
38
+ If None, it will use t_r from nifti header (default None).
39
+
40
+ Raises
41
+ ------
42
+ ValueError
43
+ If ``start`` is negative.
44
+
45
+ """
46
+
47
+ _DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
48
+
49
+ def __init__(
50
+ self,
51
+ start: float,
52
+ stop: Optional[float],
53
+ duration: Optional[float] = None,
54
+ t_r: Optional[float] = None,
55
+ ) -> None:
56
+ """Initialize the class."""
57
+ if start < 0:
58
+ raise_error("`start` cannot be negative")
59
+ else:
60
+ self.start = start
61
+ self.stop = stop
62
+ self.duration = duration
63
+ self.t_r = t_r
64
+ super().__init__(on="BOLD", required_data_types=["BOLD"])
65
+
66
+ def get_valid_inputs(self) -> list[str]:
67
+ """Get valid data types for input.
68
+
69
+ Returns
70
+ -------
71
+ list of str
72
+ The list of data types that can be used as input for this
73
+ preprocessor.
74
+
75
+ """
76
+ return ["BOLD"]
77
+
78
+ def get_output_type(self, input_type: str) -> str:
79
+ """Get output type.
80
+
81
+ Parameters
82
+ ----------
83
+ input_type : str
84
+ The data type input to the preprocessor.
85
+
86
+ Returns
87
+ -------
88
+ str
89
+ The data type output by the preprocessor.
90
+
91
+ """
92
+ # Does not add any new keys
93
+ return input_type
94
+
95
+ def preprocess(
96
+ self,
97
+ input: dict[str, Any],
98
+ extra_input: Optional[dict[str, Any]] = None,
99
+ ) -> tuple[dict[str, Any], Optional[dict[str, dict[str, Any]]]]:
100
+ """Preprocess.
101
+
102
+ Parameters
103
+ ----------
104
+ input : dict
105
+ The input from the Junifer Data object.
106
+ extra_input : dict, optional
107
+ The other fields in the Junifer Data object.
108
+
109
+ Returns
110
+ -------
111
+ dict
112
+ The computed result as dictionary.
113
+ None
114
+ Extra "helper" data types as dictionary to add to the Junifer Data
115
+ object.
116
+
117
+ Raises
118
+ ------
119
+ RuntimeError
120
+ If no time slicing will be performed or
121
+ if ``stop`` is not None when ``duration`` is provided or
122
+ if calculated stop index is greater than allowed value.
123
+
124
+ """
125
+ logger.debug("Temporal slicing")
126
+
127
+ # Get BOLD data
128
+ bold_img = input["data"]
129
+ time_dim = bold_img.shape[3]
130
+
131
+ # Check if slicing is not required
132
+ if self.start == 0:
133
+ if self.stop is None or self.stop == -1 or self.stop == time_dim:
134
+ raise_error(
135
+ "No temporal slicing will be performed as "
136
+ f"`start` = {self.start} and "
137
+ f"`stop` = {self.stop}, hence you "
138
+ "should remove the TemporalSlicer from the preprocess "
139
+ "step.",
140
+ klass=RuntimeError,
141
+ )
142
+
143
+ # Sanity check for stop and duration combination
144
+ if self.duration is not None and self.stop is not None:
145
+ raise_error(
146
+ "`stop` should be None if `duration` is not None. "
147
+ "Set `stop` = None for TemporalSlicer to continue.",
148
+ klass=RuntimeError,
149
+ )
150
+
151
+ # Set t_r
152
+ t_r = self.t_r
153
+ if t_r is None:
154
+ logger.info("No `t_r` specified, using t_r from NIfTI header")
155
+ t_r = bold_img.header.get_zooms()[3] # type: ignore
156
+ logger.info(
157
+ f"Read t_r from NIfTI header: {t_r}",
158
+ )
159
+
160
+ # Create element-specific tempdir for storing generated data
161
+ element_tempdir = WorkDirManager().get_element_tempdir(
162
+ prefix="temporal_slicer"
163
+ )
164
+
165
+ # Check stop; duration is None
166
+ if self.stop is None:
167
+ if self.duration is not None:
168
+ stop = self.start + self.duration
169
+ else:
170
+ stop = time_dim
171
+ else:
172
+ # Calculate stop index if going from end
173
+ if self.stop < 0:
174
+ stop = time_dim + 1 + self.stop
175
+ else:
176
+ stop = self.stop
177
+
178
+ # Convert slice range from seconds to indices
179
+ index = slice(int(self.start // t_r), int(stop // t_r))
180
+
181
+ # Check if stop index is out of bounds
182
+ if index.stop > time_dim:
183
+ raise_error(
184
+ f"Calculated stop index: {index.stop} is greater than "
185
+ f"allowed value: {time_dim}",
186
+ klass=IndexError,
187
+ )
188
+
189
+ logger.info(
190
+ "Computed slice range for TemporalSlicer: "
191
+ f"[{index.start},{index.stop}]"
192
+ )
193
+
194
+ # Slice image
195
+ sliced_img = nimg.index_img(bold_img, index)
196
+ # Fix t_r as nilearn messes it up
197
+ sliced_img.header["pixdim"][4] = t_r
198
+ # Save sliced data
199
+ sliced_img_path = element_tempdir / "sliced_data.nii.gz"
200
+ nib.save(sliced_img, sliced_img_path)
201
+
202
+ logger.debug("Updating `BOLD`")
203
+ input.update(
204
+ {
205
+ # Update path to sync with "data"
206
+ "path": sliced_img_path,
207
+ # Update data
208
+ "data": sliced_img,
209
+ }
210
+ )
211
+
212
+ # Check for BOLD.confounds and update if found
213
+ if input.get("confounds") is not None:
214
+ # Slice confounds
215
+ sliced_confounds_df = input["confounds"]["data"].iloc[index, :]
216
+ # Save sliced confounds
217
+ sliced_confounds_path = (
218
+ element_tempdir / "sliced_confounds_regressors.tsv"
219
+ )
220
+ sliced_confounds_df.to_csv(
221
+ sliced_confounds_path,
222
+ sep="\t",
223
+ index=False,
224
+ )
225
+
226
+ logger.debug("Updating `BOLD.confounds`")
227
+ input["confounds"].update(
228
+ {
229
+ # Update path to sync with "data"
230
+ "path": sliced_confounds_path,
231
+ # Update data
232
+ "data": sliced_confounds_df,
233
+ }
234
+ )
235
+
236
+ return input, None
@@ -0,0 +1,138 @@
1
+ """Provide tests for TemporalSlicer."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from contextlib import AbstractContextManager, nullcontext
7
+ from typing import Optional
8
+
9
+ import pytest
10
+
11
+ from junifer.datareader import DefaultDataReader
12
+ from junifer.preprocess import TemporalSlicer
13
+ from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
14
+
15
+
16
+ @pytest.mark.parametrize(
17
+ "start, stop, duration, t_r, expected_dim, expect",
18
+ (
19
+ [
20
+ 0.0,
21
+ 168.0,
22
+ None,
23
+ 2.0,
24
+ 84,
25
+ pytest.raises(RuntimeError, match="No temporal slicing"),
26
+ ], # t_r from doc is 2.0
27
+ [0.0, 167.0, None, 2.0, 83, nullcontext()], # t_r from doc is 2.0
28
+ [
29
+ 0.0,
30
+ None,
31
+ None,
32
+ 2.0,
33
+ 84,
34
+ pytest.raises(RuntimeError, match="No temporal slicing"),
35
+ ], # total with no end
36
+ [2.0, None, None, 2.0, 83, nullcontext()], # total with no end
37
+ [0.0, 84.0, None, 2.0, 42, nullcontext()], # first half
38
+ [0.0, -85.0, None, 2.0, 42, nullcontext()], # first half from end
39
+ [84.0, -1.0, None, 2.0, 42, nullcontext()], # second half
40
+ [
41
+ 33.0,
42
+ -33.0,
43
+ 33.0,
44
+ 2.0,
45
+ 42,
46
+ pytest.raises(RuntimeError, match="`stop` should be None"),
47
+ ],
48
+ [10.0, None, 30.0, 2.0, 15, nullcontext()],
49
+ [
50
+ 0.0,
51
+ 168.0,
52
+ None,
53
+ None,
54
+ 168,
55
+ pytest.raises(RuntimeError, match="No temporal slicing"),
56
+ ], # t_r from image is 1.0
57
+ [0.0, 167.0, None, None, 167, nullcontext()], # t_r from image is 1.0
58
+ [
59
+ 0.0,
60
+ None,
61
+ None,
62
+ None,
63
+ 168,
64
+ pytest.raises(RuntimeError, match="No temporal slicing"),
65
+ ], # total with no end
66
+ [0.0, 84.0, None, None, 84, nullcontext()], # first half
67
+ [0.0, -85.0, None, None, 84, nullcontext()], # first half from end
68
+ [84.0, -1.0, None, None, 84, nullcontext()], # second half
69
+ [
70
+ 33.0,
71
+ -33.0,
72
+ 33.0,
73
+ None,
74
+ 84,
75
+ pytest.raises(RuntimeError, match="`stop` should be None"),
76
+ ],
77
+ [10.0, None, 30.0, None, 30, nullcontext()],
78
+ [
79
+ -1.0,
80
+ None,
81
+ None,
82
+ None,
83
+ 84,
84
+ pytest.raises(ValueError, match="`start` cannot be negative"),
85
+ ],
86
+ [
87
+ 0.0,
88
+ 500.0,
89
+ None,
90
+ 2.0,
91
+ 42,
92
+ pytest.raises(IndexError, match="Calculated stop index:"),
93
+ ],
94
+ ),
95
+ )
96
+ def test_TemporalSlicer(
97
+ start: float,
98
+ stop: Optional[float],
99
+ duration: Optional[float],
100
+ t_r: Optional[float],
101
+ expected_dim: int,
102
+ expect: AbstractContextManager,
103
+ ) -> None:
104
+ """Test TemporalSlicer.
105
+
106
+ Parameters
107
+ ----------
108
+ start : float
109
+ The parametrized start.
110
+ stop : float or None
111
+ The parametrized stop.
112
+ duration : float or None
113
+ The parametrized duration.
114
+ t_r : float or None
115
+ The parametrized TR.
116
+ expected_dim : int
117
+ The parametrized expected time dimension size.
118
+ expect : typing.ContextManager
119
+ The parametrized ContextManager object.
120
+
121
+ """
122
+
123
+ with PartlyCloudyTestingDataGrabber() as dg:
124
+ # Read data
125
+ element_data = DefaultDataReader().fit_transform(dg["sub-01"])
126
+ # Preprocess data
127
+ with expect:
128
+ output = TemporalSlicer(
129
+ start=start, # in seconds
130
+ stop=stop, # in seconds
131
+ duration=duration, # in seconds
132
+ t_r=t_r, # in seconds
133
+ ).fit_transform(element_data)
134
+
135
+ # Check image data dim
136
+ assert output["BOLD"]["data"].shape[3] == expected_dim
137
+ # Check confounds dim
138
+ assert output["BOLD"]["confounds"]["data"].shape[0] == expected_dim
junifer/storage/hdf5.py CHANGED
@@ -157,7 +157,13 @@ class HDF5FeatureStorage(BaseFeatureStorage):
157
157
  uri.parent.mkdir(parents=True, exist_ok=True)
158
158
 
159
159
  # Available storage kinds
160
- storage_types = ["vector", "timeseries", "matrix", "scalar_table"]
160
+ storage_types = [
161
+ "vector",
162
+ "timeseries",
163
+ "matrix",
164
+ "scalar_table",
165
+ "timeseries_2d",
166
+ ]
161
167
 
162
168
  super().__init__(
163
169
  uri=uri,
@@ -180,7 +186,13 @@ class HDF5FeatureStorage(BaseFeatureStorage):
180
186
  storage.
181
187
 
182
188
  """
183
- return ["matrix", "vector", "timeseries", "scalar_table"]
189
+ return [
190
+ "matrix",
191
+ "vector",
192
+ "timeseries",
193
+ "scalar_table",
194
+ "timeseries_2d",
195
+ ]
184
196
 
185
197
  def _fetch_correct_uri_for_io(self, element: Optional[dict]) -> str:
186
198
  """Return proper URI for I/O based on `element`.
@@ -29,6 +29,7 @@ def test_get_valid_inputs() -> None:
29
29
  "vector",
30
30
  "timeseries",
31
31
  "scalar_table",
32
+ "timeseries_2d",
32
33
  ]
33
34
 
34
35
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: junifer
3
- Version: 0.0.7.dev43
3
+ Version: 0.0.7.dev65
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>
@@ -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=l9H_t4PXfYQFbMKPfkvirnOHQ2O7mdTlp6LZgcM-OPk,526
3
+ junifer/_version.py,sha256=UvOYJjZtgscj5gJPPHZyVchyaOLUCnrhEkKPBlcbPxQ,526
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
@@ -240,7 +240,7 @@ junifer/onthefly/tests/test_read_transform.py,sha256=Ed6gtj8bsD11fe0Y1AxG2JndtIT
240
240
  junifer/pipeline/__init__.py,sha256=rxKQGRwc6_sts1KhVIcVVpuXeiFABf11mQQ2h5jgA3U,194
241
241
  junifer/pipeline/__init__.pyi,sha256=hhcvNcABhtLaUQiZdTjo5sMWC3rtDkwVshL0sxD5JAE,399
242
242
  junifer/pipeline/marker_collection.py,sha256=1Kmf5f0E2MFhDpO9OBui046b_6h1u9U64AdEqrxso-o,5377
243
- junifer/pipeline/pipeline_component_registry.py,sha256=TdLXjQcUZstEF9n6IszBYAwLsiyf9SMw5QYb7Pl51MY,9413
243
+ junifer/pipeline/pipeline_component_registry.py,sha256=zSbIeubzvXFhv_CIQaAsGy_fFrGs43EJeerQUtr1TlA,9465
244
244
  junifer/pipeline/pipeline_step_mixin.py,sha256=oXfJh27yifHs1V3V_tMPCanRiHX1ggOVIbHTvMzq3cY,7853
245
245
  junifer/pipeline/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
246
246
  junifer/pipeline/update_meta_mixin.py,sha256=yzGCx8AUbc9mMnWKRu4qaIXTBBSIxtNlGH5zIQIUvzM,1812
@@ -252,7 +252,8 @@ junifer/pipeline/tests/test_pipeline_step_mixin.py,sha256=KCdhFdThm9TGkUvhGzQF3z
252
252
  junifer/pipeline/tests/test_update_meta_mixin.py,sha256=po7mWGmXCkZUi201zqgtTm7-A1HKjBgUgqMglMvedqc,1338
253
253
  junifer/pipeline/tests/test_workdir_manager.py,sha256=_rXlGb_PUQJBVgV1PWmwjFvTW971MkEbyDDY9dLfJAY,4051
254
254
  junifer/preprocess/__init__.py,sha256=91D43p254il88g-7sSN64M7HsCvwytYoiTS_GLEr37Y,342
255
- junifer/preprocess/__init__.pyi,sha256=EApXtuEZohQZnIeP6k882Y2H5IRiGmhJbVGdN7VCWFc,254
255
+ junifer/preprocess/__init__.pyi,sha256=77mlHjZXhHOsWd37deAq6d0PIdoCeme4cPGC8jk3Fmw,321
256
+ junifer/preprocess/_temporal_slicer.py,sha256=clZdDwGZrw656JYuK5eVvml54WxNVCQ2EOTbnsHhwnQ,7160
256
257
  junifer/preprocess/base.py,sha256=hARO4Yq9sQ8m2tATeuBmPMbI4BSnwNxLf2prF8Iq_Tk,6662
257
258
  junifer/preprocess/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
258
259
  junifer/preprocess/confounds/__init__.py,sha256=L3CquKcndFb2b8yVo-XLi-zsNCe8MMKUN41UOVdooWc,270
@@ -269,6 +270,7 @@ junifer/preprocess/smoothing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
269
270
  junifer/preprocess/smoothing/smoothing.py,sha256=wdOnPi8XkEqzOQdUNJ0yOm_uWi3H4DnTQhOL8z7dZDs,5281
270
271
  junifer/preprocess/smoothing/tests/test_smoothing.py,sha256=IYL-IaRYIuhOU7jGixgNuT-ed_i6q9gmDhK-ZpXAGeU,2389
271
272
  junifer/preprocess/tests/test_preprocess_base.py,sha256=-0rpe8QjqYES36H6MHuDs3cv_6upHBdVHnFMgQsmEX4,2571
273
+ junifer/preprocess/tests/test_temporal_slicer.py,sha256=bbt6kxOM-Bv_s9iPn4wrf2YxpA64xO2S0kgRYUEeBNo,4061
272
274
  junifer/preprocess/warping/__init__.py,sha256=rzUUP7-6H_nygQ7a7TBZ4_RY7p0ELacosYsWQbSdVZk,214
273
275
  junifer/preprocess/warping/__init__.pyi,sha256=Drbqp8N3uprvXcKSxqdfj90fesz9XYVLgivhPnKAYcc,65
274
276
  junifer/preprocess/warping/_ants_warper.py,sha256=l5UQKC5iU3l9P--IgqmYeHiPC-uB1l4u9iBgd2ODzC4,10166
@@ -279,12 +281,12 @@ junifer/preprocess/warping/tests/test_space_warper.py,sha256=amFHtt-q7L7v9uL4cOv
279
281
  junifer/storage/__init__.py,sha256=aPGBFPPsTcZYMdkC_o5HIrzRIIwp-bc5bJDoh_GuQmo,270
280
282
  junifer/storage/__init__.pyi,sha256=MHC-R129z_WuXVQuKBrFu8H1wqmUPAl5ZOQT_WZaXek,292
281
283
  junifer/storage/base.py,sha256=zHvrRK62uzXo7C8FsOghbgcYdRluoO_imOuxgc_kncg,11887
282
- junifer/storage/hdf5.py,sha256=P8k2KasLIukJ71piPwF9uU7-PCWmQ46xO1hkPylIeBQ,40309
284
+ junifer/storage/hdf5.py,sha256=TjmkPEl4zkGUfgnvAmwqlL_V48m_IBuxjk81ouA_UNQ,40485
283
285
  junifer/storage/pandas_base.py,sha256=SYpbPdzkslrRcJdxZkW1NRUYRnAxMGAn1XhdCA8Yals,7554
284
286
  junifer/storage/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
285
287
  junifer/storage/sqlite.py,sha256=18bQsfov5DTlGFlVMV9akTiatO7QPWMBcynZRetEBQM,21202
286
288
  junifer/storage/utils.py,sha256=dgSbYjpwzy7dutcsdacd-5ZLR8kIucRcdlXw99aAjFI,9806
287
- junifer/storage/tests/test_hdf5.py,sha256=yEh5jw_6i37bch27FXIZmDPCA8vEfc-0TehXp9sYX0Y,34144
289
+ junifer/storage/tests/test_hdf5.py,sha256=qlhc4DvQ_ruI5D6FPXL9jL8x9undFzOZZnMkmsax5EU,34169
288
290
  junifer/storage/tests/test_pandas_base.py,sha256=S7_XM9EeBFoC4ojI0wYTFEXT5XMTWeVHiW6ddXdIjEI,4082
289
291
  junifer/storage/tests/test_sqlite.py,sha256=0TQIcqHPgk67ALsR-98CA73ulDPsR2t9wGXYaem983w,28312
290
292
  junifer/storage/tests/test_storage_base.py,sha256=Ic4zaPPhaKJDujfjxOM3h0GOhCtMnfT_jdWsuskKMac,3214
@@ -318,10 +320,10 @@ junifer/utils/tests/test_config.py,sha256=7ltIXuwb_W4Mv_1dxQWyiyM10XgUAfsWKV6D_i
318
320
  junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
319
321
  junifer/utils/tests/test_helpers.py,sha256=k5qqfxK8dFyuewTJyR1Qn6-nFaYNuVr0ysc18bfPjyU,929
320
322
  junifer/utils/tests/test_logging.py,sha256=W4tFKmaf8_CxnWZ-o_-XxM7DQbhGG18RsLZJk8bZelI,8163
321
- junifer-0.0.7.dev43.dist-info/licenses/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
322
- junifer-0.0.7.dev43.dist-info/licenses/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
323
- junifer-0.0.7.dev43.dist-info/METADATA,sha256=Xft9cJiib7PUS9u_sdo_ff5bw69Lwy254HHaR6sFGmg,8387
324
- junifer-0.0.7.dev43.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
325
- junifer-0.0.7.dev43.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
326
- junifer-0.0.7.dev43.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
327
- junifer-0.0.7.dev43.dist-info/RECORD,,
323
+ junifer-0.0.7.dev65.dist-info/licenses/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
324
+ junifer-0.0.7.dev65.dist-info/licenses/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
325
+ junifer-0.0.7.dev65.dist-info/METADATA,sha256=kTbGyVOMiR-i9SfOcQRZ8LupH8hJEBOUVtie1hAhUcA,8387
326
+ junifer-0.0.7.dev65.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
327
+ junifer-0.0.7.dev65.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
328
+ junifer-0.0.7.dev65.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
329
+ junifer-0.0.7.dev65.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5