geotiff: deprecate geographic-CRS GeoKey attrs in DataArray.attrs (#1984)#2011
Merged
Conversation
Contributor
Author
PR Review: geotiff: deprecate geographic-CRS GeoKey attrsBlockers (must fix before merge)None. Suggestions (should fix, not blocking)
Nits (optional improvements)
What looks good
Checklist
|
This was referenced May 18, 2026
brendancol
added a commit
that referenced
this pull request
May 18, 2026
PR review on #2014 flagged that the three colormap deprecation sites copy-paste the same nine-line ``warnings.warn(...)`` block with only the attr name and migration hint varying. Sibling PRs #2010 / #2013 have the same shape; PR #2011 already factors a slice-specific helper. Introduce a generic helper in ``_attrs.py`` so all four PR 7 slices can share it. ``_emit_deprecated_geokey_attr(attrs, name, value, *, reason, migration=None)`` builds the canonical warning text shape: xrspatial.geotiff: attrs['<name>'] is deprecated; <reason>. [<migration>.] It will be removed in a future release. See issue #1984. then sets ``attrs[name] = value`` (read-side emission still alive for the deprecation window). ``stacklevel=2`` lands the warning at the caller of ``_populate_attrs_from_geo_info``; ``DeprecationWarning`` is silenced for library code by default in Python's filters, so this is mainly visible to test runners and developers who opt in. The colormap-variants deprecation now routes ``cmap`` and the two ``colormap_rgba`` branches (matplotlib present + ImportError fallback) through the helper, replacing 27 lines of copy-paste with three calls. The emitted warning text is byte-identical to the previous inline strings, so the existing tests (``pytest.warns`` plus the TestPalette / test_metadata_round_trip filter regexes) keep passing unchanged. Sibling PRs #2010 and #2013 can adopt the helper on rebase; #2011's existing geographic-specific helper can be inlined onto this one in a follow-up. Test plan: - pytest xrspatial/geotiff/tests/test_attrs_pr7_deprecate_colormap_variants_1984.py -- 4 passed. - pytest xrspatial/geotiff/tests/test_attrs_contract_*_1984.py xrspatial/geotiff/tests/test_metadata_round_trip_1484.py -- 57 passed. - pytest xrspatial/geotiff/tests/test_features.py::TestPalette -- 7 passed.
) The six geographic-CRS GeoKey-derived attrs (crs_name, geog_citation, datum_code, angular_units, semi_major_axis, inv_flattening) were documented in the attrs contract as best-effort pass-through, but the PR6 locking test (#2004) showed they never round-trip: build_geo_tags only emits the primary GEOKEY_GEOGRAPHIC_TYPE, so the secondary GeoKeys these attrs come from are never written back. PR 7 of the issue #1984 plan deprecates the read-side emission. Each emission now fires a DeprecationWarning with a stable wording. The values still land in attrs for one release cycle so external callers can migrate; removal is scheduled for a later release. Changes: - _attrs.py: add _DEPRECATED_GEOGRAPHIC_GEOKEY_ATTRS tuple, _deprecated_geographic_geokey_warning text helper, and _emit_deprecated_geographic_geokey emission helper. Route the six attrs through the helper in _populate_attrs_from_geo_info. - _attrs.py docstring: move the six attrs out of the best-effort pass-through tier and into a new "Deprecated" tier section. - New test_attrs_pr7_deprecate_geographic_1984.py with parametrised warning assertions, still-emits-during-deprecation assertions, a no-warning-when-field-absent guard, and a warning-text shape check. - CHANGELOG.md: document the deprecation under Unreleased.
4 tasks
Address review S2 + N1 on PR #2011. S2: drop the redundant ``field`` column from ``_DEPRECATED_CASES``. Every entry had ``attr == field`` because every deprecated attr is stored on ``GeoInfo`` under the same name it lands in ``attrs`` under. Collapse to ``(attr, value)`` and update the two parametrised tests to match. N1: add ``test_deprecated_cases_has_no_duplicates`` so a duplicate row in ``_DEPRECATED_CASES`` can no longer be silently absorbed by the set-based coverage check. Test count goes 15 -> 16.
…1984) Address review S3 on PR #2011. The geographic-GeoKey deprecation helper passed ``stacklevel=2`` to ``warnings.warn``, which pointed at ``_populate_attrs_from_geo_info`` rather than the user's ``open_geotiff(...)`` call. The exact distance to the user frame also varies by backend: the numpy path adds three internal frames, the dask path adds four. A fixed integer can only be right for one of those paths. Replace the fixed level with a frame walk (``_stacklevel_to_external_caller``) that skips every ``xrspatial.geotiff*`` frame -- minus ``xrspatial.geotiff.tests`` so the unit tests can still pose as external callers -- and returns the matching ``stacklevel``. The new ``test_warning_stacklevel_points_at_caller_file`` pins the attribution. Today the warning category is ``DeprecationWarning``, which Python silences by default for library code, so the stacklevel mostly affects test output. Get it right now so a later switch to ``FutureWarning`` does not surface the warning as if it came from ``_attrs.py``. Scope note: the inline ``warnings.warn`` calls for the projected and vertical tiers (still pass ``stacklevel=2``) are left alone in this commit; the next commit folds them into the generic helper and picks up the new walk transparently. Test count goes 16 -> 17.
Address review S1 + N2 on PR #2011. N2: promote ``_emit_deprecated_geographic_geokey`` to a generic ``_emit_deprecated_geokey_attr(attrs, name, value, *, reason)`` shared by the geographic, projected, and vertical deprecation tiers (introduced by sibling PRs #2010 and #2013 as inline ``warnings.warn`` calls during the rebase onto current main). The per-category reason clauses live in two module-level constants (``_GEOKEY_DEPRECATION_REASON_HORIZONTAL_CRS`` for geographic / projected; ``_GEOKEY_DEPRECATION_REASON_VERTICAL_CRS`` for vertical), which keeps the wording in lockstep and lets the existing ``test_warning_message_format`` assertion continue to pin the canonical text verbatim. ``_emit_deprecated_geographic_geokey`` is kept as a thin shim so the geographic call sites stay readable; the projected and vertical sites in ``_populate_attrs_from_geo_info`` now call the generic helper directly and inherit the external-caller stacklevel walk from commit B for free. S1: the rebased docstring "Deprecated" section already grew a migration recipe in the conflict resolution; this commit additionally mirrors the recipe into ``docs/source/user_guide/attrs_contract.rst`` so the user-facing docs carry the same pyproj one-liners (``crs.ellipsoid.inverse_flattening``, ``crs.datum.to_epsg()`` etc.) that the docstring lists. No new tests. The existing 17 deprecation tests plus the 42 contract tests all still pass; the warning text and category are unchanged. Sibling PRs #2010 and #2013 are already merged on main, so no sibling rebase is needed -- this PR is the consolidation point.
7b65bef to
caf0967
Compare
Contributor
Author
|
Follow-up review fixes pushed. Rebased onto current Commits on top of the rebased PR commit (0d79dbf):
Sibling PRs #2010 / #2013: both already on Test counts: deprecation suite goes 15 -> 17 (added 1 length-assert, 1 stacklevel pin); projected + vertical + canonical + pass-through contract suites unchanged (42 tests, all green). Nothing skipped. |
brendancol
added a commit
that referenced
this pull request
May 18, 2026
PR #2011 (geographic-CRS GeoKey deprecation) merged to main after my first merge commit (ac908ab) and reshaped both the module docstring and the helper layout. This commit reconciles the colormap-variants slice with that landing. xrspatial/geotiff/_attrs.py (helper layout) Origin/main now ships: * ``_DEPRECATED_GEOGRAPHIC_GEOKEY_ATTRS`` tuple of attr names. * Two per-category reason constants (``_GEOKEY_DEPRECATION_REASON_HORIZONTAL_CRS`` and ``_GEOKEY_DEPRECATION_REASON_VERTICAL_CRS``). * ``_deprecated_geokey_warning(name, *, reason)`` text builder with the canonical wording "...{reason} so it will not round-trip..." pinned by sibling tests. * ``_deprecated_geographic_geokey_warning(name)`` backward-compat shim that fixes the reason to the horizontal-CRS clause. * ``_stacklevel_to_external_caller()`` stack walker that walks past every ``xrspatial.geotiff*`` frame so deprecation warnings point at the user's ``open_geotiff(...)`` call rather than at internal read paths. Materially better than a fixed ``stacklevel=`` constant because the chain depth differs by backend (numpy: 3 frames; dask: 5). * ``_emit_deprecated_geokey_attr(attrs, name, value, *, reason)`` emitter that pairs the text builder with the stack walker. * ``_emit_deprecated_geographic_geokey(attrs, name, value)`` geographic-tier shim. HEAD had ``_DEPRECATED_COLORMAP_REASON`` / ``_DEPRECATED_CMAP_MIGRATION`` / ``_DEPRECATED_COLORMAP_RGBA_MIGRATION`` constants plus an ``_emit_deprecated_attr(attrs, name, value, *, reason, migration=None)`` emitter with a fixed ``stacklevel=3``. The colormap helper needed migration-text support (e.g. "Reshape attrs['colormap'] to (n_colors, 3) ...") that the GeoKey-tier fixed-template helper does not support. Resolution: kept both helpers, switched the colormap helper from ``stacklevel=3`` to ``_stacklevel_to_external_caller()`` so all four PR 7 slices attribute warnings consistently. The two helpers live side-by-side because their warning-text contracts are pinned by separate test suites; convergence is left as a follow-up. xrspatial/geotiff/_attrs.py (module docstring "Deprecated" section) Origin/main restructured the section into three per-axis subsections (Geographic-CRS / Projected-CRS / Vertical-CRS) plus a pyproj migration recipe. Added a fourth subsection "Colormap variants (different root cause: photometric gate, not GeoKey)" with the two colormap-derived entries. Test plan: - pytest xrspatial/geotiff/tests/test_attrs_pr7_deprecate_*_1984.py (colormap + vertical + projected + geographic) -- 23 passed. - pytest xrspatial/geotiff/tests/test_attrs_contract_*_1984.py xrspatial/geotiff/tests/test_metadata_round_trip_1484.py xrspatial/geotiff/tests/test_features.py::TestPalette -- 94 passed total, no regressions.
brendancol
added a commit
that referenced
this pull request
May 18, 2026
…1984) (#2014) * geotiff: deprecate matplotlib colormap variants in DataArray.attrs (#1984) Issue #1984 PR 7. The reader emits ``attrs['cmap']`` (when matplotlib is importable) and ``attrs['colormap_rgba']`` whenever the source file declares Photometric=3 (palette). The writer never selects Photometric=3 from attrs alone, so PR 6 (#2004) locked the round-trip drop for both keys as part of the attrs-contract pass-through tier. This PR deprecates the read-side emission of both keys for one release cycle. Callers who want a matplotlib ListedColormap should build one from the canonical ``attrs['colormap']`` (raw uint16 RGB triples from TIFF tag 320) instead. ``attrs['colormap']`` is the canonical-tier replacement and is intentionally kept: it round-trips through ``_merge_friendly_extra_tags`` already. During the deprecation window both attrs still land on the returned DataArray; only a DeprecationWarning is added. Removal is a follow-up PR. The new attrs-contract docstring split moves both keys into a "Deprecated" section so the contract stays explicit about which keys have a future. The new test file ``xrspatial/geotiff/tests/test_attrs_pr7_deprecate_colormap_variants_1984.py`` pins three behaviours: the warning fires on a Photometric=3 fixture for ``colormap_rgba`` (always) and for ``cmap`` (when matplotlib is installed); the attrs are still emitted; the plain ``attrs['colormap']`` key is unaffected. Existing tests that read palette fixtures (the TestPalette block in ``test_features.py`` and ``test_colormap_round_trip`` in ``test_metadata_round_trip_1484.py``) ignore the new DeprecationWarning locally to keep their reports clean. * geotiff: factor shared _emit_deprecated_geokey_attr helper PR review on #2014 flagged that the three colormap deprecation sites copy-paste the same nine-line ``warnings.warn(...)`` block with only the attr name and migration hint varying. Sibling PRs #2010 / #2013 have the same shape; PR #2011 already factors a slice-specific helper. Introduce a generic helper in ``_attrs.py`` so all four PR 7 slices can share it. ``_emit_deprecated_geokey_attr(attrs, name, value, *, reason, migration=None)`` builds the canonical warning text shape: xrspatial.geotiff: attrs['<name>'] is deprecated; <reason>. [<migration>.] It will be removed in a future release. See issue #1984. then sets ``attrs[name] = value`` (read-side emission still alive for the deprecation window). ``stacklevel=2`` lands the warning at the caller of ``_populate_attrs_from_geo_info``; ``DeprecationWarning`` is silenced for library code by default in Python's filters, so this is mainly visible to test runners and developers who opt in. The colormap-variants deprecation now routes ``cmap`` and the two ``colormap_rgba`` branches (matplotlib present + ImportError fallback) through the helper, replacing 27 lines of copy-paste with three calls. The emitted warning text is byte-identical to the previous inline strings, so the existing tests (``pytest.warns`` plus the TestPalette / test_metadata_round_trip filter regexes) keep passing unchanged. Sibling PRs #2010 and #2013 can adopt the helper on rebase; #2011's existing geographic-specific helper can be inlined onto this one in a follow-up. Test plan: - pytest xrspatial/geotiff/tests/test_attrs_pr7_deprecate_colormap_variants_1984.py -- 4 passed. - pytest xrspatial/geotiff/tests/test_attrs_contract_*_1984.py xrspatial/geotiff/tests/test_metadata_round_trip_1484.py -- 57 passed. - pytest xrspatial/geotiff/tests/test_features.py::TestPalette -- 7 passed. * geotiff: address self-review on shared deprecation helper Self-review on PR #2014 flagged one blocker and two suggestions on the shared-helper refactor. This commit addresses them. Blocker -- ``stacklevel`` regression (``_attrs.py:179``): Wrapping the inline ``warnings.warn(..., stacklevel=2)`` call in a helper shifted the warning location by one frame; the warning was surfacing at ``_attrs.py`` (the helper call site) instead of at the backend that called ``_populate_attrs_from_geo_info``. Bumped ``stacklevel=2`` to ``stacklevel=3`` so the warning is attributed to the backend frame, matching the pre-refactor behaviour (verified empirically: warning now surfaces at ``xrspatial/geotiff/__init__.py:518`` instead of ``_attrs.py:350``). Suggestion -- helper name (``_attrs.py:152``): Renamed ``_emit_deprecated_geokey_attr`` to ``_emit_deprecated_attr`` because the colormap variants (cmap, colormap_rgba) come from TIFF tag 320 (ColorMap), not from a GeoKey. The new name covers every PR 7 slice; docstring notes why the ``_geokey_`` qualifier was dropped. Suggestion -- user-guide doc: Added a new "Deprecated keys" section to ``docs/source/user_guide/attrs_contract.rst`` so users reading the contract page see the new tier alongside the existing canonical / compatibility-alias / pass-through tiers. Mirrors the module docstring. Suggestion -- direct unit test for the helper: Added ``test_emit_deprecated_attr_with_migration_text`` and ``test_emit_deprecated_attr_without_migration_text``. The first pins the exact warning text shape; the second pins the ``migration=None`` branch which the existing colormap tests do not exercise. Nit -- migration hints: Lifted ``_colormap_reason`` / ``_colormap_migration`` to module- level constants and split them into ``_DEPRECATED_COLORMAP_REASON``, ``_DEPRECATED_CMAP_MIGRATION``, and ``_DEPRECATED_COLORMAP_RGBA_MIGRATION``. The new ``colormap_rgba``-specific migration ("Reshape attrs['colormap'] to (n_colors, 3) and append an alpha channel") is more direct than the previous shared ``ListedColormap`` hint, which only matched ``cmap`` cleanly. Test plan: - pytest test_attrs_pr7_deprecate_colormap_variants_1984.py (6 passed) - pytest test_attrs_contract_*_1984.py test_metadata_round_trip_1484.py test_features.py::TestPalette -- 70 passed total
This was referenced May 18, 2026
brendancol
added a commit
that referenced
this pull request
May 18, 2026
…2015) The four PR 7 deprecation slices (#2010 vertical, #2011 geographic, #2013 projected, #2014 colormap variants) moved 13 attrs from the pass-through tier to a new deprecated tier in ``xrspatial/geotiff/_attrs.py`` but did not update the user-guide page, which left ``docs/source/user_guide/attrs_contract.rst`` listing those attrs as round-trip-best-effort while the Python code fires a DeprecationWarning every time one is emitted. This is docs-only: * Trim ``Pass-through keys`` table to ``image_description``, ``extra_samples``, ``colormap`` -- the three attrs that genuinely round-trip via ``_merge_friendly_extra_tags``. The 11 GeoKey- derived attrs plus the two colormap variants moved out. * Merge the two separate deprecated sections that existed pre-PR (``Deprecated keys`` for colormap variants, ``Deprecated GeoKey attrs`` for the GeoKey slices) into one ``Deprecated keys`` section with two subsections: GeoKey-derived attrs (with the shared pyproj migration recipe) and matplotlib colormap variants (with per-row migration in a list-table). * Update the page's opening summary from "three tiers" to "four tiers" so the first paragraph matches the table-of-contents. * Update the ``Round-trip invariants`` section: the pass-through subsection now reflects that only three attrs live there and all three round-trip; add a deprecated subsection that explains the warning-only phase and points at the future contract-version bump to 2 when removal lands. No production-code change. Verified the file still parses with docutils (Sphinx-specific ``:func:`` / ``:mod:`` role warnings are the same pattern used across the rest of the docs). Refs #1984.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 7 of the issue #1984 attrs-contract plan. The PR 6 locking test (merged as #2004) proved that six geographic-CRS GeoKey-derived attrs never survive a write -> read round-trip: the writer's
xrspatial/geotiff/_geotags.build_geo_tagsonly emits the primaryGEOKEY_GEOGRAPHIC_TYPEplus citation, never the secondary geographic GeoKeys these attrs are derived from.Since the contract documented the attrs as "preserved when the writer can reconstruct from canonical state" but the writer demonstrably cannot, this PR deprecates them. The deprecated attrs are:
crs_name(GTCitation / ProjCitation GeoKey; writer always overwrites fromcrs_wkt-derived data)geog_citation(GeogCitationGeoKey)datum_code(GeogGeodeticDatumGeoKey)angular_units(GeogAngularUnitsGeoKey)semi_major_axis(GeogSemiMajorAxisGeoKey)inv_flattening(GeogInvFlatteningGeoKey)This PR is the warning-only phase: the attrs still land in
DataArray.attrson read, but each emission fires aDeprecationWarning. Removal is scheduled for a later release. The contract-version bump and actual removal will come in a follow-up.Changes
xrspatial/geotiff/_attrs.py: route the six attrs through a new_emit_deprecated_geographic_geokeyhelper in_populate_attrs_from_geo_info; move the attrs out of the best-effort pass-through docstring section and into a new "Deprecated" section.xrspatial/geotiff/tests/test_attrs_pr7_deprecate_geographic_1984.py: 15 tests covering per-attr warning emission, per-attr value still landing in attrs, no-warning-when-absent guard, and warning-text shape.CHANGELOG.md: deprecation note under Unreleased.Warning text
Fires as
DeprecationWarningwithstacklevel=2.Test plan
pytest xrspatial/geotiff/tests/test_attrs_pr7_deprecate_geographic_1984.py -v(15 passed)pytest xrspatial/geotiff/tests/test_attrs_contract_passthrough_1984.py xrspatial/geotiff/tests/test_attrs_contract_canonical_1984.py -v(35 passed, no regressions)pytest xrspatial/geotiff/tests/sweep: 3541 passed, 8 pre-existing GPU failures unrelated to this change.Refs