ngio 0.2.2__py3-none-any.whl → 0.2.4__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.
@@ -14,7 +14,7 @@ from pydantic import BaseModel
14
14
 
15
15
  from ngio.common import Roi
16
16
  from ngio.tables._validators import validate_columns
17
- from ngio.tables.backends import ImplementedTableBackends
17
+ from ngio.tables.backends import BackendMeta, ImplementedTableBackends
18
18
  from ngio.utils import NgioValueError, ZarrGroupHandler
19
19
 
20
20
  REQUIRED_COLUMNS = [
@@ -91,12 +91,11 @@ def _rois_to_dataframe(rois: dict[str, Roi], index_key: str) -> pd.DataFrame:
91
91
  return dataframe
92
92
 
93
93
 
94
- class RoiTableV1Meta(BaseModel):
94
+ class RoiTableV1Meta(BackendMeta):
95
95
  """Metadata for the ROI table."""
96
96
 
97
97
  fractal_table_version: Literal["1"] = "1"
98
98
  type: Literal["roi_table"] = "roi_table"
99
- backend: str | None = None
100
99
 
101
100
 
102
101
  class RegionMeta(BaseModel):
@@ -105,12 +104,11 @@ class RegionMeta(BaseModel):
105
104
  path: str
106
105
 
107
106
 
108
- class MaskingRoiTableV1Meta(BaseModel):
107
+ class MaskingRoiTableV1Meta(BackendMeta):
109
108
  """Metadata for the ROI table."""
110
109
 
111
110
  fractal_table_version: Literal["1"] = "1"
112
111
  type: Literal["masking_roi_table"] = "masking_roi_table"
113
- backend: str | None = None
114
112
  region: RegionMeta | None = None
115
113
  instance_key: str = "label"
116
114
 
@@ -191,7 +189,7 @@ class _GenericRoiTableV1(Generic[_roi_meta]):
191
189
  )
192
190
  meta.backend = backend_name
193
191
 
194
- if not backend.implements_dataframe:
192
+ if not backend.implements_pandas:
195
193
  raise NgioValueError(
196
194
  "The backend does not implement the dataframe protocol."
197
195
  )
@@ -201,7 +199,7 @@ class _GenericRoiTableV1(Generic[_roi_meta]):
201
199
  table._meta = meta
202
200
  table._table_backend = backend
203
201
 
204
- dataframe = backend.load_as_dataframe()
202
+ dataframe = backend.load_as_pandas_df()
205
203
  dataframe = validate_columns(
206
204
  dataframe,
207
205
  required_columns=REQUIRED_COLUMNS,
@@ -229,13 +227,18 @@ class _GenericRoiTableV1(Generic[_roi_meta]):
229
227
  """List all ROIs in the table."""
230
228
  return list(self._rois.values())
231
229
 
232
- def add(self, roi: Roi | Iterable[Roi]) -> None:
233
- """Append ROIs to the current table."""
230
+ def add(self, roi: Roi | Iterable[Roi], overwrite: bool = False) -> None:
231
+ """Append ROIs to the current table.
232
+
233
+ Args:
234
+ roi: A single ROI or a list of ROIs to add to the table.
235
+ overwrite: If True, overwrite existing ROIs with the same name.
236
+ """
234
237
  if isinstance(roi, Roi):
235
238
  roi = [roi]
236
239
 
237
240
  for _roi in roi:
238
- if _roi.name in self._rois:
241
+ if not overwrite and _roi.name in self._rois:
239
242
  raise NgioValueError(f"ROI {_roi.name} already exists in the table.")
240
243
  self._rois[_roi.name] = _roi
241
244
 
@@ -253,8 +256,8 @@ class _GenericRoiTableV1(Generic[_roi_meta]):
253
256
  required_columns=REQUIRED_COLUMNS,
254
257
  optional_columns=OPTIONAL_COLUMNS,
255
258
  )
256
- self._table_backend.write_from_dataframe(
257
- dataframe, metadata=self._meta.model_dump(exclude_none=True)
259
+ self._table_backend.write(
260
+ dataframe, metadata=self._meta.model_dump(exclude_none=True), mode="pandas"
258
261
  )
259
262
 
260
263
 
ngio/utils/_zarr_utils.py CHANGED
@@ -8,7 +8,7 @@ import fsspec
8
8
  import zarr
9
9
  from filelock import BaseFileLock, FileLock
10
10
  from zarr.errors import ContainsGroupError, GroupNotFoundError
11
- from zarr.storage import DirectoryStore, FSStore, Store
11
+ from zarr.storage import DirectoryStore, FSStore, MemoryStore, Store, StoreLike
12
12
 
13
13
  from ngio.utils import NgioFileExistsError, NgioFileNotFoundError, NgioValueError
14
14
  from ngio.utils._errors import NgioError
@@ -17,7 +17,9 @@ AccessModeLiteral = Literal["r", "r+", "w", "w-", "a"]
17
17
  # StoreLike is more restrictive than it could be
18
18
  # but to make sure we can handle the store correctly
19
19
  # we need to be more restrictive
20
- NgioSupportedStore = str | Path | fsspec.mapping.FSMap | FSStore | DirectoryStore
20
+ NgioSupportedStore = (
21
+ str | Path | fsspec.mapping.FSMap | FSStore | DirectoryStore | MemoryStore
22
+ )
21
23
  GenericStore = Store | NgioSupportedStore
22
24
  StoreOrGroup = GenericStore | zarr.Group
23
25
 
@@ -48,9 +50,7 @@ def _check_group(group: zarr.Group, mode: AccessModeLiteral) -> zarr.Group:
48
50
  return group
49
51
 
50
52
 
51
- def open_group_wrapper(
52
- store: StoreOrGroup, mode: AccessModeLiteral
53
- ) -> tuple[zarr.Group, NgioSupportedStore]:
53
+ def open_group_wrapper(store: StoreOrGroup, mode: AccessModeLiteral) -> zarr.Group:
54
54
  """Wrapper around zarr.open_group with some additional checks.
55
55
 
56
56
  Args:
@@ -62,18 +62,11 @@ def open_group_wrapper(
62
62
  """
63
63
  if isinstance(store, zarr.Group):
64
64
  group = _check_group(store, mode)
65
- if hasattr(group, "store_path"):
66
- _store = group.store_path
67
- if isinstance(group.store, DirectoryStore):
68
- _store = group.store.path
69
- else:
70
- _store = group.store
71
-
72
- _store = _check_store(_store)
73
- return group, _store
65
+ _check_store(group.store)
66
+ return group
74
67
 
75
68
  try:
76
- store = _check_store(store)
69
+ _check_store(store)
77
70
  group = zarr.open_group(store=store, mode=mode)
78
71
 
79
72
  except ContainsGroupError as e:
@@ -84,7 +77,7 @@ def open_group_wrapper(
84
77
  except GroupNotFoundError as e:
85
78
  raise NgioFileNotFoundError(f"No Zarr group found at {store}") from e
86
79
 
87
- return group, store
80
+ return group
88
81
 
89
82
 
90
83
  class ZarrGroupHandler:
@@ -119,27 +112,29 @@ class ZarrGroupHandler:
119
112
  "If you want to use the lock mechanism, you should not use the cache."
120
113
  )
121
114
 
122
- _group, _store = open_group_wrapper(store, mode)
115
+ group = open_group_wrapper(store, mode)
116
+ _store = group.store
123
117
 
124
118
  # Make sure the cache is set in the attrs
125
119
  # in the same way as the cache in the handler
126
- _group.attrs.cache = cache
120
+ group.attrs.cache = cache
127
121
 
128
122
  if parallel_safe:
129
- if not isinstance(_store, str | Path):
123
+ if not isinstance(_store, DirectoryStore):
130
124
  raise NgioValueError(
131
- "The store needs to be a path to use the lock mechanism."
125
+ "The store needs to be a DirectoryStore to use the lock mechanism. "
126
+ f"Instead, got {_store.__class__.__name__}."
132
127
  )
133
- self._lock_path = f"{_store}.lock"
128
+ store_path = Path(_store.path) / group.path
129
+ self._lock_path = store_path.with_suffix(".lock")
134
130
  self._lock = FileLock(self._lock_path, timeout=10)
135
131
 
136
132
  else:
137
133
  self._lock_path = None
138
134
  self._lock = None
139
135
 
140
- self._group = _group
136
+ self._group = group
141
137
  self._mode = mode
142
- self._store = _store
143
138
  self.use_cache = cache
144
139
  self._parallel_safe = parallel_safe
145
140
  self._cache = {}
@@ -148,19 +143,23 @@ class ZarrGroupHandler:
148
143
  def __repr__(self) -> str:
149
144
  """Return a string representation of the handler."""
150
145
  return (
151
- f"ZarrGroupHandler(full_path={self.full_path}, mode={self.mode}, "
146
+ f"ZarrGroupHandler(full_url={self.full_url}, mode={self.mode}, "
152
147
  f"cache={self.use_cache}"
153
148
  )
154
149
 
155
150
  @property
156
- def store(self) -> NgioSupportedStore:
151
+ def store(self) -> StoreLike:
157
152
  """Return the store of the group."""
158
- return self._store
153
+ return self.group.store
159
154
 
160
155
  @property
161
- def full_path(self) -> str:
156
+ def full_url(self) -> str | None:
162
157
  """Return the store path."""
163
- return f"{self._store}/{self._group.path}"
158
+ if isinstance(self.store, DirectoryStore | FSStore):
159
+ _store_path = str(self.store.path)
160
+ _store_path = _store_path.rstrip("/")
161
+ return f"{self.store.path}/{self._group.path}"
162
+ return None
164
163
 
165
164
  @property
166
165
  def mode(self) -> AccessModeLiteral:
@@ -275,17 +274,25 @@ class ZarrGroupHandler:
275
274
  self,
276
275
  path: str,
277
276
  create_mode: bool = False,
277
+ overwrite: bool = False,
278
278
  ) -> zarr.Group:
279
279
  """Get a group from the group.
280
280
 
281
281
  Args:
282
282
  path (str): The path to the group.
283
283
  create_mode (bool): If True, create the group if it does not exist.
284
+ overwrite (bool): If True, overwrite the group if it exists.
284
285
 
285
286
  Returns:
286
287
  zarr.Group: The Zarr group.
287
288
 
288
289
  """
290
+ if overwrite and not create_mode:
291
+ raise NgioValueError("Cannot overwrite a group without create_mode=True.")
292
+
293
+ if overwrite:
294
+ return self.create_group(path, overwrite=overwrite)
295
+
289
296
  group = self._obj_get(path)
290
297
  if isinstance(group, zarr.Group):
291
298
  return group
@@ -361,9 +368,15 @@ class ZarrGroupHandler:
361
368
  def derive_handler(
362
369
  self,
363
370
  path: str,
371
+ overwrite: bool = False,
364
372
  ) -> "ZarrGroupHandler":
365
- """Derive a new handler from the current handler."""
366
- group = self.get_group(path, create_mode=True)
373
+ """Derive a new handler from the current handler.
374
+
375
+ Args:
376
+ path (str): The path to the group.
377
+ overwrite (bool): If True, overwrite the group if it exists.
378
+ """
379
+ group = self.get_group(path, create_mode=True, overwrite=overwrite)
367
380
  return ZarrGroupHandler(
368
381
  store=group,
369
382
  cache=self.use_cache,
@@ -375,10 +388,11 @@ class ZarrGroupHandler:
375
388
  def safe_derive_handler(
376
389
  self,
377
390
  path: str,
391
+ overwrite: bool = False,
378
392
  ) -> tuple[bool, "ZarrGroupHandler | NgioError"]:
379
393
  """Derive a new handler from the current handler."""
380
394
  try:
381
- return True, self.derive_handler(path)
395
+ return True, self.derive_handler(path, overwrite=overwrite)
382
396
  except NgioError as e:
383
397
  return False, e
384
398
 
@@ -393,7 +407,7 @@ class ZarrGroupHandler:
393
407
  )
394
408
  if n_skipped > 0:
395
409
  raise NgioValueError(
396
- f"Error copying group to {handler.full_path}, "
410
+ f"Error copying group to {handler.full_url}, "
397
411
  f"#{n_skipped} files where skipped."
398
412
  )
399
413
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngio
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Next Generation file format IO
5
5
  Project-URL: homepage, https://github.com/lorenzocerrone/ngio
6
6
  Project-URL: repository, https://github.com/lorenzocerrone/ngio
@@ -23,7 +23,9 @@ Requires-Dist: filelock
23
23
  Requires-Dist: numpy
24
24
  Requires-Dist: ome-zarr-models
25
25
  Requires-Dist: pandas>=1.2.0
26
+ Requires-Dist: polars
26
27
  Requires-Dist: pooch
28
+ Requires-Dist: pyarrow
27
29
  Requires-Dist: pydantic
28
30
  Requires-Dist: requests
29
31
  Requires-Dist: xarray
@@ -5,19 +5,19 @@ ngio/common/_axes_transforms.py,sha256=kWU0M5erNmgWBXdu5LNv-tLW3jqkT00MMYX7cz-ky
5
5
  ngio/common/_common_types.py,sha256=OkAYNSNjZkixL1MI-HPBVuXamheFBr862uJ4PvTxmhk,129
6
6
  ngio/common/_dimensions.py,sha256=UV2XulWaROb3Y2f4fv27ZkTIu-MoS53U26aDkrv-_lk,3900
7
7
  ngio/common/_masking_roi.py,sha256=-o6meGP17iTXEbkO9aGh1VX2drkc2laIcRJvCy_pRRM,4919
8
- ngio/common/_pyramid.py,sha256=8xTFnrKcldYXSnBqcVtH1E6hzS9_et8NdoPo8zfTCjc,7308
8
+ ngio/common/_pyramid.py,sha256=SJzPauuduuqcm9B7nFCJhMTzIg6Knjsnp4CY4lN61Is,7411
9
9
  ngio/common/_roi.py,sha256=dq1iVT8-G_zWuxcYWJeHfviBSbPgsyKUcDL3Vg6jx6I,5122
10
10
  ngio/common/_slicer.py,sha256=AKpwXRncOmF9nhjKYma0C_41WqAgSv860beKGx-aw-0,3075
11
11
  ngio/common/_zoom.py,sha256=KsURa5VuixmpbAAY5-6obmuQV8vfiHKZqBxZDXvchpM,5473
12
12
  ngio/hcs/__init__.py,sha256=5oAYT_454WGhms97kxo8gS4_rKHtFf_x-9uu8M_VeVM,356
13
- ngio/hcs/plate.py,sha256=Upmq8-1zi0rWtsTVHsZYAma6EwvbgaEF54nHlcL5PBU,26219
13
+ ngio/hcs/plate.py,sha256=zmhHXRsoc4s_-bPccYCH9rqoYg419j-T-NF-OojXRZM,30509
14
14
  ngio/images/__init__.py,sha256=DYbXAdBgPxyjBRdJrWvU0UnBRI0gUMmx9KaJ-Bucvz4,533
15
- ngio/images/abstract_image.py,sha256=oR0CeWUdQJkJ8bbI4kegX3xFIRcR3x5F_yaT4Z-mbag,10226
15
+ ngio/images/abstract_image.py,sha256=8PNQPZjiDz-pcTFXSJAVw7nUr4yL_iRwqDEUTKkAnp0,10266
16
16
  ngio/images/create.py,sha256=XYn30m_2OSZeHHASYHc3eK9u_gZIYy9wo6mGdRGaq5c,9473
17
- ngio/images/image.py,sha256=FSlYvwOk3Fz4ywUZwWcLeCMWLMB4wQ-U0lCGYomVI-o,16108
18
- ngio/images/label.py,sha256=bmwWuOfZWApjttL_CspiNg-j728ODF1i2zKtKIP_u6k,10120
17
+ ngio/images/image.py,sha256=mKLIR2DGQUWtQbf3my2fh0bEPkbbsabUoge_XJPhfWE,17824
18
+ ngio/images/label.py,sha256=qgbBHFPGYpUR2fHf1OiXZh4sn0FgCeeWwH1F4SrOM1c,10460
19
19
  ngio/images/masked_image.py,sha256=IBo8x2jHyXBXn7ORo8fSiwBPjV_1JOTb_vatjKNxbJ0,8531
20
- ngio/images/ome_zarr_container.py,sha256=mEKOtZtZkJbMG02ne0pRIKtlFoi8B1QV8j9WSXDavRs,28705
20
+ ngio/images/ome_zarr_container.py,sha256=-9bbzNtsVAjU1X_h78Lr5LOV0JXtOiWVWqmt3DFpXus,28927
21
21
  ngio/ome_zarr_meta/__init__.py,sha256=oZ8PEsWM7U0KwzpsnvVfX9k4UfuTz5sZ8B6B9eY5hyY,1193
22
22
  ngio/ome_zarr_meta/_meta_handlers.py,sha256=BLvYt5PONYrWkEb2XgEiAXR_OX9rfeX_C0eEqen0jQA,25549
23
23
  ngio/ome_zarr_meta/ngio_specs/__init__.py,sha256=05NQukZG0nNvjzf8AKWGu7PhjhQcImGSAOK3D3Bg-Js,1786
@@ -31,25 +31,27 @@ ngio/ome_zarr_meta/v04/__init__.py,sha256=dJRzzxyYc81kf-0Hip_bqvbdManaM8XTdQX2me
31
31
  ngio/ome_zarr_meta/v04/_custom_models.py,sha256=5GxiDERvLuvq4QvApcA6EiKLS6hLFX1R0R_9rSaa85A,530
32
32
  ngio/ome_zarr_meta/v04/_v04_spec_utils.py,sha256=05tEr2eEP_XVIfBMOAWLT7lzJV4KS5eYrpK8l94tn3w,15876
33
33
  ngio/tables/__init__.py,sha256=gxvNvAgLePgrngOm06H5qU50Axc6boeIs5iCfrmqTi0,609
34
- ngio/tables/_validators.py,sha256=RzF09YrlUSb-xdVlosuU-_BaLswtvhpduHfzLaWu2mY,6468
35
- ngio/tables/tables_container.py,sha256=elOTjpRuC_zsRuZmGeBWfMl2y6OLp2g6FmOe__xLsWE,9845
36
- ngio/tables/backends/__init__.py,sha256=NlOXmZXDA1kOCVONUyo_aqSjdMHmfqk-3xuKBLXpaUM,217
37
- ngio/tables/backends/_abstract_backend.py,sha256=JlaHqLjDm2uVnXFWxsHMojNeKo3leg6uauzoFMSF5O4,2256
38
- ngio/tables/backends/_anndata_utils.py,sha256=zo85K0bwgyQKBj8hQASfOSWugdd4BPUTyT6_tRO58fI,6917
39
- ngio/tables/backends/_anndata_v1.py,sha256=hhEwoBybWFDHqajOk_JEazniYmhAlCehOYTtmcYJfyY,2615
40
- ngio/tables/backends/_json_v1.py,sha256=9ZC0d_tMiiugm-VXDL3sRFfESzSnnY9kAtSxoXYV-dY,1910
41
- ngio/tables/backends/_table_backends.py,sha256=XXtnZqVDbMqFkbUkHbf9IBNz4teCouLpa5fWgEgVtfg,3269
34
+ ngio/tables/_validators.py,sha256=7Eqln9j1tWmNakSCnryy2q39SbTuk6V31O2iMByTWWw,3363
35
+ ngio/tables/tables_container.py,sha256=DFlz_6ts5p0lHkm6NMBtKZget30OicCYHrLbiG4xU1s,10188
36
+ ngio/tables/backends/__init__.py,sha256=Y9MIFefwUaSmhhv5FDZFn7jBdYXDML2FovO-uK-vxgI,936
37
+ ngio/tables/backends/_abstract_backend.py,sha256=--4qeVHs4YRo5i5XsREmDm6BxMsQ5tIqc6XZ810GJno,8776
38
+ ngio/tables/backends/_anndata_utils.py,sha256=DBWIcR0btnH-DIvDvzlcnMXoYhhtXc9DstryiOP0Qsg,3122
39
+ ngio/tables/backends/_anndata_v1.py,sha256=5p_8pWUkYM4rEhm7g-b2yCXt2mmyd6lFisc1ubksAio,2469
40
+ ngio/tables/backends/_csv_v1.py,sha256=o5AGlBH3UQdXx6MP2DtMm7zmXkOwnBBzu5TKk-QgQdI,5847
41
+ ngio/tables/backends/_json_v1.py,sha256=7ALjB2thKTxPBeYOleqn2LNuAy6erp0Nzd-GZ9Xcd4Y,3079
42
+ ngio/tables/backends/_table_backends.py,sha256=NXdH4pxo9XWZ_YxC5RVaw4PTOg-do30c3vlIy8KG4vQ,5831
43
+ ngio/tables/backends/_utils.py,sha256=D0zI8_b6HEBZ2ER-80JvEEh_2-aCC1kpyKJGDPBPHco,14661
42
44
  ngio/tables/v1/__init__.py,sha256=XWi6f_KbjbX45Ku-7uymJRSTVWyFAPAAEp_TXDeZV4s,314
43
- ngio/tables/v1/_feature_table.py,sha256=mR8BwvZfkUT42mQuS0KACW7fTqcsIl6Ckiwg-1UEGpY,5865
44
- ngio/tables/v1/_generic_table.py,sha256=jVW0vv0HMPXZjuP_uAvz-rPsvLQhjo3RfSa2Y9vdg1o,5679
45
- ngio/tables/v1/_roi_table.py,sha256=oE898CUh0ZbtnFXiM05T6VwTwRguv-aVYKkBmSx0Kj0,11296
45
+ ngio/tables/v1/_feature_table.py,sha256=2pZEENyZAmn_Awezu8VLGq90d-J7aXd6Ry8gNNR0wAQ,6104
46
+ ngio/tables/v1/_generic_table.py,sha256=O2fu8k_TjhWEZtJxlU3Uj7ZTZnc75vLlvjw5lweSnd8,5710
47
+ ngio/tables/v1/_roi_table.py,sha256=zGoISSC5q0UHyc0hcxfm2xpw2W6wUocoYctdi5nMmwg,11460
46
48
  ngio/utils/__init__.py,sha256=r3nuLWgp6-cQlS4ODjYSBrfgdTLkCOVke9jKbn1NpkA,1129
47
49
  ngio/utils/_datasets.py,sha256=EdYJHIifpRou4f43dIuiKsdxe4K6FaeS4f1_e_EYrcI,1727
48
50
  ngio/utils/_errors.py,sha256=pKQ12LUjQLYE1nUawemA5h7HsgznjaSvV1n2PQU33N0,759
49
51
  ngio/utils/_fractal_fsspec_store.py,sha256=7qoGLiLi8JQFh9Ej_z5WNwQQuWrujb0f6p9nj8ocsS8,548
50
52
  ngio/utils/_logger.py,sha256=HIuqD_2ShfFGDswBddcouStbKfL0Vz_ah8cAIFGhbS8,888
51
- ngio/utils/_zarr_utils.py,sha256=r075cNpr-JHZ1PaDHX1KlENIGMvLf-WTTYwpODACWm4,12924
52
- ngio-0.2.2.dist-info/METADATA,sha256=tYJbuBZdqsie42NofiB0ALq7NhAaFn-ccwLe-QQ-bY0,5170
53
- ngio-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
- ngio-0.2.2.dist-info/licenses/LICENSE,sha256=UgN_a1QCeNh9rZWfz-wORQFxE3elQzLWPQaoK6N6fxQ,1502
55
- ngio-0.2.2.dist-info/RECORD,,
53
+ ngio/utils/_zarr_utils.py,sha256=qOI-HL2HsfFLCj_yxsTR-aq4oHpSqS9KR13aEIvhGDY,13593
54
+ ngio-0.2.4.dist-info/METADATA,sha256=dzoSMNR-uED7qerei4X-ItLMcdn8U-EjUnjuFUl9aEQ,5215
55
+ ngio-0.2.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ ngio-0.2.4.dist-info/licenses/LICENSE,sha256=UgN_a1QCeNh9rZWfz-wORQFxE3elQzLWPQaoK6N6fxQ,1502
57
+ ngio-0.2.4.dist-info/RECORD,,
File without changes