ngio 0.5.0b7__py3-none-any.whl → 0.5.1__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.
@@ -1,6 +1,6 @@
1
1
  """HCS (High Content Screening) specific metadata classes for NGIO."""
2
2
 
3
- import warnings
3
+ import logging
4
4
  from typing import Annotated
5
5
 
6
6
  from ome_zarr_models.common.plate import (
@@ -16,16 +16,17 @@ from pydantic import BaseModel, SkipValidation, field_serializer
16
16
  from ngio.ome_zarr_meta.ngio_specs._ngio_image import DefaultNgffVersion, NgffVersions
17
17
  from ngio.utils import NgioValueError
18
18
 
19
+ logger = logging.getLogger(f"ngio:{__name__}")
20
+
19
21
 
20
22
  def path_in_well_validation(path: str) -> str:
21
23
  """Validate the path in the well."""
22
24
  # Check if the value contains only alphanumeric characters
23
25
  if not path.isalnum():
24
- warnings.warn(
26
+ logger.warning(
25
27
  f"Path '{path}' contains non-alphanumeric characters. "
26
28
  "This may cause issues with some tools. "
27
- "Consider using only alphanumeric characters in the path.",
28
- stacklevel=2,
29
+ "Consider using only alphanumeric characters in the path."
29
30
  )
30
31
  return path
31
32
 
@@ -147,23 +148,58 @@ class NgioWellMeta(BaseModel):
147
148
  raise NgioValueError(f"Image at path {path} not found in the well.")
148
149
 
149
150
 
150
- def _stringify_column(column: str | int) -> str:
151
+ def _format_int_column(column: int, num_digits: int = 2) -> str:
152
+ """Format the column as a string.
153
+
154
+ We make sure to pad the column with zeros
155
+ to have a consistent format.
156
+
157
+ Args:
158
+ column (int): The column to format.
159
+ num_digits (int): The number of digits to pad the column.
160
+
161
+ Returns:
162
+ str: The column as a string.
163
+ """
164
+ return f"{column:0{num_digits}d}"
165
+
166
+
167
+ def _stringify_column(column: str | int, num_digits: int = 2) -> str:
151
168
  """Convert the column to a string.
152
169
 
170
+ This will ensure that columns are always strings.
171
+ and will help with sorting and comparison.
172
+
153
173
  Args:
154
174
  column (str | int): The column to convert.
175
+ num_digits (int): The number of digits to pad the column.
155
176
 
156
177
  Returns:
157
178
  str: The column as a string.
158
179
  """
159
- if isinstance(column, str):
160
- return column
180
+ if isinstance(column, int):
181
+ return _format_int_column(column, num_digits=num_digits)
161
182
 
162
- # Maybe we should pad the column with zeros
163
- return str(column)
183
+ elif isinstance(column, str):
184
+ try:
185
+ column_int = int(column)
186
+ return _format_int_column(column_int, num_digits=num_digits)
187
+ except ValueError:
188
+ pass
189
+ return column
190
+ raise NgioValueError(f"Column {column} must be a string or an integer.")
164
191
 
165
192
 
166
193
  def _find_row_index(rows: list[str], row: str) -> int | None:
194
+ """Find the index of a row in the list of rows.
195
+
196
+ Args:
197
+ rows: List of row names.
198
+ row: The row name to find.
199
+
200
+ Returns:
201
+ The index of the row, or None if not found.
202
+ """
167
203
  try:
168
204
  return rows.index(row)
169
205
  except ValueError:
@@ -171,8 +207,17 @@ def _find_row_index(rows: list[str], row: str) -> int | None:
171
207
 
172
208
 
173
209
  def _find_column_index(columns: list[str], column: str | int) -> int | None:
174
- _num_columns = [int(columns) for columns in columns]
175
- column = int(column)
210
+ """Find the index of a column in the list of columns.
211
+
212
+ Args:
213
+ columns: List of column names.
214
+ column: The column name or number to find.
215
+
216
+ Returns:
217
+ The index of the column, or None if not found.
218
+ """
219
+ _num_columns = [_stringify_column(columns) for columns in columns]
220
+ column = _stringify_column(column)
176
221
  try:
177
222
  return _num_columns.index(column)
178
223
  except ValueError:
@@ -182,6 +227,16 @@ def _find_column_index(columns: list[str], column: str | int) -> int | None:
182
227
  def _relabel_wells(
183
228
  wells: list[WellInPlate], rows: list[Row], columns: list[Column]
184
229
  ) -> list[WellInPlate]:
230
+ """Relabel well indices after rows or columns have been added.
231
+
232
+ Args:
233
+ wells: List of wells to relabel.
234
+ rows: Updated list of rows.
235
+ columns: Updated list of columns.
236
+
237
+ Returns:
238
+ List of wells with updated row and column indices.
239
+ """
185
240
  new_wells = []
186
241
  _rows = [row.name for row in rows]
187
242
  _columns = [column.name for column in columns]
@@ -286,23 +341,8 @@ class NgioPlateMeta(BaseModel):
286
341
  Returns:
287
342
  str: The path of the well.
288
343
  """
289
- if row not in self.rows:
290
- raise NgioValueError(
291
- f"Row {row} not found in the plate. Available rows are {self.rows}."
292
- )
293
-
294
- row_idx = self.rows.index(row)
295
-
296
- _num_columns = [int(columns) for columns in self.columns]
297
-
298
- try:
299
- _column = int(column)
300
- except ValueError:
301
- raise NgioValueError(
302
- f"Column {column} must be an integer or convertible to an integer."
303
- ) from None
304
-
305
- column_idx = _num_columns.index(_column)
344
+ row_idx = _find_row_index(self.rows, row)
345
+ column_idx = _find_column_index(self.columns, column)
306
346
 
307
347
  for well in self.plate.wells:
308
348
  if well.columnIndex == column_idx and well.rowIndex == row_idx:
@@ -365,7 +405,7 @@ class NgioPlateMeta(BaseModel):
365
405
 
366
406
  columns_names.append(_stringify_column(column))
367
407
  # sort as numbers
368
- columns_names.sort(key=lambda x: int(x))
408
+ columns_names.sort(key=lambda x: _stringify_column(x))
369
409
  column_idx = columns_names.index(_stringify_column(column))
370
410
  relabel_wells = True
371
411
 
@@ -392,11 +432,14 @@ class NgioPlateMeta(BaseModel):
392
432
  row: str,
393
433
  column: str | int,
394
434
  ) -> "NgioPlateMeta":
395
- """Add an image to the well.
435
+ """Add a well to the plate.
396
436
 
397
437
  Args:
398
438
  row (str): The row of the well.
399
439
  column (str | int): The column of the well.
440
+
441
+ Returns:
442
+ NgioPlateMeta: Updated plate metadata with the new well.
400
443
  """
401
444
  plate, row_idx = self.add_row(row=row)
402
445
  plate, column_idx = plate.add_column(column=column)
@@ -509,10 +552,13 @@ class NgioPlateMeta(BaseModel):
509
552
  """Derive the plate metadata.
510
553
 
511
554
  Args:
512
- name (str): The name of the derived plate.
513
- ngff_version (NgffVersion | None): The version of the derived plate.
555
+ name (str | None): The name of the derived plate.
556
+ ngff_version (NgffVersions | None): The version of the derived plate.
514
557
  If None, use the version of the original plate.
515
558
  keep_acquisitions (bool): If True, keep the acquisitions in the plate.
559
+
560
+ Returns:
561
+ NgioPlateMeta: The derived plate metadata.
516
562
  """
517
563
  columns = self.plate.columns
518
564
  rows = self.plate.rows
@@ -129,6 +129,42 @@ class AbstractNgioImageMeta:
129
129
  datasets=new_datasets,
130
130
  )
131
131
 
132
+ def rename_axes(self, axes_names: Sequence[str]):
133
+ """Rename axes in the metadata.
134
+
135
+ Args:
136
+ axes_names(Sequence[str]): The new axes names in the order of the current
137
+ axes.
138
+ """
139
+ new_axes_handler = self.axes_handler.rename_axes(axes_names=axes_names)
140
+ new_datasets = []
141
+ for dataset in self.datasets:
142
+ new_dataset = Dataset(
143
+ path=dataset.path,
144
+ axes_handler=new_axes_handler,
145
+ scale=dataset.scale,
146
+ translation=dataset.translation,
147
+ )
148
+ new_datasets.append(new_dataset)
149
+
150
+ return type(self)(
151
+ version=self.version,
152
+ name=self.name,
153
+ datasets=new_datasets,
154
+ )
155
+
156
+ def rename_image(self, name: str):
157
+ """Rename the image in the metadata.
158
+
159
+ Args:
160
+ name(str): The new name of the image.
161
+ """
162
+ return type(self)(
163
+ version=self.version,
164
+ name=name,
165
+ datasets=self.datasets,
166
+ )
167
+
132
168
  @property
133
169
  def version(self) -> NgffVersions:
134
170
  """Version of the OME-NFF metadata used to build the object."""
@@ -119,8 +119,6 @@ def _compute_scale_translation(
119
119
  def _v04_to_ngio_datasets(
120
120
  v04_multiscale: MultiscaleV04,
121
121
  axes_setup: AxesSetup,
122
- allow_non_canonical_axes: bool = False,
123
- strict_canonical_order: bool = True,
124
122
  ) -> list[Dataset]:
125
123
  """Convert a v04 multiscale to a list of ngio datasets."""
126
124
  datasets = []
@@ -151,8 +149,6 @@ def _v04_to_ngio_datasets(
151
149
  axes_handler = AxesHandler(
152
150
  axes=axes,
153
151
  axes_setup=axes_setup,
154
- allow_non_canonical_axes=allow_non_canonical_axes,
155
- strict_canonical_order=strict_canonical_order,
156
152
  )
157
153
 
158
154
  for v04_dataset in v04_multiscale.datasets:
@@ -172,9 +168,7 @@ def _v04_to_ngio_datasets(
172
168
 
173
169
  def v04_to_ngio_image_meta(
174
170
  metadata: dict,
175
- axes_setup: AxesSetup | None = None,
176
- allow_non_canonical_axes: bool = False,
177
- strict_canonical_order: bool = True,
171
+ axes_setup: AxesSetup,
178
172
  ) -> NgioImageMeta:
179
173
  """Convert a v04 image metadata to a ngio image metadata.
180
174
 
@@ -182,8 +176,6 @@ def v04_to_ngio_image_meta(
182
176
  metadata (dict): The v04 image metadata.
183
177
  axes_setup (AxesSetup, optional): The axes setup. This is
184
178
  required to convert image with non-canonical axes names.
185
- allow_non_canonical_axes (bool, optional): Allow non-canonical axes.
186
- strict_canonical_order (bool, optional): Strict canonical order.
187
179
 
188
180
  Returns:
189
181
  NgioImageMeta: The ngio image metadata.
@@ -198,12 +190,9 @@ def v04_to_ngio_image_meta(
198
190
  v04_muliscale = v04_image.multiscales[0]
199
191
 
200
192
  channels_meta = _v04_omero_to_channels(v04_image.omero)
201
- axes_setup = axes_setup if axes_setup is not None else AxesSetup()
202
193
  datasets = _v04_to_ngio_datasets(
203
194
  v04_muliscale,
204
195
  axes_setup=axes_setup,
205
- allow_non_canonical_axes=allow_non_canonical_axes,
206
- strict_canonical_order=strict_canonical_order,
207
196
  )
208
197
 
209
198
  name = v04_muliscale.name
@@ -219,9 +208,7 @@ def v04_to_ngio_image_meta(
219
208
 
220
209
  def v04_to_ngio_label_meta(
221
210
  metadata: dict,
222
- axes_setup: AxesSetup | None = None,
223
- allow_non_canonical_axes: bool = False,
224
- strict_canonical_order: bool = True,
211
+ axes_setup: AxesSetup,
225
212
  ) -> NgioLabelMeta:
226
213
  """Convert a v04 image metadata to a ngio image metadata.
227
214
 
@@ -229,8 +216,6 @@ def v04_to_ngio_label_meta(
229
216
  metadata (dict): The v04 image metadata.
230
217
  axes_setup (AxesSetup, optional): The axes setup. This is
231
218
  required to convert image with non-canonical axes names.
232
- allow_non_canonical_axes (bool, optional): Allow non-canonical axes.
233
- strict_canonical_order (bool, optional): Strict canonical order.
234
219
 
235
220
  Returns:
236
221
  NgioImageMeta: The ngio image metadata.
@@ -243,13 +228,9 @@ def v04_to_ngio_label_meta(
243
228
  )
244
229
 
245
230
  v04_muliscale = v04_label.multiscales[0]
246
-
247
- axes_setup = axes_setup if axes_setup is not None else AxesSetup()
248
231
  datasets = _v04_to_ngio_datasets(
249
232
  v04_muliscale,
250
233
  axes_setup=axes_setup,
251
- allow_non_canonical_axes=allow_non_canonical_axes,
252
- strict_canonical_order=strict_canonical_order,
253
234
  )
254
235
 
255
236
  source = v04_label.image_label.source
@@ -133,8 +133,6 @@ def _compute_scale_translation(
133
133
  def _v05_to_ngio_datasets(
134
134
  v05_multiscale: MultiscaleV05,
135
135
  axes_setup: AxesSetup,
136
- allow_non_canonical_axes: bool = False,
137
- strict_canonical_order: bool = True,
138
136
  ) -> list[Dataset]:
139
137
  """Convert a v05 multiscale to a list of ngio datasets."""
140
138
  datasets = []
@@ -165,8 +163,6 @@ def _v05_to_ngio_datasets(
165
163
  axes_handler = AxesHandler(
166
164
  axes=axes,
167
165
  axes_setup=axes_setup,
168
- allow_non_canonical_axes=allow_non_canonical_axes,
169
- strict_canonical_order=strict_canonical_order,
170
166
  )
171
167
 
172
168
  for v05_dataset in v05_multiscale.datasets:
@@ -186,18 +182,14 @@ def _v05_to_ngio_datasets(
186
182
 
187
183
  def v05_to_ngio_image_meta(
188
184
  metadata: dict,
189
- axes_setup: AxesSetup | None = None,
190
- allow_non_canonical_axes: bool = False,
191
- strict_canonical_order: bool = True,
185
+ axes_setup: AxesSetup,
192
186
  ) -> NgioImageMeta:
193
187
  """Convert a v05 image metadata to a ngio image metadata.
194
188
 
195
189
  Args:
196
190
  metadata (dict): The v05 image metadata.
197
- axes_setup (AxesSetup, optional): The axes setup. This is
191
+ axes_setup (AxesSetup): The axes setup. This is
198
192
  required to convert image with non-canonical axes names.
199
- allow_non_canonical_axes (bool, optional): Allow non-canonical axes.
200
- strict_canonical_order (bool, optional): Strict canonical order.
201
193
 
202
194
  Returns:
203
195
  NgioImageMeta: The ngio image metadata.
@@ -212,12 +204,9 @@ def v05_to_ngio_image_meta(
212
204
  v05_multiscale = v05_image.multiscales[0]
213
205
 
214
206
  channels_meta = _v05_omero_to_channels(v05_image.omero)
215
- axes_setup = axes_setup if axes_setup is not None else AxesSetup()
216
207
  datasets = _v05_to_ngio_datasets(
217
208
  v05_multiscale,
218
209
  axes_setup=axes_setup,
219
- allow_non_canonical_axes=allow_non_canonical_axes,
220
- strict_canonical_order=strict_canonical_order,
221
210
  )
222
211
 
223
212
  name = v05_multiscale.name
@@ -233,18 +222,14 @@ def v05_to_ngio_image_meta(
233
222
 
234
223
  def v05_to_ngio_label_meta(
235
224
  metadata: dict,
236
- axes_setup: AxesSetup | None = None,
237
- allow_non_canonical_axes: bool = False,
238
- strict_canonical_order: bool = True,
225
+ axes_setup: AxesSetup,
239
226
  ) -> NgioLabelMeta:
240
227
  """Convert a v05 image metadata to a ngio image metadata.
241
228
 
242
229
  Args:
243
230
  metadata (dict): The v05 image metadata.
244
- axes_setup (AxesSetup, optional): The axes setup. This is
231
+ axes_setup (AxesSetup): The axes setup. This is
245
232
  required to convert image with non-canonical axes names.
246
- allow_non_canonical_axes (bool, optional): Allow non-canonical axes.
247
- strict_canonical_order (bool, optional): Strict canonical order.
248
233
 
249
234
  Returns:
250
235
  NgioLabelMeta: The ngio label metadata.
@@ -259,12 +244,9 @@ def v05_to_ngio_label_meta(
259
244
 
260
245
  v05_multiscale = v05_label.multiscales[0]
261
246
 
262
- axes_setup = axes_setup if axes_setup is not None else AxesSetup()
263
247
  datasets = _v05_to_ngio_datasets(
264
248
  v05_multiscale,
265
249
  axes_setup=axes_setup,
266
- allow_non_canonical_axes=allow_non_canonical_axes,
267
- strict_canonical_order=strict_canonical_order,
268
250
  )
269
251
 
270
252
  if v05_label.image_label is not None:
@@ -31,7 +31,7 @@ _resources = {
31
31
  dtype="uint8",
32
32
  ),
33
33
  ],
34
- xy_pixelsize=0.325,
34
+ pixelsize=0.325,
35
35
  z_spacing=1.0,
36
36
  time_spacing=1.0,
37
37
  name="Cardiomyocyte Differentiation",
@@ -27,7 +27,7 @@ class SampleInfo(BaseModel):
27
27
 
28
28
  img_path: Path
29
29
  labels: list[LabelsInfo] = Field(default_factory=list)
30
- xy_pixelsize: float
30
+ pixelsize: float
31
31
  z_spacing: float = 1.0
32
32
  time_spacing: float = 1.0
33
33
  space_unit: SpaceUnits = DefaultSpaceUnit
@@ -229,10 +229,10 @@ class ImplementedTables:
229
229
 
230
230
 
231
231
  class TablesContainer:
232
- """A class to handle the /labels group in an OME-NGFF file."""
232
+ """A class to handle the /tables group in an OME-NGFF file."""
233
233
 
234
234
  def __init__(self, group_handler: ZarrGroupHandler) -> None:
235
- """Initialize the LabelGroupHandler."""
235
+ """Initialize the TablesContainer."""
236
236
  self._group_handler = group_handler
237
237
 
238
238
  # Validate the group
@@ -252,7 +252,7 @@ class TablesContainer:
252
252
  )
253
253
 
254
254
  def _get_tables_list(self) -> list[str]:
255
- """Create the /tables group if it doesn't exist."""
255
+ """Return the list of table names from the group attributes."""
256
256
  attrs = self._group_handler.load_attrs()
257
257
  return attrs.get("tables", [])
258
258
 
@@ -262,7 +262,14 @@ class TablesContainer:
262
262
  return handler
263
263
 
264
264
  def list(self, filter_types: TypedTable | str | None = None) -> list[str]:
265
- """List all labels in the group."""
265
+ """List all tables in the group.
266
+
267
+ Args:
268
+ filter_types: If provided, only return tables of this type.
269
+
270
+ Returns:
271
+ A list of table names.
272
+ """
266
273
  tables = self._get_tables_list()
267
274
  if filter_types is None:
268
275
  return tables
@@ -281,7 +288,16 @@ class TablesContainer:
281
288
  backend: TableBackend | None = None,
282
289
  strict: bool = True,
283
290
  ) -> Table:
284
- """Get a label from the group."""
291
+ """Get a table from the group.
292
+
293
+ Args:
294
+ name: The name of the table.
295
+ backend: The backend to use for reading the table.
296
+ strict: If True, raise an error if the table type is not implemented.
297
+
298
+ Returns:
299
+ The table object.
300
+ """
285
301
  if name not in self.list():
286
302
  raise NgioValueError(f"Table '{name}' not found in the group.")
287
303
 
@@ -301,7 +317,16 @@ class TablesContainer:
301
317
  table_cls: type[TableType],
302
318
  backend: TableBackend | None = None,
303
319
  ) -> TableType:
304
- """Get a table from the group as a specific type."""
320
+ """Get a table from the group as a specific type.
321
+
322
+ Args:
323
+ name: The name of the table.
324
+ table_cls: The table class to use for loading the table.
325
+ backend: The backend to use for reading the table.
326
+
327
+ Returns:
328
+ The table object of the specified type.
329
+ """
305
330
  if name not in self.list():
306
331
  raise NgioValueError(f"Table '{name}' not found in the group.")
307
332
 
@@ -339,7 +364,14 @@ class TablesContainer:
339
364
  backend: TableBackend = DefaultTableBackend,
340
365
  overwrite: bool = False,
341
366
  ) -> None:
342
- """Add a table to the group."""
367
+ """Add a table to the group.
368
+
369
+ Args:
370
+ name: The name of the table.
371
+ table: The table object to add.
372
+ backend: The backend to use for writing the table.
373
+ overwrite: Whether to overwrite an existing table with the same name.
374
+ """
343
375
  existing_tables = self._get_tables_list()
344
376
  if name in existing_tables and not overwrite:
345
377
  raise NgioValueError(
@@ -4,7 +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
+ import logging
8
8
  from collections.abc import Iterable
9
9
  from typing import Literal
10
10
  from uuid import uuid4
@@ -29,6 +29,8 @@ from ngio.utils import (
29
29
  ZarrGroupHandler,
30
30
  )
31
31
 
32
+ logger = logging.getLogger(f"ngio:{__name__}")
33
+
32
34
  REQUIRED_COLUMNS = [
33
35
  "x_micrometer",
34
36
  "y_micrometer",
@@ -77,9 +79,7 @@ OPTIONAL_COLUMNS = ORIGIN_COLUMNS + TRANSLATION_COLUMNS + PLATE_COLUMNS + INDEX_
77
79
  def _check_optional_columns(col_name: str) -> None:
78
80
  """Check if the column name is in the optional columns."""
79
81
  if col_name not in OPTIONAL_COLUMNS + TIME_COLUMNS:
80
- warnings.warn(
81
- f"Column {col_name} is not in the optional columns.", stacklevel=2
82
- )
82
+ logger.warning(f"Column {col_name} is not in the optional columns.")
83
83
 
84
84
 
85
85
  def _dataframe_to_rois(
ngio/utils/_zarr_utils.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Common utilities for working with Zarr groups in consistent ways."""
2
2
 
3
3
  import json
4
- import warnings
4
+ import logging
5
5
  from pathlib import Path
6
6
  from typing import Literal, TypeAlias
7
7
 
@@ -22,6 +22,8 @@ from ngio.utils._errors import (
22
22
  NgioValueError,
23
23
  )
24
24
 
25
+ logger = logging.getLogger(f"ngio:{__name__}")
26
+
25
27
  AccessModeLiteral = Literal["r", "r+", "w", "w-", "a"]
26
28
  # StoreLike is more restrictive than it could be
27
29
  # but to make sure we can handle the store correctly
@@ -36,12 +38,10 @@ StoreOrGroup: TypeAlias = NgioSupportedStore | zarr.Group
36
38
  def _check_store(store) -> NgioSupportedStore:
37
39
  """Check the store and return a valid store."""
38
40
  if not isinstance(store, NgioSupportedStore):
39
- warnings.warn(
41
+ logger.warning(
40
42
  f"Store type {type(store)} is not explicitly supported. "
41
43
  f"Supported types are: {NgioSupportedStore}. "
42
- "Proceeding, but this may lead to unexpected behavior.",
43
- UserWarning,
44
- stacklevel=2,
44
+ "Proceeding, but this may lead to unexpected behavior."
45
45
  )
46
46
  return store
47
47
 
@@ -114,7 +114,6 @@ class ZarrGroupHandler:
114
114
 
115
115
  Args:
116
116
  store (StoreOrGroup): The Zarr store or group containing the image data.
117
- meta_mode (str): The mode of the metadata handler.
118
117
  zarr_format (int | None): The Zarr format version to use.
119
118
  cache (bool): Whether to cache the metadata.
120
119
  mode (str | None): The mode of the store.
@@ -154,11 +153,7 @@ class ZarrGroupHandler:
154
153
  return (self.store.path / self.group.path).as_posix()
155
154
  elif isinstance(self.store, MemoryStore):
156
155
  return None
157
- warnings.warn(
158
- f"Cannot determine full URL for store type {type(self.store)}. ",
159
- UserWarning,
160
- stacklevel=2,
161
- )
156
+ logger.warning(f"Cannot determine full URL for store type {type(self.store)}.")
162
157
  return None
163
158
 
164
159
  @property
@@ -524,11 +519,9 @@ def copy_group(
524
519
  _fsspec_copy(src_group.store, src_group.path, dest_group.store, dest_group.path)
525
520
  return
526
521
  if not suppress_warnings:
527
- warnings.warn(
522
+ logger.warning(
528
523
  "Fsspec copy not possible, falling back to Zarr Python API for the copy. "
529
524
  "This will preserve some tabular data non-zarr native (parquet, and csv), "
530
- "and it will be slower for large datasets.",
531
- UserWarning,
532
- stacklevel=2,
525
+ "and it will be slower for large datasets."
533
526
  )
534
527
  _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.0b7
3
+ Version: 0.5.1
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
@@ -23,7 +23,7 @@ Requires-Dist: dask[distributed]<2025.11.0
23
23
  Requires-Dist: filelock
24
24
  Requires-Dist: numpy
25
25
  Requires-Dist: ome-zarr-models
26
- Requires-Dist: pandas>=1.2.0
26
+ Requires-Dist: pandas<3.0.0,>=1.2.0
27
27
  Requires-Dist: pillow
28
28
  Requires-Dist: polars
29
29
  Requires-Dist: pooch
@@ -58,6 +58,7 @@ Requires-Dist: mkdocs-jupyter; extra == 'docs'
58
58
  Requires-Dist: mkdocs-material; extra == 'docs'
59
59
  Requires-Dist: mkdocstrings[python]; extra == 'docs'
60
60
  Requires-Dist: rich; extra == 'docs'
61
+ Requires-Dist: ruff; extra == 'docs'
61
62
  Requires-Dist: scikit-image; extra == 'docs'
62
63
  Requires-Dist: tabulate; extra == 'docs'
63
64
  Provides-Extra: test