Skip to content

fix [feature] error when == or != is used on values that are incompatible types #721#2353

Open
asukaminato0721 wants to merge 5 commits intofacebook:mainfrom
asukaminato0721:721
Open

fix [feature] error when == or != is used on values that are incompatible types #721#2353
asukaminato0721 wants to merge 5 commits intofacebook:mainfrom
asukaminato0721:721

Conversation

@asukaminato0721
Copy link
Copy Markdown
Contributor

@asukaminato0721 asukaminato0721 commented Feb 7, 2026

Summary

Fixes #721

Added a new incompatible-comparison diagnostic for ==/!= when operand types cannot overlap
Only triggers for built‑in scalar families (numeric, bytes-like, str) and only when the families are disjoint.
Skips user types, TypeVar, and type[...] comparisons.

Test Plan

add tests

@meta-cla meta-cla Bot added the cla signed label Feb 7, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as ready for review February 7, 2026 05:41
Copilot AI review requested due to automatic review settings February 7, 2026 05:41
@asukaminato0721 asukaminato0721 marked this pull request as draft February 7, 2026 05:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new diagnostic to flag likely-buggy equality/inequality comparisons (== / !=) between incompatible types, addressing #721.

Changes:

  • Introduces new ErrorKind::IncompatibleComparison diagnostic code.
  • Implements a comparison-time check in the operator/type inference pipeline to emit the diagnostic for certain incompatible equality comparisons.
  • Adds a regression test and documents the new error kind in the website docs.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
website/docs/error-kinds.mdx Documents the new incompatible-comparison diagnostic with an example.
pyrefly/lib/test/operators.rs Adds a testcase asserting the new diagnostic on int vs str equality/inequality.
pyrefly/lib/alt/operators.rs Implements the incompatible equality/inequality comparison check during compare_infer.
crates/pyrefly_config/src/error_kind.rs Adds the IncompatibleComparison error kind to the central enum.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyrefly/lib/alt/operators.rs Outdated
Comment thread pyrefly/lib/alt/operators.rs Outdated
Comment on lines +30 to +37
testcase!(
test_incompatible_equality_comparison,
r#"
def compare(x: int, y: str, z: int | str) -> None:
x == y # E: Comparison `==` between incompatible types `int` and `str`
x != y # E: Comparison `!=` between incompatible types `int` and `str`
z == y
"#,
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

This test only asserts the new error fires for int vs str, but the implementation also introduces “equivalence groups” (numeric, bytes-like) intended to not error for cases like int == float or bytes == bytearray. Adding a couple of assertions for these non-error cases would help prevent regressions/false positives in the grouping logic.

Copilot uses AI. Check for mistakes.
Comment thread website/docs/error-kinds.mdx Outdated
@asukaminato0721 asukaminato0721 marked this pull request as ready for review February 11, 2026 08:38
@github-actions

This comment has been minimized.

@kinto0
Copy link
Copy Markdown
Contributor

kinto0 commented Feb 13, 2026

this seems contreversial... I imagine we want to have this default to off? not sure. let's discuss in discord

@yangdanny97
Copy link
Copy Markdown
Contributor

I wonder if we can use disjoint bases for this? there's a disjoint_base helper already in narrow.rs https://peps.python.org/pep-0800/#specification

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the stale label Mar 16, 2026
asukaminato0721 and others added 5 commits March 22, 2026 19:25
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

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

ppb-vector (https://github.com/ppb/ppb-vector)
+ ERROR ppb_vector/__init__.py:136:44-54: Comparison `!=` between incompatible types `frozenset[str]` and `set[str]` [incompatible-comparison]
+ ERROR ppb_vector/__init__.py:564:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/test_angle.py:55:22-23: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/test_constants.py:13:50-52: Comparison `==` between incompatible types `float` and `Literal[90]` [incompatible-comparison]
+ ERROR tests/test_length.py:35:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ ERROR docs/src/examples/contrib/webscanner_helper/test_urldict.py:65:58-67: Comparison `==` between incompatible types `list[Any]` and `Literal['default']` [incompatible-comparison]
+ ERROR examples/contrib/webscanner_helper/test_urldict.py:65:58-67: Comparison `==` between incompatible types `list[Any]` and `Literal['default']` [incompatible-comparison]
+ ERROR test/mitmproxy/addons/test_view.py:45:31-40: Comparison `==` between incompatible types `float` and `Literal[946681200]` [incompatible-comparison]
+ ERROR test/mitmproxy/addons/test_view.py:62:31-40: Comparison `==` between incompatible types `float` and `Literal[946681200]` [incompatible-comparison]
+ ERROR test/mitmproxy/addons/test_view.py:80:31-40: Comparison `==` between incompatible types `float` and `Literal[946681200]` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__api.py:44:57-58: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__api.py:53:54-55: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_graphql.py:10:17-12:6: Comparison `==` between incompatible types `Literal[2]` and `float` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_graphql.py:13:17-15:6: Comparison `==` between incompatible types `Literal[2]` and `float` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_graphql.py:16:17-18:6: Comparison `==` between incompatible types `Literal[0]` and `float` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_graphql.py:19:17-22:6: Comparison `==` between incompatible types `Literal[0]` and `float` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_graphql.py:23:17-25:6: Comparison `==` between incompatible types `Literal[0]` and `float` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_graphql.py:26:17-89: Comparison `==` between incompatible types `Literal[0]` and `float` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_zip.py:54:81-82: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/mitmproxy/contentviews/test__view_zip.py:55:82-83: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/mitmproxy/proxy/layers/test_websocket.py:39:33-52: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR test/mitmproxy/test_connection.py:61:37-39: Comparison `==` between incompatible types `float` and `Literal[42]` [incompatible-comparison]

rotki (https://github.com/rotki/rotki)
+ ERROR rotkehlchen/tests/api/blockchain/test_bitcoin.py:41:22-42: Comparison `==` between incompatible types `dict[str, Any]` and `list[str]` [incompatible-comparison]
+ ERROR rotkehlchen/tests/api/test_settings.py:640:24-69: Comparison `==` between incompatible types `tuple[EvmIndexer, ...]` and `list[EvmIndexer]` [incompatible-comparison]
+ ERROR rotkehlchen/tests/db/test_db.py:1112:24-34: Comparison `==` between incompatible types `str` and `Literal[1451606400]` [incompatible-comparison]
+ ERROR rotkehlchen/tests/db/test_db.py:1130:24-34: Comparison `==` between incompatible types `str` and `Literal[1491607800]` [incompatible-comparison]

dulwich (https://github.com/dulwich/dulwich)
+ ERROR dulwich/pack.py:1623:24-41: Comparison `!=` between incompatible types `bytearray` and `bytes` [incompatible-comparison]

streamlit (https://github.com/streamlit/streamlit)
+ ERROR lib/streamlit/runtime/state/query_params.py:678:67-73: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR lib/tests/streamlit/runtime/state/query_params_test.py:75:44-47: Comparison `==` between incompatible types `Literal[1]` and `Literal['1']` [incompatible-comparison]
+ ERROR lib/tests/streamlit/runtime/state/query_params_test.py:81:46-52: Comparison `==` between incompatible types `float` and `Literal['1.23']` [incompatible-comparison]
+ ERROR lib/tests/streamlit/runtime/state/query_params_test.py:101:45-52: Comparison `==` between incompatible types `list[str]` and `Literal['test2']` [incompatible-comparison]
+ ERROR lib/tests/streamlit/runtime/state/query_params_test.py:107:45-48: Comparison `==` between incompatible types `tuple[Literal[1], Literal[2], Literal[3]]` and `Literal['3']` [incompatible-comparison]
+ ERROR lib/tests/streamlit/runtime/state/query_params_test.py:113:45-48: Comparison `==` between incompatible types `set[int]` and `Literal['3']` [incompatible-comparison]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ ERROR ddtrace/internal/sampling.py:112:33-34: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR ddtrace/internal/sampling.py:114:35-36: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR ddtrace/llmobs/_integrations/crewai.py:67:39-45: Comparison `==` between incompatible types `dict[str, Any]` and `Literal['task']` [incompatible-comparison]
+ ERROR ddtrace/llmobs/_integrations/crewai.py:73:39-45: Comparison `==` between incompatible types `dict[str, Any]` and `Literal['flow']` [incompatible-comparison]
+ ERROR ddtrace/llmobs/_integrations/crewai.py:75:39-52: Comparison `==` between incompatible types `dict[str, Any]` and `Literal['flow_method']` [incompatible-comparison]

ibis (https://github.com/ibis-project/ibis)
+ ERROR ibis/common/tests/test_typing.py:149:23-32: Comparison `!=` between incompatible types `Sentinel` and `Literal['missing']` [incompatible-comparison]
+ ERROR ibis/expr/types/_rich.py:111:29-30: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR ibis/expr/types/relations.py:1751:24-25: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR ibis/expr/types/relations.py:1753:26-27: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

cryptography (https://github.com/pyca/cryptography)
+ ERROR tests/hazmat/primitives/test_aead.py:219:23-25: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:243:23-25: Comparison `==` between incompatible types `bytearray` and `Literal[b'decrypt me']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:473:23-25: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:497:23-25: Comparison `==` between incompatible types `bytearray` and `Literal[b'decrypt me']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:705:23-25: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:729:23-25: Comparison `==` between incompatible types `bytearray` and `Literal[b'decrypt me']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:966:23-25: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:990:23-25: Comparison `==` between incompatible types `bytearray` and `Literal[b'decrypt me']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:1201:23-25: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:1223:23-25: Comparison `==` between incompatible types `bytearray` and `Literal[b'decrypt me']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:1462:23-25: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aead.py:1486:23-25: Comparison `==` between incompatible types `bytearray` and `Literal[b'decrypt me']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aes.py:34:47-73: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aes.py:294:18-22: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aes.py:319:18-22: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aes_gcm.py:195:22-26: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_aes_gcm.py:233:22-26: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_concatkdf.py:143:23-31: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_concatkdf.py:345:23-31: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_hkdf.py:157:23-60: Comparison `==` between incompatible types `bytearray` and `Literal[b'gJ\xfb{\xb1Oi\xc5sMC\xb7\xe4@\xf7u']` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_hkdf.py:287:23-26: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_kbkdf.py:146:23-31: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_padding.py:133:25-44: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_padding.py:247:25-44: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_pbkdf2hmac.py:73:23-31: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_scrypt.py:245:23-31: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_x25519.py:343:16-29: Comparison `==` between incompatible types `bytes` and `bytearray` [incompatible-comparison]
+ ERROR tests/hazmat/primitives/test_x963kdf.py:125:23-31: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]

altair (https://github.com/vega/altair)
+ ERROR tests/test_datasets.py:145:35-51: Comparison `==` between incompatible types `set[str]` and `frozenset[str]` [incompatible-comparison]
+ ERROR tests/utils/test_schemapi.py:250:40-41: Comparison `==` between incompatible types `dict[str, Any]` and `Literal[4]` [incompatible-comparison]
+ ERROR tests/utils/test_schemapi.py:254:52-65: Comparison `==` between incompatible types `dict[str, Any]` and `list[int | str]` [incompatible-comparison]
+ ERROR tests/utils/test_schemapi.py:255:60-71: Comparison `==` between incompatible types `dict[str, Any]` and `list[LiteralString]` [incompatible-comparison]
+ ERROR tests/utils/test_schemapi.py:261:29-32: Comparison `==` between incompatible types `dict[str, Any]` and `Literal['A']` [incompatible-comparison]
+ ERROR tests/utils/test_schemapi.py:265:29-32: Comparison `==` between incompatible types `dict[str, Any]` and `Literal['B']` [incompatible-comparison]

scipy (https://github.com/scipy/scipy)
+ ERROR subprojects/array_api_compat/array_api_compat/array_api_compat/torch/_aliases.py:440:26-37: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR subprojects/array_api_compat/array_api_compat/vendor_test/vendored/_compat/torch/_aliases.py:440:26-37: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR subprojects/array_api_extra/src/array_api_extra/_lib/_funcs.py:431:16-17: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

colour (https://github.com/colour-science/colour)
+ ERROR colour/algebra/tests/test_interpolation.py:896:46-47: Comparison `==` between incompatible types `float` and `Literal[3]` [incompatible-comparison]
+ ERROR colour/notation/munsell/centore2014.py:1897:27-28: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR colour/plotting/tests/test_common.py:127:86-87: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]

pyinstrument (https://github.com/joerick/pyinstrument)
+ ERROR pyinstrument/renderers/console.py:276:36-37: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR pyinstrument/session.py:175:40-41: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR pyinstrument/util.py:95:17-18: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

urllib3 (https://github.com/urllib3/urllib3)
+ ERROR test/test_response.py:1114:23-31: Comparison `==` between incompatible types `bytearray` and `Literal[b'hello']` [incompatible-comparison]
+ ERROR test/test_response.py:1120:24-33: Comparison `==` between incompatible types `bytearray` and `Literal[b' world']` [incompatible-comparison]
+ ERROR test/test_retry.py:139:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:142:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:158:44-55: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR test/test_retry.py:165:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:168:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:179:44-55: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/test_retry.py:182:44-55: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/test_retry.py:189:65-80: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR test/test_retry.py:190:69-84: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR test/test_retry.py:193:51-52: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/test_retry.py:194:51-52: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/test_retry.py:206:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:209:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:220:44-55: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/test_retry.py:223:44-55: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/test_retry.py:227:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:230:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:239:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/test_retry.py:349:52-353:10: Comparison `==` between incompatible types `frozenset[str]` and `set[str]` [incompatible-comparison]
+ ERROR test/test_retry.py:358:52-68: Comparison `==` between incompatible types `frozenset[str]` and `set[str]` [incompatible-comparison]
+ ERROR test/test_retry.py:371:50-58: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR test/test_util.py:740:50-51: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/test_util.py:742:50-52: Comparison `==` between incompatible types `float` and `Literal[37]` [incompatible-comparison]

Expression (https://github.com/cognitedata/Expression)
+ ERROR tests/test_array.py:122:18-21: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR tests/test_array.py:128:18-21: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR tests/test_parser.py:136:31-34: Comparison `==` between incompatible types `float` and `Literal[123]` [incompatible-comparison]

scikit-learn (https://github.com/scikit-learn/scikit-learn)
+ ERROR sklearn/covariance/tests/test_graphical_lasso.py:56:29-30: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/decomposition/_nmf.py:505:30-31: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/ensemble/tests/test_gradient_boosting.py:1464:54-55: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/ensemble/tests/test_gradient_boosting.py:1465:66-67: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/externals/array_api_compat/torch/_aliases.py:440:26-37: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR sklearn/externals/array_api_extra/_lib/_funcs.py:618:16-17: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/impute/tests/test_impute.py:216:21-40: Comparison `==` between incompatible types `Literal[0]` and `float` [incompatible-comparison]
+ ERROR sklearn/linear_model/tests/test_coordinate_descent.py:721:19-32: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR sklearn/linear_model/tests/test_logistic.py:504:21-26: Comparison `==` between incompatible types `list[Unknown]` and `int` [incompatible-comparison]
+ ERROR sklearn/metrics/cluster/tests/test_supervised.py:282:38-39: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/metrics/cluster/tests/test_supervised.py:296:48-49: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR sklearn/metrics/cluster/tests/test_supervised.py:297:40-41: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sklearn/tests/test_kernel_approximation.py:460:31-62: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR sklearn/tests/test_multiclass.py:181:36-67: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR sklearn/tests/test_multiclass.py:540:36-67: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR sklearn/tests/test_multiclass.py:545:36-67: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR sklearn/tests/test_multiclass.py:559:37-68: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR sklearn/tree/tests/test_tree.py:1011:34-35: Comparison `==` between incompatible types `list[Unknown]` and `Literal[2]` [incompatible-comparison]
+ ERROR sklearn/tree/tests/test_tree.py:2045:39-40: Comparison `==` between incompatible types `float64` and `Literal[0]` [incompatible-comparison]

freqtrade (https://github.com/freqtrade/freqtrade)
+ ERROR freqtrade/data/metrics.py:457:23-24: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/exchange/exchange.py:1086:89-90: Comparison `!=` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR freqtrade/exchange/exchange.py:3681:32-33: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/exchange/exchange_utils.py:195:43-44: Comparison `!=` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR freqtrade/exchange/exchange_utils.py:209:43-44: Comparison `!=` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR freqtrade/freqtradebot.py:558:76-77: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/freqtradebot.py:816:48-49: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/freqtradebot.py:954:42-43: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/freqtradebot.py:2470:23-24: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/freqtradebot.py:2601:23-24: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/misc.py:184:32-33: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR freqtrade/misc.py:184:44-46: Comparison `==` between incompatible types `float` and `Literal[-1]` [incompatible-comparison]
+ ERROR freqtrade/optimize/backtesting.py:616:23-25: Comparison `==` between incompatible types `float` and `Literal[-1]` [incompatible-comparison]
+ ERROR freqtrade/optimize/backtesting.py:724:43-44: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/optimize/backtesting.py:962:89-90: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/optimize/hyperopt_loss/hyperopt_loss_sharpe_daily.py:62:24-25: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/optimize/hyperopt_loss/hyperopt_loss_sortino_daily.py:70:26-27: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/persistence/trade_model.py:829:94-95: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/plugins/pairlist/PriceFilter.py:110:97-98: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/strategy/strategy_helper.py:143:28-30: Comparison `==` between incompatible types `float` and `Literal[-1]` [incompatible-comparison]
+ ERROR freqtrade/strategy/strategy_helper.py:143:85-86: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR freqtrade/strategy/strategy_helper.py:176:24-25: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR freqtrade/wallets.py:332:31-32: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

setuptools (https://github.com/pypa/setuptools)
+ ERROR setuptools/_distutils/compilers/C/tests/test_cygwin.py:42:63-65: Comparison `==` between incompatible types `str` and `list[@_]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_clib.py:100:36-47: Comparison `==` between incompatible types `Literal['one-dir']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:272:33-67: Comparison `==` between incompatible types `Literal['my_lib, other_lib lastlib']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:287:29-43: Comparison `==` between incompatible types `LiteralString` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:294:36-59: Comparison `==` between incompatible types `Literal['one two,three']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:303:30-58: Comparison `==` between incompatible types `Literal['one,two']` and `list[tuple[str, str]]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:310:29-43: Comparison `==` between incompatible types `Literal['one,two']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:321:33-43: Comparison `==` between incompatible types `Literal['1 2']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_build_ext.py:360:33-38: Comparison `==` between incompatible types `list[str]` and `Literal['foo']` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_cmd.py:39:31-44: Comparison `==` between incompatible types `Literal['ok,dok']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_config_cmd.py:66:36-50: Comparison `==` between incompatible types `LiteralString` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_config_cmd.py:67:33-40: Comparison `==` between incompatible types `Literal['one']` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_config_cmd.py:68:36-53: Comparison `==` between incompatible types `LiteralString` and `list[str]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_install_lib.py:37:32-33: Comparison `==` between incompatible types `Literal['2']` and `Literal[2]` [incompatible-comparison]
+ ERROR setuptools/_distutils/tests/test_sysconfig.py:300:23-29: Comparison `==` between incompatible types `bytes` and `Literal['True']` [incompatible-comparison]
+ ERROR setuptools/dist.py:1040:27-28: Comparison `==` between incompatible types `str` and `Literal[0]` [incompatible-comparison]
+ ERROR setuptools/dist.py:1052:29-30: Comparison `==` between incompatible types `str` and `Literal[1]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:230:58-74: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:235:33-49: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:241:25-41: Comparison `==` between incompatible types `frozenset[str | Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:253:25-41: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:267:25-41: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:279:25-41: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:291:25-41: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:303:25-41: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:320:25-41: Comparison `==` between incompatible types `frozenset[str | Unknown]` and `set[Unknown]` [incompatible-comparison]
+ ERROR setuptools/tests/test_manifest.py:332:25-41: Comparison `==` between incompatible types `frozenset[Unknown]` and `set[Unknown]` [incompatible-comparison]

pwndbg (https://github.com/pwndbg/pwndbg)
+ ERROR pwndbg/aglib/elf.py:313:69-79: Comparison `==` between incompatible types `bytearray` and `Literal[b'\x7fELF']` [incompatible-comparison]
+ ERROR pwndbg/aglib/elf.py:326:70-80: Comparison `==` between incompatible types `bytearray` and `Literal[b'\x7fELF']` [incompatible-comparison]
+ ERROR pwndbg/aglib/elf.py:337:74-84: Comparison `==` between incompatible types `bytearray` and `Literal[b'\x7fELF']` [incompatible-comparison]
+ ERROR pwndbg/aglib/godbg.py:815:37-44: Comparison `!=` between incompatible types `bytearray` and `Literal[b'\x00']` [incompatible-comparison]
+ ERROR pwndbg/aglib/kernel/slab.py:499:47-60: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR pwndbg/aglib/macho.py:392:19-22: Comparison `!=` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR pwndbg/aglib/objc.py:909:17-21: Comparison `!=` between incompatible types `int` and `Literal[b'[']` [incompatible-comparison]
+ ERROR pwndbg/aglib/objc.py:924:17-21: Comparison `!=` between incompatible types `int` and `Literal[b'^']` [incompatible-comparison]
+ ERROR pwndbg/commands/valist.py:40:17-25: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pwndbg/gdblib/got.py:363:20-23: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]

scikit-build-core (https://github.com/scikit-build/scikit-build-core)
+ ERROR tests/test_build_cli.py:56:35-74: Comparison `==` between incompatible types `frozenset[Any]` and `set[str]` [incompatible-comparison]
+ ERROR tests/test_build_cli.py:58:35-62:10: Comparison `==` between incompatible types `frozenset[Any]` and `set[str]` [incompatible-comparison]
+ ERROR tests/test_build_cli.py:64:35-69:10: Comparison `==` between incompatible types `frozenset[Any]` and `set[str]` [incompatible-comparison]

apprise (https://github.com/caronc/apprise)
+ ERROR apprise/plugins/kodi.py:238:29-54: Comparison `==` between incompatible types `tuple[Literal['kodi'], Literal['xbmc']]` and `int` [incompatible-comparison]
+ ERROR apprise/plugins/vapid/__init__.py:499:33-34: Comparison `==` between incompatible types `list[Unknown]` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_api.py:1337:30-39: Comparison `==` between incompatible types `list[str]` and `tuple[Literal['/tmp']]` [incompatible-comparison]
+ ERROR tests/test_apprise_utils.py:2267:27-36: Comparison `==` between incompatible types `int` and `Literal['updated']` [incompatible-comparison]
+ ERROR tests/test_plugin_discord.py:552:39-40: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_plugin_email.py:2215:29-63: Comparison `==` between incompatible types `list[bool | str]` and `tuple[Literal[False], Literal['user@custom-domain.casa']]` [incompatible-comparison]
+ ERROR tests/test_plugin_reddit.py:343:39-40: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_plugin_revolt.py:373:39-40: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_plugin_xmpp.py:4156:28-75: Comparison `==` between incompatible types `str` and `tuple[Literal['groupchat'], Literal['general@conference.example.com']]` [incompatible-comparison]

psycopg (https://github.com/psycopg/psycopg)
+ ERROR tests/test_connection_info.py:195:49-53: Comparison `==` between incompatible types `float` and `Literal[3600]` [incompatible-comparison]
+ ERROR tests/test_connection_info.py:197:49-53: Comparison `==` between incompatible types `float` and `Literal[7200]` [incompatible-comparison]

starlette (https://github.com/encode/starlette)
+ ERROR tests/test_responses.py:652:22-30: Comparison `==` between incompatible types `bytearray` and `Literal[b'chunk']` [incompatible-comparison]

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ ERROR pymongo/asynchronous/topology.py:347:27-28: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR pymongo/synchronous/topology.py:347:27-28: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

schemathesis (https://github.com/schemathesis/schemathesis)
+ ERROR src/schemathesis/generation/coverage.py:1706:17-23: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]

pip (https://github.com/pypa/pip)
+ ERROR src/pip/_vendor/rich/progress.py:1032:30-31: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

openlibrary (https://github.com/internetarchive/openlibrary)
+ ERROR openlibrary/catalog/marc/marc_binary.py:173:32-39: Comparison `!=` between incompatible types `int` and `Literal[b'\x1e']` [incompatible-comparison]
+ ERROR openlibrary/catalog/marc/marc_binary.py:176:30-37: Comparison `!=` between incompatible types `int` and `Literal[b'\x1e']` [incompatible-comparison]
+ ERROR openlibrary/i18n/__init__.py:295:37-40: Comparison `==` between incompatible types `float` and `Literal[100]` [incompatible-comparison]
+ ERROR openlibrary/i18n/__init__.py:304:40-43: Comparison `==` between incompatible types `float` and `Literal[100]` [incompatible-comparison]

egglog-python (https://github.com/egraphs-good/egglog-python)
+ ERROR python/tests/test_high_level.py:1570:25-38: Comparison `==` between incompatible types `int` and `tuple[test_binary_preserved.X, test_binary_preserved.X]` [incompatible-comparison]

cloud-init (https://github.com/canonical/cloud-init)
+ ERROR tests/unittests/distros/package_management/test_apt.py:75:23-37: Comparison `==` between incompatible types `SubpResult` and `Literal['return_thing']` [incompatible-comparison]
+ ERROR tests/unittests/sources/test_azure.py:2951:29-56: Comparison `==` between incompatible types `Literal['Running']` and `list[Unknown]` [incompatible-comparison]
+ ERROR tests/unittests/sources/test_azure.py:2962:29-56: Comparison `==` between incompatible types `Literal['Savable']` and `list[Unknown]` [incompatible-comparison]
+ ERROR tests/unittests/sources/test_cloudstack.py:526:31-35: Comparison `==` between incompatible types `dict[Unknown, Unknown]` and `Literal['md']` [incompatible-comparison]
+ ERROR tests/unittests/sources/test_gce.py:468:31-35: Comparison `==` between incompatible types `dict[Unknown, Unknown]` and `Literal['md']` [incompatible-comparison]
+ ERROR tests/unittests/sources/test_oracle.py:567:16-65: Comparison `==` between incompatible types `_Call` and `_CallList` [incompatible-comparison]

aiohttp (https://github.com/aio-libs/aiohttp)
+ ERROR aiohttp/client.py:249:53-54: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR aiohttp/http_parser.py:266:39-45: Comparison `!=` between incompatible types `bytes` and `Literal['\r\n']` [incompatible-comparison]

vision (https://github.com/pytorch/vision)
+ ERROR torchvision/ops/focal_loss.py:36:43-45: Comparison `!=` between incompatible types `float` and `Literal[-1]` [incompatible-comparison]
+ ERROR torchvision/prototype/datasets/_home.py:26:23-26: Comparison `==` between incompatible types `bool` and `Literal['1']` [incompatible-comparison]
+ ERROR torchvision/transforms/v2/functional/_geometry.py:1370:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR torchvision/transforms/v2/functional/_geometry.py:1372:21-24: Comparison `==` between incompatible types `float` and `Literal[180]` [incompatible-comparison]
+ ERROR torchvision/transforms/v2/functional/_geometry.py:1376:25-27: Comparison `==` between incompatible types `float` and `Literal[90]` [incompatible-comparison]
+ ERROR torchvision/transforms/v2/functional/_geometry.py:1378:25-28: Comparison `==` between incompatible types `float` and `Literal[270]` [incompatible-comparison]
+ ERROR torchvision/transforms/v2/functional/_geometry.py:2067:17-18: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR torchvision/transforms/v2/functional/_geometry.py:2139:17-18: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

sockeye (https://github.com/awslabs/sockeye)
+ ERROR sockeye/loss.py:151:31-32: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/unit/test_data_io.py:240:54-80: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]

zulip (https://github.com/zulip/zulip)
+ ERROR corporate/lib/activity.py:181:28-29: Comparison `==` between incompatible types `Decimal` and `Literal[0]` [incompatible-comparison]
+ ERROR corporate/lib/stripe.py:115:23-24: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR corporate/lib/stripe.py:137:32-33: Comparison `==` between incompatible types `Decimal` and `Literal[0]` [incompatible-comparison]
+ ERROR zerver/tests/test_timezone.py:50:49-55: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]

speedrun.com_global_scoreboard_webapp (https://github.com/Avasam/speedrun.com_global_scoreboard_webapp)
+ ERROR backend/services/user_updater_helpers.py:235:100-101: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]

ignite (https://github.com/pytorch/ignite)
+ ERROR ignite/engine/engine.py:1011:62-84: Comparison `!=` between incompatible types `Literal[False]` and `Literal['skip_epoch_completed']` [incompatible-comparison]
+ ERROR ignite/engine/engine.py:1044:41-57: Comparison `!=` between incompatible types `Literal[False]` and `Literal['skip_completed']` [incompatible-comparison]
+ ERROR ignite/engine/engine.py:1211:62-84: Comparison `!=` between incompatible types `Literal[False]` and `Literal['skip_epoch_completed']` [incompatible-comparison]
+ ERROR ignite/engine/engine.py:1244:41-57: Comparison `!=` between incompatible types `Literal[False]` and `Literal['skip_completed']` [incompatible-comparison]

hydpy (https://github.com/hydpy-dev/hydpy)
+ ERROR hydpy/auxs/armatools.py:269:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR hydpy/core/timetools.py:1357:26-37: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR hydpy/models/ga/ga_control.py:117:41-44: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]

pandas (https://github.com/pandas-dev/pandas)
+ ERROR pandas/core/indexes/range.py:997:56-66: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/core/indexes/range.py:998:52-62: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/core/internals/managers.py:1771:27-35: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR pandas/core/window/numba_.py:146:54-55: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR pandas/core/window/numba_.py:318:54-55: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR pandas/io/excel/_odfreader.py:203:23-33: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/io/excel/_pyxlsb.py:103:23-29: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/io/formats/css.py:385:24-27: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/io/formats/format.py:1933:20-21: Comparison `!=` between incompatible types `Decimal` and `Literal[0]` [incompatible-comparison]
+ ERROR pandas/io/parsers/readers.py:225:24-27: Comparison `!=` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/io/parsers/readers.py:2139:21-27: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR pandas/tests/arrays/numpy_/test_numpy.py:325:23-24: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR pandas/tests/frame/indexing/test_indexing.py:1080:30-31: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR pandas/tests/groupby/test_api.py:136:17-32: Comparison `!=` between incompatible types `set[str]` and `frozenset[str]` [incompatible-comparison]
+ ERROR pandas/tests/indexes/test_common.py:129:30-34: Comparison `==` between incompatible types `Literal['This is the new name for this index']` and `tuple[Literal['A'], Literal['B']]` [incompatible-comparison]
+ ERROR pandas/tests/scalar/timedelta/methods/test_as_unit.py:72:38-43: Comparison `==` between incompatible types `float` and `Literal[86400]` [incompatible-comparison]
+ ERROR pandas/tests/scalar/timedelta/methods/test_as_unit.py:80:39-44: Comparison `==` between incompatible types `float` and `Literal[86400]` [incompatible-comparison]
+ ERROR pandas/tests/scalar/timedelta/test_arithmetic.py:476:48-49: Comparison `==` between incompatible types `float` and `Literal[4]` [incompatible-comparison]
+ ERROR pandas/tests/scalar/timedelta/test_constructors.py:283:35-48: Comparison `==` between incompatible types `float` and `Literal[1000000000000]` [incompatible-comparison]
+ ERROR pandas/tests/scalar/timedelta/test_timedelta.py:132:26-30: Comparison `==` between incompatible types `float` and `Literal[1000]` [incompatible-comparison]
+ ERROR pandas/tests/scalar/timestamp/test_timestamp.py:325:38-50: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR pandas/tests/tools/test_to_datetime.py:3264:43-50: Comparison `==` between incompatible types `float64` and `Literal[2456658]` [incompatible-comparison]
+ ERROR pandas/tests/tslibs/test_timedeltas.py:89:53-57: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/tests/tslibs/test_timedeltas.py:90:65-69: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR pandas/tseries/frequencies.py:441:17-18: Comparison `!=` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR pandas/tseries/frequencies.py:442:25-35: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]

typeshed-stats (https://github.com/AlexWaygood/typeshed-stats)
+ ERROR tests/test_gather.py:1152:45-67: Comparison `==` between incompatible types `set[str]` and `frozenset[str]` [incompatible-comparison]

AutoSplit (https://github.com/Toufool/AutoSplit)
+ ERROR src/compare.py:209:16-17: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/compare.py:216:20-27: Comparison `!=` between incompatible types `float` and `Literal[255]` [incompatible-comparison]

pytest-robotframework (https://github.com/detachhead/pytest-robotframework)
+ ERROR tests/fixtures/test_python/test_assertion_fails_with_description.py:8:21-28: Comparison `==` between incompatible types `Literal[1]` and `Literal['wrong']` [incompatible-comparison]
+ ERROR tests/fixtures/test_python/test_assertion_fails_with_fail_message_hide_assert.py:8:21-28: Comparison `==` between incompatible types `Literal[1]` and `Literal['wrong']` [incompatible-comparison]
+ ERROR tests/fixtures/test_python/test_assertion_passes_custom_messages.py:14:25-32: Comparison `==` between incompatible types `Literal[1]` and `Literal['wrong']` [incompatible-comparison]

werkzeug (https://github.com/pallets/werkzeug)
+ ERROR tests/test_datastructures.py:441:42-43: Comparison `==` between incompatible types `Literal['1']` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:758:24-29: Comparison `!=` between incompatible types `tuple[Literal[1], Literal[2], Literal[3], Literal[4]]` and `ImmutableList[int]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:906:36-37: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:909:36-37: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:910:33-34: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:912:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:915:44-45: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_datastructures.py:916:41-42: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/test_http.py:41:34-35: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_http.py:75:35-36: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_http.py:86:30-31: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/test_wrappers.py:997:33-54: Comparison `==` between incompatible types `tuple[Literal['foo'], Literal['bar']]` and `list[str]` [incompatible-comparison]
+ ERROR src/werkzeug/datastructures/accept.py:145:27-28: Comparison `!=` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR src/werkzeug/test.py:1453:70-71: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

pyodide (https://github.com/pyodide/pyodide)
+ ERROR src/tests/test_typeconversions.py:2279:18-21: Comparison `==` between incompatible types `JsBigInt` and `float` [incompatible-comparison]

rich (https://github.com/Textualize/rich)
+ ERROR rich/progress.py:1032:30-31: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

core (https://github.com/home-assistant/core)
+ ERROR homeassistant/components/airtouch5/cover.py:121:32-33: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/bayesian/helpers.py:65:75-76: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR homeassistant/components/bayesian/helpers.py:66:77-78: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR homeassistant/components/camera/img_util.py:32:33-45: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR homeassistant/components/camera/img_util.py:32:72-85: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR homeassistant/components/deluge/sensor.py:36:39-40: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/deluge/sensor.py:38:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/derivative/sensor.py:235:80-81: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/derivative/sensor.py:406:38-39: Comparison `!=` between incompatible types `Decimal` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/devolo_home_control/siren.py:65:31-32: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/evohome/climate.py:202:44-45: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/fibaro/binary_sensor.py:103:51-52: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/fortios/device_tracker.py:116:44-45: Comparison `==` between incompatible types `dict[str, Any]` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/growatt_server/coordinator.py:324:48-49: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/history/websocket_api.py:255:24-25: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/history/websocket_api.py:302:44-45: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/homekit/type_lights.py:275:30-31: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/integration/sensor.py:339:80-81: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/limitlessled/light.py:287:36-37: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/modbus/climate.py:510:37-42: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR homeassistant/components/modbus/climate.py:533:39-44: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR homeassistant/components/modbus/validators.py:256:15-16: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/mold_indicator/sensor.py:358:32-33: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/mold_indicator/sensor.py:373:82-83: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/mqtt/light/schema_basic.py:421:28-29: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/number/__init__.py:339:27-28: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/owntracks/__init__.py:255:19-20: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/prana/number.py:49:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/recorder/statistics.py:639:42-57: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR homeassistant/components/rtorrent/sensor.py:191:49-50: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/rtorrent/sensor.py:193:32-33: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/tesla_fleet/sensor.py:538:65-66: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/components/xiaomi_aqara/sensor.py:199:54-55: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/util/color.py:409:19-20: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/util/color.py:572:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR homeassistant/util/unit_conversion.py:164:47-48: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

paasta (https://github.com/yelp/paasta)
+ ERROR paasta_tools/cli/cmds/mark_for_deployment.py:574:28-29: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR paasta_tools/cli/cmds/mark_for_deployment.py:574:50-53: Comparison `!=` between incompatible types `float` and `Literal[100]` [incompatible-comparison]

trio (https://github.com/python-trio/trio)
+ ERROR src/trio/_core/_run.py:2678:23-24: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_guest_mode.py:398:39-43: Comparison `==` between incompatible types `float` and `Literal[9999]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_guest_mode.py:669:39-40: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_guest_mode.py:672:39-47: Comparison `==` between incompatible types `float` and `Literal[120]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:20:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:21:32-33: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:27:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:28:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:33:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:39:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:40:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:45:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:46:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:55:23-24: Comparison `==` between incompatible types `float` and `Literal[3]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_mock_clock.py:63:45-46: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:351:50-51: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:398:39-40: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1835:58-59: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1837:58-59: Comparison `==` between incompatible types `float` and `Literal[3]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1839:58-59: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1841:58-60: Comparison `==` between incompatible types `float` and `Literal[10]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1843:58-59: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1847:58-60: Comparison `==` between incompatible types `float` and `Literal[10]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1994:49-56: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:1996:45-56: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:2002:41-46: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:2175:45-46: Comparison `==` between incompatible types `float` and `Literal[6]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_run.py:2194:45-46: Comparison `==` between incompatible types `float` and `Literal[7]` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_windows.py:163:34-38: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_windows.py:243:30-40: Comparison `==` between incompatible types `bytearray` and `Literal[b'test1\n']` [incompatible-comparison]
+ ERROR src/trio/_core/_tests/test_windows.py:248:30-40: Comparison `==` between incompatible types `bytearray` and `Literal[b'test2\n']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_dtls.py:705:42-43: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR src/trio/_tests/test_fakenet.py:105:19-39: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_fakenet.py:113:20-40: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_fakenet.py:172:24-29: Comparison `==` between incompatible types `bytearray` and `Literal[b'xy']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_fakenet.py:173:24-39: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_fakenet.py:184:24-29: Comparison `==` between incompatible types `bytearray` and `Literal[b'xy']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_highlevel_open_tcp_stream.py:402:35-38: Comparison `==` between incompatible types `float` and `Literal[100]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_highlevel_open_tcp_stream.py:422:35-38: Comparison `==` between incompatible types `float` and `Literal[100]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_highlevel_open_tcp_stream.py:487:35-36: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_highlevel_open_tcp_stream.py:688:35-36: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:241:19-22: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['x']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:961:23-41: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1003:23-43: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1021:28-33: Comparison `==` between incompatible types `bytearray` and `Literal[b'xy']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1022:28-43: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1056:52-60: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['ok ::1']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1057:53-61: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['ok ::1']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1058:55-66: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['ok faß.de']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1059:62-73: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['ok faß.de']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1060:63-74: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['ok faß.de']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1104:72-1109:10: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `tuple[Literal['custom_gai'], Literal[b'localhost'], Literal['foo'], *tuple[AddressFamily | int, ...]]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1114:19-27: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `tuple[Literal['custom_gai'], Literal[b'xn--f-1gaa'], Literal['foo'], Literal[0], Literal[0], Literal[0], Literal[0]]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_socket.py:1128:57-60: Comparison `==` between incompatible types `list[tuple[AddressFamily, SocketKind, int, str, tuple[int, bytes] | tuple[str, int] | tuple[str, int, int, int]]]` and `Literal['x']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_subprocess.py:242:28-36: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:257:33-37: Comparison `==` between incompatible types `bytearray` and `Literal[b'1']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:258:34-42: Comparison `==` between incompatible types `bytearray` and `Literal[b'23456']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:260:32-38: Comparison `==` between incompatible types `bytearray` and `Literal[b'789']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:272:37-43: Comparison `==` between incompatible types `bytearray` and `Literal[b'abc']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:276:36-40: Comparison `==` between incompatible types `bytearray` and `Literal[b'd']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:278:35-43: Comparison `==` between incompatible types `bytearray` and `Literal[b'efghi']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:286:39-45: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:304:34-37: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:305:32-35: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:306:33-36: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:307:31-34: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:332:38-42: Comparison `==` between incompatible types `bytearray` and `Literal[b'1']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:333:37-42: Comparison `==` between incompatible types `bytearray` and `Literal[b'23']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:345:40-46: Comparison `==` between incompatible types `bytearray` and `Literal[b'456']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:369:36-42: Comparison `==` between incompatible types `bytearray` and `Literal[b'xxx']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:370:36-39: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:384:42-48: Comparison `==` between incompatible types `bytearray` and `Literal[b'abc']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:460:43-52: Comparison `==` between incompatible types `bytearray` and `Literal[b'yyyxxx']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:461:43-49: Comparison `==` between incompatible types `bytearray` and `Literal[b'xxx']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:462:43-49: Comparison `==` between incompatible types `bytearray` and `Literal[b'xxx']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:466:43-49: Comparison `==` between incompatible types `bytearray` and `Literal[b'zzz']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:482:46-49: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:506:42-48: Comparison `==` between incompatible types `bytearray` and `Literal[b'123']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:510:42-46: Comparison `==` between incompatible types `bytearray` and `Literal[b'4']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:514:42-47: Comparison `==` between incompatible types `bytearray` and `Literal[b'56']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:518:42-45: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:528:40-46: Comparison `==` between incompatible types `bytearray` and `Literal[b'123']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:531:44-52: Comparison `==` between incompatible types `bytearray` and `bytes` [incompatible-comparison]
+ ERROR src/trio/_tests/test_testing.py:574:40-49: Comparison `==` between incompatible types `bytearray` and `Literal[b'456789']` [incompatible-comparison]
+ ERROR src/trio/_tests/test_timeouts.py:214:37-38: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_timeouts.py:223:40-41: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_timeouts.py:231:40-41: Comparison `==` between incompatible types `float` and `Literal[4]` [incompatible-comparison]
+ ERROR src/trio/_tests/test_timeouts.py:257:36-37: Comparison `==` between incompatible types `float` and `Literal[5]` [incompatible-comparison]
+ ERROR src/trio/_timeouts.py:108:19-20: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR src/trio/testing/_memory_streams.py:508:56-59: Comparison `==` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]
+ ERROR src/trio/testing/_memory_streams.py:539:60-63: Comparison `!=` between incompatible types `bytearray` and `Literal[b'']` [incompatible-comparison]

jinja (https://github.com/pallets/jinja)
+ ERROR src/jinja2/filters.py:719:17-18: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]

sphinx (https://github.com/sphinx-doc/sphinx)
+ ERROR sphinx/versioning.py:78:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR sphinx/versioning.py:91:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

xarray (https://github.com/pydata/xarray)
+ ERROR xarray/coding/frequencies.py:240:17-18: Comparison `!=` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR xarray/coding/frequencies.py:241:25-35: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR xarray/tests/test_indexes.py:342:37-51: Comparison `==` between incompatible types `list[Hashable | None]` and `tuple[Literal['foo'], Literal['bar']]` [incompatible-comparison]
+ ERROR xarray/tests/test_variable.py:2584:38-64: Comparison `==` between incompatible types `list[Hashable | None]` and `tuple[Literal['x_level_0'], Literal['x_level_1']]` [incompatible-comparison]

artigraph (https://github.com/artigraph/artigraph)
+ ERROR tests/arti/partitions/test_partitions.py:29:26-42: Comparison `==` between incompatible types `frozenset[str]` and `set[str]` [incompatible-comparison]
+ ERROR tests/arti/types/test_pydantic_adapters.py:94:46-56: Comparison `==` between incompatible types `set[Any]` and `frozenset[Any]` [incompatible-comparison]

spack (https://github.com/spack/spack)
+ ERROR lib/spack/spack/hooks/sbang.py:112:85-90: Comparison `!=` between incompatible types `int` and `Literal[b'\n']` [incompatible-comparison]
+ ERROR lib/spack/spack/test/fetch_strategy.py:122:37-38: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR lib/spack/spack/util/libc.py:167:52-70: Comparison `==` between incompatible types `list[str]` and `Literal['--dynamic-linker']` [incompatible-comparison]

kornia (https://github.com/kornia/kornia)
+ ERROR kornia/augmentation/base.py:165:23-24: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR kornia/augmentation/base.py:167:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR kornia/augmentation/base.py:177:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR kornia/augmentation/base.py:179:23-24: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR kornia/augmentation/presets/ada.py:267:22-23: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR kornia/models/efficient_vit/backbone.py:144:28-29: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR kornia/models/efficient_vit/backbone.py:326:28-29: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR kornia/models/yunet/processors.py:69:33-53: Comparison `!=` between incompatible types `int` and `float` [incompatible-comparison]

scrapy (https://github.com/scrapy/scrapy)
+ ERROR tests/test_http_headers.py:32:37-49: Comparison `==` between incompatible types `Literal['text/html']` and `Literal[b'text/html']` [incompatible-comparison]
+ ERROR tests/test_http_headers.py:39:40-46: Comparison `==` between incompatible types `list[str]` and `Literal[b'ip2']` [incompatible-comparison]

optuna (https://github.com/optuna/optuna)
+ ERROR optuna/distributions.py:392:85-86: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR optuna/importance/_fanova/_fanova.py:70:33-34: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR optuna/testing/pytest_storages.py:432:60-61: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR optuna/testing/pytest_storages.py:467:60-61: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/artifacts_tests/test_backoff.py:18:42-43: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/artifacts_tests/test_backoff.py:19:42-44: Comparison `==` between incompatible types `float` and `Literal[10]` [incompatible-comparison]
+ ERROR tests/hypervolume_tests/test_wfg.py:27:76-100: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR tests/hypervolume_tests/test_wfg.py:44:12-50: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR tests/samplers_tests/test_samplers.py:364:62-81: Comparison `==` between incompatible types `float` and `Literal[30]` [incompatible-comparison]
+ ERROR tests/samplers_tests/test_samplers.py:365:52-71: Comparison `==` between incompatible types `float` and `Literal[30]` [incompatible-comparison]
+ ERROR tests/terminator_tests/improvement_tests/test_evaluator.py:38:90-91: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/terminator_tests/improvement_tests/test_evaluator.py:40:90-91: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/terminator_tests/improvement_tests/test_evaluator.py:43:90-91: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/terminator_tests/improvement_tests/test_evaluator.py:45:90-91: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR tests/terminator_tests/improvement_tests/test_evaluator.py:48:90-92: Comparison `==` between incompatible types `float` and `Literal[-1]` [incompatible-comparison]
+ ERROR tests/terminator_tests/improvement_tests/test_evaluator.py:49:90-92: Comparison `==` between incompatible types `float` and `Literal[-1]` [incompatible-comparison]
+ ERROR tests/trial_tests/test_fixed.py:13:47-48: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/trial_tests/test_frozen.py:200:47-48: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR tests/trial_tests/test_frozen.py:205:47-48: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]

spark (https://github.com/apache/spark)
+ ERROR python/pyspark/sql/tests/arrow/test_arrow_cogrouped_map.py:323:33-38: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/arrow/test_arrow_cogrouped_map.py:324:34-39: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/arrow/test_arrow_grouped_map.py:362:34-39: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/arrow/test_arrow_udf_grouped_agg.py:1006:30-42: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/arrow/test_arrow_udf_window.py:779:30-38: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_cogrouped_map.py:679:33-38: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_cogrouped_map.py:680:34-39: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_grouped_map.py:950:32-37: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_grouped_map.py:1078:37-47: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_grouped_map.py:1129:37-47: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_udf_grouped_agg.py:837:30-42: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]
+ ERROR python/pyspark/sql/tests/pandas/test_pandas_udf_window.py:636:30-38: Comparison `==` between incompatible types `int` and `float` [incompatible-comparison]

attrs (https://github.com/python-attrs/attrs)
+ ERROR tests/test_next_gen.py:390:22-28: Comparison `==` between incompatible types `Literal[11]` and `Literal['11']` [incompatible-comparison]
+ ERROR tests/test_setattr.py:91:31-35: Comparison `==` between incompatible types `Literal['foobarqux']` and `Literal[42]` [incompatible-comparison]
+ ERROR tests/test_setattr.py:146:22-26: Comparison `==` between incompatible types `Literal[42]` and `Literal['41']` [incompatible-comparison]
+ ERROR tests/test_setattr.py:147:22-26: Comparison `==` between incompatible types `Literal[23]` and `Literal['22']` [incompatible-comparison]
+ ERROR tests/test_setattr.py:463:21-24: Comparison `==` between incompatible types `Literal[2]` and `Literal['2']` [incompatible-comparison]

pylox (https://github.com/sco1/pylox)
+ ERROR tests/builtins/test_loxarrayize.py:20:26-37: Comparison `==` between incompatible types `dict[Unknown, Unknown]` and `deque[Unknown]` [incompatible-comparison]

graphql-core (https://github.com/graphql-python/graphql-core)
+ ERROR src/graphql/type/scalars.py:57:23-35: Comparison `!=` between incompatible types `int` and `float` [incompatible-comparison]

django-modern-rest (https://github.com/wemake-services/django-modern-rest)
+ ERROR dmr/internal/negotiation.py:93:67-68: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR dmr/settings.py:114:42-55: Comparison `==` between incompatible types `frozenset[str]` and `set[Settings]` [incompatible-comparison]
+ ERROR dmr/validation/settings.py:35:44-57: Comparison `==` between incompatible types `frozenset[str]` and `set[Settings]` [incompatible-comparison]

prefect (https://github.com/PrefectHQ/prefect)
+ ERROR src/integrations/prefect-gcp/prefect_gcp/secret_manager.py:486:22-30: Comparison `==` between incompatible types `int` and `Literal['latest']` [incompatible-comparison]
+ ERROR src/integrations/prefect-gcp/prefect_gcp/secret_manager.py:537:22-30: Comparison `==` between incompatible types `int` and `Literal['latest']` [incompatible-comparison]
+ ERROR src/integrations/prefect-gcp/tests/test_cloud_run_worker.py:382:40-55: Comparison `==` between incompatible types `Literal['my command and args']` and `list[LiteralString]` [incompatible-comparison]
+ ERROR src/integrations/prefect-gcp/tests/test_cloud_run_worker.py:394:37-49: Comparison `==` between incompatible types `Literal['my args']` and `list[LiteralString]` [incompatible-comparison]
+ ERROR src/integrations/prefect-gcp/tests/test_vertex_worker.py:139:43-141:21: Comparison `==` between incompatible types `list[str]` and `Literal['echo -n hello']` [incompatible-comparison]
+ ERROR src/integrations/prefect-github/tests/test_repository.py:177:52-67: Comparison `==` between incompatible types `set[str]` and `str` [incompatible-comparison]
+ ERROR src/integrations/prefect-github/tests/test_repository.py:178:73-87: Comparison `==` between incompatible types `set[str]` and `str` [incompatible-comparison]
+ ERROR src/integrations/prefect-github/tests/test_repository.py:218:73-87: Comparison `==` between incompatible types `set[str]` and `str` [incompatible-comparison]
+ ERROR src/prefect/client/schemas/objects.py:774:69-70: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

flake8 (https://github.com/pycqa/flake8)
+ ERROR tests/unit/test_legacy_api.py:100:35-47: Comparison `==` between incompatible types `int` and `Literal['Fake count']` [incompatible-comparison]

pytest (https://github.com/pytest-dev/pytest)
+ ERROR testing/_py/test_local.py:743:36-41: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR testing/_py/test_local.py:745:36-41: Comparison `==` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR testing/_py/test_local.py:747:36-41: Comparison `!=` between incompatible types `float` and `int` [incompatible-comparison]
+ ERROR testing/test_pytester.py:654:22-25: Comparison `==` between incompatible types `str` and `Literal[b'']` [incompatible-comparison]
+ ERROR testing/test_pytester.py:667:22-25: Comparison `==` between incompatible types `str` and `Literal[b'']` [incompatible-comparison]

jax (https://github.com/google/jax)
+ ERROR jax/_src/export/shape_poly_decision.py:417:21-22: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR jax/_src/numpy/polynomial.py:288:15-25: Comparison `==` between incompatible types `Literal[True]` and `Literal['unscaled']` [incompatible-comparison]
+ ERROR jax/_src/scipy/signal.py:330:41-42: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR jax/_src/scipy/signal.py:465:41-42: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

materialize (https://github.com/MaterializeInc/materialize)
+ ERROR test/cluster/mzcompose.py:2556:64-65: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2557:64-65: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2590:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2592:25-26: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2602:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2604:25-26: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2642:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2646:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2648:25-26: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2662:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2664:25-26: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2706:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2708:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2714:21-22: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2716:21-22: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2718:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2720:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2730:21-22: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2734:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2741:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2745:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2747:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2758:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2762:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2764:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2776:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2778:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2787:21-22: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2789:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2791:21-22: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2794:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2796:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2910:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2912:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2914:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2916:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2918:21-22: Comparison `==` between incompatible types `float` and `Literal[2]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2922:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2937:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2941:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2943:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2947:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2951:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2953:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2956:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR test/cluster/mzcompose.py:2958:21-22: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR misc/python/materialize/cli/ci_annotate_errors.py:1019:36-56: Comparison `!=` between incompatible types `int` and `str` [incompatible-comparison]
+ ERROR misc/python/materialize/feature_benchmark/benchmark_result_evaluator.py:105:23-24: Comparison `==` between incompatible types `float` and `Literal[1]` [incompatible-comparison]
+ ERROR misc/python/materialize/workload_replay/stats.py:387:15-16: Comparison `==` between incompatible types `float` and `Literal[0]` [incompatible-comparison]
+ ERROR misc/python/materialize/workload_replay/stats.py:388:39-40: Comparison `!=` between incompatible types `float` and `Literal[0]` [incompatible-comparison]

@github-actions
Copy link
Copy Markdown

Primer Diff Classification

❌ 61 regression(s) | ➖ 2 neutral | 63 project(s) total | +555 errors

61 regression(s) across ppb-vector, mitmproxy, rotki, dulwich, streamlit, dd-trace-py, ibis, cryptography, altair, scipy, colour, pyinstrument, urllib3, Expression, scikit-learn, freqtrade, setuptools, pwndbg, scikit-build-core, apprise, psycopg, starlette, mongo-python-driver, schemathesis, pip, egglog-python, cloud-init, vision, sockeye, zulip, speedrun.com_global_scoreboard_webapp, ignite, hydpy, pandas, typeshed-stats, AutoSplit, pytest-robotframework, werkzeug, pyodide, rich, core, paasta, trio, jinja, sphinx, xarray, artigraph, spack, kornia, scrapy, optuna, spark, attrs, pylox, graphql-core, django-modern-rest, prefect, flake8, pytest, jax, materialize. error kinds: incompatible-comparison, float == int literal comparisons, float vs int literal comparisons. caused by check_incompatible_comparison(), comparison_base(), disjoint_base().

Project Verdict Changes Error Kinds Root Cause
ppb-vector ❌ Regression +5 float vs int literal comparison check_incompatible_comparison()
mitmproxy ❌ Regression +17 incompatible-comparison check_incompatible_comparison()
rotki ❌ Regression +4 dict vs list comparison (test_bitcoin.py) check_incompatible_comparison()
dulwich ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
streamlit ❌ Regression +6 incompatible-comparison check_incompatible_comparison()
dd-trace-py ❌ Regression +5 float == int literal comparisons check_incompatible_comparison()
ibis ❌ Regression +4 incompatible-comparison check_incompatible_comparison()
cryptography ❌ Regression +28 bytearray == bytes false positive check_incompatible_comparison()
altair ❌ Regression +6 incompatible-comparison check_incompatible_comparison()
scipy ❌ Regression +3 incompatible-comparison check_incompatible_comparison()
colour ❌ Regression +3 incompatible-comparison check_incompatible_comparison()
pyinstrument ❌ Regression +3 float == int literal comparison flagged as incompatible comparison_base()
urllib3 ❌ Regression +25 bytearray vs bytes literal comparisons check_incompatible_comparison()
Expression ❌ Regression +3 int-vs-float incompatible-comparison false positives check_incompatible_comparison()
scikit-learn ❌ Regression +19 incompatible-comparison: float vs int/Literal[0] check_incompatible_comparison()
freqtrade ❌ Regression +23 incompatible-comparison check_incompatible_comparison()
setuptools ❌ Regression +27 incompatible-comparison check_incompatible_comparison()
pwndbg ❌ Regression +10 incompatible-comparison check_incompatible_comparison()
scikit-build-core ❌ Regression +3 frozenset vs set incompatible-comparison false positive pyrefly/lib/alt/operators.rs
apprise ❌ Regression +9 incompatible-comparison check_incompatible_comparison()
psycopg ❌ Regression +2 incompatible-comparison comparison_base()
starlette ❌ Regression +1 incompatible-comparison between bytearray and bytes literal check_incompatible_comparison()
mongo-python-driver ❌ Regression +2 incompatible-comparison comparison_base()
schemathesis ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
pip ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
openlibrary ➖ Neutral +4 int vs bytes comparison (marc_binary.py) check_incompatible_comparison()
egglog-python ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
cloud-init ❌ Regression +6 incompatible-comparison false positives check_incompatible_comparison()
aiohttp ➖ Neutral +2 incompatible-comparison check_incompatible_comparison()
vision ❌ Regression +8 float vs int literal comparisons check_incompatible_comparison()
sockeye ❌ Regression +2 float-vs-int incompatible-comparison false positives check_incompatible_comparison()
zulip ❌ Regression +4 Decimal == int false positive check_incompatible_comparison()
speedrun.com_global_scoreboard_webapp ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
ignite ❌ Regression +4 `incompatible-comparison on bool str tagged union`
hydpy ❌ Regression +3 float-int comparison false positives check_incompatible_comparison()
pandas ❌ Regression +26 int-vs-float incompatible-comparison false positives check_incompatible_comparison()
typeshed-stats ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
AutoSplit ❌ Regression +2 float vs int/Literal[int] incompatible-comparison false positives comparison_base()
pytest-robotframework ❌ Regression +3 incompatible-comparison check_incompatible_comparison()
werkzeug ❌ Regression +14 incompatible-comparison check_incompatible_comparison()
pyodide ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
rich ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
core ❌ Regression +36 float == int literal false positives check_incompatible_comparison()
paasta ❌ Regression +2 incompatible-comparison check_incompatible_comparison()
trio ❌ Regression +91 incompatible-comparison check_incompatible_comparison()
jinja ❌ Regression +1 incompatible-comparison false positive on float vs int comparison_base()
sphinx ❌ Regression +2 incompatible-comparison check_incompatible_comparison()
xarray ❌ Regression +4 float vs int/Literal[1] incompatible-comparison check_incompatible_comparison()
artigraph ❌ Regression +2 frozenset vs set comparison false positive check_incompatible_comparison()
spack ❌ Regression +3 int vs bytes comparison (sbang.py) check_incompatible_comparison()
kornia ❌ Regression +8 float == int literal comparisons check_incompatible_comparison()
scrapy ❌ Regression +2 incompatible-comparison check_incompatible_comparison()
optuna ❌ Regression +19 incompatible-comparison: float vs int/Literal[0] check_incompatible_comparison()
spark ❌ Regression +12 incompatible-comparison: int vs float check_incompatible_comparison()
attrs ❌ Regression +5 incompatible-comparison check_incompatible_comparison()
pylox ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
graphql-core ❌ Regression +1 incompatible-comparison disjoint_base()
django-modern-rest ❌ Regression +3 float vs int comparison false positive check_incompatible_comparison()
prefect ❌ Regression +9 int vs Literal['latest'] in secret_manager.py (2 errors) check_incompatible_comparison()
flake8 ❌ Regression +1 incompatible-comparison check_incompatible_comparison()
pytest ❌ Regression +5 float vs int incompatible-comparison check_incompatible_comparison()
jax ❌ Regression +4 float != int literal comparison check_incompatible_comparison()
materialize ❌ Regression +50 incompatible-comparison check_incompatible_comparison()
Detailed analysis

❌ Regression (61)

ppb-vector (+5)

float vs int literal comparison: 4 errors flag float == Literal[int] as incompatible. Per the typing spec's numeric tower, int is a subtype of float, so these comparisons are valid. The new check_incompatible_comparison doesn't account for the int-float relationship.
frozenset vs set comparison: 1 error flags frozenset[str] != set[str] as incompatible. frozenset and set are designed to be comparable in Python — frozenset.__eq__ accepts object, and the two types share hash compatibility. This is a false positive.

Overall: These are all false positives from the new incompatible-comparison check:

  1. float == Literal[0] (3 errors at lines 564, test_angle.py:55, test_length.py:35): Literal[0] has type int, and int is a subtype of float per the typing spec's numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). Comparing float to int is completely valid — 0.0 == 0 returns True. The PR's disjoint_base() function apparently doesn't account for the int <: float relationship.

  2. float == Literal[90] (test_constants.py:13): Same issue — Literal[90] is an int, and float == int is valid.

  3. frozenset[str] != set[str] (line 136): The code frozenset(kwargs) != {'x', 'y'} compares a frozenset with a set literal. This is perfectly valid Python — frozenset.__eq__ accepts any object, and frozenset and set are explicitly designed to be comparable (they share the same hash protocol for this purpose). The PR's disjoint check incorrectly treats frozenset and set as incompatible families.

All 5 errors are pyrefly-only (neither mypy nor pyright flags them), confirming these are false positives from an overly aggressive new check.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The method calls comparison_base() which maps Literal(0) to int and float stays as float. Then disjoint_base() presumably treats int and float as different families, even though int is a subtype of float in Python's numeric tower. Similarly, frozenset and set are treated as disjoint even though they share __eq__ via object and are explicitly designed to be comparable. The has_superclass check apparently doesn't recognize that int is a virtual subclass of float per the typing spec's special numeric tower rules.

mitmproxy (+17)

These 17 new errors are all pyrefly-only, flagging == comparisons in test files. The == operator is always valid in Python (object.eq accepts any argument), and neither mypy nor pyright flags these. Many of these are legitimate test patterns (e.g., checking .get() returns a default value). The new check is too strict for real-world code, especially when type annotations don't perfectly capture overloaded return types like dict-like .get() methods.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces this check. It compares the 'disjoint base' of both operands and flags when they differ. The issue is that it doesn't account for cases where the declared return type doesn't match the actual runtime type (e.g., .get() with a default parameter returning the default value rather than the collection's value type), and it flags comparisons that are intentional in test assertions.

rotki (+4)

dict vs list comparison (test_bitcoin.py): Comparing dict[str, Any] with list[str] via == is valid Python. The static type of result is likely imprecise — wait_for_async_task_with_result probably has a broad return type annotation (e.g., dict[str, Any]) while the actual runtime value is a list. Mypy does not flag this. Pyright's reportUnnecessaryComparison could potentially flag this but is not enabled by default.
tuple vs list comparison (test_settings.py): Comparing tuple[EvmIndexer, ...] with list[EvmIndexer] via == is valid Python, though it always returns False since tuples and lists are never equal. This likely indicates the return type annotation of get_evm_indexers_order_for_chain is imprecise (it probably returns a list at runtime). The test likely passes at runtime. Mypy does not flag this by default.
str vs int literal comparison (test_db.py): Comparing str with Literal[1451606400] via == is valid Python (returns False). The get_netvalue_data method likely returns list[int] for times at runtime, but the type annotation suggests str. The test passes at runtime because the actual values are integers. Mypy does not flag this. Two instances of the same pattern.

Overall: The PR introduces a new incompatible-comparison diagnostic that flags ==/!= between types with different 'disjoint bases'. While the intent is reasonable (catching likely bugs), the implementation flags cases that are valid Python and may represent imprecise type annotations rather than actual bugs:

  1. dict vs list: Comparing these with == is valid Python and returns False. The test code at test_bitcoin.py:41 compares result (typed dict[str, Any]) against a list — the static type may be imprecise (the wait_for_async_task_with_result function likely returns a more general type while the actual runtime value is a list), but the comparison itself is not a type error.

  2. tuple vs list: At test_settings.py:640, indexers has type tuple[EvmIndexer, ...] and is compared to [EvmIndexer.BLOCKSCOUT, EvmIndexer.ETHERSCAN]. While tuple.__eq__(list) always returns False in Python (tuples and lists are never equal), this is valid Python. This could indicate either a bug in the test or an imprecise return type annotation on get_evm_indexers_order_for_chain (the method may actually return a list at runtime).

  3. str vs int literal: At test_db.py:1112 and 1130, times[0] is compared to integer literals. The get_netvalue_data method returns a tuple of (times, values) where times is likely typed as list[str] but actually contains integers at runtime. The comparison str == int is valid Python (returns False).

None of these are flagged by mypy in its default configuration. Pyright has a reportUnnecessaryComparison diagnostic that can flag some disjoint-type comparisons, but it is not enabled at the default severity level. The incompatible-comparison check has no basis in the typing spec. These are false positives that add noise to well-tested project code, likely caused by imprecise type annotations rather than actual bugs.

Attribution: The new check_incompatible_comparison() method added in pyrefly/lib/alt/operators.rs is directly responsible for all 4 new errors. This method fires on every ==/!= comparison where the two sides have different 'disjoint bases'. The issue is that the check is too broad:

  1. dict[str, Any] vs list[str] (test_bitcoin.py:41): result is typed as dict[str, Any] but at runtime wait_for_async_task_with_result returns a list. The comparison is intentional test code.
  2. tuple[EvmIndexer, ...] vs list[EvmIndexer] (test_settings.py:640): CachedSettings().[get_evm_indexers_order_for_chain()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/operators.rs) returns a tuple but the test compares it to a list. In Python, tuple == list returns False but is valid code — and the return type annotation may be imprecise.
  3. str vs Literal[1451606400] (test_db.py:1112, 1130): times is a list of strings (from DB query), and the test compares times[0] to an integer literal. This is valid Python that returns False if types differ — but the test expects equality, suggesting the actual runtime values match (the DB returns ints, not strings, so the static type may be wrong).

All of these are cases where the comparison_base / disjoint_base logic in the new code treats tuple vs list, dict vs list, and str vs int as incompatible, but Python allows == between any objects.

dulwich (+1)

This is a false positive. bytearray and bytes are closely related bytes-like types that fully support equality comparison with each other. The new incompatible-comparison check incorrectly treats them as disjoint families. This is a bug in the new check's family classification logic — bytearray should be grouped with bytes in the bytes-like family.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs incorrectly classifies bytearray and bytes as belonging to disjoint type families. The disjoint_base() function fails to recognize that bytearray and bytes are in the same bytes-like family and are explicitly designed to be compared with each other.

streamlit (+6)

Comparing float with int using == is completely valid Python. The numeric tower supports this natively — float.__eq__ handles int arguments. The code on line 678 (v == int(v)) is a standard idiom for checking if a float is a whole number. The PR's incompatible-comparison check incorrectly treats float and int as disjoint type families. All 6 errors are false positives — neither mypy nor pyright flags any of them.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs introduces this diagnostic. The disjoint_base() function (not shown but referenced) apparently treats float and int as belonging to different disjoint families, when they should be recognized as compatible numeric types. The has_superclass() check should handle this but apparently fails for the float/int relationship.

dd-trace-py (+5)

float == int literal comparisons: Comparing float to int literals (0, 1) is standard Python. While int is not actually a subclass of float in Python's runtime class hierarchy, the typing spec has a special case where int is accepted where float is expected. Additionally, float.__eq__ accepts any object argument, so this comparison is always valid. The new check_incompatible_comparison() incorrectly treats them as disjoint. These are false positives.
dict[str, Any] == str literal comparisons: The **kwargs: dict[str, Any] annotation means each kwarg value has type dict[str, Any] (per PEP 484), so kwargs.get('operation') returns dict[str, Any] | None. The annotation is clearly imprecise (the values are actually strings), but the new check fires on dict vs str comparison. The check was supposed to be limited to 'primitive scalar types' per the PR description, but the implementation accepts any ClassType. Additionally, dict[str, Any] contains Any which should suppress the check, and dict.__eq__ accepts any object. These are false positives.

Overall: All 5 errors are false positives (regressions):

float == Literal[1] and float == Literal[0] (lines 112, 114): self._sample_rate is typed as float, and comparing a float to an int literal is completely valid Python. The typing spec has a special case where int is accepted where float is expected (though int is not actually a subclass of float in Python's runtime class hierarchy). More importantly, float.__eq__ accepts any object argument, so comparing float to int literals is always valid. This is an extremely common Python pattern. Neither mypy nor pyright flags this.

dict[str, Any] == Literal['task'/'flow'/'flow_method'] (lines 67, 73, 75): The method signature is **kwargs: dict[str, Any]. Per PEP 484, **kwargs: X means each individual keyword argument value has type X. So **kwargs: dict[str, Any] means each kwarg value is typed as dict[str, Any], making kwargs itself dict[str, dict[str, Any]]. Therefore kwargs.get('operation') returns dict[str, Any] | None. While the annotation is clearly imprecise (the code treats these values as strings, not dicts), the new check should not fire here because: (a) dict is not a primitive scalar type and the check was intended for primitive scalar comparisons, (b) dict[str, Any] contains Any which should suppress the check, and (c) dict.__eq__ accepts any object argument. Neither mypy nor pyright flags this.

Per-category reasoning:

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is directly responsible for all 5 errors. Specifically:

  1. For float == 1 and float == 0: The disjoint_base() method (not shown but called) apparently treats float and int (from Literal[1]) as disjoint families. This is wrong because int is a subtype of float per the typing spec's numeric tower. The has_superclass() check should catch this but apparently doesn't handle the int <: float special case.

  2. For dict[str, Any] == 'task': The comparison_base() method returns Some for ClassType, which includes dict. The PR description says it's limited to 'primitive scalar types' but the implementation doesn't actually filter to scalars — it accepts any ClassType. The Any in dict[str, Any] should also cause the check to bail out, but the code only checks left.[is_any()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/operators.rs) || right.[is_any()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/operators.rs) at the top level, not recursively within generic parameters.

ibis (+4)

All 4 new errors are false positives from the new incompatible-comparison check. Three errors flag float == Literal[0] and float == Literal[1] comparisons in the sample() method (lines 1751, 1753) and the floating-point formatter (line 111). These comparisons are valid in Python: float.__eq__ accepts any object, and comparing floats to ints is standard practice. While the typing spec treats int as consistent with float for parameter passing, the comparison operators on float also accept int operands without issue. The fourth error flags Sentinel != Literal['missing'] where Sentinel is a metaclass and missing is a class instance of that metaclass. Since object.__ne__ (or any custom __ne__ on the metaclass) accepts arbitrary objects, this comparison is valid. None of these are flagged by mypy or pyright, confirming they are false positives in pyrefly's new incompatible-comparison check.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs introduced all 4 errors. The comparison_base() method converts Literal[0]/Literal[1] to int via general_class_type, and then disjoint_base() treats float and int as disjoint families despite the numeric tower. Additionally, the Sentinel metaclass comparison should have been skipped per the PR's stated intent to skip user types, but the comparison_base() function returns Some for ClassType, which includes user-defined types like Sentinel.

cryptography (+28)

bytearray == bytes false positive: All 28 errors flag assert buf == ct where buf is bytearray and ct is bytes. This is completely valid Python — bytearray and bytes support cross-type equality comparison. The new incompatible-comparison check incorrectly treats them as disjoint types because neither inherits from the other, but they are explicitly designed to be comparable.

Overall: All 28 new errors flag bytearray == bytes comparisons as incompatible. This is a false positive — bytearray and bytes are explicitly comparable in Python. The new check_incompatible_comparison logic in pyrefly/lib/alt/operators.rs fails to recognize that bytearray and bytes are related types (they don't have a direct inheritance relationship but are designed to be cross-comparable). Neither mypy nor pyright flags these comparisons.

Per-category reasoning:

  • bytearray == bytes false positive: All 28 errors flag assert buf == ct where buf is bytearray and ct is bytes. This is completely valid Python — bytearray and bytes support cross-type equality comparison. The new incompatible-comparison check incorrectly treats them as disjoint types because neither inherits from the other, but they are explicitly designed to be comparable.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs uses disjoint_base() and has_superclass() to determine if two types are in different families. Since bytearray does not inherit from bytes (nor vice versa), the check incorrectly treats them as incompatible. The disjoint_base() function (not fully shown in the diff) apparently maps bytearray and bytes to different base types, failing to recognize they are in the same bytes-like family.

altair (+6)

These are false positives. The new incompatible-comparison check is too aggressive: (1) set == frozenset is a standard Python idiom — both types explicitly support cross-comparison. (2) Comparing dict (the static return type of to_dict()) with int or list is valid because == is defined on object and never raises. Neither mypy nor pyright flags any of these. The check needs to either recognize set/frozenset as compatible, or more fundamentally, recognize that == is always valid between any two Python objects.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces the IncompatibleComparison error kind. The comparison_base() method maps types to their base classes, and the disjoint_base() + has_superclass() checks determine incompatibility. The issue is that: (1) set and frozenset are not recognized as compatible comparison partners despite both inheriting comparison support, and (2) dict compared with int/list is flagged even though == is universally valid in Python via object.__eq__.

scipy (+3)

All three new errors flag comparisons between float and int (or float and Literal[0]). Per the typing spec's special cases for float and complex (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is accepted wherever float is expected. These types are NOT incompatible — they are part of the numeric tower. The new check_incompatible_comparison() function fails to account for this special relationship, producing false positives. At runtime, float == int comparisons are perfectly valid and commonly used in numeric code (especially in array API compatibility libraries, which is exactly what these projects are).
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs is responsible. The function calls disjoint_base() and has_superclass() to determine if two types are incompatible. The bug is that it does not account for Python's special numeric tower relationship where int is considered a subtype of float. The comparison_base() function resolves Literal[0] to int (via general_class_type), and then disjoint_base() / has_superclass() apparently does not recognize that int is a subclass of float in the typing sense. This causes all three false positives: float != int and float == Literal[0] are incorrectly flagged.

colour (+3)

Comparing float with int (or Literal[int]) is completely valid in Python. The typing spec explicitly treats int as compatible with float (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). The new incompatible-comparison check incorrectly treats float and int as disjoint type families, producing false positives on standard numeric comparisons like hue_angle_lower == 0 and kernel_interpolator.window == 3.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces the incompatible-comparison diagnostic. The comparison_base() method maps Literal[3] to int's class type, and the disjoint_base() function (not shown but called) apparently treats float and int as separate disjoint families. The has_superclass() check fails to account for the special int-is-subtype-of-float relationship defined in the typing spec. This causes all three false positives.

pyinstrument (+3)

float == int literal comparison flagged as incompatible: All 3 errors flag float == 0 as incompatible. Per the typing spec, int is a subtype of float, so this comparison is valid. The new check_incompatible_comparison method doesn't handle the numeric tower. This is a regression — false positives on idiomatic Python code.

Overall: Per the typing spec's special cases for float and complex, int is accepted wherever float is expected. Comparing float == 0 (where 0 is Literal[0], i.e., int) is perfectly valid. The new check_incompatible_comparison in pyrefly/lib/alt/operators.rs fails to account for this numeric tower relationship. All three errors flag the extremely common pattern of comparing a float to 0, which is idiomatic Python. Neither mypy nor pyright flags this. These are false positives introduced by the new check.

Attribution: The new check_incompatible_comparison method in pyrefly/lib/alt/operators.rs introduces this diagnostic. The method calls comparison_base() which converts Literal[0] to its general class type (int), and then disjoint_base() presumably determines that float and int are in different 'families'. The bug is that the check does not account for the special int-float subtype relationship defined in the typing spec. The has_superclass check should recognize that int is a subclass of float in the type system (or at least that they are compatible for comparison), but it apparently does not.

urllib3 (+25)

bytearray vs bytes literal comparisons: bytearray and bytes are both bytes-like types that support mutual equality comparison. bytearray.__eq__ accepts bytes arguments. Flagging bytearray == b'hello' is a false positive.
float vs int literal comparisons: float and int are both numeric types. float.__eq__ handles int arguments natively. Flagging float_value == 0 is a false positive.

Overall: The analysis is factually correct. The key claims are:

  1. bytearray vs bytes: bytearray.__eq__ does accept bytes arguments and returns a meaningful boolean result. In CPython, bytearray.__eq__(b'hello') works correctly and returns True or False. Neither mypy nor pyright flags bytearray == b'hello' as an incompatible comparison. The cross-check confirms 0/25 errors appear in mypy or pyright.

  2. float vs int: float.__eq__ does handle int operands natively (Python's numeric tower ensures this). 1.0 == 0 is a perfectly valid comparison. Neither mypy nor pyright flags this.

  3. The error messages shown confirm the specific comparisons: bytearray vs Literal[b'hello'] and (from test_retry.py) float vs Literal[0] (since Retry.get_backoff_time() returns float and it's compared with == 0).

  4. The conclusion that the incompatible-comparison check's disjoint family logic is too coarse-grained is supported by the evidence — these are well-known compatible type pairs that other type checkers correctly allow.

All factual claims check out against the source code and Python type system.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces the incompatible-comparison diagnostic. The disjoint_base() and has_superclass() logic incorrectly treats bytearray vs bytes (Literal[b'...']) as disjoint families, and float vs int (Literal[0]) as disjoint families. The comparison_base() method maps literals to their general class type but the disjoint check doesn't account for cross-family compatibility (bytes-like types are mutually comparable, and float/int are mutually comparable).

Expression (+3)

int-vs-float incompatible-comparison false positives: All 3 errors flag int == float or float == Literal[int] comparisons as incompatible. Python's typing spec treats int as a virtual subtype of float, and these comparisons are valid both at the type level and at runtime. Neither mypy nor pyright flags them. The PR's check_incompatible_comparison() function needs to handle the int/float/complex special case.

Overall: The PR introduces a new incompatible-comparison check but fails to account for Python's special int-float relationship. Per the typing spec, int is treated as a subtype of float (and complex). Comparing int == float is perfectly valid Python — 3 == 3.0 is True at runtime. All three errors are false positives:

  1. test_array.py:122ys is typed as int (from array.sum which likely returns int), compared with 3.0 (a float literal). This is valid.
  2. test_array.py:128 — Same pattern with array.sum_by, comparing int result with 6.0.
  3. test_parser.py:136success is typed as float (from pfloat), compared with Literal[123] (an int literal). This is valid — 123.0 == 123 is True.

Neither mypy nor pyright flags these. The disjoint_base() / has_superclass() logic in the PR doesn't handle the int <: float <: complex special case.

Per-category reasoning:

  • int-vs-float incompatible-comparison false positives: All 3 errors flag int == float or float == Literal[int] comparisons as incompatible. Python's typing spec treats int as a virtual subtype of float, and these comparisons are valid both at the type level and at runtime. Neither mypy nor pyright flags them. The PR's check_incompatible_comparison() function needs to handle the int/float/complex special case.

Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs introduces the incompatible-comparison diagnostic. The function calls disjoint_base() and has_superclass() to determine if two types are in disjoint 'families'. The bug is that it treats int and float as disjoint families, when in fact Python's type system treats int as a virtual subclass of float (per the typing spec's special case). The comparison_base() function resolves Literal[123] to int (via general_class_type), and then the disjoint check incorrectly considers int and float incompatible. The PR's own test case only tests int vs str, not int vs float.

scikit-learn (+19)

incompatible-comparison: float vs int/Literal[0]: All 19 errors flag comparisons like float == 0 or float == Literal[0]. Since int is a subtype of float in Python's type system, these are not incompatible types. The new check_incompatible_comparison() function incorrectly treats float and int as disjoint families. This is a false positive — the code is correct and neither mypy nor pyright flags it.

Overall: All 19 errors flag float == 0 or float == Literal[0] comparisons as incompatible. Per the typing spec's numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is treated as a subtype of float. Comparing a float to an int literal is completely valid and extremely common in numerical Python code. Neither mypy nor pyright flags these. The PR's check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs fails to account for the int <: float relationship when checking if types are disjoint, producing 19 false positives across sklearn's codebase.

Per-category reasoning:

  • incompatible-comparison: float vs int/Literal[0]: All 19 errors flag comparisons like float == 0 or float == Literal[0]. Since int is a subtype of float in Python's type system, these are not incompatible types. The new check_incompatible_comparison() function incorrectly treats float and int as disjoint families. This is a false positive — the code is correct and neither mypy nor pyright flags it.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The method calls disjoint_base() and has_superclass() to determine if two types are incompatible. The bug is that it treats float and int (or Literal[0]) as disjoint types, when in fact int is a subtype of float per the typing spec's numeric tower. The disjoint_base() or has_superclass() functions apparently don't account for the special int <: float relationship.

freqtrade (+23)

All 23 errors flag float != Literal[0] or similar float-vs-int comparisons. Per the typing spec's numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is a subtype of float, so these types are in the same numeric family and the comparison is valid. The new check_incompatible_comparison() function has a bug where it doesn't recognize the int-float subtype relationship, producing 23 false positives on a well-tested trading bot project. Neither mypy nor pyright flags any of these.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs and its helper comparison_base() / disjoint_base() / has_superclass() fail to recognize that int (and Literal[0]) is a subtype of float per the typing spec's numeric tower. The has_superclass() check doesn't account for the special int <: float <: complex relationship, causing float vs Literal[0] (int) to be incorrectly flagged as disjoint types.

setuptools (+27)

All 27 errors are false positives. The pattern is consistent: test code assigns a string to an attribute (e.g., cmd.libraries = 'my_lib, other_lib lastlib' or cmd.include_dirs = 'one-dir'), then calls a method like finalize_options() that internally converts the string attribute to a list, and then the test asserts equality with a list. Pyrefly's static type for the attribute remains str (from the explicit assignment) because it does not track type changes to attributes that occur as side effects within called methods. The new incompatible-comparison check in pyrefly/lib/alt/operators.rs fires based on these stale static types, comparing str against list[...]. Similarly, for compiler.runtime_library_dir_option('/foo'), the return type annotation may indicate str while the actual runtime return is []. While the incompatible-comparison check is a reasonable idea in principle, it produces false positives when the static type doesn't reflect attribute mutations performed by called methods. The fact that 0/27 are co-reported by mypy or pyright confirms these are not real bugs.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs introduces this check. The comparison_base() function resolves types to their base class types, and when the left side is statically typed as str (because pyrefly doesn't track the mutation from finalize_options()) and the right side is a list, it flags them as incompatible. The check correctly identifies that str and list are disjoint families, but incorrectly assumes the static type is accurate after method calls that mutate attribute types.

pwndbg (+10)

The analysis is factually correct. The errors are comparing bytearray == bytes (specifically bytearray == Literal[b'\x7fELF']). In Python, bytearray.__eq__ does accept bytes arguments and performs element-wise comparison - this is standard Python behavior. The pwndbg.aglib.memory.read() function returns a bytearray, and comparing it with a bytes literal like b"\x7fELF" is perfectly valid and idiomatic Python. The fact that 0/10 errors appear in mypy and 0/10 appear in pyright confirms these are false positives from pyrefly's new incompatible-comparison diagnostic. The diagnostic's disjointness logic incorrectly treats bytearray and bytes as incompatible types for equality comparison, when in fact they are explicitly designed to be comparable in Python.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduced this check. The disjoint_base() function (not shown but called by the new code) apparently classifies bytearray and bytes (or Literal[b'...']) as belonging to different disjoint families, when in fact they are both bytes-like types that support mutual equality comparison. The comparison_base() method converts Literal[b'\x7fELF'] to its general class type (bytes), but the disjoint check then incorrectly determines that bytearray and bytes cannot be compared.

scikit-build-core (+3)

frozenset vs set incompatible-comparison false positive: All 3 errors flag frozenset(jout) == {"...", ...} as incompatible. frozenset.__eq__ supports comparison with set — this is standard Python behavior. Both frozenset and set are subtypes of collections.abc.Set and their equality comparison is well-defined and cross-type compatible. The incompatible-comparison check incorrectly treats frozenset[Any] and set[str] as incompatible types.

Overall: These are false positives. Comparing frozenset with set using == is a standard, well-defined Python operation. In CPython, both frozenset and set inherit from the same internal base type, and their __eq__ methods are designed to support cross-type comparison — a frozenset can equal a set if they contain the same elements. In the typeshed stubs, frozenset.__eq__ (inherited from object) accepts object as its parameter, meaning any equality comparison is valid from a type-checking perspective. Furthermore, both frozenset and set are subtypes of collections.abc.Set (i.e., AbstractSet), making them closely related collection types rather than disjoint families. The incompatible-comparison check is incorrectly flagging frozenset[Any] == set[str] as incompatible when these types are fully interoperable for equality comparison.

Attribution: The new check_incompatible_comparison function in pyrefly/lib/alt/operators.rs is responsible. The comparison_base method maps types to their base class types, and the disjoint_base function (not shown but referenced) apparently considers frozenset and set as belonging to different 'families' that cannot overlap. This is incorrect — frozenset and set are both set types and their equality comparison is well-defined. The PR's disjoint_base logic is too aggressive: it treats frozenset[Any] and set[str] as incompatible when they are perfectly comparable. The PR description says it targets 'built-in scalar families (numeric, bytes-like, str)' but it's incorrectly catching set/frozenset comparisons too.

apprise (+9)

Mixed results: 1 of the visible errors (vapid list==1) catches a real bug, but the kodi.py error is a false positive due to instance attribute shadowing the class attribute type. The test_api.py error also appears to be a false positive. With 0/9 mypy/pyright agreement and the majority being false positives from type resolution issues (especially the class vs instance attribute confusion), this is a net regression. The check is too aggressive in its current form — it doesn't handle common Python patterns like instance attributes overriding class attributes with different types.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs introduced all 9 errors. The comparison_base() function resolves types to their base class types, but doesn't account for instance attribute shadowing (kodi.py case where class attribute is tuple but instance attribute is int). The disjoint_base() check then flags tuple vs int as incompatible.

psycopg (+2)

The timedelta.total_seconds() method returns float. Comparing float == 3600 (where 3600 is Literal[3600], i.e., int) is perfectly valid because int is a subtype of float per the typing spec (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). The new incompatible-comparison check incorrectly treats float and int as disjoint scalar families. This is a false positive — the PR's disjoint_base() function doesn't account for the numeric tower where intfloatcomplex.
Attribution: The new check_incompatible_comparison method in pyrefly/lib/alt/operators.rs is responsible. The method calls comparison_base() to get the base type and then disjoint_base() to determine the 'family'. The issue is that float and Literal[3600] (which resolves to int) are being treated as disjoint families, when in fact int is a subtype of float per the typing spec's special numeric tower rules. The has_superclass check or disjoint_base logic fails to recognize that float and int are in the same numeric hierarchy. Specifically, Literal[3600] gets mapped via comparison_base()general_class_type() to int, and then disjoint_base() apparently maps float and int to different bases, missing the int <: float relationship.

starlette (+1)

incompatible-comparison between bytearray and bytes literal: False positive. bytearray and bytes are interoperable bytes-like types that support mutual == comparison. The new check_incompatible_comparison() incorrectly treats them as disjoint families. This is a bug in the PR's family classification logic.

Overall: This is a false positive. bytearray and bytes (the underlying type of Literal[b'chunk']) are both bytes-like types that support equality comparison with each other. At runtime, chunks == b"chunk" where chunks is a bytearray works correctly and returns True when the contents match. The PR's disjoint_base() or has_superclass() logic fails to recognize that bytearray and bytes belong to the same bytes-like family, even though the PR description explicitly states the check should only fire for disjoint families. Neither mypy nor pyright flag this.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The comparison_base() method maps Literal[b'chunk'] to its general class type (likely bytes), and bytearray maps to itself. The disjoint_base() method (not shown in the diff but called) and has_superclass() check apparently fail to recognize that bytearray and bytes are in the same bytes-like family. While bytearray doesn't inherit from bytes, they are both bytes-like types that support mutual equality comparison. The PR's own description says it should only trigger 'when the families are disjoint,' but bytearray and bytes are in the same family — so this is a bug in the implementation of the family grouping logic.

mongo-python-driver (+2)

These are false positives. The code timeout == 0 where timeout: float is perfectly valid Python. int is compatible with float per the typing spec's numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). The PR's new incompatible-comparison check incorrectly treats float and int/Literal[0] as disjoint types. Neither mypy nor pyright flags this. This is an extremely common pattern in Python code — comparing a float to an integer literal.
Attribution: The new check_incompatible_comparison method in pyrefly/lib/alt/operators.rs is responsible. The method calls comparison_base() to get the base type, then disjoint_base() to classify types into families. The bug is that float and Literal[0] (which resolves to int) are being treated as disjoint families, when in fact int is a subclass of float in Python's numeric tower. The has_superclass check should recognize that int is a subclass of float, but apparently the disjoint_base function maps them to different base classes, and the superclass check fails to detect the int <: float relationship (possibly because this is a special-case in the typing spec rather than actual class inheritance in CPython).

schemathesis (+1)

This is a false positive. The function _is_non_integer_float(x: float) -> bool compares x != int(x), which compares a float with an int. Per the typing spec's special case for float and complex (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is accepted wherever float is expected. The float.__ne__ method accepts int arguments. This comparison is valid Python code used to check whether a float has a fractional part. The new check_incompatible_comparison diagnostic incorrectly treats float and int as disjoint types when they are not — int is a subtype of float in the numeric tower.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The method checks if the 'disjoint base' types of the left and right operands differ, and if so, reports an error. The bug is in the disjoint_base() or has_superclass() logic — it fails to recognize that int is a subclass of float in Python's numeric tower (or more precisely, that int is compatible with float for comparison purposes). The PR's own test case only tests int vs str, but the implementation incorrectly treats float vs int as incompatible too.

pip (+1)

Comparing float to Literal[0] (an int literal) is valid because int is a virtual subtype of float per the typing spec (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). The new incompatible-comparison check doesn't account for this numeric tower relationship, producing a false positive on a very common Python pattern (if total_time == 0: where total_time is float).
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduced this false positive. The method's disjoint_base() and has_superclass() checks fail to account for the special int <: float relationship defined in the typing spec. When Literal[0] is converted to int via comparison_base(), the subsequent disjoint check incorrectly treats float and int as incompatible types.

egglog-python (+1)

This is a false positive. The expression 10 + X(1) should resolve via X.__radd__ to tuple[X, X], making the comparison tuple[X, X] == tuple[X, X] which is valid. Pyrefly incorrectly infers 10 + X(1) as int (from int.__add__) instead of tuple[X, X] (from X.__radd__). Note that X.__radd__ is defined at line 1565 with return type tuple[X, X] and is not decorated with @method(preserve=True), but at runtime the egglog framework handles the reflected operator dispatch. The underlying type inference for reflected operators is incorrect, causing the false incompatible-comparison error.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduced this error. The comparison_base() function includes Type::Tuple(_) as a valid comparison base, which causes tuples to participate in disjointness checks against scalar types like int. Combined with what appears to be a type inference issue where 10 + X(1) is inferred as int instead of tuple[X, X] (via __radd__), this produces a false positive.

cloud-init (+6)

incompatible-comparison false positives: All 6 errors flag == comparisons that are valid at runtime. In test_apt.py, the mock's side_effect makes m_subp return a string instead of SubpResult, so ret == "return_thing" is valid at runtime but the type checker sees SubpResult vs Literal['return_thing']. In test_azure.py, cfg dict values from read_azure_ovf have union types (e.g., Optional[str] or str | bool | None) where string comparisons are valid but the checker flags them as incompatible. Similar patterns with mocks or dict lookups produce false positives in the other test files. 0/6 are co-reported by mypy or pyright.

Overall: These are false positives from the new incompatible-comparison check. The errors flag valid comparisons in test code where (1) mocks override return types so the actual runtime value is a string (test_apt.py: _wait_for_apt_command returns SubpResult per its type signature, but the mock's side_effect makes it return "return_thing"), and (2) dict values from read_azure_ovf have value types that include None/bool/str unions, making the checker consider string literal comparisons incompatible (test_azure.py). Similar mock/dict patterns apply to the other test files. Neither mypy nor pyright flag any of these. The check appears to be too aggressive in determining type disjointness, particularly for mock-overridden return types and dict value types that are union types including str.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces the incompatible-comparison diagnostic. The comparison_base() method resolves types to their base class for comparison, and the check fires when the bases are in different 'families'. The issue is that the check is too aggressive — it flags comparisons involving dict value lookups (which may return str | None or Any) and mock return values where the static type differs from the runtime type. The disjoint_base() function (not shown in diff but called) appears to incorrectly classify some types as disjoint when they aren't.

vision (+8)

float vs int literal comparisons: 7 of the 8 errors flag float == Literal[int_value] comparisons (e.g., alpha != -1, angle == 0). In Python's numeric tower, int is a subtype of float, so these comparisons are completely valid. The disjoint_base() function incorrectly treats float and int as disjoint families.
str vs str literal comparison: 1 error flags use == "1" where use: str (narrowed from str | None). Both sides are str-based, so this should never be flagged as incompatible. This appears to be a bug in the comparison_base/disjoint_base logic.

Overall: These are all false positives introduced by the new incompatible-comparison check:

  1. float vs Literal[-1]/Literal[0] (focal_loss.py line 36, _geometry.py line 1370): alpha: float is compared to -1 and 0. In Python, int is in the numeric tower under floatfloat.__eq__(int_value) is completely valid and expected. The PR's disjoint_base() function apparently treats float and int as separate disjoint families, but per https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex, int is compatible with float.

  2. str vs Literal["1"] (_home.py line 26): use is str (after None narrowing from os.getenv()), and "1" is Literal["1"]. Both are str-based — this comparison should never be flagged. This suggests a bug in the comparison_base or disjoint_base logic.

  3. Remaining errors in _geometry.py: Similar float vs int literal comparisons (angle == 0, angle == 180, angle == 90, angle == 270). All are standard numeric comparisons.

Neither mypy nor pyright flags any of these. The new check is too aggressive in its disjointness analysis, particularly failing to account for the int/float numeric tower relationship.

Per-category reasoning:

  • float vs int literal comparisons: 7 of the 8 errors flag float == Literal[int_value] comparisons (e.g., alpha != -1, angle == 0). In Python's numeric tower, int is a subtype of float, so these comparisons are completely valid. The disjoint_base() function incorrectly treats float and int as disjoint families.
  • str vs str literal comparison: 1 error flags use == "1" where use: str (narrowed from str | None). Both sides are str-based, so this should never be flagged as incompatible. This appears to be a bug in the comparison_base/disjoint_base logic.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible for all 8 errors. The method checks if the 'disjoint base' types of the left and right operands differ and flags them. The bug is in how it handles the numeric tower: float vs Literal[-1] (which resolves to int) are treated as disjoint families, but int is a subtype of float in Python's type system. The has_superclass check apparently doesn't recognize that int is a subclass of float for the numeric tower special case. Similarly, the str vs Literal["1"] case (line 26 of _home.py) appears to be a bug where os.getenv() returns str and "1" is Literal["1"] — both are str-based, so this shouldn't be flagged at all (this might be a different issue where os.getenv returns str | None and after the is not None check it's str, but the literal resolves correctly).

sockeye (+2)

float-vs-int incompatible-comparison false positives: Both errors flag float == int or float == Literal[0] as incompatible comparisons. The typing spec explicitly makes int compatible with float via the numeric tower. Neither mypy nor pyright flag these. These are false positives from the new check not handling the int/float relationship.

Overall: Both errors flag float == int comparisons as incompatible. Per the typing spec, int is accepted wherever float is expected, making these types compatible. Comparing float to int or Literal[0] is standard Python and not a type error. The new check's disjoint_base() logic doesn't handle the numeric tower special case.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces this check. The comparison_base() method resolves Literal[0] to int via general_class_type, and then disjoint_base() / has_superclass() apparently treats int and float as disjoint families. The implementation fails to account for the typing spec's numeric tower where int is a virtual subclass of float.

zulip (+4)

Decimal == int false positive: Decimal supports equality with int natively. Decimal(0) == 0 is idiomatic Python. Errors at activity.py:181 and stripe.py:137.
float == int false positive: float and int are in the same numeric tower. 0.0 == 0 is fundamental Python. Error at stripe.py:115.
int == float false positive: int and float support cross-type equality. Error at test_timezone.py:50.

Overall: The PR adds an incompatible-comparison diagnostic that is too aggressive for numeric types. Python's numeric tower (int, float, complex, Decimal) explicitly supports cross-type equality comparisons. Decimal.__eq__ handles int and float operands. float.__eq__ handles int operands. These are not incompatible types for == — they are part of the same numeric ecosystem. The disjoint_base() function needs to recognize that int, float, and Decimal are compatible for equality comparisons. All 4 errors are false positives.

Per-category reasoning:

  • Decimal == int false positive: Decimal supports equality with int natively. Decimal(0) == 0 is idiomatic Python. Errors at activity.py:181 and stripe.py:137.
  • float == int false positive: float and int are in the same numeric tower. 0.0 == 0 is fundamental Python. Error at stripe.py:115.
  • int == float false positive: int and float support cross-type equality. Error at test_timezone.py:50.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces this check. The disjoint_base() method (not shown but called) apparently treats int, float, and Decimal as belonging to different 'families', when in fact they are all part of Python's numeric hierarchy and support cross-type equality. The comparison_base() method resolves Literal[0] to its general class type (int), and then disjoint_base() incorrectly considers Decimal vs int, float vs int, and int vs float as disjoint families.

speedrun.com_global_scoreboard_webapp (+1)

This is a false positive. The code on line 235 does run.level_fraction == 1 where level_fraction is typed as float. In Python, int is a subtype of float per the typing spec's special cases for float and complex. Comparing a float to an int literal is completely valid — float.__eq__ accepts int arguments natively. The new check_incompatible_comparison() function fails to account for the numeric tower relationship where int is compatible with float. Neither mypy nor pyright flag this comparison, confirming it's a false positive.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The method calls disjoint_base() to determine the 'family' of each operand. For float and Literal[1] (which resolves to int), the method apparently considers float and int as belonging to different disjoint families, even though int is a subtype of float in Python's type system. The has_superclass() check should catch this (since int is conceptually a subclass of float for typing purposes), but it appears the numeric tower special case (int is compatible with float) is not being handled by the new comparison logic.

ignite (+4)

incompatible-comparison on bool|str tagged union: All 4 errors flag Literal[False] != Literal['skip_epoch_completed'] or Literal[False] != Literal['skip_completed'] comparisons. The variables are declared bool | str and are intentionally used as tagged unions where event handlers can change them from False to string values between the initial assignment and the comparison. Pyrefly over-narrows the type and then incorrectly flags the comparison as incompatible. Neither mypy nor pyright flags these.

Overall: These are false positives. The should_terminate and should_terminate_single_epoch attributes are declared as bool | str (line 144-147). The code intentionally uses them as tagged unions: False = no action, True = action, "skip_completed"/"skip_epoch_completed" = action with special behavior. The comparisons like self.should_terminate_single_epoch != "skip_epoch_completed" are the correct way to check which variant is active. Pyrefly's new check_incompatible_comparison diagnostic is too aggressive here — it narrows the type to Literal[False] based on the assignment on line 988 (self.should_terminate = self.should_terminate_single_epoch = ... = False) but ignores that the variable can be reassigned to a string value by event handlers called via _fire_event() between the assignment and the comparison. The terminate() method (line 648) sets self.should_terminate = "skip_completed" and terminate_epoch() (line 672) sets self.should_terminate_single_epoch = "skip_epoch_completed". These can be called by user event handlers during _fire_event() calls that happen between line 988 and the comparisons at lines 1011/1044. The check should either not trigger when the declared type is a union that includes both families, or should use the declared type rather than the narrowed type.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. It calls comparison_base() which maps Literal[False] to bool (via general_class_type) and Literal['skip_epoch_completed'] to str. Then disjoint_base() determines these are in different 'families' and flags the comparison. The problem is that the check doesn't account for the fact that the variable's declared type is bool | str — pyrefly is narrowing the type at the comparison point (e.g., after self.should_terminate = ... = False on line 988, it infers Literal[False] at line 1011) and then comparing the narrowed type against the string literal. But the declared type bool | str explicitly allows both bool and str values, so comparing against a string sentinel is perfectly valid.

hydpy (+3)

float-int comparison false positives: All 3 errors flag float↔int comparisons (== or !=) as incompatible. Per the typing spec's special case for float/complex, int is accepted wherever float is expected. These are standard Python idioms (e.g., checking if a float is a whole number via number != int(number), or t == 0 where t is a float counter). The PR's disjoint_base logic doesn't account for the int↔float numeric tower.

Overall: Per the typing spec, int is accepted wherever float is expected. Comparing float with int via == or != is valid Python and valid typing. The new check_incompatible_comparison incorrectly treats int and float as disjoint families. All 3 errors are false positives — pyrefly-only, not flagged by mypy or pyright.

Per-category reasoning:

  • float-int comparison false positives: All 3 errors flag float↔int comparisons (== or !=) as incompatible. Per the typing spec's special case for float/complex, int is accepted wherever float is expected. These are standard Python idioms (e.g., checking if a float is a whole number via number != int(number), or t == 0 where t is a float counter). The PR's disjoint_base logic doesn't account for the int↔float numeric tower.

Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs treats float and int as disjoint type families, but they are not — int is a virtual subclass of float per the typing spec's special case for float/complex. The disjoint_base() and has_superclass() checks fail to recognize the int↔float numeric tower relationship, producing false positives for all three errors.

pandas (+26)

int-vs-float incompatible-comparison false positives: All 26 errors flag int == float or float == int comparisons as incompatible. Python's numeric tower makes these valid comparisons. The disjoint_base() implementation in the PR fails to recognize int and float as related types. Examples: abs(start_s - start_o) == step_s / 2 (int vs float from true division), fac == int(fac) (float vs int). All are pyrefly-only, confirming false positives.

Overall: The analysis is factually correct. The key claims are:

  1. int and float comparisons are valid in Python - This is correct. Python's int.__eq__ and float.__eq__ both handle cross-type comparisons. 4 == 2.0 returns True, and these comparisons are completely standard.

  2. int is treated as compatible with float in the numeric tower - This is correct. PEP 484 specifies that int is implicitly promotable to float for type checking purposes, and both mypy and pyright treat int as compatible with float.

  3. Neither mypy nor pyright flags these - Confirmed by the cross-check data showing 0/26 errors in mypy and 0/26 in pyright.

  4. The specific code examples are correctly analyzed:

    • Line 997: abs(start_s - start_o) == step_s / 2 - start_s and start_o are int (from self.start, other.start), step_s is int, and step_s / 2 produces float (true division). So this is int == float, which is valid.
    • Line 998: abs(end_s - end_o) == step_s / 2 - Same pattern, int == float.
    • Line 1771: fac == int(fac) - fac is float (from true division len(new_columns) / len(self.items)), and int(fac) is int. So this is float == int, which is valid.
  5. All 26 errors are false positives - The analysis correctly identifies that the incompatible-comparison check incorrectly treats int and float as incompatible types for == comparison, when they are in fact compatible.

The analysis is accurate in all respects.

Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs incorrectly treats int and float as disjoint types. The disjoint_base() and has_superclass() checks fail to recognize that int is a subtype of float in Python's numeric tower, causing false positives when comparing int values with float values (e.g., abs(start_s - start_o) == step_s / 2 where the left side is int and right side is float).

typeshed-stats (+1)

Comparing set[str] == frozenset[str] is completely valid Python. Both types implement __eq__ to handle this cross-type comparison. The new incompatible-comparison check is too broad — it flags container types (set vs frozenset) despite the PR description claiming it only targets scalar families. This is a false positive that neither mypy nor pyright would report.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs uses disjoint_base() and has_superclass() to determine if two types are incompatible for equality comparison. The method fails to account for sibling classes like set and frozenset that legitimately support cross-type equality. The PR description claims the check is limited to 'built-in scalar families' but the implementation doesn't enforce this limitation — it fires on any two ClassTypes where neither is a superclass of the other.

AutoSplit (+2)

float vs int/Literal[int] incompatible-comparison false positives: Both errors flag float == Literal[0] and float != Literal[255] as incompatible comparisons. Since int is a subtype of float in Python's type system, these comparisons are perfectly valid. The new check fails to handle the numeric tower relationship. Line 208 annotates mean as float, and comparing it to 0 or MAXBYTE (which is 255, an int) is standard Python. Both errors are pyrefly-only false positives.

Overall: These are false positives. The PR introduces a new incompatible-comparison check, but it fails to account for Python's numeric tower where int is treated as a subtype of float (per https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). Comparing float == 0 (where 0 is Literal[0], i.e., an int) is completely valid — int values are routinely compared with float values. The disjoint_base() function apparently treats int and float as disjoint families, which is incorrect. The PR's own test case only tests int vs str, not float vs int. Neither mypy nor pyright flags these comparisons.

Per-category reasoning:

  • float vs int/Literal[int] incompatible-comparison false positives: Both errors flag float == Literal[0] and float != Literal[255] as incompatible comparisons. Since int is a subtype of float in Python's type system, these comparisons are perfectly valid. The new check fails to handle the numeric tower relationship. Line 208 annotates mean as float, and comparing it to 0 or MAXBYTE (which is 255, an int) is standard Python. Both errors are pyrefly-only false positives.

Attribution: The new check_incompatible_comparison function in pyrefly/lib/alt/operators.rs introduces this diagnostic. The function calls comparison_base() which maps Literal[0] to its general class type (which would be int), and maps float to float. Then disjoint_base() presumably determines that float and int are in different 'families', failing to account for the fact that int is a subtype of float in Python's numeric tower. The has_superclass check should recognize that int is a subclass of float (or at least that they're compatible for comparison), but it apparently doesn't handle the special int-is-float relationship from the typing spec.

pytest-robotframework (+3)

These test files in the pytest-robotframework project are intentionally comparing 1 == 'wrong' — the tests are specifically designed to test assertion failure behavior. The comparisons are meant to fail (return False), and the test verifies that the assertion error is raised with the correct description/message. This is valid Python code that runs without error.

The key question is whether these new errors are correct or not. The comparison 1 == 'wrong' is indeed comparing incompatible types — an int literal against a str literal. While this is valid Python, it will always evaluate to False, which in production code would almost certainly be a bug. In these test files, it's intentional, but the type checker has no way to know that.

The PR introduces a new diagnostic category incompatible-comparison that flags comparisons between types that have no overlap. This is similar to mypy's comparison-overlap error (which mypy does enable by default in recent versions) and pyright's reportUnnecessaryComparison. So this is not a pyrefly-only concept — other major type checkers do flag similar patterns.

The errors are technically correct: int and str do not have overlapping values, so == between them will always return False. The __eq__ method on object accepts object, so the code is valid at runtime, but the comparison is semantically meaningless.

For a well-tested project like pytest-robotframework, these are false positives in context — the code is intentionally written this way for testing assertion failure behavior. However, the type checker is working as designed. These are the kind of diagnostics that test fixture code commonly triggers.

Note that line 15 of test_assertion_passes_custom_messages.py (assert right == "wrong", "does appear3") performs the same incompatible comparison but does not appear in the error list, which may warrant investigation for consistency.

Since other type checkers (mypy, pyright) have similar checks, this is a reasonable diagnostic to include. The errors are correct from a type-checking perspective, even though they flag intentional test code.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is directly responsible. It fires for ==/!= between types whose disjoint_base values differ (e.g., int vs str). The comparison_base() method maps Literal values to their general class types, so Literal[1] maps to int and Literal['wrong'] maps to str, which are disjoint. This new check is called from the compare operator handler at line 535.

werkzeug (+14)

The new incompatible-comparison check flags several categories of comparisons. Most are legitimate false positives where pyrefly's static type inference doesn't account for runtime type conversion (e.g., TypeConversionDict.get() with type=int statically returns str but actually returns int at runtime, and Accept.__getitem__ with a string key returns a numeric quality value via custom __getitem__). The tuple-vs-list comparison at line 758 (assert data != store where data is a tuple and store is an ImmutableList subclass of list) is technically a valid warning since tuples and lists are never equal in Python, though the test intentionally asserts this inequality. 12/14 errors are pyrefly-only (2/14 also flagged by mypy, 0/14 by pyright), indicating the check is overly aggressive for practical use on real-world code.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs and the comparison_base() helper are responsible. The comparison_base() function maps Literal values to their general class type, but the overall check fails to account for runtime type conversions (via type=int parameters), custom __getitem__ return types, and valid cross-container comparisons (tuple vs list).

pyodide (+1)

This is a false positive. JsBigInt is a user-defined class from pyodide.ffi that supports == comparison with float values. The test code clearly demonstrates this: lines 2247-2249 show JsBigInt being compared with int literals (assert m1 == -1, assert abs(m1) == 1), and line 2279 explicitly compares JsBigInt with float (assert p1 == 1.0). Lines 2269-2271 also show successful < comparisons between JsBigInt and float values (assert 0.8 < p1). The JsBigInt class implements __eq__ (and other comparison methods) to handle cross-type comparisons with both int and float. The type checker's incompatible-comparison diagnostic incorrectly flags this comparison because it cannot determine from static type information alone that JsBigInt.__eq__ accepts float arguments. This is a limitation of the incompatible comparison check when applied to third-party classes whose __eq__ implementations accept broader types than the checker expects.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The comparison_base() function returns Some(ty.clone()) for any Type::ClassType, which includes user-defined classes like JsBigInt. The disjoint_base() function (not shown in diff but called) then apparently classifies JsBigInt into a numeric family that's considered disjoint from float. The PR intended to skip user types but the implementation doesn't actually filter them out — any ClassType passes through comparison_base().

rich (+1)

This is a false positive. The comparison total_time == 0 where total_time: float is completely valid Python. Per the typing spec (https://typing.readthedocs.io/en/latest/spec/concepts.html#special-cases-for-float-and-complex), int is accepted wherever float is expected, so float and int/Literal[0] are not disjoint types. The new incompatible-comparison check incorrectly treats them as disjoint.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs treats float and Literal[0] (resolved to int) as belonging to disjoint type families. The disjoint_base / has_superclass logic does not account for the typing spec's special numeric tower where int is a subtype of float. This causes the false positive on float == 0.

core (+36)

float == int literal false positives: All 36 errors flag comparisons like float == 0, float == Literal[0], or round(float, int) == 1 as incompatible. These are valid Python operations because int is a subtype of float per the numeric tower. In the round() cases, round(float, ndigits) returns float per typeshed overloads, so the comparison is float == Literal[1], which is the same float-vs-int issue. Neither mypy nor pyright flags any of these. The new check_incompatible_comparison() in pyrefly/lib/alt/operators.rs incorrectly treats float and int as disjoint type families.

Overall: These are false positives. The most common pattern is float == 0 (comparing a float parameter to an integer literal), which is completely valid Python. In Python's numeric tower, int is a subtype of float (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), so float and int/Literal[int] are NOT disjoint types. The round() cases (round(x, 1) == 1) are also valid since round(float, int) returns float per typeshed overloads, and comparing float == Literal[1] is the same float-vs-int comparison issue. The fact that 0/36 errors are flagged by mypy or pyright confirms these are all false positives introduced by the new check_incompatible_comparison feature that doesn't account for the numeric tower. The PR's test case only tests int vs str (truly disjoint), but the real-world code reveals the float vs int gap.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is the direct cause. The method calls comparison_base() to extract the base type, then disjoint_base() to determine the 'family'. The bug is that float and int (or Literal[0]) are being treated as disjoint families, when in fact int is a subtype of float in Python's type system. The has_superclass check should catch this but apparently doesn't handle the special int <: float relationship from the numeric tower. Looking at the two main error patterns:

  1. float == Literal[0] (e.g., position_percent == 0 where position_percent: float) — Literal[0] maps to int via general_class_type, and float vs int are incorrectly treated as disjoint.
  2. round(float + float, 1) == Literal[1]round() returns int, and int == Literal[1] should be fine (same family), but pyrefly may be inferring round() as returning float or there's another issue.

The core problem is the disjoint_base() function (not shown in the diff but called by the new code) doesn't properly handle the numeric tower where int <: float <: complex.

paasta (+2)

These are false positives. The Progress class declares percent: float (line 565), and line 574 compares self.percent != 0 and self.percent != 100. The literals 0 and 100 have type Literal[0] and Literal[100] (subtypes of int). Per the typing spec's special case (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is accepted wherever float is expected, meaning float and int are NOT disjoint. The new check_incompatible_comparison() in pyrefly/lib/alt/operators.rs fails to account for this int <: float special relationship, causing it to incorrectly flag float != Literal[0] and float != Literal[100] as incompatible comparisons. Neither mypy nor pyright flags these.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The method calls disjoint_base() and has_superclass() to determine if two types are incompatible. The bug is that it treats float and int (including Literal[0] and Literal[100]) as disjoint types, but per the typing spec's special case for float and complex, int is accepted wherever float is expected. The disjoint_base() or has_superclass() functions apparently don't account for the int <: float special-case relationship defined in the typing spec. The PR's test case only tests int vs str, not float vs int.

trio (+91)

All 91 errors are false positives. The most common pattern is float == Literal[N] or float != Literal[N], which maps to float vs int comparison. Per the typing spec's special cases for float and complex, int is accepted wherever float is expected. Python's float.__eq__ natively handles int operands. The PR's disjoint-type logic doesn't account for this numeric tower relationship, causing it to incorrectly flag float/int comparisons as incompatible.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs fails to account for the numeric tower. The comparison_base() maps Literal[0] to int, and then disjoint_base() treats float and int as disjoint families. The has_superclass() check doesn't recognize that int is compatible with float per the typing spec's special case for numeric types.

jinja (+1)

incompatible-comparison false positive on float vs int: The new check incorrectly treats float and int/Literal[1] as incompatible types. In Python, float and int are both numeric types and float.__eq__ natively handles int comparisons. The disjoint_base() function fails to recognize that float and int belong to the same numeric family, likely because int doesn't literally subclass float in Python's MRO, even though they are comparison-compatible.

Overall: This is a false positive. The code on line 719 compares a float variable (bytes = float(value) on line 706) with the integer literal 1. In Python, float == int is a perfectly valid and common comparison — float.__eq__ handles int arguments natively. The PR's new incompatible-comparison check is intended to catch comparisons between truly disjoint type families (e.g., int vs str), but it incorrectly flags float vs Literal[1] (which resolves to int). Since float and int are both numeric types, this comparison should not be flagged. Neither mypy nor pyright report this error.

Attribution: The new check_incompatible_comparison method in pyrefly/lib/alt/operators.rs is responsible. The method calls comparison_base() to resolve types and disjoint_base() to determine the 'family'. The bug is likely in disjoint_base() (not shown in the diff) — it appears to treat float and int (or Literal[1] which maps to int via general_class_type) as belonging to different disjoint families, when in fact float and int are both numeric types. The comparison_base function correctly converts Literal[1] to int via general_class_type, but then disjoint_base apparently doesn't recognize that float and int are in the same numeric family. The has_superclass check also fails because while int is conceptually compatible with float for comparison purposes, int doesn't literally inherit from float in Python's class hierarchy (though float.__eq__ does accept int arguments).

sphinx (+2)

The get_ratio function (line 116) returns float. The code ratio == 0 on lines 78 and 91 compares a float to the integer literal 0. This is completely valid Python — int is a subtype of float per the typing spec numeric tower, and float.__eq__(int) works at runtime. The new check_incompatible_comparison in pyrefly/lib/alt/operators.rs fails to account for the int-float compatibility, treating them as disjoint scalar families when they are not. Neither mypy nor pyright flags this. These are false positives introduced by the PR's new check being too aggressive with numeric types.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces this diagnostic. The method calls disjoint_base() and has_superclass() to determine if two types are in disjoint families. The bug is that it treats float and Literal[0] (an int literal) as incompatible, but per the typing spec's numeric tower, int is compatible with float. The disjoint_base() function (not shown but implied) apparently classifies float and int as separate base types without accounting for the int <: float subtyping relationship defined in the spec.

xarray (+4)

float vs int/Literal[1] incompatible-comparison: False positive. int is a virtual subclass of float per the typing spec. Comparing float to int is one of the most common operations in Python. The disjoint_base logic incorrectly treats them as separate families.
list vs tuple incompatible-comparison: False positive. Python's eq protocol allows comparing any objects. list == tuple is a valid comparison that returns False without error. This is a standard pattern in test assertions.

Overall: All 4 new errors are false positives from the new incompatible-comparison diagnostic. (1) float vs int: The typing spec treats int as compatible with float (https://typing.readthedocs.io/en/latest/spec/concepts.html#special-cases-for-float-and-complex), so these should never be flagged as incompatible. (2) list vs tuple: Python's equality protocol allows comparing any objects; list.eq(tuple) is valid and commonly used in assertions. Neither mypy nor pyright flags any of these patterns.

Per-category reasoning:

  • float vs int/Literal[1] incompatible-comparison: False positive. int is a virtual subclass of float per the typing spec. Comparing float to int is one of the most common operations in Python. The disjoint_base logic incorrectly treats them as separate families.
  • list vs tuple incompatible-comparison: False positive. Python's eq protocol allows comparing any objects. list == tuple is a valid comparison that returns False without error. This is a standard pattern in test assertions.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is the direct cause. The disjoint_base() function (not shown but referenced) apparently treats float/int as disjoint families and list/tuple as disjoint families, both of which are incorrect. The has_superclass() check doesn't catch that int is a virtual subclass of float per the typing spec, and doesn't account for the fact that __eq__ comparisons between any objects are valid in Python.

artigraph (+2)

frozenset vs set comparison false positive: Both errors flag frozenset == set or set == frozenset comparisons. These are valid Python operations — frozenset.__eq__ and set.__eq__ are designed to work with each other. The PR's check_incompatible_comparison() in pyrefly/lib/alt/operators.rs incorrectly treats frozenset and set as disjoint types because neither is a superclass of the other in the MRO, but they share compatible __eq__ implementations. The comparison_base() function returns Type::ClassType for both, and disjoint_base() / has_superclass() don't recognize the set/frozenset relationship. This is a false positive — the check should either be restricted to the scalar families mentioned in the PR description, or should recognize set/frozenset as compatible for equality.

Overall: The analysis is factually correct. Both errors flag frozenset == set comparisons which are valid Python operations. frozenset.__eq__ and set.__eq__ are designed to work with each other — frozenset({1,2}) == {1,2} returns True. Neither mypy nor pyright flags these comparisons. The first error (line 29) compares a frozenset variable components against a set literal {"value", "hex"}. The second error (line 94) compares set(expected_args) against spec.items which is typed as frozenset[Any]. Both are false positives from an overly broad incompatible-comparison check that doesn't recognize set and frozenset as compatible for equality comparison despite them not being in a direct superclass relationship.

Attribution: The check_incompatible_comparison() function added in pyrefly/lib/alt/operators.rs is responsible. The function calls comparison_base() to extract the base type, then disjoint_base() to get a canonical base, and checks if the two bases are related via has_superclass(). The issue is that frozenset and set are treated as unrelated types (neither is a superclass of the other in the type hierarchy), so the check incorrectly flags them as incompatible. However, frozenset.__eq__ and set.__eq__ both accept object and are specifically designed to compare with each other. The PR description says the check should be limited to 'built-in scalar families (numeric, bytes-like, str)' but the implementation is broader than intended — it catches frozenset vs set comparisons which are not disjoint scalar families.

spack (+3)

int vs bytes comparison (sbang.py): True positive. old_shebang_line[-1] returns int when indexing a bytes object, and comparing int != b'\n' (a bytes literal) will always be True. This is a real bug — the code should use old_shebang_line[-1:] != b'\n' or old_shebang_line[-1] != ord(b'\n'). Pyrefly correctly catches this.
float vs int comparison (fetch_strategy.py): False positive. progress.last_printed is a float (timestamp initialized to 0.0), and comparing float == 0 (int literal) is perfectly valid in Python. 0.0 == 0 evaluates to True. Python's numeric tower means int and float are compatible for equality comparisons. The PR's disjoint_base() incorrectly treats float and int as disjoint families.
list[str] vs str comparison (libc.py): True positive. Line 167 has args == '--dynamic-linker' where args is list[str]. This is clearly a typo — it should be arg == '--dynamic-linker' (the local variable defined on line 166). A list will never equal a string. Pyrefly correctly catches this real bug.

Overall: This is a mixed bag. Two of the three errors catch genuine bugs (sbang.py comparing int to bytes, libc.py comparing list to str — a clear typo). However, the float vs int comparison in fetch_strategy.py is a false positive — Python's numeric tower means float == int comparisons are perfectly valid and common. The comparison_base() function in the PR needs to account for the numeric tower relationship between int and float. Since 2 out of 3 errors are true positives catching real bugs, and 1 is a false positive, the net effect is positive — pyrefly is catching real issues that neither mypy nor pyright catch. The false positive on float==int is a minor issue that should be fixed but doesn't outweigh the value of the two true positives.

Per-category reasoning:

  • int vs bytes comparison (sbang.py): True positive. old_shebang_line[-1] returns int when indexing a bytes object, and comparing int != b'\n' (a bytes literal) will always be True. This is a real bug — the code should use old_shebang_line[-1:] != b'\n' or old_shebang_line[-1] != ord(b'\n'). Pyrefly correctly catches this.
  • float vs int comparison (fetch_strategy.py): False positive. progress.last_printed is a float (timestamp initialized to 0.0), and comparing float == 0 (int literal) is perfectly valid in Python. 0.0 == 0 evaluates to True. Python's numeric tower means int and float are compatible for equality comparisons. The PR's disjoint_base() incorrectly treats float and int as disjoint families.
  • list[str] vs str comparison (libc.py): True positive. Line 167 has args == '--dynamic-linker' where args is list[str]. This is clearly a typo — it should be arg == '--dynamic-linker' (the local variable defined on line 166). A list will never equal a string. Pyrefly correctly catches this real bug.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible for all three errors. It checks if the 'disjoint base' types of the left and right operands differ and have no superclass relationship. For error 1 (int vs bytes literal), the check correctly identifies a real bug. For error 3 (list[str] vs str literal), the check correctly identifies a real bug (typo: args instead of arg). For error 2 (float vs int literal 0), the check is a false positive — float and int are closely related in Python's numeric tower, and float.__eq__(int) works correctly. The comparison_base() and disjoint_base() functions apparently treat float and int as disjoint scalar families, which is incorrect since int is a virtual subclass of float in Python's numeric hierarchy.

kornia (+8)

float == int literal comparisons: All 8 errors flag comparisons like p_batch == 1 or p == 0 where the left operand is float and the right is Literal[1] or Literal[0]. Since int is a subtype of float per the typing spec's numeric tower, these comparisons are perfectly valid. The new check_incompatible_comparison diagnostic fails to account for the int-float compatibility. 0/8 co-reported by mypy or pyright. These are false positives.

Overall: All 8 errors flag float == Literal[1] (or float == Literal[0]) as incompatible comparisons. The parameter p_batch is typed as float, and the code compares it to integer literals 0 and 1. In Python's type system, int is a subtype of float per the numeric tower special case. Comparing a float to an int is completely valid — this is an extremely common Python pattern. Neither mypy nor pyright flags this. The new check_incompatible_comparison diagnostic incorrectly treats float and int as disjoint types, failing to account for the numeric tower relationship. These are all false positives.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The comparison_base() function resolves Literal[1] to its general class type (likely int), and then disjoint_base() apparently treats float and int as disjoint families. However, per the typing spec's numeric tower, int is compatible with float. The has_superclass() check should catch that int is a subclass of float (or that they share a numeric relationship), but it appears to fail — likely because int doesn't literally inherit from float in typeshed stubs, even though the typing spec treats them as compatible. The PR's disjoint_base() function (not shown in the diff but referenced) apparently classifies int and float into different 'families' without accounting for the numeric tower special case.

scrapy (+2)

These are false positives caused by the new incompatible-comparison check interacting with incorrect type inference for the Headers class. The Headers class (from scrapy) stores and returns bytes values, so h["Content-Type"] returns bytes at runtime, not str. The test correctly compares the result with b"text/html" (a bytes literal). Pyrefly appears to be inferring the wrong return type for Headers.__getitem__ — getting str or list[str] instead of bytes. The new check then flags the comparison as incompatible. Neither mypy nor pyright flags these.

The error messages confirm the type inference problem: for line 32, pyrefly infers h["Content-Type"] as Literal['text/html'] (a str) when it should be bytes, producing Literal['text/html'] vs Literal[b'text/html']. For line 39, pyrefly infers h["X-Forwarded-For"] as list[str] when it should be bytes, producing list[str] vs Literal[b'ip2']. The second case is doubly wrong — not only is the element type wrong (str instead of bytes), but the container type is also wrong (list[str] instead of a scalar bytes value).

The root cause is that pyrefly is not correctly resolving the Headers class's __getitem__ return type. The Headers class internally converts values to bytes, but pyrefly appears to be using the input types (from the str values passed to __setitem__) rather than the actual return type annotation. The PR's own test for incompatible-comparison only covers int vs str — it doesn't account for cases where the type checker's inference of the left operand may be wrong, causing false positives on third-party library types.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. The comparison_base() function resolves Literal['text/html'] to str and Literal[b'text/html'] to bytes, then disjoint_base() determines these are in different scalar families. The problem is that pyrefly is inferring the return type of Headers.__getitem__ incorrectly — it appears to be inferring str (or list[str]) for the left-hand side when the actual runtime return type is bytes. The new incompatible-comparison check then flags the comparison between the incorrectly-inferred str/list[str] type and the bytes literal on the right. This is a combination of (1) incorrect type inference for Headers.__getitem__ and (2) the new check surfacing that incorrect inference as a user-visible error.

optuna (+19)

incompatible-comparison: float vs int/Literal[0]: All 19 errors flag comparisons between float and int/Literal[0] as incompatible. Per PEP 484's numeric tower, int is a virtual subtype of float, so these types are compatible. Neither mypy nor pyright flags these. The check_incompatible_comparison() function's disjoint_base() logic doesn't account for the numeric tower, producing false positives on extremely common Python patterns like x == 0 where x: float.

Overall: All 19 new errors flag float == Literal[0] or float == int comparisons as incompatible. Per PEP 484's numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is accepted wherever float is expected, making these types compatible. Comparing a float to 0 (an int literal) is extremely common Python code and works correctly at runtime. Neither mypy nor pyright flags any of these. The disjoint_base() / has_superclass() logic in the new check_incompatible_comparison() function fails to recognize the int <: float virtual subtyping relationship, producing false positives.

Specific examples from the code:

  • optuna/distributions.py:392: (value - self.low) % self.step == 0 where value is float (the parameter param_value_in_internal_repr: float), self.low is int, and self.step is int. The arithmetic (value - self.low) % self.step produces a float result, and comparing it to Literal[0] is standard arithmetic comparison.
  • optuna/importance/_fanova/_fanova.py:70: tree.variance == 0 where variance is a float property of _FanovaTree — comparing float to zero is ubiquitous.
  • optuna/testing/pytest_storages.py:432: storage.get_trial_param(...) == 2 — comparing a float return value to an int literal.

All of these are perfectly valid Python code. The new check is too aggressive because it doesn't respect the numeric tower.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduced this diagnostic. The method calls disjoint_base() and has_superclass() to determine if two types are incompatible. The bug is that it treats float vs int (including Literal[0]) as incompatible types, when in fact int is a virtual subclass of float per PEP 484's numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). The has_superclass check apparently doesn't account for the implicit int <: float relationship. This causes all 19 errors, which are comparisons like float == 0, float == Literal[0], etc.

spark (+12)

incompatible-comparison: int vs float: All 12 errors flag == comparisons between int and float (e.g., len(table) == n / 2). Per the typing spec, int is accepted wherever float is expected, making them compatible types. Additionally, both int.__eq__ and float.__eq__ accept object, so these comparisons are inherently valid. The new check_incompatible_comparison() in pyrefly/lib/alt/operators.rs doesn't handle the numeric tower special case. These are false positives — 0/12 are flagged by mypy or pyright.

Overall: All 12 errors flag int == float comparisons (e.g., len(left) == m / 2 where m is an int, so m / 2 produces a float). Per the typing spec's special cases for float and complex (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex), int is accepted wherever float is expected — effectively making int a subtype of float for typing purposes. Additionally, both int.__eq__ and float.__eq__ accept object as their parameter type, so comparing int with float using == is completely standard Python — it works at runtime and is not a type error. The new check_incompatible_comparison feature fails to account for the numeric tower relationship between int and float, causing 12 false positives. Neither mypy nor pyright flags any of these.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces the incompatible-comparison diagnostic. The method calls disjoint_base() and has_superclass() to determine if two types are incompatible. The bug is that the implementation does not account for Python's numeric tower special case where int is considered a subtype of float. The disjoint_base() function (not shown in the diff but called) apparently treats int and float as disjoint types, when in fact they are related via the numeric tower. The PR's own test case only tests int vs str, not int vs float.

attrs (+5)

All 5 errors are false positives caused by pyrefly not understanding attrs runtime semantics (converters, validators, and on_setattr hooks). In each case:

  1. test_next_gen.py:390C.x has converter=int, so inst.x = "11" converts the string to int 11 at runtime. The assertion 11 == inst.x is correct. Pyrefly sees the assigned value "11" as Literal['11'] and doesn't account for the converter transforming it to int.

  2. test_setattr.py:91va.y was initialized with "foobarqux". The assignment va.y = 42 on line 89 is inside a with pytest.raises(TypeError) block — the validator rejects the assignment, raising TypeError, so va.y retains its original value "foobarqux". Pyrefly doesn't understand that the assignment failed due to the validator, so it infers va.y as Literal[42] (as if the assignment succeeded), then flags the comparison "foobarqux" == va.y as comparing Literal['foobarqux'] to Literal[42].

  3. test_setattr.py:146-147p.x1 and p.x2 have converter=int plus an on_setattr hook that increments by 1 (lambda _, __, nv: nv + 1). So p.x1 = "41" is first converted to 41 by the converter, then incremented to 42 by the hook. Similarly p.x2 = "22" becomes 23. The assertions 42 == p.x1 and 23 == p.x2 are correct at runtime.

  4. test_setattr.py:463C.x has converter=[int] (a list containing int), so c.x = "2" is converted to 2 at runtime. The assertion 2 == c.x is correct.

The new incompatible-comparison check doesn't account for dynamic type transformations (attrs converters, on_setattr hooks) or for assignments that fail due to validators inside exception-catching blocks. Since neither mypy nor pyright flags these, and the code is correct at runtime, these are all false positives. The check itself may be useful in principle, but it needs to be more conservative when it can't fully resolve the runtime type of an expression.

Attribution: The new check_incompatible_comparison() method added in pyrefly/lib/alt/operators.rs is directly responsible. It calls comparison_base() which resolves Literal types to their general class types (e.g., Literal[11]int, Literal['11']str), then disjoint_base() determines they're in different families, triggering the error. The fundamental issue is that pyrefly doesn't understand that attrs converters change the runtime type of the attribute — it sees the static type annotation (or inferred type from the string assignment) rather than the post-converter type.

pylox (+1)

The PR introduces an incompatible-comparison check that is supposed to only flag comparisons between disjoint built-in scalar families (numeric, bytes-like, str). However, the implementation in comparison_base() accepts any Type::ClassType(_), which includes non-scalar types like dict and deque. The error dict[Unknown, Unknown] == deque[Unknown] is a false positive — comparing a dict to a deque via == is perfectly valid Python (it returns False at runtime). Neither mypy nor pyright flags this. The check is firing outside its intended scope, making this a regression.
Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs is responsible. The comparison_base() method returns Some(ty.clone()) for Type::ClassType(_), which means it applies to ALL class types (like dict and deque), not just built-in scalar families as the PR description claims. The PR description says 'Only triggers for built-in scalar families (numeric, bytes-like, str) and only when the families are disjoint. Skips user types.' However, the implementation in comparison_base() matches on Type::ClassType(_) broadly — it doesn't filter to only scalar types. This means dict vs deque triggers the check even though neither is a scalar type. The disjoint_base() function (not shown in the diff) presumably maps types to their base families, and since dict and deque have different bases, they're flagged as incompatible. This is a bug in the implementation — it doesn't match the stated intent of only checking scalar families.

graphql-core (+1)

This is a false positive. In Python, int is considered a subtype of float per the typing spec's numeric tower. Comparing int != float is not only valid but is a common pattern — this exact code checks whether a float value has a fractional part by converting to int and comparing back. The PR's new check_incompatible_comparison logic doesn't account for the int/float/complex special subtyping relationships. Neither mypy nor pyright flag this comparison.
Attribution: The new check_incompatible_comparison method in pyrefly/lib/alt/operators.rs is responsible. The method calls disjoint_base() to determine the base types and then checks if they have a superclass relationship via has_superclass(). The issue is that the implementation doesn't account for the special int-is-a-subtype-of-float relationship defined in the typing spec's numeric tower. The PR's disjoint_base() function likely resolves int and float to different base classes and doesn't recognize that int is a virtual subclass of float for typing purposes. The has_superclass check fails because int doesn't literally inherit from float in the class hierarchy — the relationship is special-cased in the typing spec.

django-modern-rest (+3)

float vs int comparison false positive: float != 0 is valid; int is a subtype of float per the numeric tower. MediaType.quality is typed as float, and Literal[0] is an int literal. The disjoint_base check incorrectly treats them as incompatible.
frozenset vs set comparison false positive: frozenset.eq accepts any object (its signature is eq(self, object) -> bool), and Python explicitly supports equality between frozenset and set instances. Additionally, Settings is a StrEnum (subclass of str), so frozenset[str] and set[Settings] have compatible element types. These are not disjoint types.

Overall: All three errors are false positives from the new incompatible-comparison check. (1) float != Literal[0]: int is compatible with float per the numeric tower (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). MediaType.quality is typed as float, and 0 is Literal[0] (an int), but int is a subtype of float in the type system, so != between them is valid. (2-3) frozenset[str] == set[Settings]: SettingsDict.__optional_keys__ and _SettingsModel.__optional_keys__ have type frozenset[str], while set(Settings) produces set[Settings]. Since Settings is defined as enum.StrEnum (a subclass of str), the element types are compatible. Furthermore, frozenset.__eq__ and set.__eq__ both accept object as the argument type, and Python explicitly supports equality comparison between frozenset and set (they compare equal when they contain the same elements). The incompatible-comparison check is incorrectly treating these as disjoint types.

Attribution: The new check_incompatible_comparison() function in pyrefly/lib/alt/operators.rs introduced all three false positives. The disjoint_base() function (called but not fully shown) incorrectly treats float/int as disjoint and frozenset/set as disjoint. The comparison_base() function correctly resolves Literal[0] to int, but the subsequent disjointness check fails to recognize that int is a subtype of float. Similarly, frozenset and set are treated as incompatible despite supporting cross-type equality.

prefect (+9)

int vs Literal['latest'] in secret_manager.py (2 errors): These flag version_id: int being compared to 'latest'. This is a genuine type inconsistency — the comparison can never be True given the annotation. However, this is a common defensive coding pattern and neither mypy nor pyright flags it. Borderline improvement/regression.
Test file comparisons (7 errors): These flag comparisons in test assertions where dict values have been mutated by method calls (e.g., container['command'] == command.split() after _populate_or_format_command() transforms the value). Pyrefly's static type for the dict value doesn't reflect the runtime mutation. These are false positives — the tests are correct.

Overall: Mixed bag: 2 errors in secret_manager.py flag a genuine type inconsistency (version_id is typed int but checked against 'latest' — though the sibling functions use Union[str, int], suggesting the annotation may be wrong). However, the test file errors (7 of 9) are likely false positives where pyrefly's static type for dict values doesn't reflect runtime mutations. Since 0/9 are co-reported by mypy/pyright and the majority are false positives in test code, this is a net regression.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduced this diagnostic. The comparison_base() method resolves types to their base class for comparison, and the disjoint_base() check determines if two types are from different 'families'. The check fires for any ==/!= between disjoint scalar families, but doesn't account for dict values that may have been mutated at runtime or values whose static type doesn't reflect their actual runtime type after method calls.

flake8 (+1)

This is a false positive. The test uses mock.Mock(result_count='Fake count') to create a mock application object. The Report class's total_errors property proxies to app.result_count, which at runtime is the string 'Fake count'. The test correctly asserts report.total_errors == 'Fake count'. Pyrefly sees the static type annotation of total_errors as int and flags the comparison with a string literal as incompatible. However, this is a mock-based test where the actual runtime type differs from the static annotation — a completely standard Python testing pattern. Neither mypy nor pyright flag this. The new incompatible-comparison check is too aggressive in test code with mocks, producing a false positive.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible. It resolves report.total_errors to int (based on the type annotation of Report.total_errors) and 'Fake count' to Literal['Fake count'] (a str literal). Since int and str are in disjoint scalar families, it fires the incompatible-comparison error. The method's comparison_base() function maps Literal values to their general class types, and the disjoint check then determines int vs str are incompatible. The issue is that the check doesn't account for mock objects — app is a mock.Mock(), so report.total_errors is actually a Mock at runtime, not an int.

pytest (+5)

float vs int incompatible-comparison: 3 false positives flagging float != int and float == int comparisons. Python's numeric tower makes int compatible with float for all comparisons. Neither mypy nor pyright flags these.
str vs Literal[b''] incompatible-comparison: 2 false positives flagging str == Literal[b'']. While str/bytes equality always returns False, this is valid Python commonly used in tests. Neither mypy nor pyright flags these.

Overall: These are all false positives from the new incompatible-comparison check:

  1. float vs int (3 errors): On lines 741-747, mtime = int(time.time()) - 100 makes mtime an int, and path.mtime() returns float. Comparing float with int via ==/!= is completely standard Python. Per PEP 484's numeric tower, int is compatible with float. Neither mypy nor pyright flags this. The new check's has_superclass logic doesn't account for the numeric tower.

  2. str vs Literal[b''] (2 errors): These are test assertions checking that a string value is not equal to b''. While str == bytes always returns False at runtime, this is valid Python and a reasonable test assertion. Neither mypy nor pyright flags this.

All 5 errors are pyrefly-only, confirming these are false positives from an overly aggressive new lint rule.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible for all 5 errors. The method checks if the 'disjoint base' of the left and right types differ, but it fails to account for the numeric tower relationship between int and float. Python's int is a virtual subclass of float (per PEP 484's numeric tower), so float != int comparisons should not be flagged. Similarly, the str vs Literal[b''] check is too aggressive — while comparing str to bytes always returns False, this is valid Python and commonly used in tests. The has_superclass check in the implementation doesn't recognize the int-float numeric tower relationship.

jax (+4)

float != int literal comparison: Three errors flag float != 0 as incompatible. Per the typing spec, int is a special subtype of float, so these types are NOT disjoint. The new check_incompatible_comparison() fails to account for this. All three are false positives.
bool == string literal comparison: One error flags cov == 'unscaled' where cov is narrowed to Literal[True] (after the elif cov: branch at line 284 filters out falsy values). The parameter actually accepts bool | str at runtime (incomplete annotation). This is an intentional dispatch pattern. False positive.

Overall: All 4 new errors are false positives:

float != 0 errors (3 occurrences): The typing spec explicitly states that int is compatible with float (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex). Comparing a float with an int literal 0 is completely valid. The check_incompatible_comparison function's disjoint_base() apparently treats float and int as separate families, but they are not disjoint — int is a subtype of float in the type system.

Literal[True] == 'unscaled' error: The function polyfit has cov: bool = False but the docstring explicitly says cov can be 'unscaled' (a string). The type annotation is incomplete — this is a well-known NumPy API pattern. Even setting that aside, comparing bool with str using == is valid Python and is used intentionally here as a dispatch mechanism. Neither mypy nor pyright flags this.

All 4 errors are pyrefly-only, confirming these are false positives from the new check_incompatible_comparison() check that doesn't properly handle the int <: float relationship or intentional cross-type equality checks.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible for all 4 errors. The method calls comparison_base() to resolve types and disjoint_base() to check family membership. The bug is twofold: (1) It treats float vs Literal[0] (int) as disjoint families, but int is a special subtype of float per the typing spec. The has_superclass check apparently doesn't recognize the int <: float special relationship. (2) For the Literal[True] vs Literal['unscaled'] case, pyrefly narrows cov: bool to Literal[True] after the truthiness check, then flags the comparison with a string literal as incompatible, not accounting for the fact that the parameter's runtime type is actually bool | str (the annotation is just incomplete).

materialize (+50)

All 50 errors compare float (return type of Metrics methods like get_value, get_last_command_received, etc.) against integer literals (Literal[0], Literal[1], etc.). In Python, float == int is a perfectly valid and extremely common comparison pattern. The PR's disjoint_base logic incorrectly treats float and int as incompatible families, but per PEP 484 and the typing spec (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-forms-for-unions), int is compatible with float. Neither mypy nor pyright flags these comparisons.
Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduces this check. The comparison_base() method resolves Literal[0] to int, and then disjoint_base() treats float and int as disjoint families. The has_superclass() check fails to recognize that int is compatible with float for comparison purposes (per Python's numeric tower where int is accepted wherever float is expected).

➖ Neutral (2)

openlibrary (+4)

int vs bytes comparison (marc_binary.py): These 2 errors correctly identify a real bug: data[offset] returns int when indexing bytes, but the code compares it with b'\x1e' (a bytes object). This will always be False. The same file uses the correct pattern b'\x1e'[0] elsewhere (lines 54, 139). This is a true positive — improvement.
float vs int comparison (i18n/init.py): These 2 errors are false positives. percent_complete is a float (from / division), and comparing float == 100 is valid Python — 100.0 == 100 evaluates to True. The float.__eq__ method accepts object, so any equality comparison with float is technically valid. Furthermore, float and int share a numeric hierarchy where cross-type equality is well-defined and extremely common. Neither mypy nor pyright flags this pattern. The disjoint_base() implementation incorrectly treats float and int as disjoint for comparison purposes. This is a regression.

Overall: This is a mixed bag. The marc_binary.py errors (2 of 4) are genuine bugs — comparing int (from bytes indexing) with bytes literal b'\x1e' will always be False. The same file shows the correct pattern on line 54: line[-2] == b'\x1e'[0]. However, the i18n/init.py errors (2 of 4) are false positives — comparing float with int via == is perfectly valid in Python. At runtime, 100.0 == 100 evaluates to True, and float.__eq__ accepts object as its parameter type. Additionally, float and int share a numeric hierarchy where cross-type equality is well-defined and commonly used. Neither mypy nor pyright flags these comparisons. The disjoint_base() function in the PR apparently treats float and int as belonging to different families, which is incorrect for comparison purposes — they are interoperable numeric types. Since 2 of 4 errors are false positives on a common and correct pattern (float == int), and neither mypy nor pyright flags any of these, the overall impact leans toward regression. The int vs bytes catches are good, but the float vs int false positives are problematic because float-int comparisons are extremely common in Python code.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs is responsible for all 4 errors. It checks if ==/!= operands belong to disjoint 'families' of built-in scalar types. For marc_binary.py, it correctly identifies int != bytes as incompatible. For i18n/init.py, it incorrectly identifies float == int as incompatible — but float and int share a numeric hierarchy (int is a subtype of float in the type system per PEP 484's numeric tower, and at runtime float.__eq__(int) works correctly).

aiohttp (+2)

Error 1 (float == Literal[0]) is a false positive. The field self.total is typed as float | None. After the is not None check, it's narrowed to float. The literal 0 has type Literal[0] (which is an int). Per Python's typing spec, int is a special-case subtype of float, meaning int values are acceptable where float is expected. Since int is compatible with float, comparing float == int is a perfectly valid and common pattern. The float.__eq__ method accepts object at runtime, and the numeric tower relationship means these types are closely related. This is a false positive caused by the checker not accounting for the numeric tower special case.

Error 2 (bytes != str) is a true positive. Looking at line 266, self._lines is typed as list[bytes] (initialized on line 241 as self._lines: list[bytes] = []), so self._lines[-1] is bytes. However, the comparison is against "\r\n" which is a str literal, not b"\r\n". Comparing bytes with str using != will always return True in Python 3 (they are never equal), so this is a genuine bug - the code should use b"\r\n" instead of "\r\n".

Overall, Error 1 is a false positive that will cause widespread noise for a very common pattern (comparing float values with integer literals), while Error 2 is a valuable true positive catching a real bug. The net impact is mixed - the false positive on float/int comparison is problematic, but the bytes/str catch is genuinely useful.

Attribution: The new check_incompatible_comparison() method in pyrefly/lib/alt/operators.rs introduced both errors. The comparison_base() and disjoint_base() functions fail to account for the int-is-subtype-of-float special case from the typing spec, causing the false positive on float == Literal[0]. The bytes vs str detection is correct.

Suggested fixes

Summary: The new check_incompatible_comparison() in operators.rs causes ~500+ false positives across 61 projects because it doesn't handle the numeric tower (int<:float<:complex), bytes-like family (bytearray/bytes), set family (frozenset/set), Decimal, and applies too broadly to non-scalar ClassTypes.

1. In check_incompatible_comparison() in pyrefly/lib/alt/operators.rs, add numeric tower awareness: after computing left_base and right_base, before the disjoint check, add a guard that treats int/float/complex/Decimal as compatible. Specifically, create a helper function is_numeric_compatible(a, b) -> bool that returns true when both types are in {int, float, complex, Decimal} (checking by class name against stdlib). Add this check: if is_numeric_compatible(&left_base, &right_base) { return; }. This eliminates the vast majority of false positives (~400+ errors across ~45 projects).

Files: pyrefly/lib/alt/operators.rs
Confidence: high
Affected projects: materialize, trio, core, pandas, freqtrade, scikit-learn, optuna, spark, streamlit, kornia, vision, dd-trace-py, ibis, scipy, colour, pyinstrument, urllib3, Expression, ppb-vector, mongo-python-driver, schemathesis, pip, sockeye, speedrun.com_global_scoreboard_webapp, hydpy, paasta, rich, jinja, sphinx, xarray, AutoSplit, graphql-core, django-modern-rest, psycopg, jax, pytest, zulip, pyodide, aiohttp, openlibrary, spack
Fixes: incompatible-comparison
The typing spec (https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex) explicitly states int is accepted where float is expected, and float where complex is expected. The has_superclass() check fails because int doesn't literally inherit from float in Python's MRO — this is a special-case in the typing spec. Every single project with float-vs-int errors (materialize: 50, trio: 91, core: 36, pandas: 26, freqtrade: 23, scikit-learn: 19, optuna: 19, spark: 12, etc.) would be fixed. Also fixes Decimal==int in zulip.

2. In check_incompatible_comparison() in pyrefly/lib/alt/operators.rs, add bytes-like family awareness: create a helper is_bytes_like_compatible(a, b) -> bool that returns true when both types are in {bytes, bytearray, memoryview}. Add: if is_bytes_like_compatible(&left_base, &right_base) { return; }. This eliminates all bytearray==bytes false positives.

Files: pyrefly/lib/alt/operators.rs
Confidence: high
Affected projects: cryptography, urllib3, pwndbg, dulwich, starlette
Fixes: incompatible-comparison
bytearray and bytes are explicitly designed to be cross-comparable in Python. bytearray.eq accepts bytes arguments and returns meaningful boolean results. Neither inherits from the other, so has_superclass() fails, but they are in the same bytes-like family. This fixes 28 errors in cryptography, 24 in urllib3, 10 in pwndbg, 1 in dulwich, 1 in starlette — approximately 64 errors across 5 projects.

3. In check_incompatible_comparison() in pyrefly/lib/alt/operators.rs, add set family awareness: create a helper is_set_like_compatible(a, b) -> bool that returns true when both types are in {set, frozenset}. Add: if is_set_like_compatible(&left_base, &right_base) { return; }. This eliminates all frozenset==set false positives.

Files: pyrefly/lib/alt/operators.rs
Confidence: high
Affected projects: ppb-vector, scikit-build-core, altair, typeshed-stats, artigraph, django-modern-rest
Fixes: incompatible-comparison
frozenset and set explicitly support cross-type equality comparison — frozenset({1,2}) == {1,2} returns True. Neither inherits from the other (both inherit from collections.abc.Set), so has_superclass() fails. This fixes errors in ppb-vector (1), scikit-build-core (3), altair (partial), typeshed-stats (1), artigraph (2), django-modern-rest (partial) — approximately 10 errors across 6 projects.

4. In comparison_base() in pyrefly/lib/alt/operators.rs, restrict the ClassType match to only known built-in scalar/container types instead of accepting ANY ClassType. Change Type::ClassType(_) | Type::Tuple(_) => Some(ty.clone()) to check if the class is one of a known set (int, float, complex, bool, str, bytes, bytearray, list, tuple, set, frozenset, dict, Decimal). For unknown/user-defined ClassTypes, return None. This prevents the check from firing on user-defined types like JsBigInt, Sentinel, attrs classes, etc.

Files: pyrefly/lib/alt/operators.rs
Confidence: high
Affected projects: pyodide, ibis, attrs, cloud-init, pylox, egglog-python, flake8, apprise
Fixes: incompatible-comparison
The PR description says 'does not analyze unions, None, or user-defined classes' and 'Only triggers for built-in scalar families', but comparison_base() accepts ANY ClassType. This causes false positives on: JsBigInt in pyodide (1 error), Sentinel in ibis (1 error), attrs converter-transformed types in attrs (5 errors), SubpResult vs str in cloud-init (mock returns), dict vs deque in pylox (1 error), and many other user/library types. Restricting to known builtins would eliminate these ~15+ errors across ~8 projects.

5. In check_incompatible_comparison() in pyrefly/lib/alt/operators.rs, add handling for union types: before calling comparison_base(), unwrap union types and check if ANY member of the union is compatible with the other side. Currently unions fall through to None from comparison_base() (since Type::Union is not matched), but narrowed types from unions (e.g., bool|str narrowed to Literal[False]) still trigger false positives. Add a check: if the declared/widened type of either operand is a union containing members from different families, skip the check.

Files: pyrefly/lib/alt/operators.rs
Confidence: medium
Affected projects: ignite, jax
Fixes: incompatible-comparison
In ignite, variables declared as bool | str are narrowed to Literal[False] at the comparison point, then flagged when compared to string literals. The check should respect that the declared type allows both bool and str values. This fixes 4 errors in ignite and potentially similar patterns in other projects where narrowing causes false positives on intentional tagged-union dispatch patterns.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (63 LLM)

@github-actions github-actions Bot removed the stale label Mar 23, 2026
@yangdanny97 yangdanny97 removed their assignment Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] error when == or != is used on values that are incompatible types

4 participants