Skip to content

Commit 72aea74

Browse files
committed
Annotate open_geotiff(source) as str | BinaryIO (#1754)
The api-consistency sweep on 2026-05-12 found a parameter-annotation gap on open_geotiff(source): the docstring documents "str or binary file-like" and the runtime accepts BytesIO, but the signature has no annotation. Sibling reader functions (read_geotiff_gpu, read_geotiff_dask, read_vrt) all annotate source: str because they reject file-like sources. The writer entry points (to_geotiff, write_geotiff_gpu) carry path: str | BinaryIO for the same file-like-or-string surface that open_geotiff exposes. PR #1654 annotated window, path, and on_gpu_failure across the public geotiff surface but missed source on open_geotiff. This PR closes that gap. test_signature_annotations_1654.py picks up four new pins: - open_geotiff(source) must accept both str and BinaryIO - read_geotiff_dask, read_geotiff_gpu, read_vrt stay str-only - a runtime smoke test that a BytesIO buffer round-trips through open_geotiff Annotation-only change; no runtime behaviour, no defaults, no kwarg renames. BinaryIO is already imported under TYPE_CHECKING in xrspatial/geotiff/__init__.py from PR #1654. Also updates the api-consistency sweep state CSV with the v3 row for geotiff.
1 parent e7b9cde commit 72aea74

3 files changed

Lines changed: 63 additions & 2 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module,last_inspected,issue,severity_max,categories_found,notes
2-
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."
2+
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."
33
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)."

xrspatial/geotiff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ def _populate_attrs_from_geo_info(attrs: dict, geo_info, *, window=None) -> None
641641
break
642642

643643

644-
def open_geotiff(source, *, dtype=None,
644+
def open_geotiff(source: str | BinaryIO, *, dtype=None,
645645
window: tuple | None = None,
646646
overview_level: int | None = None,
647647
band: int | None = None,

xrspatial/geotiff/tests/test_signature_annotations_1654.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,40 @@ def test_write_vrt_vrt_path_annotated():
8989
assert _annotation(write_vrt, 'vrt_path') == 'str'
9090

9191

92+
# --- source: str or BinaryIO (open_geotiff is the public dispatch) ---
93+
94+
95+
def test_open_geotiff_source_annotated():
96+
"""``open_geotiff(source, ...)`` accepts ``str | BinaryIO`` to match
97+
the writer ``path`` annotation and the runtime behaviour the
98+
docstring documents (BytesIO buffers are routed through the eager
99+
numpy reader). The dedicated reader entry points stay ``str``-only
100+
because they reject file-like sources at runtime. See issue #1754.
101+
"""
102+
ann = _annotation(open_geotiff, 'source')
103+
assert 'str' in ann
104+
assert 'BinaryIO' in ann
105+
106+
107+
def test_read_geotiff_dask_source_str_only():
108+
"""``read_geotiff_dask(source: str)`` stays str-only: the dask path
109+
reopens the source by path from each worker task and does not
110+
support file-like buffers."""
111+
assert _annotation(read_geotiff_dask, 'source') == 'str'
112+
113+
114+
def test_read_geotiff_gpu_source_str_only():
115+
"""``read_geotiff_gpu(source: str)`` stays str-only: GPU decode
116+
paths read by path / mmap and do not support file-like buffers."""
117+
assert _annotation(read_geotiff_gpu, 'source') == 'str'
118+
119+
120+
def test_read_vrt_source_str_only():
121+
"""``read_vrt(source: str)`` stays str-only: the VRT XML references
122+
its own source files on disk."""
123+
assert _annotation(read_vrt, 'source') == 'str'
124+
125+
92126
# --- on_gpu_failure: 'auto' | 'strict' (GPU failure policy) ---
93127

94128

@@ -129,3 +163,30 @@ def test_open_geotiff_window_kwarg_runtime(tmp_path):
129163
to_geotiff(da, path)
130164
r = open_geotiff(path, window=(0, 0, 4, 4))
131165
assert r.shape == (4, 4)
166+
167+
168+
def test_open_geotiff_bytesio_source_runtime(tmp_path):
169+
"""``open_geotiff`` routes a ``BytesIO`` source through the eager
170+
numpy reader. The annotation pins this contract at the type level;
171+
this test pins it at the runtime level so a future refactor that
172+
drops the file-like branch fails CI. See issue #1754.
173+
"""
174+
import io
175+
import numpy as np
176+
import xarray as xr
177+
178+
arr = np.arange(64, dtype=np.float32).reshape(8, 8)
179+
da = xr.DataArray(
180+
arr, dims=['y', 'x'],
181+
coords={'y': np.arange(8.0, 0, -1), 'x': np.arange(8.0)},
182+
attrs={'crs': 4326, 'transform': (1.0, 0, 0.0, 0, -1.0, 8.0)},
183+
)
184+
185+
path = str(tmp_path / 'bytesio_source.tif')
186+
to_geotiff(da, path)
187+
with open(path, 'rb') as f:
188+
buffer = io.BytesIO(f.read())
189+
190+
r = open_geotiff(buffer)
191+
assert r.shape == (8, 8)
192+
assert r.dtype == np.float32

0 commit comments

Comments
 (0)