Skip to content

Pin nodata + streaming_buffer_bytes annotations on writer trio (#1705)#1707

Merged
brendancol merged 1 commit into
mainfrom
deep-sweep-api-consistency-geotiff-2026-05-12-1778611459-2954-01
May 12, 2026
Merged

Pin nodata + streaming_buffer_bytes annotations on writer trio (#1705)#1707
brendancol merged 1 commit into
mainfrom
deep-sweep-api-consistency-geotiff-2026-05-12-1778611459-2954-01

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #1654. The api-consistency sweep on 2026-05-12 found two more annotation gaps across xrspatial.geotiff's writer trio.

  • nodata: write_vrt got float | int | None in geotiff: write_vrt nodata type hint is float-only but to_geotiff/write_geotiff_gpu accept int #1684, but to_geotiff and write_geotiff_gpu still exposed bare nodata=None. Type-checkers inferred Any on the eager writers while inferring float | int | None on the VRT writer, even though all three docstrings describe the same accepted-type set.
  • streaming_buffer_bytes: int = 256*1024*1024 on to_geotiff vs int | None = None on write_geotiff_gpu. The GPU writer no-ops the kwarg (del streaming_buffer_bytes on entry) so the type signature was the only consistency dimension. Pin both to int = 256*1024*1024 so a caller forwarding the same kwargs to either entry point sees the same hint and default.

Annotation-only change; no runtime behaviour, defaults (effective for to_geotiff and the GPU no-op), or kwarg renames. The GPU writer's del-on-entry remains, so the value continues to be ignored on the GPU path.

Closes #1705.

Test plan

  • pytest xrspatial/geotiff/tests/test_signature_annotations_1705.py covers the new pinned-annotation assertions plus a to_geotiff(nodata=<int>) round-trip smoke test and a no-op assertion on the GPU writer's streaming_buffer_bytes kwarg.
  • pytest xrspatial/geotiff/tests/test_signature_annotations_1654.py still passes (the Public geotiff API: type annotations missing on window / path / on_gpu_failure #1654 annotations are unchanged).
  • pytest xrspatial/geotiff/tests/test_signature_parity_1631.py still passes (__all__ and inspect.signature contracts unaffected).
  • pytest xrspatial/geotiff/tests/test_features.py -k all still passes.

@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 12, 2026
@brendancol brendancol requested a review from Copilot May 12, 2026 18:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns public GeoTIFF writer trio type annotations in xrspatial.geotiff by pinning nodata and streaming_buffer_bytes annotations (and, for write_geotiff_gpu, the streaming_buffer_bytes signature default) to remove type-hint drift between sibling APIs.

Changes:

  • Annotate nodata on to_geotiff and write_geotiff_gpu as float | int | None.
  • Change write_geotiff_gpu.streaming_buffer_bytes annotation to int and default to 256 * 1024 * 1024 to match to_geotiff.
  • Add a new regression test module asserting annotation/default parity plus light runtime smoke tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
xrspatial/geotiff/__init__.py Pins nodata annotations and aligns write_geotiff_gpu.streaming_buffer_bytes signature with to_geotiff.
xrspatial/geotiff/tests/test_signature_annotations_1705.py Adds regression tests to prevent future annotation/default drift across the writer trio.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

path = str(tmp_path / 'nodata_int.tif')
to_geotiff(da, path, nodata=-9999)
r = open_geotiff(path)
assert r.attrs.get('nodata') == -9999
Comment on lines +135 to +142
p1 = str(tmp_path / 'default.tif')
p2 = str(tmp_path / 'override.tif')
write_geotiff_gpu(da_gpu, p1)
write_geotiff_gpu(da_gpu, p2, streaming_buffer_bytes=8 * 1024 * 1024)
# Both files have identical sizes -- the buffer kwarg is a no-op.
import os

assert os.path.getsize(p1) == os.path.getsize(p2)
bigtiff: bool | None = None,
max_z_error: float = 0.0,
streaming_buffer_bytes: int | None = None) -> None:
streaming_buffer_bytes: int = 256 * 1024 * 1024) -> None:
Comment on lines +125 to +127
import numpy as np
import xarray as xr

Follow-up to #1654. The api-consistency sweep on 2026-05-12 found two
remaining annotation gaps across xrspatial.geotiff's writer trio.

* nodata: write_vrt got `float | int | None` in #1684, but to_geotiff
  and write_geotiff_gpu still exposed bare `nodata=None`. Type-checkers
  inferred Any on the eager writers while inferring float|int|None on
  the VRT writer, even though all three docstrings describe the same
  accepted-type set.
* streaming_buffer_bytes: `int = 256*1024*1024` on to_geotiff vs
  `int | None = None` on write_geotiff_gpu. The GPU writer no-ops the
  kwarg (`del streaming_buffer_bytes`) so the type signature was the
  only consistency dimension. Pin both to `int = 256*1024*1024` so a
  caller forwarding the same kwargs to either entry point sees the
  same hint and default.

Annotation-only change; no runtime behaviour, defaults (effective for
to_geotiff and the GPU no-op), or kwarg renames. The existing
write_geotiff_gpu del-on-entry remains, so the value continues to be
ignored on the GPU path.

test_signature_annotations_1705.py pins each annotation plus a
runtime smoke test for to_geotiff(nodata=int) and a no-op assertion
for write_geotiff_gpu(streaming_buffer_bytes=...) so the GPU writer's
output stays byte-identical regardless of the kwarg value.

Closes #1705.
@brendancol brendancol force-pushed the deep-sweep-api-consistency-geotiff-2026-05-12-1778611459-2954-01 branch from d324ccf to 1554d00 Compare May 12, 2026 19:29
@brendancol brendancol merged commit bfd914b into main May 12, 2026
10 checks passed
brendancol added a commit that referenced this pull request May 12, 2026
- Relax nodata read-back assertion to numeric comparison: the reader
  parses GDAL_NODATA as a float (`_geotags.extract_geo_info` does
  `nodata = float(nodata_str)`), so compare `float(...) == -9999.0`
  rather than the raw `== -9999`.
- Pin GPU writer no-op by full file-bytes equality instead of just
  file sizes -- the writer is deterministic for a fixed input, so a
  regression that produces different bytes at the same size still
  trips the assertion.
- Tighten GPU skip to the geotiff test-suite convention:
  `pytest.importorskip('cupy')` plus `cupy.cuda.is_available()` check
  with the canonical `cupy + CUDA required` reason string.

The streaming_buffer_bytes default change on write_geotiff_gpu
(`int | None = None` -> `int = 256 * 1024 * 1024`) is intentional --
the PR's goal is annotation + default parity with `to_geotiff` so a
caller forwarding the same kwargs to either entry point sees the same
signature. The kwarg is a runtime no-op (deleted on entry) so the
surface default change has no behavioural effect.
@brendancol
Copy link
Copy Markdown
Contributor Author

Addressed the four Copilot review comments in f06720b:

  1. test_signature_annotations_1705.py:110 (nodata int-vs-float) -- relaxed to float(r.attrs['nodata']) == -9999.0 with a comment pointing at _geotags.extract_geo_info which does nodata = float(nodata_str) on read.

  2. test_signature_annotations_1705.py:142 (file-size assertion) -- replaced os.path.getsize(p1) == os.path.getsize(p2) with p1.read_bytes() == p2.read_bytes(). The writer is deterministic for a fixed input so byte equality is the right contract and a regression producing different bytes at the same size still trips.

  3. test_signature_annotations_1705.py:127 (GPU skip) -- switched to the geotiff test-suite convention: cupy = pytest.importorskip('cupy') followed by if not cupy.cuda.is_available(): pytest.skip('cupy + CUDA required'). Matches test_predictor3_big_endian.py, test_dask_cupy_combined.py, and friends.

  4. xrspatial/geotiff/__init__.py:2868 (streaming_buffer_bytes default change) -- intentionally not reverted. The PR's stated purpose is annotation + default parity with to_geotiff so callers forwarding the same kwargs to either entry point see the same signature via inspect.signature. The kwarg is a runtime no-op (del streaming_buffer_bytes on entry) so the surface default change has no behavioural effect. The test at test_write_geotiff_gpu_streaming_buffer_bytes_annotated explicitly pins both the annotation ('int') and the default (256 * 1024 * 1024).

All 7 tests in test_signature_annotations_1705.py pass locally, including the GPU runtime no-op on cupy + CUDA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

geotiff: nodata + streaming_buffer_bytes type-hint drift across writer trio

2 participants