ngio 0.5.0a2__py3-none-any.whl → 0.5.0a3__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.
- ngio/__init__.py +2 -2
- ngio/common/__init__.py +11 -6
- ngio/common/_masking_roi.py +12 -41
- ngio/common/_pyramid.py +206 -76
- ngio/common/_roi.py +257 -329
- ngio/experimental/iterators/_feature.py +3 -3
- ngio/experimental/iterators/_rois_utils.py +10 -11
- ngio/hcs/_plate.py +50 -43
- ngio/images/_abstract_image.py +417 -35
- ngio/images/_create_synt_container.py +35 -42
- ngio/images/_create_utils.py +423 -0
- ngio/images/_image.py +154 -176
- ngio/images/_label.py +144 -119
- ngio/images/_ome_zarr_container.py +361 -196
- ngio/io_pipes/_io_pipes.py +9 -9
- ngio/io_pipes/_io_pipes_masked.py +7 -7
- ngio/io_pipes/_io_pipes_roi.py +6 -6
- ngio/io_pipes/_io_pipes_types.py +3 -3
- ngio/io_pipes/_match_shape.py +5 -4
- ngio/io_pipes/_ops_slices_utils.py +8 -5
- ngio/ome_zarr_meta/__init__.py +15 -18
- ngio/ome_zarr_meta/_meta_handlers.py +334 -713
- ngio/ome_zarr_meta/ngio_specs/_axes.py +1 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +13 -22
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +54 -61
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +14 -68
- ngio/ome_zarr_meta/v04/__init__.py +1 -1
- ngio/ome_zarr_meta/v04/{_v04_spec_utils.py → _v04_spec.py} +16 -61
- ngio/ome_zarr_meta/v05/__init__.py +1 -1
- ngio/ome_zarr_meta/v05/{_v05_spec_utils.py → _v05_spec.py} +18 -61
- ngio/tables/_tables_container.py +2 -4
- ngio/tables/backends/_anndata.py +57 -8
- ngio/tables/backends/_anndata_utils.py +1 -6
- ngio/tables/backends/_csv.py +3 -19
- ngio/tables/backends/_json.py +10 -13
- ngio/tables/backends/_parquet.py +3 -31
- ngio/tables/backends/_py_arrow_backends.py +222 -0
- ngio/tables/v1/_roi_table.py +41 -24
- ngio/utils/__init__.py +4 -12
- ngio/utils/_zarr_utils.py +160 -53
- {ngio-0.5.0a2.dist-info → ngio-0.5.0a3.dist-info}/METADATA +6 -2
- {ngio-0.5.0a2.dist-info → ngio-0.5.0a3.dist-info}/RECORD +44 -45
- {ngio-0.5.0a2.dist-info → ngio-0.5.0a3.dist-info}/WHEEL +1 -1
- ngio/images/_create.py +0 -287
- ngio/tables/backends/_non_zarr_backends.py +0 -196
- ngio/utils/_logger.py +0 -50
- {ngio-0.5.0a2.dist-info → ngio-0.5.0a3.dist-info}/licenses/LICENSE +0 -0
ngio/tables/v1/_roi_table.py
CHANGED
|
@@ -4,6 +4,7 @@ This class follows the roi_table specification at:
|
|
|
4
4
|
https://fractal-analytics-platform.github.io/fractal-tasks-core/tables/
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import warnings
|
|
7
8
|
from collections.abc import Iterable
|
|
8
9
|
from typing import Literal
|
|
9
10
|
from uuid import uuid4
|
|
@@ -26,7 +27,6 @@ from ngio.utils import (
|
|
|
26
27
|
NgioTableValidationError,
|
|
27
28
|
NgioValueError,
|
|
28
29
|
ZarrGroupHandler,
|
|
29
|
-
ngio_warn,
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
REQUIRED_COLUMNS = [
|
|
@@ -77,7 +77,9 @@ OPTIONAL_COLUMNS = ORIGIN_COLUMNS + TRANSLATION_COLUMNS + PLATE_COLUMNS + INDEX_
|
|
|
77
77
|
def _check_optional_columns(col_name: str) -> None:
|
|
78
78
|
"""Check if the column name is in the optional columns."""
|
|
79
79
|
if col_name not in OPTIONAL_COLUMNS + TIME_COLUMNS:
|
|
80
|
-
|
|
80
|
+
warnings.warn(
|
|
81
|
+
f"Column {col_name} is not in the optional columns.", stacklevel=2
|
|
82
|
+
)
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
def _dataframe_to_rois(
|
|
@@ -120,17 +122,17 @@ def _dataframe_to_rois(
|
|
|
120
122
|
else:
|
|
121
123
|
label = getattr(row, "label", None)
|
|
122
124
|
|
|
123
|
-
|
|
125
|
+
slices = {
|
|
126
|
+
"x": (row.x_micrometer, row.len_x_micrometer),
|
|
127
|
+
"y": (row.y_micrometer, row.len_y_micrometer),
|
|
128
|
+
"z": (z_micrometer, z_length_micrometer),
|
|
129
|
+
}
|
|
130
|
+
if t_second is not None or t_length_second is not None:
|
|
131
|
+
slices["t"] = (t_second, t_length_second)
|
|
132
|
+
roi = Roi.from_values(
|
|
124
133
|
name=str(row.Index),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
z=z_micrometer,
|
|
128
|
-
t=t_second,
|
|
129
|
-
x_length=row.len_x_micrometer, # type: ignore (type can not be known here)
|
|
130
|
-
y_length=row.len_y_micrometer, # type: ignore (type can not be known here)
|
|
131
|
-
z_length=z_length_micrometer,
|
|
132
|
-
t_length=t_length_second,
|
|
133
|
-
unit="micrometer",
|
|
134
|
+
slices=slices,
|
|
135
|
+
space="world",
|
|
134
136
|
label=label,
|
|
135
137
|
**extras,
|
|
136
138
|
)
|
|
@@ -143,24 +145,39 @@ def _rois_to_dataframe(rois: dict[str, Roi], index_key: str | None) -> pd.DataFr
|
|
|
143
145
|
data = []
|
|
144
146
|
for roi in rois.values():
|
|
145
147
|
# This normalization is necessary for backward compatibility
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
if roi.space != "world":
|
|
149
|
+
raise NotImplementedError(
|
|
150
|
+
"Only ROIs in world coordinates can be serialized."
|
|
151
|
+
)
|
|
148
152
|
|
|
153
|
+
z_slice = roi.get("z")
|
|
154
|
+
if z_slice is None:
|
|
155
|
+
z_micrometer = 0.0
|
|
156
|
+
len_z_micrometer = 1.0
|
|
157
|
+
else:
|
|
158
|
+
z_micrometer = z_slice.start if z_slice.start is not None else 0.0
|
|
159
|
+
len_z_micrometer = z_slice.length if z_slice.length is not None else 1.0
|
|
160
|
+
|
|
161
|
+
x_slice = roi.get("x")
|
|
162
|
+
if x_slice is None:
|
|
163
|
+
raise NgioValueError("ROI is missing 'x' slice.")
|
|
164
|
+
y_slice = roi.get("y")
|
|
165
|
+
if y_slice is None:
|
|
166
|
+
raise NgioValueError("ROI is missing 'y' slice.")
|
|
149
167
|
row = {
|
|
150
168
|
index_key: roi.get_name(),
|
|
151
|
-
"x_micrometer":
|
|
152
|
-
"y_micrometer":
|
|
169
|
+
"x_micrometer": x_slice.start if x_slice.start is not None else 0.0,
|
|
170
|
+
"y_micrometer": y_slice.start if y_slice.start is not None else 0.0,
|
|
153
171
|
"z_micrometer": z_micrometer,
|
|
154
|
-
"len_x_micrometer":
|
|
155
|
-
"len_y_micrometer":
|
|
172
|
+
"len_x_micrometer": x_slice.length if x_slice.length is not None else 1.0,
|
|
173
|
+
"len_y_micrometer": y_slice.length if y_slice.length is not None else 1.0,
|
|
156
174
|
"len_z_micrometer": len_z_micrometer,
|
|
157
175
|
}
|
|
158
176
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
row["len_t_second"] = roi.t_length
|
|
177
|
+
t_slice = roi.get("t")
|
|
178
|
+
if t_slice is not None:
|
|
179
|
+
row["t_second"] = t_slice.start if t_slice.start is not None else 0.0
|
|
180
|
+
row["len_t_second"] = t_slice.length if t_slice.length is not None else 1.0
|
|
164
181
|
|
|
165
182
|
if roi.label is not None and index_key != "label":
|
|
166
183
|
row["label"] = roi.label
|
|
@@ -183,7 +200,7 @@ class RoiDictWrapper:
|
|
|
183
200
|
self._rois_by_name = {}
|
|
184
201
|
self._rois_by_label = {}
|
|
185
202
|
for roi in rois:
|
|
186
|
-
name = roi.
|
|
203
|
+
name = roi.name
|
|
187
204
|
if name in self._rois_by_name:
|
|
188
205
|
name = f"{name}_{uuid4().hex[:8]}"
|
|
189
206
|
self._rois_by_name[name] = roi
|
ngio/utils/__init__.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"""Various utilities for the ngio package."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
from ngio.utils._datasets import (
|
|
6
4
|
download_ome_zarr_dataset,
|
|
7
5
|
list_ome_zarr_datasets,
|
|
8
6
|
print_datasets_infos,
|
|
9
7
|
)
|
|
10
8
|
from ngio.utils._errors import (
|
|
9
|
+
NgioError,
|
|
11
10
|
NgioFileExistsError,
|
|
12
11
|
NgioFileNotFoundError,
|
|
13
12
|
NgioTableValidationError,
|
|
@@ -15,22 +14,19 @@ from ngio.utils._errors import (
|
|
|
15
14
|
NgioValueError,
|
|
16
15
|
)
|
|
17
16
|
from ngio.utils._fractal_fsspec_store import fractal_fsspec_store
|
|
18
|
-
from ngio.utils._logger import ngio_logger, ngio_warn, set_logger_level
|
|
19
17
|
from ngio.utils._zarr_utils import (
|
|
20
18
|
AccessModeLiteral,
|
|
21
19
|
NgioCache,
|
|
22
20
|
StoreOrGroup,
|
|
23
21
|
ZarrGroupHandler,
|
|
22
|
+
copy_group,
|
|
24
23
|
open_group_wrapper,
|
|
25
24
|
)
|
|
26
25
|
|
|
27
|
-
set_logger_level(os.getenv("NGIO_LOGGER_LEVEL", "WARNING"))
|
|
28
|
-
|
|
29
26
|
__all__ = [
|
|
30
|
-
# Zarr
|
|
31
27
|
"AccessModeLiteral",
|
|
32
28
|
"NgioCache",
|
|
33
|
-
|
|
29
|
+
"NgioError",
|
|
34
30
|
"NgioFileExistsError",
|
|
35
31
|
"NgioFileNotFoundError",
|
|
36
32
|
"NgioTableValidationError",
|
|
@@ -38,14 +34,10 @@ __all__ = [
|
|
|
38
34
|
"NgioValueError",
|
|
39
35
|
"StoreOrGroup",
|
|
40
36
|
"ZarrGroupHandler",
|
|
41
|
-
|
|
37
|
+
"copy_group",
|
|
42
38
|
"download_ome_zarr_dataset",
|
|
43
39
|
"fractal_fsspec_store",
|
|
44
40
|
"list_ome_zarr_datasets",
|
|
45
|
-
# Logger
|
|
46
|
-
"ngio_logger",
|
|
47
|
-
"ngio_warn",
|
|
48
41
|
"open_group_wrapper",
|
|
49
42
|
"print_datasets_infos",
|
|
50
|
-
"set_logger_level",
|
|
51
43
|
]
|
ngio/utils/_zarr_utils.py
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
"""Common utilities for working with Zarr groups in consistent ways."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import warnings
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Literal
|
|
6
7
|
|
|
8
|
+
import dask.array as da
|
|
7
9
|
import fsspec
|
|
8
10
|
import zarr
|
|
9
11
|
from filelock import BaseFileLock, FileLock
|
|
12
|
+
from pydantic_zarr.v2 import ArraySpec as AnyArraySpecV2
|
|
13
|
+
from pydantic_zarr.v3 import ArraySpec as AnyArraySpecV3
|
|
10
14
|
from zarr.abc.store import Store
|
|
11
15
|
from zarr.errors import ContainsGroupError
|
|
12
|
-
from zarr.storage import FsspecStore, LocalStore, MemoryStore
|
|
16
|
+
from zarr.storage import FsspecStore, LocalStore, MemoryStore, ZipStore
|
|
13
17
|
|
|
14
18
|
from ngio.utils._cache import NgioCache
|
|
15
19
|
from ngio.utils._errors import (
|
|
16
|
-
NgioError,
|
|
17
20
|
NgioFileExistsError,
|
|
18
21
|
NgioFileNotFoundError,
|
|
19
22
|
NgioValueError,
|
|
@@ -24,7 +27,13 @@ AccessModeLiteral = Literal["r", "r+", "w", "w-", "a"]
|
|
|
24
27
|
# but to make sure we can handle the store correctly
|
|
25
28
|
# we need to be more restrictive
|
|
26
29
|
NgioSupportedStore = (
|
|
27
|
-
str
|
|
30
|
+
str
|
|
31
|
+
| Path
|
|
32
|
+
| fsspec.mapping.FSMap
|
|
33
|
+
| FsspecStore
|
|
34
|
+
| MemoryStore
|
|
35
|
+
| LocalStore
|
|
36
|
+
| ZipStore
|
|
28
37
|
)
|
|
29
38
|
GenericStore = Store | NgioSupportedStore
|
|
30
39
|
StoreOrGroup = GenericStore | zarr.Group
|
|
@@ -125,6 +134,7 @@ class ZarrGroupHandler:
|
|
|
125
134
|
|
|
126
135
|
self._group_cache: NgioCache[zarr.Group] = NgioCache(use_cache=cache)
|
|
127
136
|
self._array_cache: NgioCache[zarr.Array] = NgioCache(use_cache=cache)
|
|
137
|
+
self._handlers_cache: NgioCache[ZarrGroupHandler] = NgioCache(use_cache=cache)
|
|
128
138
|
self._lock: tuple[Path, BaseFileLock] | None = None
|
|
129
139
|
|
|
130
140
|
def __repr__(self) -> str:
|
|
@@ -144,8 +154,17 @@ class ZarrGroupHandler:
|
|
|
144
154
|
"""Return the store path."""
|
|
145
155
|
if isinstance(self.store, LocalStore):
|
|
146
156
|
return (self.store.root / self.group.path).as_posix()
|
|
147
|
-
|
|
148
|
-
return self.store.
|
|
157
|
+
elif isinstance(self.store, FsspecStore):
|
|
158
|
+
return f"{self.store.path}/{self.group.path}"
|
|
159
|
+
elif isinstance(self.store, ZipStore):
|
|
160
|
+
return (self.store.path / self.group.path).as_posix()
|
|
161
|
+
elif isinstance(self.store, MemoryStore):
|
|
162
|
+
return None
|
|
163
|
+
warnings.warn(
|
|
164
|
+
f"Cannot determine full URL for store type {type(self.store)}. ",
|
|
165
|
+
UserWarning,
|
|
166
|
+
stacklevel=2,
|
|
167
|
+
)
|
|
149
168
|
return None
|
|
150
169
|
|
|
151
170
|
@property
|
|
@@ -325,25 +344,6 @@ class ZarrGroupHandler:
|
|
|
325
344
|
self._group_cache.set(path, group, overwrite=overwrite)
|
|
326
345
|
return group
|
|
327
346
|
|
|
328
|
-
def safe_get_group(
|
|
329
|
-
self, path: str, create_mode: bool = False
|
|
330
|
-
) -> tuple[bool, zarr.Group | NgioError]:
|
|
331
|
-
"""Get a group from the group.
|
|
332
|
-
|
|
333
|
-
Args:
|
|
334
|
-
path (str): The path to the group.
|
|
335
|
-
create_mode (bool): If True, create the group if it does not exist.
|
|
336
|
-
|
|
337
|
-
Returns:
|
|
338
|
-
zarr.Group | None: The Zarr group or None if it does not exist
|
|
339
|
-
or an error occurs.
|
|
340
|
-
|
|
341
|
-
"""
|
|
342
|
-
try:
|
|
343
|
-
return True, self.get_group(path, create_mode)
|
|
344
|
-
except NgioError as e:
|
|
345
|
-
return False, e
|
|
346
|
-
|
|
347
347
|
def get_array(self, path: str) -> zarr.Array:
|
|
348
348
|
"""Get an array from the group."""
|
|
349
349
|
array = self._array_cache.get(path)
|
|
@@ -358,47 +358,49 @@ class ZarrGroupHandler:
|
|
|
358
358
|
raise NgioValueError(f"The object at {path} is not an array, but a group.")
|
|
359
359
|
raise NgioFileNotFoundError(f"No array found at {path}")
|
|
360
360
|
|
|
361
|
-
def
|
|
361
|
+
def get_handler(
|
|
362
362
|
self,
|
|
363
363
|
path: str,
|
|
364
|
+
create_mode: bool = True,
|
|
364
365
|
overwrite: bool = False,
|
|
365
366
|
) -> "ZarrGroupHandler":
|
|
366
|
-
"""
|
|
367
|
+
"""Get a new handler for a group in the current handler group.
|
|
367
368
|
|
|
368
369
|
Args:
|
|
369
370
|
path (str): The path to the group.
|
|
371
|
+
create_mode (bool): If True, create the group if it does not exist.
|
|
370
372
|
overwrite (bool): If True, overwrite the group if it exists.
|
|
371
373
|
"""
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
374
|
+
handler = self._handlers_cache.get(path)
|
|
375
|
+
if handler is not None:
|
|
376
|
+
return handler
|
|
377
|
+
group = self.get_group(path, create_mode=create_mode, overwrite=overwrite)
|
|
378
|
+
mode = "r" if group.read_only else "r+"
|
|
379
|
+
handler = ZarrGroupHandler(
|
|
380
|
+
store=group, zarr_format=self.zarr_format, cache=self.use_cache, mode=mode
|
|
375
381
|
)
|
|
382
|
+
self._handlers_cache.set(path, handler)
|
|
383
|
+
return handler
|
|
376
384
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
"""
|
|
383
|
-
try:
|
|
384
|
-
return True, self.derive_handler(path, overwrite=overwrite)
|
|
385
|
-
except NgioError as e:
|
|
386
|
-
return False, e
|
|
385
|
+
@property
|
|
386
|
+
def is_listable(self) -> bool:
|
|
387
|
+
return is_group_listable(self.group)
|
|
388
|
+
|
|
389
|
+
def delete_group(self, path: str) -> None:
|
|
390
|
+
"""Delete a group from the current group.
|
|
387
391
|
|
|
388
|
-
|
|
392
|
+
Args:
|
|
393
|
+
path (str): The path to the group to delete.
|
|
394
|
+
"""
|
|
395
|
+
if self.group.read_only:
|
|
396
|
+
raise NgioValueError("Cannot delete a group in read only mode.")
|
|
397
|
+
self.group.__delitem__(path)
|
|
398
|
+
self._group_cache._cache.pop(path, None)
|
|
399
|
+
self._handlers_cache._cache.pop(path, None)
|
|
400
|
+
|
|
401
|
+
def copy_group(self, dest_group: zarr.Group):
|
|
389
402
|
"""Copy the group to a new store."""
|
|
390
|
-
|
|
391
|
-
source=self.group.store,
|
|
392
|
-
dest=handler.group.store,
|
|
393
|
-
source_path=self.group.path,
|
|
394
|
-
dest_path=handler.group.path,
|
|
395
|
-
if_exists="replace",
|
|
396
|
-
)
|
|
397
|
-
if n_skipped > 0:
|
|
398
|
-
raise NgioValueError(
|
|
399
|
-
f"Error copying group to {handler.full_url}, "
|
|
400
|
-
f"#{n_skipped} files where skipped."
|
|
401
|
-
)
|
|
403
|
+
copy_group(self.group, dest_group)
|
|
402
404
|
|
|
403
405
|
|
|
404
406
|
def find_dimension_separator(array: zarr.Array) -> Literal[".", "/"]:
|
|
@@ -417,8 +419,113 @@ def find_dimension_separator(array: zarr.Array) -> Literal[".", "/"]:
|
|
|
417
419
|
else:
|
|
418
420
|
separator = array.metadata.chunk_key_encoding
|
|
419
421
|
if not isinstance(separator, DefaultChunkKeyEncoding):
|
|
420
|
-
raise
|
|
422
|
+
raise NgioValueError(
|
|
421
423
|
"Only DefaultChunkKeyEncoding is supported in this example."
|
|
422
424
|
)
|
|
423
425
|
separator = separator.separator
|
|
424
426
|
return separator
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def is_group_listable(group: zarr.Group) -> bool:
|
|
430
|
+
"""Check if a Zarr group is listable.
|
|
431
|
+
|
|
432
|
+
A group is considered listable if it contains at least one array or subgroup.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
group (zarr.Group): The Zarr group to check.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
bool: True if the group is listable, False otherwise.
|
|
439
|
+
"""
|
|
440
|
+
if not group.store.supports_listing:
|
|
441
|
+
# If the store does not support listing
|
|
442
|
+
# then for sure it is not listable
|
|
443
|
+
return False
|
|
444
|
+
try:
|
|
445
|
+
next(group.keys())
|
|
446
|
+
return True
|
|
447
|
+
except StopIteration:
|
|
448
|
+
# Group is listable but empty
|
|
449
|
+
return True
|
|
450
|
+
except Exception as _:
|
|
451
|
+
# Some stores may raise errors when listing
|
|
452
|
+
# consider those not listable
|
|
453
|
+
return False
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _make_sync_fs(fs: fsspec.AbstractFileSystem) -> fsspec.AbstractFileSystem:
|
|
457
|
+
fs_dict = json.loads(fs.to_json())
|
|
458
|
+
fs_dict["asynchronous"] = False
|
|
459
|
+
return fsspec.AbstractFileSystem.from_json(json.dumps(fs_dict))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _get_mapper(store: LocalStore | FsspecStore, path: str):
|
|
463
|
+
if isinstance(store, LocalStore):
|
|
464
|
+
fs = fsspec.filesystem("file")
|
|
465
|
+
full_path = (store.root / path).as_posix()
|
|
466
|
+
else:
|
|
467
|
+
fs = _make_sync_fs(store.fs)
|
|
468
|
+
full_path = f"{store.path}/{path}"
|
|
469
|
+
return fs.get_mapper(full_path)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _fsspec_copy(
|
|
473
|
+
src_fs: LocalStore | FsspecStore,
|
|
474
|
+
src_path: str,
|
|
475
|
+
dest_fs: LocalStore | FsspecStore,
|
|
476
|
+
dest_path: str,
|
|
477
|
+
):
|
|
478
|
+
src_mapper = _get_mapper(src_fs, src_path)
|
|
479
|
+
dest_mapper = _get_mapper(dest_fs, dest_path)
|
|
480
|
+
for key in src_mapper.keys():
|
|
481
|
+
dest_mapper[key] = src_mapper[key]
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _zarr_python_copy(src_group: zarr.Group, dest_group: zarr.Group):
|
|
485
|
+
# Copy attributes
|
|
486
|
+
dest_group.attrs.put(src_group.attrs.asdict())
|
|
487
|
+
# Copy arrays
|
|
488
|
+
for name, array in src_group.arrays():
|
|
489
|
+
if array.metadata.zarr_format == 2:
|
|
490
|
+
spec = AnyArraySpecV2.from_zarr(array)
|
|
491
|
+
else:
|
|
492
|
+
spec = AnyArraySpecV3.from_zarr(array)
|
|
493
|
+
dst = spec.to_zarr(
|
|
494
|
+
store=dest_group.store,
|
|
495
|
+
path=f"{dest_group.path}/{name}",
|
|
496
|
+
overwrite=True,
|
|
497
|
+
)
|
|
498
|
+
if array.ndim > 0:
|
|
499
|
+
dask_array = da.from_zarr(array)
|
|
500
|
+
da.to_zarr(dask_array, dst, overwrite=False)
|
|
501
|
+
# Copy subgroups
|
|
502
|
+
for name, subgroup in src_group.groups():
|
|
503
|
+
dest_subgroup = dest_group.create_group(name, overwrite=True)
|
|
504
|
+
_zarr_python_copy(subgroup, dest_subgroup)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def copy_group(src_group: zarr.Group, dest_group: zarr.Group):
|
|
508
|
+
if src_group.metadata.zarr_format != dest_group.metadata.zarr_format:
|
|
509
|
+
raise NgioValueError(
|
|
510
|
+
"Different Zarr format versions between source and destination, "
|
|
511
|
+
"cannot copy."
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
if not is_group_listable(src_group):
|
|
515
|
+
raise NgioValueError("Source group is not listable, cannot copy.")
|
|
516
|
+
|
|
517
|
+
if dest_group.read_only:
|
|
518
|
+
raise NgioValueError("Destination group is read only, cannot copy.")
|
|
519
|
+
if isinstance(src_group.store, LocalStore | FsspecStore) and isinstance(
|
|
520
|
+
dest_group.store, LocalStore | FsspecStore
|
|
521
|
+
):
|
|
522
|
+
_fsspec_copy(src_group.store, src_group.path, dest_group.store, dest_group.path)
|
|
523
|
+
return
|
|
524
|
+
warnings.warn(
|
|
525
|
+
"Fsspec copy not possible, falling back to Zarr Python API for the copy. "
|
|
526
|
+
"This will preserve some tabular data non-zarr native (parquet, and csv), "
|
|
527
|
+
"and it will be slower for large datasets.",
|
|
528
|
+
UserWarning,
|
|
529
|
+
stacklevel=2,
|
|
530
|
+
)
|
|
531
|
+
_zarr_python_copy(src_group, dest_group)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ngio
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.0a3
|
|
4
4
|
Summary: Next Generation file format IO
|
|
5
5
|
Project-URL: homepage, https://github.com/BioVisionCenter/ngio
|
|
6
6
|
Project-URL: repository, https://github.com/BioVisionCenter/ngio
|
|
@@ -31,7 +31,6 @@ Requires-Dist: pydantic
|
|
|
31
31
|
Requires-Dist: requests
|
|
32
32
|
Requires-Dist: zarr>3
|
|
33
33
|
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: devtools; extra == 'dev'
|
|
35
34
|
Requires-Dist: matplotlib; extra == 'dev'
|
|
36
35
|
Requires-Dist: mypy; extra == 'dev'
|
|
37
36
|
Requires-Dist: napari; extra == 'dev'
|
|
@@ -60,8 +59,13 @@ Requires-Dist: rich; extra == 'docs'
|
|
|
60
59
|
Requires-Dist: scikit-image; extra == 'docs'
|
|
61
60
|
Requires-Dist: tabulate; extra == 'docs'
|
|
62
61
|
Provides-Extra: test
|
|
62
|
+
Requires-Dist: boto; extra == 'test'
|
|
63
|
+
Requires-Dist: devtools; extra == 'test'
|
|
64
|
+
Requires-Dist: moto[server]; extra == 'test'
|
|
63
65
|
Requires-Dist: pytest; extra == 'test'
|
|
64
66
|
Requires-Dist: pytest-cov; extra == 'test'
|
|
67
|
+
Requires-Dist: pytest-httpserver; extra == 'test'
|
|
68
|
+
Requires-Dist: s3fs; extra == 'test'
|
|
65
69
|
Requires-Dist: scikit-image; extra == 'test'
|
|
66
70
|
Description-Content-Type: text/markdown
|
|
67
71
|
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
ngio/__init__.py,sha256=
|
|
2
|
-
ngio/common/__init__.py,sha256=
|
|
1
|
+
ngio/__init__.py,sha256=A7o63AVFPNbhOpePPOb-BZvszc52tNqp2NW277h3rAo,1433
|
|
2
|
+
ngio/common/__init__.py,sha256=F3zAHQIhwig1xUA-SpmFVRtMeOrEj926-nHWhj-wS6c,684
|
|
3
3
|
ngio/common/_dimensions.py,sha256=w8PYgyWxA8hgJETjFbw5CXf7WrasCL5FbzgfL1in86M,11361
|
|
4
|
-
ngio/common/_masking_roi.py,sha256=
|
|
5
|
-
ngio/common/_pyramid.py,sha256=
|
|
6
|
-
ngio/common/_roi.py,sha256=
|
|
4
|
+
ngio/common/_masking_roi.py,sha256=YSEZ5nv3-TqMrQ04cI70U9NGx3LSy7Z1_cbw_NE-78k,4831
|
|
5
|
+
ngio/common/_pyramid.py,sha256=vPlxHUWBmfw6BtRoZeA7YNu3ft1YdAzyW1tWG8gA_to,14117
|
|
6
|
+
ngio/common/_roi.py,sha256=UMr7ie7dIZ6gMcM0_AlFMev_PRKxBdzUy_1WKRZ4zQU,11403
|
|
7
7
|
ngio/common/_synt_images_utils.py,sha256=B6uYOW1NyrM06YMR-csca3_YnAAkPRTbvnbLdy9tk9E,3188
|
|
8
8
|
ngio/common/_zoom.py,sha256=U01c-vqXjzZkrpd9Yvs24frVfTls_xPJeeaFCGmUwYI,6727
|
|
9
9
|
ngio/experimental/__init__.py,sha256=3pmBtHi-i8bKjTsvrOJM56ZyRX3Pv_dceCdt88-8COQ,147
|
|
10
10
|
ngio/experimental/iterators/__init__.py,sha256=TECOMGb5PEEZ0yXxt8pqIGvLKG41g_L1fTJU-zGPeV8,488
|
|
11
11
|
ngio/experimental/iterators/_abstract_iterator.py,sha256=7aAoMI-6vYGCMKxO3M10WpuBMTdqX4zr0K-TutAOp88,13195
|
|
12
|
-
ngio/experimental/iterators/_feature.py,sha256=
|
|
12
|
+
ngio/experimental/iterators/_feature.py,sha256=OnqeSP-UMWku7AIBcutsDsAWqnFqPrjCJiMvMGM96fk,6714
|
|
13
13
|
ngio/experimental/iterators/_image_processing.py,sha256=cM7sL7xgdcjSOKAu-6367Aov89o6wgiJ_wqCGkU2Bsw,5091
|
|
14
14
|
ngio/experimental/iterators/_mappers.py,sha256=VVVsjems57wJUnWeufUFcgqa23k7VPeFL4Nc04HVw4o,1399
|
|
15
|
-
ngio/experimental/iterators/_rois_utils.py,sha256=
|
|
15
|
+
ngio/experimental/iterators/_rois_utils.py,sha256=5foGjt3qrACNrO29LlvSUbJ4yfI0z6MhU2oVCzEU214,4363
|
|
16
16
|
ngio/experimental/iterators/_segmentation.py,sha256=xzotGvTn04HPeMeXZ_URnQqWco6d2lH6Ng6vkCUh9NM,9153
|
|
17
17
|
ngio/hcs/__init__.py,sha256=G8j9vD-liLeB_UeGtKYIgshWvJnUA6ks9GwjvWBLdHs,357
|
|
18
|
-
ngio/hcs/_plate.py,sha256
|
|
18
|
+
ngio/hcs/_plate.py,sha256=-8Ffx07s73jKTDKBj11yrJTtfNfwBP0DwUbzJQKPCWM,43503
|
|
19
19
|
ngio/images/__init__.py,sha256=9Whvt7GTiCgT_vXaEEqGnDaY1-UsRk3dhLTv091F_g4,1211
|
|
20
|
-
ngio/images/_abstract_image.py,sha256=
|
|
21
|
-
ngio/images/
|
|
22
|
-
ngio/images/
|
|
23
|
-
ngio/images/_image.py,sha256=
|
|
24
|
-
ngio/images/_label.py,sha256=
|
|
20
|
+
ngio/images/_abstract_image.py,sha256=xCpI--oe9B81s3A99JGSYNiZzN-DaYheUJoP19n9Uwg,31915
|
|
21
|
+
ngio/images/_create_synt_container.py,sha256=Cvg_J0KSxK0PH8IBzlKLIcCwH2vRTuBj-nZo5uOKXXk,5182
|
|
22
|
+
ngio/images/_create_utils.py,sha256=hXVbFM8D_0mZTfBAhcZiuGX2lLXSJCep8THuxpH4d4E,14374
|
|
23
|
+
ngio/images/_image.py,sha256=YSO6dzEKdrgh4AE-cE4j_hxsU4QBpxUc_j6-Xpq5YqM,33664
|
|
24
|
+
ngio/images/_label.py,sha256=Y7Y0yLgoRUJtRzqgNPWc1yZ3HgDu4bbXbwhQJlnPKA8,14047
|
|
25
25
|
ngio/images/_masked_image.py,sha256=YhbBzgPZMav6rX0WYue1BaxAzEIsfaQrxUIOK6ZWZcw,18848
|
|
26
|
-
ngio/images/_ome_zarr_container.py,sha256=
|
|
26
|
+
ngio/images/_ome_zarr_container.py,sha256=_fI23IHb1iuhgLUqWdVvOwbFKtFpLY3J6y_k_i6mkjA,46044
|
|
27
27
|
ngio/images/_table_ops.py,sha256=jFv_AMqoB4JBpoWsMtZppZVW7dAOC_u-JpfNm8b33kY,15292
|
|
28
28
|
ngio/io_pipes/__init__.py,sha256=arW_7GWzZs82kPNKdm_6B1sIDFV0lWwp-ZaORr9Q1FQ,2412
|
|
29
|
-
ngio/io_pipes/_io_pipes.py,sha256=
|
|
30
|
-
ngio/io_pipes/_io_pipes_masked.py,sha256=
|
|
31
|
-
ngio/io_pipes/_io_pipes_roi.py,sha256=
|
|
32
|
-
ngio/io_pipes/_io_pipes_types.py,sha256=
|
|
33
|
-
ngio/io_pipes/_match_shape.py,sha256=
|
|
29
|
+
ngio/io_pipes/_io_pipes.py,sha256=l85mmjj1l0uYU3qzsSHg9l8cMIEevInm_MTD-8MlXgw,10603
|
|
30
|
+
ngio/io_pipes/_io_pipes_masked.py,sha256=uvfNIuW8prWux3fZ-a25l44zDfZW4qBaXZEm0TkhloA,16916
|
|
31
|
+
ngio/io_pipes/_io_pipes_roi.py,sha256=HJHlItx2nsFYcE4OjvVR-W0lFMMK8CcyYK23dKUrP8w,4472
|
|
32
|
+
ngio/io_pipes/_io_pipes_types.py,sha256=PcRdnjBJIsXcDT1_dbH2LZiH6d3z6D7y48cmybyZCXk,1358
|
|
33
|
+
ngio/io_pipes/_match_shape.py,sha256=e3cQM6BI8n2YODmU6wgnFoaguwRVZw9bk5eafzmcTBM,13185
|
|
34
34
|
ngio/io_pipes/_ops_axes.py,sha256=Geg4ZXxB0njWWopX9YeiwRJJ9Ef2GKfG0NIUafOmi2c,10043
|
|
35
35
|
ngio/io_pipes/_ops_slices.py,sha256=hHMIOQ_niUSK9uFl8P2-10dP_K4GX3Do6vivN4fGRE0,14520
|
|
36
|
-
ngio/io_pipes/_ops_slices_utils.py,sha256=
|
|
36
|
+
ngio/io_pipes/_ops_slices_utils.py,sha256=mps_I0eTI4gdBVM9MCKsd8rCyefdo9bIK9fEmqwr23E,6633
|
|
37
37
|
ngio/io_pipes/_ops_transforms.py,sha256=uITs6v6sZ7DQ_Hpw3JdX8MuPOzir-bihvGzY84Qn4wY,2934
|
|
38
38
|
ngio/io_pipes/_zoom_transform.py,sha256=WBY1tO6_Qhf8FaDujfTdipuuqFf7PSi204wx5VKKs88,6884
|
|
39
|
-
ngio/ome_zarr_meta/__init__.py,sha256=
|
|
40
|
-
ngio/ome_zarr_meta/_meta_handlers.py,sha256=
|
|
39
|
+
ngio/ome_zarr_meta/__init__.py,sha256=3_QbSt-Ta8MXL8R_tGmgrYMGePgawI96Hc5msrbZCgM,1139
|
|
40
|
+
ngio/ome_zarr_meta/_meta_handlers.py,sha256=70R0waA1BNX7yNOZnvVuNBeIIxkzGQN6xEKebFpYFx0,13673
|
|
41
41
|
ngio/ome_zarr_meta/ngio_specs/__init__.py,sha256=U2FqZR91Ob2N6CqKdyw-_Ll-wMgmGuPZGVRCr6wuRFY,1698
|
|
42
|
-
ngio/ome_zarr_meta/ngio_specs/_axes.py,sha256=
|
|
42
|
+
ngio/ome_zarr_meta/ngio_specs/_axes.py,sha256=CY63mWf7_ALoi7o_1QDVK1lAG56xN0gvgwfcuNRmMIg,16446
|
|
43
43
|
ngio/ome_zarr_meta/ngio_specs/_channels.py,sha256=TDxIy-yVc2YaWPIFJRYnYwZbA8O5Ee_OiWppHYrEdpU,16647
|
|
44
|
-
ngio/ome_zarr_meta/ngio_specs/_dataset.py,sha256=
|
|
45
|
-
ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py,sha256=
|
|
46
|
-
ngio/ome_zarr_meta/ngio_specs/_ngio_image.py,sha256=
|
|
44
|
+
ngio/ome_zarr_meta/ngio_specs/_dataset.py,sha256=5YdAplk90koX3vjoIJimms-CJYxt095rJ9YagZSQg88,2872
|
|
45
|
+
ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py,sha256=_KM9Ql-sBL1zV_OLhDiFBj9QhEjwVcW8W5sMUA7xtzM,16944
|
|
46
|
+
ngio/ome_zarr_meta/ngio_specs/_ngio_image.py,sha256=kjlC2Otz7z__ojrSHYsFJaQW_OKNQoevVlAn1cZYNJs,14442
|
|
47
47
|
ngio/ome_zarr_meta/ngio_specs/_pixel_size.py,sha256=4VF1djY9T5tp6GCJXppFrUJwALI1XgIm0imoM5rNvdE,3876
|
|
48
|
-
ngio/ome_zarr_meta/v04/__init__.py,sha256=
|
|
48
|
+
ngio/ome_zarr_meta/v04/__init__.py,sha256=WHT8RFLxqx9QBtm8wwg8tZ_EBuC1l8PG_JxwFl02Tk4,577
|
|
49
49
|
ngio/ome_zarr_meta/v04/_custom_models.py,sha256=5GxiDERvLuvq4QvApcA6EiKLS6hLFX1R0R_9rSaa85A,530
|
|
50
|
-
ngio/ome_zarr_meta/v04/
|
|
51
|
-
ngio/ome_zarr_meta/v05/__init__.py,sha256=
|
|
50
|
+
ngio/ome_zarr_meta/v04/_v04_spec.py,sha256=qmp9xAfA2z9UErrs4xvS0qmO5G8Dli-CenAL3rO4TFo,14348
|
|
51
|
+
ngio/ome_zarr_meta/v05/__init__.py,sha256=zGWjeVCXQX7ezs4hEmmLrgOcQNhR6nZJ4LXKpj1oCfE,577
|
|
52
52
|
ngio/ome_zarr_meta/v05/_custom_models.py,sha256=ZN3bE9nwx4y3tElhsYafI4S2zp_WzdkQKcyuuBiaXXo,530
|
|
53
|
-
ngio/ome_zarr_meta/v05/
|
|
53
|
+
ngio/ome_zarr_meta/v05/_v05_spec.py,sha256=JBZrm3BRZVbxHB29UFsfo3tuedhpMdy8KuWf4vDL3_g,15134
|
|
54
54
|
ngio/resources/__init__.py,sha256=4E4TXTNYEgRHt26C1XcC4pPobJJsmZRYm1Ml4uAuAkE,1664
|
|
55
55
|
ngio/resources/resource_model.py,sha256=eE1m0dyk-2psPC4X8Ifyan524QHUOd52TEQdvoU0m8I,861
|
|
56
56
|
ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png,sha256=g3QmxQdmeciAtBe5cTCRfR6yw3keG9cBYfjizMo6EGo,11890
|
|
@@ -58,32 +58,31 @@ ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png,sha
|
|
|
58
58
|
ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg,sha256=82lejQAIokj5w9g-qqhysDTWpHtNvJTkdURG_BjqIxQ,37743
|
|
59
59
|
ngio/tables/__init__.py,sha256=_BV3sclNMLITu_J8_3DkkUrCB6Kro0HzeWLDCD1ivKM,877
|
|
60
60
|
ngio/tables/_abstract_table.py,sha256=rwGa47TzbFmosucBWVfFq6JEXtgGvOdUVtU9DIelV88,8204
|
|
61
|
-
ngio/tables/_tables_container.py,sha256=
|
|
61
|
+
ngio/tables/_tables_container.py,sha256=DV_uX_bf3kCcmWLFKjY9FhKX8iBewbHkZSECqKjsCIw,12333
|
|
62
62
|
ngio/tables/backends/__init__.py,sha256=MwSRXNF1rWQBFOTDA_vT3oGoNZpviVgytsL5Txnu08I,1619
|
|
63
63
|
ngio/tables/backends/_abstract_backend.py,sha256=M1ogsBpWBiQMV65YweZhA845PAtkzG2BsZCPN_7Xp8U,7613
|
|
64
|
-
ngio/tables/backends/_anndata.py,sha256=
|
|
65
|
-
ngio/tables/backends/_anndata_utils.py,sha256=
|
|
66
|
-
ngio/tables/backends/_csv.py,sha256=
|
|
67
|
-
ngio/tables/backends/_json.py,sha256=
|
|
68
|
-
ngio/tables/backends/
|
|
69
|
-
ngio/tables/backends/
|
|
64
|
+
ngio/tables/backends/_anndata.py,sha256=brJaTZCBe_ipexWbGQgFM2-B30sRKI1kxNLuSTcY2uE,4546
|
|
65
|
+
ngio/tables/backends/_anndata_utils.py,sha256=PoHiLkGeDhBgPsEMJi9QH-NejHmfrfILcwj1CYubyCM,3095
|
|
66
|
+
ngio/tables/backends/_csv.py,sha256=iZJNLHOXYysV_2iq6Lmekq0XXYsVE7OYrKz2HP2TU9w,479
|
|
67
|
+
ngio/tables/backends/_json.py,sha256=A4iaKOIc5Q_XKDOm321QNqAN4DAOuA-dEinnfTlk1Fk,3091
|
|
68
|
+
ngio/tables/backends/_parquet.py,sha256=Fi3VZlTH5UTykk0eqr43_e_Qt_GQcEN-3pHK07XFBwk,503
|
|
69
|
+
ngio/tables/backends/_py_arrow_backends.py,sha256=lxxI5TN4lFYwpsjD1g1xAxEt4lZ9Mu7YW3-m3nIuo2g,8587
|
|
70
70
|
ngio/tables/backends/_table_backends.py,sha256=ksP2NAosXZkNMZf-IMrLx7bjQgp_eKfvPYK4vMdT1A8,7250
|
|
71
71
|
ngio/tables/backends/_utils.py,sha256=t4dLXSPxx2AnJvVtj0GIwrLoO11h4Ges6U7hj4md0hY,19730
|
|
72
72
|
ngio/tables/v1/__init__.py,sha256=Wr1_9RZFpaN8FYMTnxT9Yjkw4AS7y9FMWailmB_uj5g,617
|
|
73
73
|
ngio/tables/v1/_condition_table.py,sha256=T0Uq5BKkmMoEspt_Rx0U99Ow6S9GAMZDHqvUO5obCAM,1780
|
|
74
74
|
ngio/tables/v1/_feature_table.py,sha256=n9uMHwoBh-_dlOhUXCFbmAjXFVXncNCR3SjE2qzXI68,3821
|
|
75
75
|
ngio/tables/v1/_generic_table.py,sha256=1ktJHeuv7U1g5Z8PFUuTkCjOzcYMQd8xegKHKUedJB8,1240
|
|
76
|
-
ngio/tables/v1/_roi_table.py,sha256=
|
|
76
|
+
ngio/tables/v1/_roi_table.py,sha256=DuKJlDmtQtLOfL0g4CSdncfm4hBsKWG6F6fkMUpt4Nk,17821
|
|
77
77
|
ngio/transforms/__init__.py,sha256=JA0-Ui7skbXkm9ofN-AEhU1FTLutkMkwTdVD-310frQ,113
|
|
78
78
|
ngio/transforms/_zoom.py,sha256=otyE-vxFnywUJ8U4mHjat-bNG_7_jv62ckTpqDMxyVQ,550
|
|
79
|
-
ngio/utils/__init__.py,sha256=
|
|
79
|
+
ngio/utils/__init__.py,sha256=1eIInpFEEWQtMeGaRhAyVjqSKWUCuksMkx2cqg9D9nI,987
|
|
80
80
|
ngio/utils/_cache.py,sha256=Ey9fgc_BTdMyqg6c80C0CuGDhOafln8-3e_1MQ0MFzw,1283
|
|
81
81
|
ngio/utils/_datasets.py,sha256=6GtxfPkjutNaeg5BHuJDBP0GudvQXHLU6mmHp_o0bGA,5650
|
|
82
82
|
ngio/utils/_errors.py,sha256=pKQ12LUjQLYE1nUawemA5h7HsgznjaSvV1n2PQU33N0,759
|
|
83
83
|
ngio/utils/_fractal_fsspec_store.py,sha256=RdcCFOgHexRKX9zZvJV5RI-5OPc7VOPS6q_IeRxm24I,1548
|
|
84
|
-
ngio/utils/
|
|
85
|
-
ngio/
|
|
86
|
-
ngio-0.5.
|
|
87
|
-
ngio-0.5.
|
|
88
|
-
ngio-0.5.
|
|
89
|
-
ngio-0.5.0a2.dist-info/RECORD,,
|
|
84
|
+
ngio/utils/_zarr_utils.py,sha256=asAmD4FSFspyAfIDB8ISwHLCZYctsKENyCCAQHEfubA,17817
|
|
85
|
+
ngio-0.5.0a3.dist-info/METADATA,sha256=yvzf4IseuuNv8cAvtQLnrWwrdbZYVl422KnEjnQpyQc,6294
|
|
86
|
+
ngio-0.5.0a3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
87
|
+
ngio-0.5.0a3.dist-info/licenses/LICENSE,sha256=UgN_a1QCeNh9rZWfz-wORQFxE3elQzLWPQaoK6N6fxQ,1502
|
|
88
|
+
ngio-0.5.0a3.dist-info/RECORD,,
|