From 65eb533c55d7cd8b212693d3f0ac783a9160c539 Mon Sep 17 00:00:00 2001 From: Thomas Maschler Date: Sun, 26 Jul 2026 15:42:39 -0400 Subject: [PATCH 1/2] [rasterio] Accept WarpedVRT and __geo_interface__ geometries Widen dataset parameters from the concrete DatasetReader to the shared DatasetReaderBase so any readable dataset (including WarpedVRT) is accepted, matching the runtime duck-typed contract: - merge.merge and stack.stack sources - features.geometry_window and features.dataset_features - mask.mask and mask.raster_geometry_mask - sample.sample_gen - the AnyDataset alias (now DatasetReaderBase | MemoryFile) Introduce a shared Geometry alias in _typing (a GeoJSON-like Mapping or any object implementing __geo_interface__, e.g. shapely geometries) and use it wherever the runtime unwraps __geo_interface__: - features.rasterize, geometry_mask, bounds, is_valid_geom, geometry_window - mask.mask and mask.raster_geometry_mask shapes - warp.transform_geom and _warp._transform_geom --- stubs/rasterio/rasterio/_typing.pyi | 22 ++++++++++++++++++---- stubs/rasterio/rasterio/_warp.pyi | 6 +++--- stubs/rasterio/rasterio/features.pyi | 15 +++++++-------- stubs/rasterio/rasterio/mask.pyi | 13 +++++++------ stubs/rasterio/rasterio/merge.pyi | 6 +++--- stubs/rasterio/rasterio/sample.pyi | 7 +++++-- stubs/rasterio/rasterio/stack.pyi | 4 ++-- stubs/rasterio/rasterio/warp.pyi | 6 +++--- 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/stubs/rasterio/rasterio/_typing.pyi b/stubs/rasterio/rasterio/_typing.pyi index b35e63a6bdaa..974afc3a8369 100644 --- a/stubs/rasterio/rasterio/_typing.pyi +++ b/stubs/rasterio/rasterio/_typing.pyi @@ -1,12 +1,26 @@ -from collections.abc import Callable, Sequence +from collections.abc import Callable, Mapping, Sequence from enum import Enum -from typing import Any, BinaryIO, TypeAlias +from typing import Any, BinaryIO, Protocol, TypeAlias, type_check_only from rasterio.crs import CRS -from rasterio.io import BufferedDatasetWriter, DatasetReader, DatasetWriter, MemoryFile +from rasterio.io import DatasetReaderBase, MemoryFile from rasterio.windows import Window -AnyDataset: TypeAlias = DatasetReader | DatasetWriter | BufferedDatasetWriter | MemoryFile +# `DatasetReaderBase` covers every readable dataset handle: DatasetReader, +# DatasetWriter, BufferedDatasetWriter, MemoryDataset, and WarpedVRT (via +# WarpedVRTReaderBase). `MemoryFile` is a file wrapper, not a dataset. +AnyDataset: TypeAlias = DatasetReaderBase | MemoryFile + +@type_check_only +class _SupportsGeoInterface(Protocol): + @property + def __geo_interface__(self) -> Mapping[str, Any]: ... + +# A GeoJSON-like mapping, or any object exposing one through the +# `__geo_interface__` protocol (e.g. shapely / geopandas geometries). +# The runtime unwraps `__geo_interface__` before use, so both forms are +# accepted anywhere a geometry is expected. +Geometry: TypeAlias = Mapping[str, Any] | _SupportsGeoInterface # noqa: Y047 Colormap: TypeAlias = dict[int, tuple[int, int, int] | tuple[int, int, int, int]] CRSInput: TypeAlias = str | dict[str, str] | CRS FileOrBytes: TypeAlias = BinaryIO | bytes diff --git a/stubs/rasterio/rasterio/_warp.pyi b/stubs/rasterio/rasterio/_warp.pyi index f19a75a6bd32..b98484b6fd62 100644 --- a/stubs/rasterio/rasterio/_warp.pyi +++ b/stubs/rasterio/rasterio/_warp.pyi @@ -1,10 +1,10 @@ -from collections.abc import Mapping, Sequence +from collections.abc import Sequence from typing import Any, Final from numpy.typing import DTypeLike, NDArray from rasterio._affine_types import Affine from rasterio._io import DatasetReaderBase -from rasterio._typing import CRSInput, Indexes, ShapeND, WindowInput, _GDALOption, _NestedScalar +from rasterio._typing import CRSInput, Geometry, Indexes, ShapeND, WindowInput, _GDALOption, _NestedScalar from rasterio.control import GroundControlPoint from rasterio.crs import CRS from rasterio.enums import Resampling @@ -16,7 +16,7 @@ DEFAULT_NODATA_FLAG: Final[object] def recursive_round(val: _NestedScalar, precision: int) -> _NestedScalar: ... def _transform_geom( - src_crs: CRSInput, dst_crs: CRSInput, geom: Mapping[str, Any] | Sequence[Mapping[str, Any]], precision: int + src_crs: CRSInput, dst_crs: CRSInput, geom: Geometry | Sequence[Geometry], precision: int ) -> dict[str, Any] | list[dict[str, Any]]: ... def _reproject( source: NDArray[Any] | Any, diff --git a/stubs/rasterio/rasterio/features.pyi b/stubs/rasterio/rasterio/features.pyi index 33315f663d02..d3689801f08e 100644 --- a/stubs/rasterio/rasterio/features.pyi +++ b/stubs/rasterio/rasterio/features.pyi @@ -1,20 +1,19 @@ import logging import os -from collections.abc import Iterable, Iterator, Mapping -from typing import Any, Final, TypeAlias, overload +from collections.abc import Iterable, Iterator +from typing import Any, Final, overload from typing_extensions import deprecated import numpy as np from numpy.typing import DTypeLike, NDArray from rasterio._affine_types import Affine +from rasterio._typing import Geometry as Geometry from rasterio.enums import MergeAlg as MergeAlg -from rasterio.io import DatasetReader +from rasterio.io import DatasetReaderBase from rasterio.windows import Window as Window log: Final[logging.Logger] -Geometry: TypeAlias = Mapping[str, Any] - def geometry_mask( geometries: Iterable[Geometry], out_shape: tuple[int, int], transform: Affine, all_touched: bool = False, invert: bool = False ) -> NDArray[np.bool_]: ... @@ -44,7 +43,7 @@ def bounds(geometry: Geometry, north_up: bool = True, transform: Affine | None = @overload def geometry_window( - dataset: DatasetReader, shapes: Iterable[Geometry], pad_x: float = 0, pad_y: float = 0, *, boundless: bool = False + dataset: DatasetReaderBase, shapes: Iterable[Geometry], pad_x: float = 0, pad_y: float = 0, *, boundless: bool = False ) -> Window: ... @overload @deprecated( @@ -52,7 +51,7 @@ def geometry_window( "unused since rasterio 1.2.1 and will be removed in a future release." ) def geometry_window( - dataset: DatasetReader, + dataset: DatasetReaderBase, shapes: Iterable[Geometry], pad_x: float = 0, pad_y: float = 0, @@ -64,7 +63,7 @@ def geometry_window( def is_valid_geom(geom: Geometry) -> bool: ... def dataset_features( - src: DatasetReader, + src: DatasetReaderBase, bidx: int | None = None, sampling: int = 1, band: bool = True, diff --git a/stubs/rasterio/rasterio/mask.pyi b/stubs/rasterio/rasterio/mask.pyi index bfb30da2077e..016f621c567f 100644 --- a/stubs/rasterio/rasterio/mask.pyi +++ b/stubs/rasterio/rasterio/mask.pyi @@ -1,18 +1,19 @@ import logging -from collections.abc import Iterable, Mapping +from collections.abc import Iterable from typing import Any, Final from numpy.typing import NDArray from rasterio._affine_types import Affine +from rasterio._typing import Geometry from rasterio.errors import WindowError as WindowError from rasterio.features import geometry_mask as geometry_mask, geometry_window as geometry_window -from rasterio.io import DatasetReader +from rasterio.io import DatasetReaderBase logger: Final[logging.Logger] def raster_geometry_mask( - dataset: DatasetReader, - shapes: Iterable[Mapping[str, Any]], + dataset: DatasetReaderBase, + shapes: Iterable[Geometry], all_touched: bool = False, invert: bool = False, crop: bool = False, @@ -20,8 +21,8 @@ def raster_geometry_mask( pad_width: float = 0.5, ) -> tuple[NDArray[Any], Affine, tuple[int, int, int, int]]: ... def mask( - dataset: DatasetReader, - shapes: Iterable[Mapping[str, Any]], + dataset: DatasetReaderBase, + shapes: Iterable[Geometry], all_touched: bool = False, invert: bool = False, nodata: float | None = None, diff --git a/stubs/rasterio/rasterio/merge.pyi b/stubs/rasterio/rasterio/merge.pyi index 30907635d249..d9d767d53c2e 100644 --- a/stubs/rasterio/rasterio/merge.pyi +++ b/stubs/rasterio/rasterio/merge.pyi @@ -7,7 +7,7 @@ from typing_extensions import deprecated from numpy.typing import DTypeLike, NDArray from rasterio._affine_types import Affine from rasterio.enums import Resampling -from rasterio.io import DatasetReader +from rasterio.io import DatasetReaderBase logger: Final[logging.Logger] @@ -26,7 +26,7 @@ def copy_count(merged_data: _Arr, new_data: _Arr, merged_mask: _Arr, new_mask: _ @overload def merge( - sources: Sequence[DatasetReader | str | os.PathLike[str]], + sources: Sequence[DatasetReaderBase | str | os.PathLike[str]], bounds: tuple[float, float, float, float] | None = None, res: float | tuple[float, float] | None = None, nodata: float | None = None, @@ -46,7 +46,7 @@ def merge( @overload @deprecated("The `precision` parameter is unused since rasterio 1.3 and will be removed in 2.0.0.") def merge( - sources: Sequence[DatasetReader | str | os.PathLike[str]], + sources: Sequence[DatasetReaderBase | str | os.PathLike[str]], bounds: tuple[float, float, float, float] | None = None, res: float | tuple[float, float] | None = None, nodata: float | None = None, diff --git a/stubs/rasterio/rasterio/sample.pyi b/stubs/rasterio/rasterio/sample.pyi index 8c87ecae7fcf..4d7722bae293 100644 --- a/stubs/rasterio/rasterio/sample.pyi +++ b/stubs/rasterio/rasterio/sample.pyi @@ -2,9 +2,12 @@ from collections.abc import Iterable, Iterator, Sequence from typing import Any from numpy.typing import NDArray -from rasterio.io import DatasetReader +from rasterio.io import DatasetReaderBase def sample_gen( - dataset: DatasetReader, xy: Iterable[tuple[float, float]], indexes: int | Sequence[int] | None = None, masked: bool = False + dataset: DatasetReaderBase, + xy: Iterable[tuple[float, float]], + indexes: int | Sequence[int] | None = None, + masked: bool = False, ) -> Iterator[NDArray[Any]]: ... def sort_xy(xy: Iterable[tuple[float, float]]) -> list[tuple[float, float]]: ... diff --git a/stubs/rasterio/rasterio/stack.pyi b/stubs/rasterio/rasterio/stack.pyi index 56a1553c55c9..68680ffe9b1e 100644 --- a/stubs/rasterio/rasterio/stack.pyi +++ b/stubs/rasterio/rasterio/stack.pyi @@ -6,12 +6,12 @@ from typing import Any, Final from numpy.typing import DTypeLike, NDArray from rasterio._affine_types import Affine from rasterio.enums import Resampling -from rasterio.io import DatasetReader +from rasterio.io import DatasetReaderBase logger: Final[logging.Logger] def stack( - sources: Sequence[DatasetReader | str | os.PathLike[str]], + sources: Sequence[DatasetReaderBase | str | os.PathLike[str]], bounds: tuple[float, float, float, float] | None = None, res: float | tuple[float, float] | None = None, nodata: float | None = None, diff --git a/stubs/rasterio/rasterio/warp.pyi b/stubs/rasterio/rasterio/warp.pyi index b1ee2388a87a..09e4a86ca08d 100644 --- a/stubs/rasterio/rasterio/warp.pyi +++ b/stubs/rasterio/rasterio/warp.pyi @@ -5,7 +5,7 @@ from typing_extensions import deprecated from numpy.typing import ArrayLike, NDArray from rasterio._affine_types import Affine -from rasterio._typing import CRSInput, _GDALOption +from rasterio._typing import CRSInput, Geometry, _GDALOption from rasterio.control import GroundControlPoint from rasterio.enums import Resampling from rasterio.rpc import RPC @@ -22,7 +22,7 @@ def transform( @overload def transform_geom( - src_crs: CRSInput, dst_crs: CRSInput, geom: Mapping[str, Any] | Sequence[Mapping[str, Any]], *, precision: float = -1 + src_crs: CRSInput, dst_crs: CRSInput, geom: Geometry | Sequence[Geometry], *, precision: float = -1 ) -> dict[str, Any] | list[dict[str, Any]]: ... @overload @deprecated( @@ -33,7 +33,7 @@ def transform_geom( def transform_geom( src_crs: CRSInput, dst_crs: CRSInput, - geom: Mapping[str, Any] | Sequence[Mapping[str, Any]], + geom: Geometry | Sequence[Geometry], antimeridian_cutting: bool | None = None, antimeridian_offset: float | None = None, precision: float = -1, From bc584215faab596b4d8a4f384e33d81117ae6b81 Mon Sep 17 00:00:00 2001 From: Thomas Maschler Date: Sun, 26 Jul 2026 15:53:20 -0400 Subject: [PATCH 2/2] [rasterio] Remove unused stubtest allowlist entry `Geometry` now lives in the blanket-allowlisted `rasterio._typing` module and is only re-exported from `rasterio.features`, so stubtest no longer flags `rasterio.features.Geometry` and the allowlist entry was unused. --- stubs/rasterio/@tests/stubtest_allowlist.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/stubs/rasterio/@tests/stubtest_allowlist.txt b/stubs/rasterio/@tests/stubtest_allowlist.txt index eaf85b9af215..ce1edb64708e 100644 --- a/stubs/rasterio/@tests/stubtest_allowlist.txt +++ b/stubs/rasterio/@tests/stubtest_allowlist.txt @@ -3,7 +3,6 @@ rasterio\._typing rasterio\._affine_types # Stubs-only type aliases referenced in signatures only. -rasterio\.features\.Geometry rasterio\.merge\.MethodFunction # Cython implementation-detail attributes auto-generated on every