diff --git a/ome_zarr/cli.py b/ome_zarr/cli.py index c95ae9e6..4024f556 100644 --- a/ome_zarr/cli.py +++ b/ome_zarr/cli.py @@ -40,7 +40,7 @@ def view(args: argparse.Namespace) -> None: def finder(args: argparse.Namespace) -> None: """Wrap the :func:`~ome_zarr.utils.finder` method.""" - config_logging(logging.WARN, args) + config_logging(logging.WARNING, args) bff_finder(args.path, args.port) diff --git a/ome_zarr/format.py b/ome_zarr/format.py index 40170ac1..097b4b89 100644 --- a/ome_zarr/format.py +++ b/ome_zarr/format.py @@ -2,8 +2,8 @@ import logging from abc import ABC, abstractmethod -from collections.abc import Iterator -from typing import Any, Dict +from collections.abc import Iterator, Mapping +from typing import Any from zarr.storage import FsspecStore, LocalStore @@ -63,7 +63,7 @@ def zarr_format(self) -> int: # pragma: no cover @property @abstractmethod - def chunk_key_encoding(self) -> Dict[str, str]: # pragma: no cover + def chunk_key_encoding(self) -> dict[str, str]: # pragma: no cover raise NotImplementedError() @abstractmethod @@ -134,7 +134,7 @@ class FormatV01(Format): Initial format. (2020) """ - REQUIRED_PLATE_WELL_KEYS: dict[str, type] = {"path": str} + REQUIRED_PLATE_WELL_KEYS: Mapping[str, type] = {"path": str} @property def version(self) -> str: @@ -145,7 +145,7 @@ def zarr_format(self) -> int: return 2 @property - def chunk_key_encoding(self) -> Dict[str, str]: + def chunk_key_encoding(self) -> dict[str, str]: return {"name": "v2", "separator": "."} def matches(self, metadata: dict) -> bool: @@ -179,7 +179,7 @@ def generate_well_dict( def validate_well_dict( self, well: dict, rows: list[str], columns: list[str] ) -> None: - if any(e not in self.REQUIRED_PLATE_WELL_KEYS for e in well.keys()): + if any(e not in self.REQUIRED_PLATE_WELL_KEYS for e in well): LOGGER.debug("%s contains unspecified keys", well) for key, key_type in self.REQUIRED_PLATE_WELL_KEYS.items(): if key not in well: @@ -213,7 +213,7 @@ def version(self) -> str: return "0.2" @property - def chunk_key_encoding(self) -> Dict[str, str]: + def chunk_key_encoding(self) -> dict[str, str]: return {"name": "v2", "separator": "/"} @@ -234,7 +234,11 @@ class FormatV04(FormatV03): introduce coordinate_transformations in multiscales (Nov 2021) """ - REQUIRED_PLATE_WELL_KEYS = {"path": str, "rowIndex": int, "columnIndex": int} + REQUIRED_PLATE_WELL_KEYS: Mapping[str, type] = { + "path": str, + "rowIndex": int, + "columnIndex": int, + } @property def version(self) -> str: @@ -367,7 +371,7 @@ def zarr_format(self) -> int: return 3 @property - def chunk_key_encoding(self) -> Dict[str, str]: + def chunk_key_encoding(self) -> dict[str, str]: # this is default for Zarr v3. Could return None? return {"name": "default", "separator": "/"} diff --git a/ome_zarr/reader.py b/ome_zarr/reader.py index 4430eb08..353b0919 100644 --- a/ome_zarr/reader.py +++ b/ome_zarr/reader.py @@ -112,7 +112,7 @@ def add( prepend: bool = False, visibility: bool | None = None, plate_labels: bool = False, - ) -> "Optional[Node]": + ) -> "Node | None": """Create a child node if this location has not yet been seen. Newly created nodes may be considered higher or lower priority than @@ -269,10 +269,7 @@ class Multiscales(Spec): @staticmethod def matches(zarr: ZarrLocation) -> bool: """is multiscales metadata present?""" - if zarr.zgroup: - if "multiscales" in zarr.root_attrs: - return True - return False + return bool(zarr.zgroup) and "multiscales" in zarr.root_attrs def __init__(self, node: Node) -> None: super().__init__(node) @@ -345,7 +342,7 @@ def __init__(self, node: Node) -> None: try: len(channels) - except Exception: + except TypeError: LOGGER.warning("error counting channels: %s", channels) return # EARLY EXIT diff --git a/ome_zarr/scale.py b/ome_zarr/scale.py index 0fb3e85e..abd2bdab 100644 --- a/ome_zarr/scale.py +++ b/ome_zarr/scale.py @@ -26,8 +26,8 @@ LOGGER = logging.getLogger("ome_zarr.scale") -ListOfArrayLike = Union[list[da.Array], list[np.ndarray]] -ArrayLike = Union[da.Array, np.ndarray] +ListOfArrayLike = Union[list[da.Array], list[np.ndarray]] # noqa: UP007 # FIXME +ArrayLike = Union[da.Array, np.ndarray] # noqa: UP007 # FIXME @dataclass diff --git a/ome_zarr/types.py b/ome_zarr/types.py index 019acb73..3b2d4310 100644 --- a/ome_zarr/types.py +++ b/ome_zarr/types.py @@ -1,11 +1,11 @@ """Definition of complex types for use elsewhere.""" from collections.abc import Callable -from typing import Any, Union +from typing import Any -LayerData = Union[tuple[Any], tuple[Any, dict], tuple[Any, dict, str]] +LayerData = tuple[Any] | tuple[Any, dict] | tuple[Any, dict, str] -PathLike = Union[str, list[str]] +PathLike = str | list[str] ReaderFunction = Callable[[PathLike], list[LayerData]] diff --git a/ome_zarr/utils.py b/ome_zarr/utils.py index 3d58459d..ff3bf11e 100644 --- a/ome_zarr/utils.py +++ b/ome_zarr/utils.py @@ -149,7 +149,7 @@ def find_multiscales(path_to_zattrs): plate_name = os.path.basename(path_to_zattrs) return [[path_to_zarr, plate_name, os.path.dirname(path_to_zattrs)]] else: - LOGGER.info(f"No wells found in plate{path_to_zattrs}") + LOGGER.info("No wells found in plate%s", path_to_zattrs) return [] elif zattrs.get("bioformats2raw.layout") == 3: # Open OME/METADATA.ome.xml @@ -178,7 +178,7 @@ def find_multiscales(path_to_zattrs): ) series += 1 return images - except Exception as ex: + except Exception as ex: # noqa: BLE001 print(ex) elif zattrs.get("multiscales"): return [ diff --git a/ome_zarr/writer.py b/ome_zarr/writer.py index df836ac4..75a08ca5 100644 --- a/ome_zarr/writer.py +++ b/ome_zarr/writer.py @@ -77,7 +77,7 @@ def _validate_well_images( if isinstance(image, str): validated_images.append({"path": str(image)}) elif isinstance(image, dict): - if any(e not in VALID_KEYS for e in image.keys()): + if any(e not in VALID_KEYS for e in image): LOGGER.debug("%s contains unspecified keys", image) if "path" not in image: raise ValueError(f"{image} must contain a path key") @@ -106,7 +106,7 @@ def _validate_plate_acquisitions( for acquisition in acquisitions: if not isinstance(acquisition, dict): raise ValueError(f"{acquisition} must be a dictionary") - if any(e not in VALID_KEYS for e in acquisition.keys()): + if any(e not in VALID_KEYS for e in acquisition): LOGGER.debug("%s contains unspecified keys", acquisition) if "id" not in acquisition: raise ValueError(f"{acquisition} must contain an id key") @@ -298,7 +298,7 @@ def write_multiscale( del options["chunk_key_encoding"] # handle any 'chunks' option from storage_options if chunks_opt is not None: - data = da.array(data).rechunk(chunks=chunks_opt) + data = da.array(data).rechunk(chunks=chunks_opt) # noqa: PLW2901 da_delayed = da.to_zarr( arr=data, url=group.store, @@ -402,7 +402,7 @@ def write_multiscales_metadata( if omero_metadata is None: raise KeyError("If `'omero'` is present, value cannot be `None`.") for c in omero_metadata["channels"]: - if "color" in c: + if "color" in c: # noqa: SIM102 if not isinstance(c["color"], str) or len(c["color"]) != 6: raise TypeError("`'color'` must be a hex code string.") if "window" in c: diff --git a/pyproject.toml b/pyproject.toml index 3a5e40a5..c84292a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,18 +57,53 @@ Changelog = "https://github.com/ome/ome-zarr-py/blob/master/CHANGELOG.md" [tool.ruff.lint] extend-select = [ - "I", # isort - # "D", # pydocstyle # FIXME - # "BLE", # flake8-blind-except # FIXME - "A", # flake8-builtins - # "G", # flake8-logging-format # FIXME + "S", # flake8-bandit + "BLE", # flake8-blind-except + "B", # flake8-bugbear + "A", # flake8-builtins + "C4", # flake8-comprehensions + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "G", # flake8-logging-format + "PIE", # flake8-pie + "PYI", # flake8-pyi + "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + "FLY", # flynt + "I", # isort + "PERF", # Perflint + # "D", # pydocstyle # FIXME + "PGH", # pygrep-hooks + "PLE", # Pylint Error + "PLW", # Pylint Warning + "UP", # pyupgrade + "FURB", # refurb + "RUF", # Ruff-specific rules +] + +ignore = [ + "S101", # assert + "S311", # suspicious-non-cryptographic-random-usage + "S314", # suspicious-xml-element-tree-usage # FIXME + "B007", # unused-loop-control-variable + "B008", # function-call-in-default-argument # FIXME + "B011", # assert-false + "B024", # abstract-base-class-without-abstract-method # FIXME + "B028", # no-explicit-stacklevel + "B905", # zip-without-explicit-strict + "C408", # unnecessary-collection-call + "SIM108", # if-else-block-instead-of-if-exp + "PERF203", # try-except-in-loop + "PLW1641", # eq-without-hash # FIXME + "UP038", # non-pep604-isinstance + "RUF005", # collection-literal-concatenation ] [tool.ruff.lint.extend-per-file-ignores] "ome_zarr/csv.py" = ["A005"] "ome_zarr/io.py" = ["A005"] "ome_zarr/types.py" = ["A005"] -"tests/*.py" = ["D"] +"tests/*.py" = ["SIM201", "D"] [tool.setuptools] packages = ["ome_zarr"] diff --git a/tests/test_cli.py b/tests/test_cli.py index aacc4e6a..f67ce633 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -144,7 +144,7 @@ def _rotate_and_test(self, *hierarchy: Path, reverse: bool = True): copy = [list(x.parts) for x in firstpass] common = strip_common_prefix(copy) - assert "d" == common + assert common == "d" assert {tuple(x) for x in copy} == {tuple(x) for x in results} if reverse: diff --git a/tests/test_writer.py b/tests/test_writer.py index 5461025f..e9a5d073 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -876,7 +876,7 @@ def test_minimal_plate(self, fmt): attrs = attrs["ome"] assert attrs["version"] == fmt.version else: - attrs["plate"]["version"] == fmt.version + assert attrs["plate"]["version"] == fmt.version assert "plate" in attrs assert attrs["plate"]["columns"] == [{"name": "1"}] @@ -1256,7 +1256,7 @@ def test_multiple_images(self, images): {"path": "1"}, {"path": "2"}, ] - self.root_v3.attrs["ome"]["version"] == FormatV05().version + assert self.root_v3.attrs["ome"]["version"] == FormatV05().version @pytest.mark.parametrize("fmt", (FormatV01(), FormatV02(), FormatV03())) def test_version(self, fmt):