Skip to content

Pin or bound the mypy version used by the mypy and stubtest tox environments #20444

Description

@nstarman

Opened by Claude Code on my behalf and at my request while investigating #19044.

Description

Follow-up to #19044, which adds a [tool.mypy] allowlist and a CI job that type checks the modules listed in it.

The new environment declares its tool dependency with no version constraint:

[testenv:mypy]
deps =
    mypy

This is not a new habit introduced by that PR — [testenv:stubtest] on main already does the same thing, and stubtest ships as part of mypy, so both environments float together. But #19044 makes the consequence sharper, because the entire premise of the allowlist is "the modules on this list are green, and stay green." That signal is only worth having if a red job implies an astropy change. With an unpinned checker, a mypy release can redden the job on a PR that touches nothing related, and the failure lands on whichever contributor happens to open a PR that morning.

Type checkers are unusually prone to this compared to most test dependencies: new releases routinely add error codes, tighten inference, and change how existing constructs are analysed. Code that is legitimately correct and passed yesterday can be reported today, by design.

This is not hypothetical

mypy's recent release cadence, from PyPI:

Version Released
1.19.0 2025-11-28
1.20.0 2026-03-31
2.0.0 2026-05-06
2.1.0 2026-05-11
2.2.0 2026-07-08
2.3.0 2026-07-13
2.3.1 2026-08-15

Twelve releases in roughly twelve months, including a major version bump.

The commits in #19044 are themselves an illustration: they were authored 2025-12-10, when mypy 1.19 was current, and rebased 2026-09-17, by which point 2.3.1 was. The PR sat across a 1.x → 2.x transition. In this instance the allowlist happens to still pass under 2.3.1 — I verified that locally — but that was luck rather than a property the configuration guarantees.

Proposal

At minimum, give both environments a lower bound, so the configuration records which mypy version the allowlist was actually validated against:

[testenv:mypy]
deps =
    mypy >=2.3

I want to be upfront that a lower bound alone does not solve the problem described above — it prevents older mypy from being used, while the failure mode here is a newer one. Genuinely decoupling the job from upstream releases needs one of:

I lean towards treating this as a special case of #17596 rather than inventing a separate mechanism for it, and would defer to @neutrinoceros on that. Flagging it here so the decision is made deliberately rather than inherited by default.

Additional context

Whatever is decided should be applied to [testenv:stubtest] at the same time. #19044 also switches that environment from .stubtest.ini to reading [tool.mypy] in pyproject.toml, so after it merges the two jobs share both a config file and an unpinned checker.

See also #20443, on the third-party stub packages that the same environment is missing. That issue mentions this pinning question in passing; this one is the dedicated discussion.

Found while surveying astropy.cosmology for allowlist candidates in #19044.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    typingrelated to type annotations

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions