diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61fbd30..ef1ae06 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.15.6 + rev: v0.16.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/setup.py b/setup.py index cbb86cc..48730d6 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ if __name__ == "__main__": try: setup(use_scm_version={"version_scheme": "no-guess-dev"}) - except: # noqa + except: print( "\n\nAn error occurred while building the project, " "please ensure you have the most updated version of setuptools, " diff --git a/src/spatialexperiment/__init__.py b/src/spatialexperiment/__init__.py index 8fd41e7..8dd20a4 100644 --- a/src/spatialexperiment/__init__.py +++ b/src/spatialexperiment/__init__.py @@ -26,11 +26,11 @@ ) __all__ = [ - "read_tenx_visium", - "SpatialExperiment", "LoadedSpatialImage", "RemoteSpatialImage", + "SpatialExperiment", "StoredSpatialImage", "VirtualSpatialImage", "construct_spatial_image_class", + "read_tenx_visium", ] diff --git a/src/spatialexperiment/_combineutils.py b/src/spatialexperiment/_combineutils.py index 0ff2805..51763a3 100644 --- a/src/spatialexperiment/_combineutils.py +++ b/src/spatialexperiment/_combineutils.py @@ -1,15 +1,14 @@ from __future__ import annotations -from typing import List, Tuple -from warnings import warn -from copy import deepcopy import itertools +from copy import deepcopy +from warnings import warn -from biocframe import BiocFrame import biocutils as ut +from biocframe import BiocFrame -def _append_indices_to_samples(bframes: List[BiocFrame]) -> List[BiocFrame]: +def _append_indices_to_samples(bframes: list[BiocFrame]) -> list[BiocFrame]: """Append indices to sample IDs for a list of `BiocFrames`. For each `BiocFrame`, appends an index to all sample IDs to ensure uniqueness @@ -29,7 +28,7 @@ def _append_indices_to_samples(bframes: List[BiocFrame]) -> List[BiocFrame]: return modified_bframes -def merge_spatial_frames(x: List["SpatialExperiment"], relaxed: bool = False) -> Tuple[BiocFrame, BiocFrame]: +def merge_spatial_frames(x: list[SpatialExperiment], relaxed: bool = False) -> tuple[BiocFrame, BiocFrame]: """Merge column data and image data from multiple ``SpatialExperiment`` objects. If duplicate sample IDs exist across objects, appends indices to make them unique. @@ -70,7 +69,7 @@ def merge_spatial_frames(x: List["SpatialExperiment"], relaxed: bool = False) -> return _new_cols, _new_img_data -def merge_spatial_coordinates(spatial_coords: List[BiocFrame], relaxed: bool = False) -> BiocFrame: +def merge_spatial_coordinates(spatial_coords: list[BiocFrame], relaxed: bool = False) -> BiocFrame: """Merge spatial coordinates from multiple frames. Args: diff --git a/src/spatialexperiment/_imgutils.py b/src/spatialexperiment/_imgutils.py index 6d47b0c..279c7bd 100644 --- a/src/spatialexperiment/_imgutils.py +++ b/src/spatialexperiment/_imgutils.py @@ -1,12 +1,12 @@ -from typing import Union, List - import os from io import BytesIO from pathlib import Path from urllib.parse import urlparse + import numpy as np -from PIL import Image from biocframe import BiocFrame +from PIL import Image + from .spatialimage import construct_spatial_image_class __author__ = "keviny2" @@ -43,7 +43,7 @@ def read_image(input_image): def construct_img_data( - img: Union[str, os.PathLike], scale_factor: str, sample_id: str, image_id: str, load: bool = True + img: str | os.PathLike, scale_factor: str, sample_id: str, image_id: str, load: bool = True ) -> BiocFrame: """ Construct an image data dataframe. @@ -77,9 +77,9 @@ def construct_img_data( def get_img_idx( img_data: BiocFrame, - sample_id: Union[str, bool, None] = None, - image_id: Union[str, bool, None] = None, -) -> List[int]: + sample_id: str | bool | None = None, + image_id: str | bool | None = None, +) -> list[int]: """ Retrieve the row index/indices of image(s) with matching 'sample_id' and 'image_id' from the 'img_data'. diff --git a/src/spatialexperiment/_initutils.py b/src/spatialexperiment/_initutils.py index 2f794af..35c97d8 100644 --- a/src/spatialexperiment/_initutils.py +++ b/src/spatialexperiment/_initutils.py @@ -1,19 +1,19 @@ from copy import deepcopy -from typing import List, Tuple from biocframe import BiocFrame from PIL import Image -from .spatialimage import construct_spatial_image_class from summarizedexperiment._frameutils import _sanitize_frame +from .spatialimage import construct_spatial_image_class + __author__ = "keviny2" __copyright__ = "keviny2" __license__ = "MIT" def construct_spatial_coords_from_names( - spatial_coords_names: List[str], column_data: BiocFrame -) -> Tuple[BiocFrame, BiocFrame]: + spatial_coords_names: list[str], column_data: BiocFrame +) -> tuple[BiocFrame, BiocFrame]: """Construct the `spatial_coords` dataframe from names. Args: @@ -54,8 +54,8 @@ def construct_spatial_coords_from_names( def construct_img_data( sample_id: str, image_id: str, - image_sources: List[str], - scale_factors: List[float], + image_sources: list[str], + scale_factors: list[float], load_image: bool = False, ) -> BiocFrame: """Construct the image data for a `SpatialExperiment`. diff --git a/src/spatialexperiment/_validators.py b/src/spatialexperiment/_validators.py index b2b884d..25533b8 100644 --- a/src/spatialexperiment/_validators.py +++ b/src/spatialexperiment/_validators.py @@ -1,7 +1,7 @@ import warnings -from biocframe import BiocFrame import biocutils as ut +from biocframe import BiocFrame __author__ = "keviny2" __copyright__ = "keviny2" diff --git a/src/spatialexperiment/io/tenx_visium.py b/src/spatialexperiment/io/tenx_visium.py index 43c3855..f19ca98 100644 --- a/src/spatialexperiment/io/tenx_visium.py +++ b/src/spatialexperiment/io/tenx_visium.py @@ -1,17 +1,17 @@ """Creates a ``SpatialExperiment`` from the Space Ranger output directories for 10x Genomics Visium spatial gene expression data""" -from typing import List, Union, Optional -from warnings import warn +import json import os import re -import json +from warnings import warn -from biocframe import BiocFrame import biocutils as ut +from biocframe import BiocFrame from singlecellexperiment import read_tenx_mtx -from ..spatialexperiment import SpatialExperiment + from .._imgutils import construct_img_data from .._initutils import construct_spatial_coords_from_names +from ..spatialexperiment import SpatialExperiment def read_tissue_positions(tissue_positions_path) -> "pd.DataFrame": @@ -46,8 +46,8 @@ def read_tissue_positions(tissue_positions_path) -> "pd.DataFrame": def read_img_data( path: str = ".", - sample_ids: Optional[List[str]] = None, - image_sources: Optional[List[str]] = None, + sample_ids: list[str] | None = None, + image_sources: list[str] | None = None, scale_factors: str = None, load: bool = True, ) -> BiocFrame: @@ -118,11 +118,11 @@ def read_img_data( def read_tenx_visium( - samples: List[Union[str, os.PathLike]], - sample_ids: Optional[List[str]] = None, + samples: list[str | os.PathLike], + sample_ids: list[str] | None = None, type: str = "HDF5", data: str = "filtered", - images: List[str] = "lowres", + images: list[str] = "lowres", load: bool = True, ): """Create a ``SpatialExperiment`` from the Space Ranger output directories for 10x Genomics Visium spatial gene expression data. diff --git a/src/spatialexperiment/spatialexperiment.py b/src/spatialexperiment/spatialexperiment.py index 5c805fa..b52fe5d 100644 --- a/src/spatialexperiment/spatialexperiment.py +++ b/src/spatialexperiment/spatialexperiment.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Sequence from pathlib import Path -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union +from typing import Any from urllib.parse import urlparse from warnings import warn @@ -56,21 +57,21 @@ class SpatialExperiment(SingleCellExperiment): def __init__( self, - assays: Dict[str, Any] = None, - row_ranges: Optional[GRangesOrGRangesList] = None, - row_data: Optional[BiocFrame] = None, - column_data: Optional[BiocFrame] = None, - row_names: Optional[List[str]] = None, - column_names: Optional[List[str]] = None, - metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None, - reduced_dims: Optional[Dict[str, Any]] = None, - main_experiment_name: Optional[str] = None, - alternative_experiments: Optional[Dict[str, Any]] = None, + assays: dict[str, Any] = None, + row_ranges: GRangesOrGRangesList | None = None, + row_data: BiocFrame | None = None, + column_data: BiocFrame | None = None, + row_names: list[str] | None = None, + column_names: list[str] | None = None, + metadata: dict[str, Any] | ut.NamedList | None = None, + reduced_dims: dict[str, Any] | None = None, + main_experiment_name: str | None = None, + alternative_experiments: dict[str, Any] | None = None, alternative_experiment_check_dim_names: bool = True, - row_pairs: Optional[Any] = None, - column_pairs: Optional[Any] = None, - spatial_coords: Optional[Union[BiocFrame, np.ndarray]] = None, - img_data: Optional[BiocFrame] = None, + row_pairs: Any | None = None, + column_pairs: Any | None = None, + spatial_coords: BiocFrame | np.ndarray | None = None, + img_data: BiocFrame | None = None, _validate: bool = True, **kwargs, ) -> None: @@ -376,7 +377,7 @@ def __str__(self) -> str: output += f"row_pairs({len(self.row_pair_names)}): {ut.print_truncated_list(self.row_pair_names)}\n" output += f"column_pairs({len(self.column_pair_names)}): {ut.print_truncated_list(self.column_pair_names)}\n" - output += f"metadata({str(len(self.metadata))}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" + output += f"metadata({len(self.metadata)!s}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" output += f"spatial_coords columns({len(self.spatial_coords_names)}): {ut.print_truncated_list(self.spatial_coords_names)}\n" output += f"img_data columns({len(self._img_data.column_names)}): {ut.print_truncated_list(self._img_data.column_names)}" @@ -387,7 +388,7 @@ def __str__(self) -> str: #####>> spatial_coords <<##### ############################## - def get_spatial_coordinates(self) -> Union[BiocFrame, np.ndarray]: + def get_spatial_coordinates(self) -> BiocFrame | np.ndarray: """Access spatial coordinates. Returns: @@ -401,7 +402,7 @@ def get_spatial_coords(self) -> BiocFrame: def set_spatial_coordinates( self, - spatial_coords: Optional[Union[BiocFrame, np.ndarray]], + spatial_coords: BiocFrame | np.ndarray | None, in_place: bool = False, ) -> SpatialExperiment: """Set new spatial coordinates. @@ -436,7 +437,7 @@ def set_spatial_coordinates( def set_spatial_coords( self, - spatial_coords: Optional[Union[BiocFrame, np.ndarray]], + spatial_coords: BiocFrame | np.ndarray | None, in_place: bool = False, ) -> SpatialExperiment: """Alias for :py:meth:`~set_spatial_coordinates`.""" @@ -448,7 +449,7 @@ def spatial_coords(self) -> BiocFrame: return self.get_spatial_coordinates() @spatial_coords.setter - def spatial_coords(self, spatial_coords: Optional[Union[BiocFrame, np.ndarray]]): + def spatial_coords(self, spatial_coords: BiocFrame | np.ndarray | None): """Alias for :py:meth:`~set_spatial_coordinates`.""" warn( "Setting property 'spatial_coords' is an in-place operation, use 'set_spatial_coordinates' instead.", @@ -462,7 +463,7 @@ def spatial_coordinates(self) -> BiocFrame: return self.get_spatial_coordinates() @spatial_coordinates.setter - def spatial_coordinates(self, spatial_coords: Optional[Union[BiocFrame, np.ndarray]]): + def spatial_coordinates(self, spatial_coords: BiocFrame | np.ndarray | None): """Alias for :py:meth:`~set_spatial_coordinates`.""" warn( "Setting property 'spatial_coords' is an in-place operation, use 'set_spatial_coordinates' instead.", @@ -474,7 +475,7 @@ def spatial_coordinates(self, spatial_coords: Optional[Union[BiocFrame, np.ndarr ##>> spatial_coords_names <<## ############################## - def get_spatial_coordinates_names(self) -> List[str]: + def get_spatial_coordinates_names(self) -> list[str]: """Access spatial coordinates names. Returns: @@ -485,12 +486,12 @@ def get_spatial_coordinates_names(self) -> List[str]: return self._spatial_coords.columns.as_list() - def get_spatial_coords_names(self) -> List[str]: + def get_spatial_coords_names(self) -> list[str]: """Alias for :py:meth:`~get_spatial_coordinate_names`.""" return self.get_spatial_coordinate_names() def set_spatial_coordinates_names( - self, spatial_coords_names: List[str], in_place: bool = False + self, spatial_coords_names: list[str], in_place: bool = False ) -> SpatialExperiment: """Set new spatial coordinates names. @@ -518,17 +519,17 @@ def set_spatial_coordinates_names( output._spatial_coords = new_spatial_coords return output - def set_spatial_coords_names(self, spatial_coords_names: List[str], in_place: bool = False) -> SpatialExperiment: + def set_spatial_coords_names(self, spatial_coords_names: list[str], in_place: bool = False) -> SpatialExperiment: """Alias for :py:meth:`~set_spatial_coordinates_names`.""" return self.set_spatial_coordinates_names(spatial_coords_names=spatial_coords_names, in_place=in_place) @property - def spatial_coords_names(self) -> List[str]: + def spatial_coords_names(self) -> list[str]: """Alias for :py:meth:`~get_spatial_coordinates_names`.""" return self.get_spatial_coordinates_names() @spatial_coords_names.setter - def spatial_coords_names(self, spatial_coords_names: List[str]): + def spatial_coords_names(self, spatial_coords_names: list[str]): """Alias for :py:meth:`~set_spatial_coordinates_names`.""" warn( "Setting property 'spatial_coords_names' is an in-place operation, use 'set_spatial_coordinates_names' instead.", @@ -537,12 +538,12 @@ def spatial_coords_names(self, spatial_coords_names: List[str]): self.set_spatial_coordinates_names(spatial_coords_names=spatial_coords_names, in_place=True) @property - def spatial_coordinates_names(self) -> List[str]: + def spatial_coordinates_names(self) -> list[str]: """Alias for :py:meth:`~get_spatial_coordinates_names`.""" return self.get_spatial_coordinates_names() @spatial_coordinates_names.setter - def spatial_coordinates_names(self, spatial_coords_names: List[str]): + def spatial_coordinates_names(self, spatial_coords_names: list[str]): """Alias for :py:meth:`~set_spatial_coordinates_names`.""" warn( "Setting property 'spatial_coords_names' is an in-place operation, use 'set_spatial_coordinates_names' instead.", @@ -566,7 +567,7 @@ def get_img_data(self) -> BiocFrame: """Alias for :py:meth:`~get_image_data`.""" return self.get_image_data() - def set_image_data(self, img_data: Optional[BiocFrame], in_place: bool = False) -> SpatialExperiment: + def set_image_data(self, img_data: BiocFrame | None, in_place: bool = False) -> SpatialExperiment: """Set new image data. Args: @@ -633,9 +634,9 @@ def image_data(self, img_data: BiocFrame): def get_scale_factors( self, - sample_id: Union[str, bool, None] = None, - image_id: Union[str, bool, None] = None, - ) -> List[float]: + sample_id: str | bool | None = None, + image_id: str | bool | None = None, + ) -> list[float]: """Return scale factor(s) of image(s) based on the provided sample and image ids. See :py:meth:`~get_img` for more details on the behavior for various combinations of `sample_id` and `image_id` values. @@ -667,7 +668,7 @@ def get_scale_factors( def set_column_data( self, - cols: Optional[BiocFrame], + cols: BiocFrame | None, replace_column_names: bool = False, in_place: bool = False, ) -> SpatialExperiment: @@ -712,8 +713,8 @@ def set_column_data( def get_slice( self, - rows: Optional[Union[str, int, bool, Sequence]], - columns: Optional[Union[str, int, bool, Sequence]], + rows: str | int | bool | Sequence | None, + columns: str | int | bool | Sequence | None, ) -> SpatialExperiment: """Alias for :py:attr:`~__getitem__`.""" @@ -756,9 +757,9 @@ def get_slice( def get_img( self, - sample_id: Union[str, bool, None] = None, - image_id: Union[str, bool, None] = None, - ) -> Union[VirtualSpatialImage, List[VirtualSpatialImage]]: + sample_id: str | bool | None = None, + image_id: str | bool | None = None, + ) -> VirtualSpatialImage | list[VirtualSpatialImage]: """Retrieve spatial images based on the provided sample and image ids. Args: @@ -818,10 +819,10 @@ def get_img( def add_img( self, - image_source: Union[Image.Image, np.ndarray, str, Path], + image_source: Image.Image | np.ndarray | str | Path, scale_factor: float, - sample_id: Union[str, bool, None], - image_id: Union[str, bool, None], + sample_id: str | bool | None, + image_id: str | bool | None, load: bool = True, in_place: bool = False, ) -> SpatialExperiment: @@ -886,7 +887,7 @@ def add_img( return output def remove_img( - self, sample_id: Union[str, bool, None] = None, image_id: Union[str, bool, None] = None, in_place: bool = False + self, sample_id: str | bool | None = None, image_id: str | bool | None = None, in_place: bool = False ) -> SpatialExperiment: """Remove an image entry. @@ -930,10 +931,10 @@ def remove_img( def img_source( self, - sample_id: Union[str, bool, None] = None, - image_id: Union[str, bool, None] = None, + sample_id: str | bool | None = None, + image_id: str | bool | None = None, path=False, - ) -> Union[str, Path, None, List[Union[str, Path]]]: + ) -> str | Path | None | list[str | Path]: """Retrieve the source(s) for images stored in the SpatialExperiment object. Args: @@ -972,7 +973,7 @@ def img_source( return img_sources - def img_raster(self, sample_id=None, image_id=None) -> Union[Image.Image, List[Image.Image], None]: + def img_raster(self, sample_id=None, image_id=None) -> Image.Image | list[Image.Image] | None: """Retrieve and load (if necessary) the images stored in the SpatialExperiment object. Args: @@ -1025,7 +1026,7 @@ def to_spatial_experiment(): def to_anndata( self, include_alternative_experiments: bool = False - ) -> Tuple["anndata.AnnData", Dict[str, "anndata.AnnData"]]: + ) -> tuple[anndata.AnnData, dict[str, anndata.AnnData]]: """Transform :py:class:`~SpatialExperiment`-like into a :py:class:`~anndata.AnnData` representation. This method converts the main experiment data, spatial coordinates, @@ -1128,7 +1129,7 @@ def combine_columns(*x: SpatialExperiment) -> SpatialExperiment: _new_rdim = merge_generic(x, by="row", attr="reduced_dims") except Exception as e: warn( - f"Cannot combine 'reduced_dimensions' across experiments, {str(e)}", + f"Cannot combine 'reduced_dimensions' across experiments, {e!s}", UserWarning, ) @@ -1137,7 +1138,7 @@ def combine_columns(*x: SpatialExperiment) -> SpatialExperiment: _new_alt_expt = merge_generic(x, by="column", attr="alternative_experiments") except Exception as e: warn( - f"Cannot combine 'alternative_experiments' across experiments, {str(e)}", + f"Cannot combine 'alternative_experiments' across experiments, {e!s}", UserWarning, ) @@ -1197,7 +1198,7 @@ def relaxed_combine_columns( _new_rdim = relaxed_merge_numpy_generic(x, by="row", attr="reduced_dims") except Exception as e: warn( - f"Cannot combine 'reduced_dimensions' across experiments, {str(e)}", + f"Cannot combine 'reduced_dimensions' across experiments, {e!s}", UserWarning, ) @@ -1206,7 +1207,7 @@ def relaxed_combine_columns( _new_alt_expt = relaxed_merge_generic(x, by="column", attr="alternative_experiments") except Exception as e: warn( - f"Cannot combine 'alternative_experiments' across experiments, {str(e)}", + f"Cannot combine 'alternative_experiments' across experiments, {e!s}", UserWarning, ) diff --git a/src/spatialexperiment/spatialimage.py b/src/spatialexperiment/spatialimage.py index a4a3f0b..758fd3c 100644 --- a/src/spatialexperiment/spatialimage.py +++ b/src/spatialexperiment/spatialimage.py @@ -3,7 +3,6 @@ from abc import abstractmethod from functools import lru_cache from pathlib import Path -from typing import Optional, Tuple, Union from urllib.parse import urlparse from warnings import warn @@ -22,7 +21,7 @@ class VirtualSpatialImage(ut.BiocObject): """Base class for spatial images.""" - def __init__(self, metadata: Optional[dict] = None): + def __init__(self, metadata: dict | None = None): super().__init__(metadata=metadata) ######################### @@ -53,13 +52,13 @@ def affine(self, scale_factor: float = 1.0) -> Affine: """ return Affine.scale(scale_factor, scale_factor) - def get_dimensions(self) -> Tuple[int, int]: + def get_dimensions(self) -> tuple[int, int]: """Get image dimensions (width, height) in pixels.""" img = self.img_raster() return img.size @property - def dimensions(self) -> Tuple[int, int]: + def dimensions(self) -> tuple[int, int]: """Alias for :py:meth:`~get_dimensions`.""" return self.get_dimensions() @@ -68,7 +67,7 @@ def dimensions(self) -> Tuple[int, int]: ############################ @abstractmethod - def img_source(self, as_path: bool = False) -> Union[str, Path, None]: + def img_source(self, as_path: bool = False) -> str | Path | None: """Get the source of the image. Args: @@ -77,12 +76,10 @@ def img_source(self, as_path: bool = False) -> Union[str, Path, None]: Returns: Source path/URL of the image, or None if loaded in memory. """ - pass @abstractmethod def img_raster(self) -> Image.Image: """Get the image as a PIL Image object.""" - pass def to_numpy(self, **kwargs) -> np.ndarray: """Convert the image raster to a NumPy array. @@ -132,7 +129,7 @@ def mirror_img(self, axis: str = "h") -> "LoadedSpatialImage": ) -def _sanitize_loaded_image(image: Union[Image.Image, np.ndarray]) -> Image.Image: +def _sanitize_loaded_image(image: Image.Image | np.ndarray) -> Image.Image: if isinstance(image, np.ndarray): # trying to infer mode for multi-channel arrays if not RGBA/RGB if image.ndim == 3: @@ -159,7 +156,7 @@ def _sanitize_loaded_image(image: Union[Image.Image, np.ndarray]) -> Image.Image class LoadedSpatialImage(VirtualSpatialImage): """Class for images loaded into memory.""" - def __init__(self, image: Union[Image.Image, np.ndarray], metadata: Optional[dict] = None): + def __init__(self, image: Image.Image | np.ndarray, metadata: dict | None = None): """Initialize the object. Args: @@ -260,7 +257,7 @@ def __str__(self) -> str: """ output = f"class: {type(self).__name__}\n" output += f"image: ({self._image})\n" - output += f"metadata({str(len(self.metadata))}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" + output += f"metadata({len(self.metadata)!s}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" return output @@ -272,7 +269,7 @@ def get_image(self) -> Image.Image: """Get the PIL Image object.""" return self._image - def set_image(self, image: Union[Image.Image, np.ndarray], in_place: bool = False) -> "LoadedSpatialImage": + def set_image(self, image: Image.Image | np.ndarray, in_place: bool = False) -> "LoadedSpatialImage": """Set new image. Args: @@ -295,7 +292,7 @@ def image(self) -> Image.Image: return self.get_image() @image.setter - def image(self, image: Union[Image.Image, np.ndarray]): + def image(self, image: Image.Image | np.ndarray): """Alias for :py:attr:`~set_image` with ``in_place = True``. As this mutates the original object, a warning is raised. @@ -308,7 +305,7 @@ def image(self, image: Union[Image.Image, np.ndarray]): def img_source(self, as_path: bool = False) -> None: """Get the source of the loaded image (always None for in-memory).""" - return None + return ############################ ######>> img utils <<####### @@ -319,7 +316,7 @@ def img_raster(self) -> Image.Image: return self._image -def _sanitize_path(path: Union[str, Path]) -> Path: +def _sanitize_path(path: str | Path) -> Path: _path = Path(path).resolve() if not _path.exists(): raise FileNotFoundError(f"Image file not found: {path}") @@ -330,7 +327,7 @@ def _sanitize_path(path: Union[str, Path]) -> Path: class StoredSpatialImage(VirtualSpatialImage): """Class for images stored on local filesystem.""" - def __init__(self, path: Union[str, Path], metadata: Optional[dict] = None): + def __init__(self, path: str | Path, metadata: dict | None = None): """Initialize the object. Args: @@ -414,8 +411,8 @@ def __str__(self) -> str: A pretty-printed string containing the contents of this object. """ output = f"class: {type(self).__name__}\n" - output += f"path: ({str(self._path)})\n" - output += f"metadata({str(len(self.metadata))}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" + output += f"path: ({self._path!s})\n" + output += f"metadata({len(self.metadata)!s}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" return output @@ -427,7 +424,7 @@ def get_path(self) -> Path: """Get the path to the image file.""" return self._path - def set_path(self, path: Union[str, Path], in_place: bool = False) -> "StoredSpatialImage": + def set_path(self, path: str | Path, in_place: bool = False) -> "StoredSpatialImage": """Update the path to the image file. Args: @@ -455,7 +452,7 @@ def path(self) -> Path: return self.get_path() @path.setter - def path(self, path: Union[str, Path]): + def path(self, path: str | Path): """Alias for :py:attr:`~set_path` with ``in_place = True``. As this mutates the original object, a warning is raised. @@ -499,7 +496,7 @@ def _validate_url(url: str): class RemoteSpatialImage(VirtualSpatialImage): """Class for remotely hosted images.""" - def __init__(self, url: str, metadata: Optional[dict] = None, validate: bool = True): + def __init__(self, url: str, metadata: dict | None = None, validate: bool = True): """Initialize the object. Args: @@ -592,7 +589,7 @@ def __str__(self) -> str: """ output = f"class: {type(self).__name__}\n" output += f"url: ({self._url})\n" - output += f"metadata({str(len(self.metadata))}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" + output += f"metadata({len(self.metadata)!s}): {ut.print_truncated_list(list(self.metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" return output @@ -676,7 +673,7 @@ def _get_cached_path(self) -> Path: # If download fails, remove incomplete cache file and re-raise if cache_path.exists(): cache_path.unlink(missing_ok=True) - raise IOError(f"Failed to download image from {self._url}: {e}.") from e + raise OSError(f"Failed to download image from {self._url}: {e}.") from e except ValueError as e: raise ValueError(f"Invalid URL for download {self._url}: {e}.") from e return cache_path @@ -713,9 +710,9 @@ def img_source(self, as_path: bool = False) -> str: def construct_spatial_image_class( - x: Union[str, Path, Image.Image, np.ndarray, VirtualSpatialImage], - metadata: Optional[dict] = None, - is_url: Optional[bool] = None, + x: str | Path | Image.Image | np.ndarray | VirtualSpatialImage, + metadata: dict | None = None, + is_url: bool | None = None, ) -> VirtualSpatialImage: """Factory function to create appropriate SpatialImage object.