Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/sweep-api-consistency-state.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module,last_inspected,issue,severity_max,categories_found,notes
geotiff,2026-05-12,1683;1684;1685,MEDIUM,3;5,"Sweep v2 (deep-sweep-api-consistency-geotiff-2026-05-12-v2). 3 MEDIUM findings filed and fixed: #1683 bigtiff docstring gap on to_geotiff (Cat 3, PR #1686); #1684 write_vrt nodata float|None widened to float|int|None (Cat 3, PR #1687); #1685 open_geotiff silent overview_level/on_gpu_failure drop on VRT sources (Cat 5, PR #1689). Prior v1 fix (#1654) covered type-annotation parity across window/path/on_gpu_failure. cuda-validated."
geotiff,2026-05-12,1754,MEDIUM,3,"Sweep v3 (deep-sweep-api-consistency-geotiff-2026-05-12-v3). 1 MEDIUM finding filed and fixed: #1754 open_geotiff source kwarg lacks str|BinaryIO type annotation (Cat 3). PR #1754 adds the annotation + pinning tests for source on all 4 reader entry points. Prior sweep findings (#1683, #1684, #1685, #1654) all confirmed fixed. cuda-validated."
reproject,2026-05-10,1570,HIGH,2;5,"Filed cross-module attrs['vertical_crs'] type collision (string vs EPSG int) vs xrspatial.geotiff. Fixed in PR (TBD): reproject now writes EPSG int and preserves friendly token under vertical_datum. MEDIUM kwarg-order drift (transform_precision vs chunk_size) and missing type hints vs geotiff documented but not fixed (cosmetic, kwarg-only)."
2 changes: 1 addition & 1 deletion xrspatial/geotiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ def _populate_attrs_from_geo_info(attrs: dict, geo_info, *, window=None) -> None
break


def open_geotiff(source, *, dtype=None,
def open_geotiff(source: str | BinaryIO, *, dtype=None,
window: tuple | None = None,
overview_level: int | None = None,
band: int | None = None,
Expand Down
61 changes: 61 additions & 0 deletions xrspatial/geotiff/tests/test_signature_annotations_1654.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,40 @@ def test_write_vrt_vrt_path_annotated():
assert _annotation(write_vrt, 'vrt_path') == 'str'


# --- source: str or BinaryIO (open_geotiff is the public dispatch) ---


def test_open_geotiff_source_annotated():
"""``open_geotiff(source, ...)`` accepts ``str | BinaryIO`` to match
the writer ``path`` annotation and the runtime behaviour the
docstring documents (BytesIO buffers are routed through the eager
numpy reader). The dedicated reader entry points stay ``str``-only
because they reject file-like sources at runtime. See issue #1754.
"""
ann = _annotation(open_geotiff, 'source')
assert 'str' in ann
assert 'BinaryIO' in ann


def test_read_geotiff_dask_source_str_only():
"""``read_geotiff_dask(source: str)`` stays str-only: the dask path
reopens the source by path from each worker task and does not
support file-like buffers."""
assert _annotation(read_geotiff_dask, 'source') == 'str'


def test_read_geotiff_gpu_source_str_only():
"""``read_geotiff_gpu(source: str)`` stays str-only: GPU decode
paths read by path / mmap and do not support file-like buffers."""
assert _annotation(read_geotiff_gpu, 'source') == 'str'


def test_read_vrt_source_str_only():
"""``read_vrt(source: str)`` stays str-only: the VRT XML references
its own source files on disk."""
assert _annotation(read_vrt, 'source') == 'str'


# --- on_gpu_failure: 'auto' | 'strict' (GPU failure policy) ---


Expand Down Expand Up @@ -129,3 +163,30 @@ def test_open_geotiff_window_kwarg_runtime(tmp_path):
to_geotiff(da, path)
r = open_geotiff(path, window=(0, 0, 4, 4))
assert r.shape == (4, 4)


def test_open_geotiff_bytesio_source_runtime(tmp_path):
"""``open_geotiff`` routes a ``BytesIO`` source through the eager
numpy reader. The annotation pins this contract at the type level;
this test pins it at the runtime level so a future refactor that
drops the file-like branch fails CI. See issue #1754.
"""
import io
import numpy as np
import xarray as xr

arr = np.arange(64, dtype=np.float32).reshape(8, 8)
da = xr.DataArray(
arr, dims=['y', 'x'],
coords={'y': np.arange(8.0, 0, -1), 'x': np.arange(8.0)},
attrs={'crs': 4326, 'transform': (1.0, 0, 0.0, 0, -1.0, 8.0)},
)

path = str(tmp_path / 'bytesio_source.tif')
to_geotiff(da, path)
with open(path, 'rb') as f:
buffer = io.BytesIO(f.read())

r = open_geotiff(buffer)
assert r.shape == (8, 8)
assert r.dtype == np.float32
Loading