Skip to content

Add type stubs for rasterio (and possibly affine) #15870

@thomas-maschler

Description

@thomas-maschler

I'd like to contribute stubs for rasterio (and, with your guidance, possibly affine), and would appreciate guidance before opening the PR(s). Per CONTRIBUTING.md this seems large enough to warrant an issue first.

Context

  • Rasterio maintainers decided not to have type hints in their code base
  • A community types-rasterio stub repo has existed for a while at kylebarron/types-rasterio. It was never published to PyPI, so the types-rasterio PyPI name is unclaimed.
  • The maintainer has agreed to transfer the work, and I've updated and modernized those stubs to track the current rasterio==1.5.x release.
  • rasterio depends heavily on affine — it's a hard runtime dependency, and affine.Affine appears in roughly 30 rasterio signatures (every transform / open() call / warp.reproject / WarpedVRT / Window helper). The rasterio stubs' usefulness is therefore tied to how typed affine is at the call site, which is the subject of the open questions below.
  • A sibling types-affine repo by the same maintainer is published on PyPI at 0.1.0; I've updated that locally to track affine==2.4.x. However, affine v3 is upcoming and will ship its own inline type hints — so affine stubs in typeshed would be obsoleted by the obsolete-since flow within months of v3's release.
  • Neither rasterio nor affine currently exists under stubs/ in typeshed; shapely is the closest existing sibling.

What is ready

For both packages:

  • Stub files in the typeshed <pkg>/<pkg>/*.pyi layout.
  • METADATA.toml (current format), @tests/stubtest_allowlist.txt.
  • partial-stub = true (Cython implementation details deferred to _typeshed.Incomplete where appropriate; entire public API surface covered).
  • All of the following pass locally: stubtest, mypy --strict against a runtime smoke script, pyright, ruff, flake8-pyi, plus per-overload positive/negative cases (currently in pytest-mypy-plugins YAML; I'll port to @tests/test_cases/check_*.py for the PR).

For rasterio specifically:

  • 43 stub files covering rasterio 1.5's public API including the modules added since the previous community version: abc, cache, stack, tools, shutil, plus the _-prefixed Cython modules transitively required by public re-exports.
  • 12 PEP 702 @deprecated markers matching upstream's RasterioDeprecationWarning sites, including overload-based parameter deprecations (warp.transform_geom(antimeridian_*), windows.from_bounds(height/width/precision), merge(precision), features.geometry_window(north_up/rotated/pixel_precision), transform.TransformerBase.rowcol(precision), windows.WindowMethodsMixin.window(precision)). Whole-symbol @deprecated on CRS.is_valid, Window.round_shape, DatasetReaderBase.statistics, DatasetBase.is_tiled, env.hascreds, env.ensure_env_credentialled, io.FilePath, rasterio.path.{parse_path,vsi_path}.

For affine specifically: 1 stub file matching affine==2.4.0, plus the same @deprecated treatment on Affine.__rmul__ per upstream.

Open questions

1. affine.Affine reference strategy in the rasterio stubs

About 30 rasterio signatures touch affine.Affine (e.g. open(transform=), warp.reproject, WarpedVRT, every transform module helper). Options I've considered:

  • (a) from affine import Affine directly. Until affine v3 ships py.typed, type checkers see Affine = Any; once v3 lands the stubs automatically gain precision. Zero follow-up work.
  • (b) _typeshed.Incomplete everywhere Affine would appear, explicit about the lossy state. A follow-up PR swaps to Affine once affine v3 lands.
  • (c) Local _AffineLike Protocol inside the rasterio stubs (matrix-like a/b/c/d/e/f, xoff/yoff, __mul__). Slight precision gain over Incomplete, no follow-up needed when v3 lands.
  • (d) Bundle affine stubs (see question 2) so from affine import Affine resolves to typeshed-owned definitions immediately.

Do you have a preference?

2. Should typeshed accept affine stubs at all?

The runtime is small (one class, ~600 lines of source). The stubs would be useful for the months until affine v3 ships, but you'd then need to mark obsolete-since and rely on stubsabot to remove them after 6 months. Happy to submit if you want them, happy to skip if the short lifespan isn't worth the maintenance.

If skipped, I'll publish my affine stubs at the existing PyPI types-affine name as a final 0.2.0 bridge release and archive the GitHub repo.

3. PR shape

  • One bundled PR for stubs/rasterio/ and stubs/affine/, or
  • Two separate PRs.

I'll defer to whichever is easier to review.

Known maintenance flags

  • Cython introspection lies about parameter names in many of rasterio's _-prefixed modules. I've handled this with broad allowlist regex (rasterio\._base.*, etc.) in @tests/stubtest_allowlist.txt. Open to alternatives.
  • partial-stub = true — the entire public API surface is covered, but a handful of internal Cython helpers are Incomplete.
  • One deprecation can't be expressed via @deprecated overload because **rpc_options swallows the deprecated kwarg in the modern overload (transform.rowcol(precision=) and transform.TransformMethodsMixin.index(precision=)). Documented inline; runtime warning is the only signal there.

Happy to provide more detail on any of the above. Thanks for considering!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions