Skip to content

Fix flipped operator in comparison error message - #22380

Open
xh123637 wants to merge 1 commit into
python:masterfrom
xh123637:fix/22316-comparison-operator-message
Open

xh123637 wants to merge 1 commit into
python:masterfrom
xh123637:fix/22316-comparison-operator-message

Conversation

@xh123637

@xh123637 xh123637 commented Nov 6, 2026

Copy link
Copy Markdown

Fix flipped operator in comparison error message

Fixes #22316.

def f(x: int | None) -> bool: return x >= 0 reported

error: Unsupported operand types for <= ("int" and "None")  [operator]

even though the code uses >=. The same mirroring affected all four
ordering operators (>= was reported as <=, <= as >=, > as <,
< as >).

Root cause

When the left operand has no forward comparison method (here NoneType
has no __ge__), check_op_reversible falls back to the reflected call
on the right operand (int.__le__(x)), and the error is rendered from
the callee's method name alone in MessageBuilder.incompatible_argument.
The renderer cannot tell a reflected __le__ call apart from a genuine
<=, so it reports the reflected operator with the operands in
reflected order.

Fix

check_op_reversible now records the operator as written in the source
code while trying each candidate method, using a small
MessageBuilder.operator_context (the same push/pop pattern as the
existing disable_type_names). incompatible_argument uses that context
for the four ordering methods (__lt__, __le__, __gt__, __ge__):
the symbol is taken from the source operator, and the operands are
reported in source order when the failing call was the reflected one.
Forward calls, __r*__ operators, ==/!=, in, and the
"unsupported left operand" path (when neither method exists) are
unaffected.

One judgment call, following the analysis in the issue: the operands are
reported in source order (>= ("None" and "int")), matching CPython's
runtime message ('>=' not supported between instances of 'NoneType' and 'int'), rather than keeping the old reflected order with only the
symbol fixed.

Tests

New regression test testComparisonOperatorMessageWithNone in
test-data/unit/check-expressions.test (using fixtures/ops.pyi, which
gives int its comparison methods). It failed on unmodified code with
the mirrored operators and passes with the fix. The existing assertions
that baked in the mirrored rendering were updated (17 lines across
check-expressions.test, check-incremental.test, and
check-plugin-attrs.test).

Test runs (all with the repo venv):

  • pytest mypy/test/testcheck.py -k "check-expressions.test or check-plugin-attrs.test": 342 passed
  • pytest mypy/test/testcheck.py -k "check-incremental.test": 359 passed, 4 skipped
  • pytest mypy/test/testcheck.py (full suite): 8317 passed, 27 skipped, 7 xfailed
  • Live check with the built mypy: x >= 0 now reports >= ("None" and "int"), and the control case x: int; s: str; x >= s still reports >= ("int" and "str")

Note: I used an AI coding assistant while working on this fix, and
verified the change and the tests myself.

When the left operand lacks the forward comparison method, the error
was rendered from the reflected method name (e.g. `x >= 0` reported
` <= ` instead of ` >= `); report the operator as written, with
operands in source order.

Fixes python#22316.
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

spark (https://github.com/apache/spark)
- python/pyspark/pandas/groupby.py:756: error: Unsupported operand types for >= ("int" and "Number")  [operator]
+ python/pyspark/pandas/groupby.py:756: error: Unsupported operand types for <= ("Number" and "int")  [operator]

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/cli/work_queue.py:742: error: Unsupported operand types for > ("datetime" and "None")  [operator]
+ src/prefect/cli/work_queue.py:742: error: Unsupported operand types for < ("None" and "datetime")  [operator]

discord.py (https://github.com/Rapptz/discord.py)
- discord/ext/tasks/__init__.py:232: error: Unsupported operand types for >= ("datetime" and "None")  [operator]
+ discord/ext/tasks/__init__.py:232: error: Unsupported operand types for <= ("None" and "datetime")  [operator]

pyodide (https://github.com/pyodide/pyodide)
- pyodide-build/pyodide_build/tests/test_xbuildenv.py:398: error: Unsupported operand types for <= ("str" and "None")  [operator]
+ pyodide-build/pyodide_build/tests/test_xbuildenv.py:398: error: Unsupported operand types for >= ("None" and "str")  [operator]
- pyodide-build/pyodide_build/tests/test_xbuildenv.py:401: error: Unsupported operand types for <= ("str" and "None")  [operator]
+ pyodide-build/pyodide_build/tests/test_xbuildenv.py:401: error: Unsupported operand types for >= ("None" and "str")  [operator]

cvxpy (https://github.com/cvxpy/cvxpy)
- cvxpy/expressions/expression.py:424: error: Unsupported operand types for < ("int" and "generic[Any]")  [operator]
+ cvxpy/expressions/expression.py:424: error: Unsupported operand types for > ("generic[Any]" and "int")  [operator]
- cvxpy/expressions/expression.py:424: error: Unsupported operand types for < ("int" and "sparray[Any, tuple[Any, ...]]")  [operator]
+ cvxpy/expressions/expression.py:424: error: Unsupported operand types for > ("sparray[Any, tuple[Any, ...]]" and "int")  [operator]
- cvxpy/expressions/expression.py:424: error: Unsupported operand types for < ("int" and "complex")  [operator]
+ cvxpy/expressions/expression.py:424: error: Unsupported operand types for > ("complex" and "int")  [operator]
- cvxpy/expressions/constants/constant.py:134: error: Unsupported operand types for < ("int" and "None")  [operator]
+ cvxpy/expressions/constants/constant.py:134: error: Unsupported operand types for > ("None" and "int")  [operator]
- cvxpy/utilities/power_tools.py:175: error: Unsupported operand types for < ("int" and "Integral")  [operator]
+ cvxpy/utilities/power_tools.py:175: error: Unsupported operand types for > ("Integral" and "int")  [operator]
- cvxpy/utilities/power_tools.py:197: error: Unsupported operand types for <= ("int" and "Integral")  [operator]
+ cvxpy/utilities/power_tools.py:197: error: Unsupported operand types for >= ("Integral" and "int")  [operator]

jax (https://github.com/google/jax)
- jax/_src/lax/slicing.py:3801: error: Unsupported operand types for > ("int" and "complex")  [operator]
+ jax/_src/lax/slicing.py:3801: error: Unsupported operand types for < ("complex" and "int")  [operator]
- jax/_src/numpy/indexing.py:364: error: Unsupported operand types for > ("int" and "slice[Any, Any, Any]")  [operator]
+ jax/_src/numpy/indexing.py:364: error: Unsupported operand types for < ("slice[Any, Any, Any]" and "int")  [operator]
- jax/_src/numpy/indexing.py:364: error: Unsupported operand types for > ("int" and "EllipsisType")  [operator]
- jax/_src/numpy/indexing.py:364: error: Unsupported operand types for > ("int" and "None")  [operator]
+ jax/_src/numpy/indexing.py:364: error: Unsupported operand types for < ("EllipsisType" and "int")  [operator]
- jax/_src/numpy/indexing.py:364: error: Unsupported operand types for > ("int" and "Sequence[int | integer[Any]]")  [operator]
+ jax/_src/numpy/indexing.py:364: error: Unsupported operand types for < ("None" and "int")  [operator]
+ jax/_src/numpy/indexing.py:364: error: Unsupported operand types for < ("Sequence[int | integer[Any]]" and "int")  [operator]
- jax/experimental/mosaic/gpu/fragmented_array.py:5547: error: Unsupported operand types for < ("int" and "object")  [operator]
+ jax/experimental/mosaic/gpu/fragmented_array.py:5547: error: Unsupported operand types for > ("object" and "int")  [operator]
- jax/experimental/mosaic/gpu/fragmented_array.py:5657: error: Unsupported operand types for < ("int" and "object")  [operator]
+ jax/experimental/mosaic/gpu/fragmented_array.py:5657: error: Unsupported operand types for > ("object" and "int")  [operator]

apprise (https://github.com/caronc/apprise)
- apprise/persistent_store.py:1528: error: Unsupported operand types for > ("datetime" and "None")  [operator]
+ apprise/persistent_store.py:1528: error: Unsupported operand types for < ("None" and "datetime")  [operator]
- apprise/plugins/amqp/base.py:414: error: Unsupported operand types for >= ("int" and "object")  [operator]
+ apprise/plugins/amqp/base.py:414: error: Unsupported operand types for <= ("object" and "int")  [operator]
- apprise/plugins/mobilemessage.py:255: error: Unsupported operand types for >= ("int" and "object")  [operator]
+ apprise/plugins/mobilemessage.py:255: error: Unsupported operand types for <= ("object" and "int")  [operator]

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong comparison in type error for comparisons

1 participant