You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Nondeterminism observed on pandas in typeshed primer #21445
The interesting thing is I suspect this will not repro with stock mypy (e.g. we haven't seen it in mypy's mypy_primer)
I think what is going in is that you also need these typeshed PRs to operator.py. I reverted these PRs for us because they are bad for our users (for other reasons): #21232 (comment)
I set Codex on this a while ago but didn't submit its PR yet because I didn't feel like it was addressing the root cause. Codex thought it was because operator and _operator are part of an SCC and some types are different dependent on the order in which the files in the SCC are processed.
Depending on the order, some dict in pandas gets inferred as either dict[str, Overload(...)] or dict[str, function], and one of the two leads to errors. Codex proposed to fix that by changing how we union those two types, which felt like a bandaid.
If you want the LLM's full verbose output:
Details
Title
Nondeterministic inference for operator functions can make dict(zip(...)) alternate between precise callable types and builtins.function
Summary
I observed nondeterministic mypy output in mypy-primer on pandas. The reported diff was that this pandas error sometimes appeared and sometimes disappeared with identical code:
pandas/core/computation/ops.py:328: error: Need type annotation for "_binary_ops_dict" [var-annotated]
Repeated runs against the same checkout sometimes infer _arith_ops_dict as a precise overloaded-function dict, and sometimes as a bare builtins.function dict. That then changes the loop item type for d.
Observed shapes
In one run:
for d in (...): d
# dict[str, def (Any, Any) -> Any]
In another run:
for d in (...): d
# dict[str, def (Any, Any) -> Any] | dict[str, function]
The latter shape prevents partial dict inference for _binary_ops_dict = {}, so mypy emits the annotation error.
Likely source of nondeterminism
The root appears to be interaction between typeshed’s operator / _operator stubs and mypy’s SCC processing.
So operator and _operator are in the same SCC. Mypy’s build graph / SCC code passes around sets in a few places, for example SCCs and ready groups. Depending on processing order within that SCC, operator.add and friends may be seen as precise overloaded functions or as the coarser fallback type builtins.function.
That gives _arith_ops_dict two possible inferred shapes:
dict[str, Overload(...)]
or:
dict[str, function]
Then later union simplification preserves this difference, which leaks into pandas’ _binary_ops_dict inference.
Notes
This was observed with mypy-primer using compiled mypy.
The pandas/typeshed code was unchanged between runs.
The typeshed PR that exposed it was unrelated (sys._jit), so it likely just perturbed processing enough to reveal an existing nondeterminism.
A local mitigation was to make union simplification prefer a precise function-like type over bare builtins.function when the latter is only a fallback inside the same container shape. That makes the pandas loop infer one deterministic type, but it does not fully explain/fix the earlier operator/_operator shape flip.
changed the title [-][possible 1.20 regression] Nondeterminism observed on pandas in typeshed primer[/-][+]Nondeterminism observed on pandas in typeshed primer[/+]on May 9, 2026
Yeah, I was right. It is similar to some bugs that I fixed a while back, caused by non-associative operations that get exposed because of set iteration order.
My Codex kind of got there but it needed help. #21455 is the PR
(We could just use dict.fromkeys as a heavy hammer, but the interesting thing is that some orders of operations are better for users so we should try to pick those. Also we really want operations to at least be commutative. So I kind of don't mind learning about it and having chosen behaviour instead of arbitrary behaviour)
Activity
JelleZijlstra commentedon May 9, 2026
I set Codex on this a while ago but didn't submit its PR yet because I didn't feel like it was addressing the root cause. Codex thought it was because operator and _operator are part of an SCC and some types are different dependent on the order in which the files in the SCC are processed.
Depending on the order, some dict in pandas gets inferred as either
dict[str, Overload(...)]ordict[str, function], and one of the two leads to errors. Codex proposed to fix that by changing how we union those two types, which felt like a bandaid.If you want the LLM's full verbose output:
Details
Title
Nondeterministic inference for
operatorfunctions can makedict(zip(...))alternate between precise callable types andbuiltins.functionSummary
I observed nondeterministic mypy output in mypy-primer on pandas. The reported diff was that this pandas error sometimes appeared and sometimes disappeared with identical code:
The code is roughly:
Repeated runs against the same checkout sometimes infer
_arith_ops_dictas a precise overloaded-function dict, and sometimes as a barebuiltins.functiondict. That then changes the loop item type ford.Observed shapes
In one run:
In another run:
The latter shape prevents partial dict inference for
_binary_ops_dict = {}, so mypy emits the annotation error.Likely source of nondeterminism
The root appears to be interaction between typeshed’s
operator/_operatorstubs and mypy’s SCC processing.In current typeshed, there is an import cycle:
So
operatorand_operatorare in the same SCC. Mypy’s build graph / SCC code passes around sets in a few places, for example SCCs and ready groups. Depending on processing order within that SCC,operator.addand friends may be seen as precise overloaded functions or as the coarser fallback typebuiltins.function.That gives
_arith_ops_dicttwo possible inferred shapes:or:
Then later union simplification preserves this difference, which leaks into pandas’
_binary_ops_dictinference.Notes
sys._jit), so it likely just perturbed processing enough to reveal an existing nondeterminism.builtins.functionwhen the latter is only a fallback inside the same container shape. That makes the pandas loop infer one deterministic type, but it does not fully explain/fix the earlieroperator/_operatorshape flip.[-][possible 1.20 regression] Nondeterminism observed on pandas in typeshed primer[/-][+]Nondeterminism observed on pandas in typeshed primer[/+]hauntsaninja commentedon May 9, 2026
Hm I kind of doubt that explanation... I will ask my Codex to investigate
hauntsaninja commentedon May 10, 2026
Yeah, I was right. It is similar to some bugs that I fixed a while back, caused by non-associative operations that get exposed because of set iteration order.
My Codex kind of got there but it needed help. #21455 is the PR
hauntsaninja commentedon May 10, 2026
(We could just use dict.fromkeys as a heavy hammer, but the interesting thing is that some orders of operations are better for users so we should try to pick those. Also we really want operations to at least be commutative. So I kind of don't mind learning about it and having chosen behaviour instead of arbitrary behaviour)
Fix nondeterminism from nonassociativity of overload joins (#21455)
Fix nondeterminism from nonassociativity of overload joins (#21455)