Skip to content

Handle diverging protocol - #22036

Merged
ilevkivskyi merged 1 commit into
python:masterfrom
ilevkivskyi:limit-proto-stack-fr
Sep 24, 2026
Merged

ilevkivskyi merged 1 commit into
python:masterfrom
ilevkivskyi:limit-proto-stack-fr

Conversation

@ilevkivskyi

Copy link
Copy Markdown
Member

Fixes #17326
Fixes #21739
Fixes #21985

There are scenarios where recursive protocols are genuinely undecidable (a.k.a. diverging). Consider the example in testDivergingProtocol: to check if C <: P[int] we need to first check if C <: P[list[int]] assuming C <: P[int], but to check that we need to first check if C <: P[list[list[int]]] etc. This (classic) algorithm never converges.

Note I set a relatively large limit of 40. After playing manually with this I found that some libraries can use up to ~25 assumptions in the stack, so I want to be on the safe side initially, we can optimize this later. To be clear, this should not cause any performance issues for libraries that don't crash currently.

Now on why do we do this only for recursive protocols and not for recursive type aliases:

  • This comes up more often for protocols (FWIW I don't think there is a single issue about the same problem with recursive aliases)
  • There is already a way to detect such problematic aliases early during semantic analysis, so we give a normal error, not a crash.
  • Fixing this for recursive aliases is more tricky, since it will require to patch a lot of visitors involving the seen_aliases pattern.

cc @JukkaL

@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@JukkaL JukkaL left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Makes sense -- potential for very rare false negatives is better than crashing/hanging.

@ilevkivskyi
ilevkivskyi merged commit 865308b into python:master Sep 24, 2026
25 checks passed
@ilevkivskyi
ilevkivskyi deleted the limit-proto-stack-fr branch September 24, 2026 23:16
@quentin

quentin commented Sep 25, 2026

Copy link
Copy Markdown

Many thanks!

headtr1ck added a commit to headtr1ck/xarray that referenced this pull request Sep 25, 2026
pint>=0.26's annotations send mypy into infinite recursion when optype
is installed, segfaulting the Mypy (Python 3.14) CI job. Skip analysing
pint until a mypy release includes python/mypy#22036.

Co-authored-by: Claude <noreply@anthropic.com>
finsberg added a commit to ComputationalPhysiology/simcardemsx that referenced this pull request Sep 28, 2026
Pre-commit's mypy failed in CI on both dolfinx legs, with an INTERNAL
ERROR at pint/facets/numpy/quantity.py:63 on stable and a segfault (exit
-11) on nightly. This is python/mypy#21985: pint >= 0.26 imports optype
whenever it is installed, and optype's types send mypy's variance
inference for pint's PEP 695 classes into infinite recursion. It is fixed
on mypy master (python/mypy#22036) but not in a release; 2.3.1 is the
latest.

optype was only installed because scipy-stubs, added to the dev extra in
d50d275, requires it. It passed locally only because the container has
pint 0.25.3, which does not import optype; CI installs 0.26.1.

scipy-stubs is dropped again, and scipy is ignored as an untyped import
instead. That costs little: the stubs only typed the one
scipy.optimize.newton call in zero_d.py, whose result is already wrapped
in float(). They can come back once a mypy release carries the fix.

Reproduced with pint 0.26.1: a file containing only "import pint" crashes
mypy 2.3.1 with optype installed and passes without it. With this change,
mypy is clean on all 33 files under both pint 0.25.3 and 0.26.1.

No runtime change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants