Skip to content

Fix unannotated attribute type inference bug (issue #1159)#3835

Draft
QEDady wants to merge 1 commit into
facebook:mainfrom
QEDady:union-unannotated-attribute-assignments
Draft

Fix unannotated attribute type inference bug (issue #1159)#3835
QEDady wants to merge 1 commit into
facebook:mainfrom
QEDady:union-unannotated-attribute-assignments

Conversation

@QEDady

@QEDady QEDady commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR resolves the unannotated attribute type inference bug where Pyrefly locked onto the first textual assignment to an attribute inside constructor methods (leading to false-positive bad-assignment errors and incorrect type inference).

Behavioral Changes

This PR changes the following type-checking behaviors:

  1. Constructor Branch Unioning for Unannotated Attributes: Pyrefly now computes the type of unannotated attributes as the union of all assignments across different control flow branches inside constructor methods. For example, an attribute initialized to int in the body of __init__ and conditionally assigned an str will resolve to int | str rather than throwing a bad-assignment error on the reassignment.
  2. Order-Independent Class Method Receiver Visibility: If an attribute is assigned via both a class receiver (cls.val) and an instance receiver (self.val) in constructors, it correctly resolves as class-visible (a class attribute) regardless of the declaration order of the constructors in the class body.
  3. Constructor Prioritization Over Helper Methods: Assignments inside recognized constructors (like __init__, __new__, __post_init__) always take precedence over unrecognized helper methods.
    • If multiple constructors assign to the same attribute (e.g. __new__ and __init__), we union their types.
    • If the attribute is defined only in unrecognized helper methods, we union those helper method assignments (and flag them as implicit definitions if strict diagnostics are enabled).
    • However, if any constructor assignment exists, helper method assignments are ignored for type inference and will be flagged as bad-assignment errors in case types are incompatible.
  4. Preserved Explicit Declarations: Explicit annotations (e.g. self.val: int) and class body definitions (e.g. val = 0) still strictly override type inference as before, ensuring that type constraints are still strictly checked.

High-Level Implementation Details

To support these behavioral improvements, we transitioned Pyrefly's class analysis from a "first-assignment-wins" model to an accumulator-based model:

  • Collection: Modified scope analysis to collect all assignments to an attribute across constructors (e.g. __new__, __init__, __post_init__) into a vector instead of discarding subsequent ones.
  • Visibility Tracking: Added a consolidated receiver_kind field to track field visibility independently from individual method signatures, upgrading it to Class if any constructor assigns to cls.val.
  • Solver Unioning: Updated the class field solver to solve the type of all collected assignments individually and union them.

Fixes #1159

Test Plan

  • Unit & Integration Tests: Added various test cases in attributes.rs covering branching unions, unrecognized helper methods, class-level priority, annotation overrides, and class method receiver union/upgrade behaviors.
  • Verification: Ran python3 test.py

Previously, Pyrefly incorrectly locked onto the first textual assignment to an unannotated attribute inside a constructor, leading to false-positive bad-assignment errors if the attribute was assigned different types across different control flow branches or initialization steps.

This change modifies Pyrefly's class scope compiler to collect all assignments to an attribute inside constructors (and other recognized setup methods), and then unions their types in the solver. Type annotations, if present, still strictly override and enforce the declared type.
@github-actions

Copy link
Copy Markdown

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

pip (https://github.com/pypa/pip)
- ERROR src/pip/_vendor/pkg_resources/__init__.py:3395:16-30: Returned type `dict[None, list[Unknown]]` is not assignable to declared return type `dict[str | None, list[Requirement]]` [bad-return]

tornado (https://github.com/tornadoweb/tornado)
- ERROR tornado/httpserver.py:308:35-39: `None` is not assignable to attribute `address_family` with type `AddressFamily` [bad-assignment]
+ ERROR tornado/netutil.py:476:13-35: Object of class `NoneType` has no attribute `shutdown` [missing-attribute]

cki-lib (https://gitlab.com/cki-project/cki-lib)
+ ERROR cki_lib/kcidb/file.py:49:34-38: `list[dict[@_, @_]]` is not assignable to dict key with type `dict[str, Unknown]` [bad-assignment]
+ ERROR cki_lib/kcidb/file.py:52:35-36: Cannot index into `dict[str, Unknown]` [bad-index]
+ ERROR cki_lib/kcidb/file.py:55:16-25: Cannot index into `str` [bad-index]
+ ERROR cki_lib/kcidb/file.py:65:33-42: No matching overload found for function `typing.MutableMapping.setdefault` called with arguments: (Unknown, list[@_]) [no-matching-overload]
+ ERROR cki_lib/kcidb/file.py:66:13-34: Object of class `dict` has no attribute `append` [missing-attribute]
+ ERROR cki_lib/kcidb/file.py:68:21-41: Object of class `dict` has no attribute `index` [missing-attribute]
+ ERROR cki_lib/kcidb/file.py:76:38-50: Argument `str | Unknown` is not assignable to parameter `config` with type `dict[str, Any]` in function `cki_lib.config_tree.merge_dicts` [bad-argument-type]
+ ERROR cki_lib/kcidb/file.py:77:21-41: Object of class `dict` has no attribute `index` [missing-attribute]
+ ERROR cki_lib/kcidb/file.py:80:33-42: No matching overload found for function `typing.MutableMapping.setdefault` called with arguments: (Unknown, list[@_]) [no-matching-overload]
+ ERROR cki_lib/kcidb/file.py:81:13-34: Object of class `dict` has no attribute `append` [missing-attribute]

mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ ERROR mitmproxy/tools/console/flowview.py:346:33-63: `Message | Unknown | None` is not assignable to `Message` [bad-assignment]

steam.py (https://github.com/Gobot1234/steam.py)
- ERROR steam/app.py:458:31-35: `None` is not assignable to attribute `updated_at` with type `datetime` [bad-assignment]
- ERROR steam/app.py:1753:35-39: `None` is not assignable to attribute `price_overview` with type `AppPriceOverview` [bad-assignment]

bokeh (https://github.com/bokeh/bokeh)
- ERROR src/bokeh/server/server.py:439:22-32: `int` is not assignable to attribute `_port` with type `None` [bad-assignment]
- ERROR src/bokeh/server/server.py:440:29-41: `str | None` is not assignable to attribute `_address` with type `None` [bad-assignment]

jax (https://github.com/google/jax)
+ ERROR jax/experimental/jax2tf/tests/cross_compilation_check.py:84:14-31: Object of class `NoneType` has no attribute `exists` [missing-attribute]
+ ERROR jax/experimental/jax2tf/tests/cross_compilation_check.py:90:14-33: Object of class `NoneType` has no attribute `makedirs` [missing-attribute]
+ ERROR jax/experimental/jax2tf/tests/cross_compilation_check.py:97:11-27: Object of class `NoneType` has no attribute `GFile` [missing-attribute]
+ ERROR jax/experimental/pallas/ops/tpu/paged_attention/paged_attention_kernel.py:84:9-38: Object of class `NoneType` has no attribute `at` [missing-attribute]

spark (https://github.com/apache/spark)
+ ERROR python/pyspark/core/broadcast.py:269:42-86: Object of class `NoneType` has no attribute `setupDecryptionServer` [missing-attribute]
+ ERROR python/pyspark/core/broadcast.py:271:17-65: Object of class `NoneType` has no attribute `waitTillBroadcastDataSent` [missing-attribute]
+ ERROR python/pyspark/core/context.py:562:9-30: Object of class `NoneType` has no attribute `setLogLevel` [missing-attribute]
+ ERROR python/pyspark/core/context.py:616:16-33: Object of class `NoneType` has no attribute `version` [missing-attribute]
+ ERROR python/pyspark/core/context.py:634:16-28: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:652:16-28: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:665:16-35: Object of class `NoneType` has no attribute `startTime` [missing-attribute]
+ ERROR python/pyspark/core/context.py:679:16-28: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:693:16-28: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:738:20-38: Object of class `NoneType` has no attribute `emptyRDD` [missing-attribute]
+ ERROR python/pyspark/core/context.py:872:54-66: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:976:20-40: Object of class `NoneType` has no attribute `objectFile` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1040:20-38: Object of class `NoneType` has no attribute `textFile` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1120:13-37: Object of class `NoneType` has no attribute `wholeTextFiles` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1176:13-34: Object of class `NoneType` has no attribute `binaryFiles` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1225:20-43: Object of class `NoneType` has no attribute `binaryRecords` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1726:16-40: Object of class `NoneType` has no attribute `checkpointFile` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1783:20-35: Object of class `NoneType` has no attribute `union` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1948:9-21: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:1962:17-29: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2076:9-21: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2090:17-29: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2113:9-21: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2128:16-28: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2129:20-32: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2216:9-30: Object of class `NoneType` has no attribute `setJobGroup` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2240:9-39: Object of class `NoneType` has no attribute `setInterruptOnCancel` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2302:9-28: Object of class `NoneType` has no attribute `addJobTag` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2335:9-31: Object of class `NoneType` has no attribute `removeJobTag` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2363:16-36: Object of class `NoneType` has no attribute `getJobTags` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2386:9-31: Object of class `NoneType` has no attribute `clearJobTags` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2414:9-35: Object of class `NoneType` has no attribute `setLocalProperty` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2427:16-42: Object of class `NoneType` has no attribute `getLocalProperty` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2445:9-36: Object of class `NoneType` has no attribute `setJobDescription` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2453:16-28: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2471:9-21: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2493:16-43: Object of class `NoneType` has no attribute `cancelJobsWithTag` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2507:9-21: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2515:30-53: Object of class `NoneType` has no attribute `statusTracker` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2571:48-60: Object of class `NoneType` has no attribute `sc` [missing-attribute]
+ ERROR python/pyspark/core/context.py:2627:22-41: Object of class `NoneType` has no attribute `resources` [missing-attribute]
+ ERROR python/pyspark/sql/types.py:1587:31-47: Argument `StructField | Unknown` is not assignable to parameter `fields` with type `list[StructField] | None` in function `StructType.__init__` [bad-argument-type]
+ ERROR python/pyspark/streaming/tests/test_context.py:32:18-38: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:37:9-23: Object of class `NoneType` has no attribute `start` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:38:9-22: Object of class `NoneType` has no attribute `stop` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:43:9-23: Object of class `NoneType` has no attribute `start` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:44:9-22: Object of class `NoneType` has no attribute `stop` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:45:9-22: Object of class `NoneType` has no attribute `stop` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:49:19-39: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:81:19-39: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:82:20-40: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:83:20-34: Object of class `NoneType` has no attribute `union` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:89:20-40: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:90:20-40: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:91:20-40: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:97:19-37: Object of class `NoneType` has no attribute `transform` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:103:19-39: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:110:9-29: Object of class `NoneType` has no attribute `queueStream` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:111:9-23: Object of class `NoneType` has no attribute `start` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:115:9-22: Object of class `NoneType` has no attribute `stop` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:167:9-23: Object of class `NoneType` has no attribute `start` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:168:26-60: Object of class `NoneType` has no attribute `awaitTerminationOrTimeout` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:169:9-22: Object of class `NoneType` has no attribute `stop` [missing-attribute]
+ ERROR python/pyspark/streaming/tests/test_context.py:170:25-59: Object of class `NoneType` has no attribute `awaitTerminationOrTimeout` [missing-attribute]
+ ERROR python/pyspark/util.py:611:29-49: Object of class `NoneType` has no attribute `addTag` [missing-attribute]
+ ERROR python/pyspark/util.py:630:28-48: Object of class `NoneType` has no attribute `client` [missing-attribute]

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/integrations/prefect-sqlalchemy/prefect_sqlalchemy/database.py:193:31-48: No matching overload found for function `sqlalchemy.engine.create.create_engine` called with arguments: (**dict[str, dict[str, Any] | Any]) [no-matching-overload]
+ ERROR src/integrations/prefect-sqlalchemy/prefect_sqlalchemy/database.py:193:31-48: No matching overload found for function `sqlalchemy.engine.create.create_engine` called with arguments: (**dict[str, URL | dict[str, Any] | Unknown]) [no-matching-overload]
- ERROR src/integrations/prefect-sqlalchemy/prefect_sqlalchemy/database.py:824:38-53: Unpacked keyword argument `dict[str, Any] | Any` is not assignable to parameter `url` with type `URL | str` in function `sqlalchemy.ext.asyncio.engine.create_async_engine` [bad-argument-type]
+ ERROR src/integrations/prefect-sqlalchemy/prefect_sqlalchemy/database.py:824:38-53: Unpacked keyword argument `URL | dict[str, Any] | Unknown` is not assignable to parameter `url` with type `URL | str` in function `sqlalchemy.ext.asyncio.engine.create_async_engine` [bad-argument-type]
+ ERROR src/prefect/task_engine.py:973:17-55: Class `NotSet` has no class attribute `isolation_level` [missing-attribute]
+ ERROR src/prefect/task_engine.py:1603:17-55: Class `NotSet` has no class attribute `isolation_level` [missing-attribute]
- ERROR src/prefect/tasks.py:556:33-40: `CachePolicy` is not assignable to attribute `cache_policy` with type `_None | None` [bad-assignment]
- ERROR src/prefect/tasks.py:561:73-85: `CacheKeyFnPolicy | CachePolicy | _None | type[NotSet]` is not assignable to attribute `cache_policy` with type `_None | None` [bad-assignment]
- ERROR src/prefect/tasks.py:581:17-36: `NoneType | float | int | list[float]` is not assignable to attribute `retry_delay_seconds` with type `list[float]` [bad-assignment]
- ERROR src/prefect/tasks.py:805:17-807:46: Argument `((int) -> list[float]) | float | int | list[float] | type[NotSet]` is not assignable to parameter `retry_delay_seconds` with type `((int) -> list[float]) | float | int | list[float] | None` in function `Task.__init__` [bad-argument-type]
+ ERROR src/prefect/tasks.py:805:17-807:46: Argument `((int) -> list[float]) | float | int | list[float] | type[NotSet] | None` is not assignable to parameter `retry_delay_seconds` with type `((int) -> list[float]) | float | int | list[float] | None` in function `Task.__init__` [bad-argument-type]

aiortc (https://github.com/aiortc/aiortc)
+ ERROR src/aiortc/rtcrtpreceiver.py:476:43-48: Argument `int | list[int] | Unknown` is not assignable to parameter `bitrate` with type `int` in function `aiortc.rtp.pack_remb_fci` [bad-argument-type]
+ ERROR src/aiortc/rtcrtpreceiver.py:476:43-48: Argument `int | list[int] | Unknown` is not assignable to parameter `ssrcs` with type `list[int]` in function `aiortc.rtp.pack_remb_fci` [bad-argument-type]

materialize (https://github.com/MaterializeInc/materialize)
- ERROR misc/python/materialize/output_consistency/enum/enum_operation_param.py:49:34-38: `None` is not assignable to attribute `invalid_value` with type `str` [bad-assignment]
- ERROR misc/python/materialize/output_consistency/validation/validation_message.py:129:45-49: `None` is not assignable to attribute `concerned_expression_str` with type `str` [bad-assignment]
- ERROR misc/python/materialize/output_consistency/validation/validation_message.py:130:46-50: `None` is not assignable to attribute `concerned_expression_hash` with type `int` [bad-assignment]

freqtrade (https://github.com/freqtrade/freqtrade)
- ERROR freqtrade/freqai/freqai_interface.py:350:21-41: Argument `DataFrame | Series` is not assignable to parameter `dataframe` with type `DataFrame` in function `freqtrade.strategy.interface.IStrategy.set_freqai_targets` [bad-argument-type]
+ ERROR freqtrade/freqai/freqai_interface.py:350:21-41: Argument `DataFrame | Series | Unknown` is not assignable to parameter `dataframe` with type `DataFrame` in function `freqtrade.strategy.interface.IStrategy.set_freqai_targets` [bad-argument-type]
- ERROR freqtrade/freqai/freqai_interface.py:354:21-44: Argument `DataFrame | Series` is not assignable to parameter `dataframe` with type `DataFrame` in function `freqtrade.strategy.interface.IStrategy.set_freqai_targets` [bad-argument-type]
+ ERROR freqtrade/freqai/freqai_interface.py:354:21-44: Argument `DataFrame | Series | Unknown` is not assignable to parameter `dataframe` with type `DataFrame` in function `freqtrade.strategy.interface.IStrategy.set_freqai_targets` [bad-argument-type]

more-itertools (https://github.com/more-itertools/more-itertools)
- ERROR more_itertools/more.py:3041:27-44: `deque[@_]` is not assignable to attribute `_cache` with type `list[Unknown]` [bad-assignment]
+ ERROR more_itertools/more.py:5508:21-28: Cannot set item in `list[None]` [unsupported-operation]

pwndbg (https://github.com/pwndbg/pwndbg)
- ERROR pwndbg/aglib/heap/ptmalloc.py:443:34-92: Argument `Value | None` is not assignable to parameter `x` with type `Buffer | SupportsIndex | SupportsInt | SupportsTrunc | str` in function `int.__new__` [bad-argument-type]
+ ERROR pwndbg/aglib/heap/ptmalloc.py:443:34-92: Argument `Value | Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsIndex | SupportsInt | SupportsTrunc | str` in function `int.__new__` [bad-argument-type]
- ERROR pwndbg/aglib/heap/ptmalloc.py:452:17-75: Argument `Value | None` is not assignable to parameter `x` with type `Buffer | SupportsIndex | SupportsInt | SupportsTrunc | str` in function `int.__new__` [bad-argument-type]
+ ERROR pwndbg/aglib/heap/ptmalloc.py:452:17-75: Argument `Value | Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsIndex | SupportsInt | SupportsTrunc | str` in function `int.__new__` [bad-argument-type]
- ERROR pwndbg/aglib/heap/ptmalloc.py:1359:25-44: Argument `Value | None` is not assignable to parameter `x` with type `Buffer | SupportsIndex | SupportsInt | SupportsTrunc | str` in function `int.__new__` [bad-argument-type]
+ ERROR pwndbg/aglib/heap/ptmalloc.py:1359:25-44: Argument `Value | Unknown | None` is not assignable to parameter `x` with type `Buffer | SupportsIndex | SupportsInt | SupportsTrunc | str` in function `int.__new__` [bad-argument-type]

setuptools (https://github.com/pypa/setuptools)
+ ERROR setuptools/_distutils/command/build_clib.py:91:47-60: Argument `Compiler | Unknown` is not assignable to parameter `compiler` with type `str | None` in function `setuptools._distutils.compilers.C.base.new_compiler` [bad-argument-type]
+ ERROR setuptools/_distutils/command/build_ext.py:327:22-35: Argument `Compiler | Unknown` is not assignable to parameter `compiler` with type `str | None` in function `setuptools._distutils.compilers.C.base.new_compiler` [bad-argument-type]

comtypes (https://github.com/enthought/comtypes)
- ERROR comtypes/automation.py:398:30-35: `_Pointer[Unknown]` is not assignable to attribute `__keepref` with type `_CArgObject` [bad-assignment]

apprise (https://github.com/caronc/apprise)
- ERROR apprise/apprise_attachment.py:123:29-33: `None` is not assignable to attribute `location` with type `ContentLocation` [bad-assignment]
- ERROR apprise/attachment/base.py:172:26-30: `None` is not assignable to attribute `cache` with type `bool | int` [bad-assignment]
- ERROR apprise/attachment/http.py:150:26-30: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.get` [bad-argument-type]
+ ERROR apprise/attachment/http.py:150:26-30: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.get` [bad-argument-type]
- ERROR apprise/config/http.py:186:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/config/http.py:186:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/persistent_store.py:467:37-469:26: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, str) [no-matching-overload]
+ ERROR apprise/persistent_store.py:550:31-77: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, str) [no-matching-overload]
- ERROR apprise/persistent_store.py:416:32-36: `None` is not assignable to attribute `__base_path` with type `str` [bad-assignment]
- ERROR apprise/persistent_store.py:417:32-36: `None` is not assignable to attribute `__temp_path` with type `str` [bad-assignment]
- ERROR apprise/persistent_store.py:418:32-36: `None` is not assignable to attribute `__data_path` with type `str` [bad-assignment]
+ ERROR apprise/persistent_store.py:779:31-77: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, str) [no-matching-overload]
- ERROR apprise/persistent_store.py:793:31-799:18: No matching overload found for function `gzip.open` called with arguments: (str, compresslevel=int, encoding=str | None, errors=str | None, newline=str | None) [no-matching-overload]
+ ERROR apprise/persistent_store.py:793:31-799:18: No matching overload found for function `gzip.open` called with arguments: (Unknown, compresslevel=int, encoding=str | None, errors=str | None, newline=str | None) [no-matching-overload]
+ ERROR apprise/persistent_store.py:1007:33-51: No matching overload found for function `posixpath.basename` called with arguments: (str | Unknown | None) [no-matching-overload]
+ ERROR apprise/persistent_store.py:1020:29-45: Argument `str | Unknown | None` is not assignable to parameter `name` with type `PathLike[bytes] | PathLike[str] | bytes | str` in function `os.makedirs` [bad-argument-type]
+ ERROR apprise/persistent_store.py:1035:29-45: Argument `str | Unknown | None` is not assignable to parameter `name` with type `PathLike[bytes] | PathLike[str] | bytes | str` in function `os.makedirs` [bad-argument-type]
+ ERROR apprise/persistent_store.py:1049:29-45: Argument `str | Unknown | None` is not assignable to parameter `name` with type `PathLike[bytes] | PathLike[str] | bytes | str` in function `os.makedirs` [bad-argument-type]
+ ERROR apprise/persistent_store.py:1298:37-1301:26: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, str) [no-matching-overload]
+ ERROR apprise/persistent_store.py:1305:28-68: `+` is not supported between `None` and `Literal['[/\\\\].+']` [unsupported-operation]
+ ERROR apprise/persistent_store.py:1305:37-55: `str | Unknown | None` is not assignable to any of constraints `str`, `bytes` of type variable `AnyStr` [bad-specialization]
+ ERROR apprise/persistent_store.py:1308:21-1309:33: `+` is not supported between `None` and `Literal['[/\\\\].+']` [unsupported-operation]
+ ERROR apprise/persistent_store.py:1308:30-48: `str | Unknown | None` is not assignable to any of constraints `str`, `bytes` of type variable `AnyStr` [bad-specialization]
+ ERROR apprise/persistent_store.py:1321:41-70: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, Literal['**'], Literal['*']) [no-matching-overload]
+ ERROR apprise/persistent_store.py:1326:71-77: No matching overload found for function `re.Pattern.match` called with arguments: (PathLike[bytes] | PathLike[str] | bytes | int | str) [no-matching-overload]
+ ERROR apprise/persistent_store.py:1336:41-70: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, Literal['**'], Literal['*']) [no-matching-overload]
+ ERROR apprise/persistent_store.py:1853:28-1856:10: No matching overload found for function `posixpath.join` called with arguments: (str | Unknown | None, str) [no-matching-overload]
- ERROR apprise/persistent_store.py:1866:16-27: Returned type `Unknown | None` is not assignable to declared return type `PersistentStoreMode` [bad-return]
+ ERROR apprise/persistent_store.py:1866:16-27: Returned type `PersistentStoreMode | Unknown | None` is not assignable to declared return type `PersistentStoreMode` [bad-return]
- ERROR apprise/plugins/apprise_api.py:400:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/apprise_api.py:400:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/bark.py:279:26-30: `None` is not assignable to attribute `badge` with type `int` [bad-assignment]
- ERROR apprise/plugins/bark.py:282:26-30: `None` is not assignable to attribute `badge` with type `int` [bad-assignment]
- ERROR apprise/plugins/bark.py:436:26-30: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/bark.py:436:26-30: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/bulkvs.py:257:26-30: Argument `tuple[Unknown | None, Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/bulkvs.py:257:26-30: Argument `tuple[str | Unknown | None, str | Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/clicksend.py:208:26-52: Argument `tuple[Unknown | None, Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/clicksend.py:208:26-52: Argument `tuple[str | Unknown | None, str | Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/custom_form.py:406:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.request` [bad-argument-type]
+ ERROR apprise/plugins/custom_form.py:406:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.request` [bad-argument-type]
- ERROR apprise/plugins/custom_json.py:321:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.request` [bad-argument-type]
+ ERROR apprise/plugins/custom_json.py:321:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.request` [bad-argument-type]
- ERROR apprise/plugins/custom_xml.py:412:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.request` [bad-argument-type]
+ ERROR apprise/plugins/custom_xml.py:412:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.request` [bad-argument-type]
- ERROR apprise/plugins/dapnet.py:271:34-43: Argument `Unknown | None` is not assignable to parameter `username` with type `bytes | str` in function `requests.auth.HTTPBasicAuth.__init__` [bad-argument-type]
+ ERROR apprise/plugins/dapnet.py:271:34-43: Argument `str | Unknown | None` is not assignable to parameter `username` with type `bytes | str` in function `requests.auth.HTTPBasicAuth.__init__` [bad-argument-type]
- ERROR apprise/plugins/dapnet.py:271:54-67: Argument `Unknown | None` is not assignable to parameter `password` with type `bytes | str` in function `requests.auth.HTTPBasicAuth.__init__` [bad-argument-type]
+ ERROR apprise/plugins/dapnet.py:271:54-67: Argument `str | Unknown | None` is not assignable to parameter `password` with type `bytes | str` in function `requests.auth.HTTPBasicAuth.__init__` [bad-argument-type]
- ERROR apprise/plugins/dbus.py:288:27-31: `None` is not assignable to attribute `x_axis` with type `int` [bad-assignment]
- ERROR apprise/plugins/dbus.py:289:27-31: `None` is not assignable to attribute `y_axis` with type `int` [bad-assignment]
- ERROR apprise/plugins/discord.py:294:26-30: `None` is not assignable to attribute `flags` with type `int` [bad-assignment]
- ERROR apprise/plugins/email/base.py:318:17-320:59: `NotifyFormat | OverflowMode | bool | float | str | None` is not assignable to attribute `secure_mode` with type `str | None` [bad-assignment]
- ERROR apprise/plugins/email/base.py:407:34-410:18: `tuple[Literal[False] | str | Unknown, str | Any | Unknown]` is not assignable to attribute `from_addr` with type `list[bool | str]` [bad-assignment]
- ERROR apprise/plugins/email/base.py:590:40-592:22: `bool | int | str | tuple[str] | Unknown | None` is not assignable to attribute `secure_mode` with type `str | None` [bad-assignment]
+ ERROR apprise/plugins/email/base.py:590:40-592:22: `NotifyFormat | OverflowMode | bool | float | int | str | tuple[str] | Unknown | None` is not assignable to attribute `secure_mode` with type `NotifyFormat | OverflowMode | bool | float | str | None` [bad-assignment]
+ ERROR apprise/plugins/email/base.py:624:21-38: Cannot set item in `tuple[bool | str | Unknown, str | Any | Unknown]` [unsupported-operation]
- ERROR apprise/plugins/email/base.py:662:17-26: Argument `Unknown | None` is not assignable to parameter `port` with type `int` in function `smtplib.SMTP.__init__` [bad-argument-type]
+ ERROR apprise/plugins/email/base.py:662:17-26: Argument `int | Unknown | None` is not assignable to parameter `port` with type `int` in function `smtplib.SMTP.__init__` [bad-argument-type]
- ERROR apprise/plugins/email/base.py:662:17-26: Argument `Unknown | None` is not assignable to parameter `port` with type `int` in function `smtplib.SMTP_SSL.__init__` [bad-argument-type]
+ ERROR apprise/plugins/email/base.py:662:17-26: Argument `int | Unknown | None` is not assignable to parameter `port` with type `int` in function `smtplib.SMTP_SSL.__init__` [bad-argument-type]
- ERROR apprise/plugins/email/base.py:705:25-42: Argument `bool | str` is not assignable to parameter `from_addr` with type `str` in function `smtplib.SMTP.sendmail` [bad-argument-type]
+ ERROR apprise/plugins/email/base.py:705:25-42: Argument `bool | str | Unknown` is not assignable to parameter `from_addr` with type `str` in function `smtplib.SMTP.sendmail` [bad-argument-type]
- ERROR apprise/plugins/email/base.py:808:21-51: Argument `tuple[Literal[True] | str, Literal[True] | str]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
+ ERROR apprise/plugins/email/base.py:808:21-51: Argument `tuple[Literal[True] | str | Unknown, Literal[True] | str | Unknown]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
- ERROR apprise/plugins/email/base.py:813:41-59: Argument `tuple[Literal[False], Literal[True] | str]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
+ ERROR apprise/plugins/email/base.py:813:41-59: Argument `tuple[Literal[False], Literal[True] | str | Unknown]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
- ERROR apprise/plugins/email/base.py:817:17-43: Argument `tuple[Literal[False], bool | str]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
+ ERROR apprise/plugins/email/base.py:817:17-43: Argument `tuple[Literal[False], bool | str | Unknown]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
- ERROR apprise/plugins/emby.py:680:32-53: Argument `tuple[Literal['UserId'], Unknown | None]` is not assignable to parameter `object` with type `tuple[str, str]` in function `list.append` [bad-argument-type]
+ ERROR apprise/plugins/emby.py:680:32-53: Argument `tuple[Literal['UserId'], str | Unknown | None]` is not assignable to parameter `object` with type `tuple[str, str]` in function `list.append` [bad-argument-type]
- ERROR apprise/plugins/enigma2.py:170:32-68: `LazyTranslation | NotifyFormat | OverflowMode | bool | float | frozenset[str] | str | None` is not assignable to attribute `timeout` with type `int` [bad-assignment]
- ERROR apprise/plugins/enigma2.py:174:28-68: `NotifyFormat | OverflowMode | bool | float | None` is not assignable to attribute `timeout` with type `int` [bad-assignment]
+ ERROR apprise/plugins/enigma2.py:289:24-30: Argument `dict[str, LazyTranslation | NotifyFormat | OverflowMode | bool | float | frozenset[str] | int | str | Unknown | None]` is not assignable to parameter `params` with type `Iterable[tuple[bytes | float | int | str, Iterable[bytes | float | int | str] | bytes | float | int | str | None]] | SupportsItems[bytes | float | int | str, Iterable[bytes | float | int | str] | bytes | float | int | str | None] | bytes | str | tuple[bytes | float | int | str, Iterable[bytes | float | int | str] | bytes | float | int | str | None] | None` in function `requests.api.get` [bad-argument-type]
- ERROR apprise/plugins/enigma2.py:291:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.get` [bad-argument-type]
+ ERROR apprise/plugins/enigma2.py:291:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.get` [bad-argument-type]
- ERROR apprise/plugins/fcm/__init__.py:244:17-246:34: `LazyTranslation | bool | str` is not assignable to attribute `mode` with type `str` [bad-assignment]
+ ERROR apprise/plugins/fcm/color.py:103:20-36: `+` is not supported between `Literal['#']` and `Match[str]` [unsupported-operation]
+ ERROR apprise/plugins/fcm/oauth.py:255:21-42: Object of class `DHPrivateKey` has no attribute `sign`
+ Object of class `MLKEM1024PrivateKey` has no attribute `sign`
+ Object of class `MLKEM768PrivateKey` has no attribute `sign`
+ Object of class `X25519PrivateKey` has no attribute `sign`
+ Object of class `X448PrivateKey` has no attribute `sign` [missing-attribute]
- ERROR apprise/plugins/fluxer.py:404:26-30: `None` is not assignable to attribute `flags` with type `int` [bad-assignment]
- ERROR apprise/plugins/fortysixelks.py:225:26-52: Argument `tuple[Unknown | None, Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/fortysixelks.py:225:26-52: Argument `tuple[str | Unknown | None, str | Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/glib.py:242:27-31: `None` is not assignable to attribute `x_axis` with type `int` [bad-assignment]
- ERROR apprise/plugins/glib.py:243:27-31: `None` is not assignable to attribute `y_axis` with type `int` [bad-assignment]
- ERROR apprise/plugins/ifttt.py:176:35-45: `list[Unknown] | set[Unknown] | tuple[Unknown, ...]` is not assignable to attribute `del_tokens` with type `list[Unknown]` [bad-assignment]
- ERROR apprise/plugins/ifttt.py:180:35-57: `set[Unknown]` is not assignable to attribute `del_tokens` with type `list[Unknown]` [bad-assignment]
- ERROR apprise/plugins/jira.py:399:27-66: `NotifyFormat | OverflowMode | bool | float | int | str | None` is not assignable to attribute `action` with type `str | None` [bad-assignment]
- ERROR apprise/plugins/kodi.py:274:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/kodi.py:274:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/mailgun.py:290:34-293:18: `tuple[Literal[False] | str | Unknown, str | Any | Unknown]` is not assignable to attribute `from_addr` with type `list[str]` [bad-assignment]
- ERROR apprise/plugins/mailgun.py:434:31-45: Argument `list[str]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
+ ERROR apprise/plugins/mailgun.py:434:31-45: Argument `list[str] | tuple[bool | str | Unknown, str | Any | Unknown]` is not assignable to parameter `pair` with type `tuple[str | None, str]` in function `email.utils.formataddr` [bad-argument-type]
- ERROR apprise/plugins/mastodon.py:320:31-74: `NotifyFormat | OverflowMode | bool | float | str | None` is not assignable to attribute `visibility` with type `str | None` [bad-assignment]
- ERROR apprise/plugins/mattermost.py:264:25-62: `NotifyFormat | OverflowMode | bool | float | str | None` is not assignable to attribute `mode` with type `str | None` [bad-assignment]
- ERROR apprise/plugins/misskey.py:184:31-74: `NotifyFormat | OverflowMode | bool | float | str | None` is not assignable to attribute `visibility` with type `str | None` [bad-assignment]
- ERROR apprise/plugins/nextcloud.py:344:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/nextcloud.py:344:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/nextcloud.py:344:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.get` [bad-argument-type]
+ ERROR apprise/plugins/nextcloud.py:344:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.get` [bad-argument-type]
- ERROR apprise/plugins/nextcloudtalk.py:224:26-52: Argument `tuple[Unknown | None, Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/nextcloudtalk.py:224:26-52: Argument `tuple[str | Unknown | None, str | Unknown | None]` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/notica.py:238:22-26: Argument `tuple[Unknown, Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
+ ERROR apprise/plugins/notica.py:238:22-26: Argument `tuple[str | Unknown, str | Unknown | None] | None` is not assignable to parameter `auth` with type `((PreparedRequest) -> PreparedRequest) | AuthBase | tuple[str, str] | None` in function `requests.api.post` [bad-argument-type]
- ERROR apprise/plugins/notificationapi.py:360:33-362:14: `Unknown | None` is not assignable to attribute `message_type` with type `str` [bad-assignment]
- ERROR apprise/plugins/notificationapi.py:687:28-691:14: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[Unknown, Unknown]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:706:36-714:22: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, str | Unknown]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:725:36-732:22: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, str | Unknown]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:735:25-70: Object of class `str` has no attribute `update` [missing-attribute]
+ ERROR apprise/plugins/notificationapi.py:735:25-70: Object of class `NoneType` has no attribute `update`
+ Object of class `str` has no attribute `update` [missing-attribute]
- ERROR apprise/plugins/notificationapi.py:743:36-750:22: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, str | Unknown | None]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:753:36-761:22: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, str | Unknown | None]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:764:36-771:22: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, str | Unknown]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:774:36-782:22: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, str | Unknown]]) [no-matching-overload]
- ERROR apprise/plugins/notificationapi.py:787:28-796:14: No matching overload found for function `typing.MutableMapping.update` called with arguments: (dict[str, dict[str, dict[str, Unknown]]]) [no-matching-overload]

... (truncated 75 lines) ...

operator (https://github.com/canonical/operator)
- ERROR ops/_private/harness.py:3159:25-32: `_TestingModelBackend` is not assignable to attribute `_backend` with type `type[_TestingModelBackend]` [bad-assignment]

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ ERROR pymongo/asynchronous/change_stream.py:246:13-25: Argument `AsyncCollection[_DocumentType] | AsyncCollection[Any] | AsyncDatabase[_DocumentType] | AsyncDatabase[Any] | AsyncMongoClient[_DocumentType] | Unknown` is not assignable to parameter `target` with type `AsyncCollection[Any] | AsyncDatabase[Any]` in function `pymongo.asynchronous.aggregation._AggregationCommand.__init__` [bad-argument-type]
+ ERROR pymongo/synchronous/change_stream.py:244:13-25: Argument `Collection[_DocumentType] | Collection[Any] | Database[_DocumentType] | Database[Any] | MongoClient[_DocumentType] | Unknown` is not assignable to parameter `target` with type `Collection[Any] | Database[Any]` in function `pymongo.synchronous.aggregation._AggregationCommand.__init__` [bad-argument-type]

beartype (https://github.com/beartype/beartype)
+ ERROR beartype/vale/_core/_valecore.py:257:16-30: Returned type `(() -> str) | object | str` is not assignable to declared return type `(() -> str) | str` [bad-return]

pandera (https://github.com/pandera-dev/pandera)
+ ERROR pandera/api/xarray/model.py:696:63-68: Argument `type[Any] | tuple[Any, ...] | Any | None` is not assignable to parameter `nested_data_array_model` with type `type[Any] | None` in function `pandera.api.xarray.model_components.XarrayFieldInfo.__init__` [bad-argument-type]
+ ERROR pandera/api/xarray/model.py:718:37-52: Class `Any` has no class attribute `to_schema`
+ Object of class `tuple` has no attribute `to_schema` [missing-attribute]
+ ERROR pandera/typing/common.py:259:39-47: Argument `tuple[Any, ...] | Any` is not assignable to parameter `obj` with type `(...) -> Any` in function `inspect.signature` [bad-argument-type]
- ERROR pandera/typing/common.py:251:20-45: `tuple[Any, ...] | Any | None` is not assignable to attribute `arg` with type `None` [bad-assignment]
- ERROR pandera/typing/common.py:277:28-31: `type[Any]` is not assignable to attribute `arg` with type `None` [bad-assignment]
- ERROR pandera/typing/common.py:280:28-42: `type[Any] | Any` is not assignable to attribute `arg` with type `None` [bad-assignment]

aiohttp (https://github.com/aio-libs/aiohttp)
- ERROR aiohttp/web_fileresponse.py:323:17-50: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_fileresponse.py:359:21-54: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_fileresponse.py:379:13-49: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_fileresponse.py:380:13-37: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_fileresponse.py:390:9-42: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_fileresponse.py:395:13-46: Cannot set item in `Mapping[str, str]` [unsupported-operation]
+ ERROR aiohttp/web_response.py:110:18-26: Class member `StreamResponse._headers` overrides parent class `HeadersMixin` in an inconsistent manner [bad-override-mutable-attribute]
- ERROR aiohttp/web_response.py:201:16-29: Returned type `Mapping[str, str]` is not assignable to declared return type `CIMultiDict[str]` [bad-return]
- ERROR aiohttp/web_response.py:216:13-47: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:218:13-30: Object of class `Mapping` has no attribute `pop` [missing-attribute]
- ERROR aiohttp/web_response.py:264:13-30: Object of class `Mapping` has no attribute `pop` [missing-attribute]
- ERROR aiohttp/web_response.py:266:13-46: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:270:13-46: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:274:13-46: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:298:13-30: Object of class `Mapping` has no attribute `pop` [missing-attribute]
- ERROR aiohttp/web_response.py:302:13-37: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:305:13-37: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:309:13-37: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:326:9-36: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:332:9-45: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:338:9-29: Object of class `Mapping` has no attribute `popall` [missing-attribute]
- ERROR aiohttp/web_response.py:384:35-42: Argument `Mapping[str, str]` is not assignable to parameter `headers` with type `CIMultiDict[str]` in function `aiohttp.helpers.populate_with_cookies` [bad-argument-type]
- ERROR aiohttp/web_response.py:397:17-48: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:404:25-56: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:414:21-49: Cannot delete item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:418:21-52: Cannot delete item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:421:13-31: Object of class `Mapping` has no attribute `setdefault` [missing-attribute]
- ERROR aiohttp/web_response.py:422:9-27: Object of class `Mapping` has no attribute `setdefault` [missing-attribute]
- ERROR aiohttp/web_response.py:423:9-27: Object of class `Mapping` has no attribute `setdefault` [missing-attribute]
- ERROR aiohttp/web_response.py:429:21-45: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:431:17-41: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:441:49-62: Argument `Mapping[str, str]` is not assignable to parameter `headers` with type `CIMultiDict[str]` in function `aiohttp.abc.AbstractStreamWriter.write_headers` [bad-argument-type]
- ERROR aiohttp/web_response.py:624:17-43: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:630:25-37: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:702:21-55: Cannot delete item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:706:21-55: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:713:21-55: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:733:9-45: Cannot set item in `Mapping[str, str]` [unsupported-operation]
- ERROR aiohttp/web_response.py:734:9-43: Cannot set item in `Mapping[str, str]` [unsupported-operation]

vision (https://github.com/pytorch/vision)
- ERROR torchvision/transforms/v2/_augment.py:81:26-30: `None` is not assignable to attribute `value` with type `list[float]` [bad-assignment]

zulip (https://github.com/zulip/zulip)
- ERROR corporate/lib/stripe.py:4433:44-54: `dict[str, Any]` is not assignable to dict key `extra_data` with type `Realm | datetime | int` [bad-assignment]
- ERROR corporate/lib/stripe.py:4436:45-54: `UserProfile` is not assignable to dict key `acting_user` with type `Realm | datetime | int` [bad-assignment]

sockeye (https://github.com/awslabs/sockeye)
+ ERROR sockeye/checkpoint_decoder.py:166:83-89: Argument `tuple[Any, ...]` is not assignable to parameter `strings` with type `list[str]` in function `sockeye.inference.make_input_from_multiple_strings` [bad-argument-type]
+ ERROR sockeye/checkpoint_decoder.py:221:71-95: Argument `tuple[Any, ...] | Unknown` is not assignable to parameter `strings` with type `list[str]` in function `sockeye.inference.make_input_from_multiple_strings` [bad-argument-type]
- ERROR sockeye/data_io.py:284:34-38: `None` is not assignable to attribute `unk_id_source` with type `int` [bad-assignment]
- ERROR sockeye/data_io.py:1281:36-47: Argument `Unknown | None` is not assignable to parameter `object` with type `int` in function `list.insert` [bad-argument-type]
+ ERROR sockeye/data_io.py:1281:36-47: Argument `int | Unknown | None` is not assignable to parameter `object` with type `int` in function `list.insert` [bad-argument-type]
- ERROR sockeye/data_io.py:1283:33-44: Argument `Unknown | None` is not assignable to parameter `object` with type `int` in function `list.append` [bad-argument-type]
+ ERROR sockeye/data_io.py:1283:33-44: Argument `int | Unknown | None` is not assignable to parameter `object` with type `int` in function `list.append` [bad-argument-type]
+ ERROR sockeye/rerank.py:79:23-75: Object of class `float` has no attribute `score`
+ Object of class `int` has no attribute `score` [missing-attribute]

bandersnatch (https://github.com/pypa/bandersnatch)
- ERROR src/bandersnatch/mirror.py:263:16-265:10: Returned type `int | None` is not assignable to declared return type `int` [bad-return]
- ERROR src/bandersnatch/mirror.py:263:19-265:10: `int | None` is not assignable to upper bound `SupportsDunderGT[Any] | SupportsDunderLT[Any]` of type variable `SupportsRichComparisonT` [bad-specialization]
- ERROR src/bandersnatch/mirror.py:356:52-70: Argument `int | None` is not assignable to parameter `serial` with type `int` in function `bandersnatch.simple.SimpleAPI.sync_index_page` [bad-argument-type]
- ERROR src/bandersnatch/storage.py:68:34-54: `type[Any]` is not assignable to attribute `configuration` with type `ConfigParser` [bad-assignment]
+ ERROR src/bandersnatch/storage.py:70:31-59: `type[Any]` is not subscriptable [unsupported-operation]
+ ERROR src/bandersnatch_storage_plugins/s3.py:139:21-45: `type[Any]` is not subscriptable [unsupported-operation]
+ ERROR src/bandersnatch_storage_plugins/s3.py:159:25-49: `type[Any]` is not subscriptable [unsupported-operation]

ignite (https://github.com/pytorch/ignite)
+ ERROR ignite/handlers/checkpoint.py:475:57-65: Argument `Number | int | Unknown` is not assignable to parameter `new` with type `float | int` in function `Checkpoint._compare_fn` [bad-argument-type]
+ ERROR ignite/handlers/checkpoint.py:516:48-56: Argument `Number | int | Unknown` is not assignable to parameter `priority` with type `float | int` in function `Item.__new__` [bad-argument-type]

dragonchain (https://github.com/dragonchain/dragonchain)
- ERROR dragonchain/lib/dto/models_utest.py:344:30-54: Argument `Unknown | None` is not assignable to parameter `d1` with type `Mapping[Any, object]` in function `unittest.case.TestCase.assertDictEqual` [bad-argument-type]
+ ERROR dragonchain/lib/dto/models_utest.py:344:30-54: Argument `Any | Unknown | None` is not assignable to parameter `d1` with type `Mapping[Any, object]` in function `unittest.case.TestCase.assertDictEqual` [bad-argument-type]

websockets (https://github.com/aaugustin/websockets)
- ERROR src/websockets/asyncio/connection.py:69:34-52: `int | None` is not assignable to attribute `max_queue_low` with type `None` [bad-assignment]
- ERROR src/websockets/asyncio/connection.py:73:36-56: `int | None` is not assignable to attribute `write_limit_low` with type `None` [bad-assignment]
- ERROR src/websockets/protocol.py:120:36-58: `int | None` is not assignable to attribute `max_fragment_size` with type `None` [bad-assignment]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ERROR ddtrace/contrib/internal/subprocess/patch.py:327:40-54: Argument `list[Unknown] | None` is not assignable to parameter `iterable` with type `Iterable[Unknown]` in function `collections.deque.__init__` [bad-argument-type]
+ ERROR ddtrace/contrib/internal/subprocess/patch.py:327:40-54: Argument `list[str] | list[Unknown] | None` is not assignable to parameter `iterable` with type `Iterable[str]` in function `collections.deque.__init__` [bad-argument-type]
- ERROR ddtrace/contrib/internal/tornado/stack_context.py:68:33-37: `None` is not assignable to attribute `new_contexts` with type `tuple[Unknown, Self@TracerStackContext]` [bad-assignment]
- ERROR ddtrace/vendor/dogstatsd/base.py:503:30-34: `None` is not assignable to attribute `_flush_thread` with type `Thread` [bad-assignment]
- ERROR ddtrace/vendor/ply/yacc.py:1437:27-43: `tuple[Unknown, ...]` is not assignable to attribute `prod` with type `list[Unknown]` [bad-assignment]

pandas (https://github.com/pandas-dev/pandas)
- ERROR pandas/core/dtypes/dtypes.py:652:16-32: Returned type `Index | None` is not assignable to declared return type `Index` [bad-return]
+ ERROR pandas/core/dtypes/dtypes.py:652:16-32: Returned type `Index | Any | None` is not assignable to declared return type `Index` [bad-return]
- ERROR pandas/core/strings/accessor.py:201:27-37: `Index` is not assignable to attribute `_index` with type `None` [bad-assignment]
- ERROR pandas/core/strings/accessor.py:202:26-35: `Hashable` is not assignable to attribute `_name` with type `None` [bad-assignment]
+ ERROR pandas/io/parsers/base_parser.py:245:37-46: Argument `int | integer` is not assignable to parameter `object` with type `tuple[Any, ...]` in function `list.insert` [bad-argument-type]
+ ERROR pandas/io/parsers/c_parser_wrapper.py:217:52-62: Argument `Sequence[Hashable] | list[Unknown] | Unknown` is not assignable to parameter `column_names` with type `list[str]` in function `_concatenate_chunks` [bad-argument-type]
+ ERROR pandas/io/parsers/python_parser.py:303:21-35: Argument `list[int | integer] | list[Unknown] | Unknown` is not assignable to parameter `index_col` with type `Sequence[int] | bool | None` in function `pandas.io.common.is_potential_multi_index` [bad-argument-type]
+ ERROR pandas/io/parsers/python_parser.py:340:17-31: Argument `list[int | integer] | list[Unknown] | Unknown` is not assignable to parameter `index_col` with type `Sequence[int] | bool | None` in function `pandas.io.common.is_potential_multi_index` [bad-argument-type]
+ ERROR pandas/io/sas/sas7bdat.py:560:63-80: Argument `bytes | str | Unknown` is not assignable to parameter `b` with type `bytes` in function `SAS7BDATReader._convert_header_text` [bad-argument-type]
+ ERROR pandas/io/stata.py:1757:24-38: Object of class `list` has no attribute `startswith` [missing-attribute]
+ ERROR pandas/io/stata.py:1759:81-84: Argument `list[list[list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown` is not assignable to parameter `fmt` with type `str` in function `_stata_elapsed_date_to_datetime_vec` [bad-argument-type]
+ ERROR pandas/io/stata.py:1764:47-60: Argument `list[list[list[list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[list[str] | list[Unknown] | str | Unknown] | list[str] | list[Unknown] | str | Unknown] | list[str]` is not assignable to parameter `lbllist` with type `Sequence[str]` in function `StataReader._do_convert_categoricals` [bad-argument-type]

meson (https://github.com/mesonbuild/meson)
- ERROR mesonbuild/cmake/interpreter.py:373:96-114: Argument `Unknown | None` is not assignable to parameter `clib_compiler` with type `Compiler | MissingCompiler` in function `mesonbuild.cmake.tracetargets.resolve_cmake_trace_targets` [bad-argument-type]
+ ERROR mesonbuild/cmake/interpreter.py:373:96-114: Argument `Compiler | Unknown | None` is not assignable to parameter `clib_compiler` with type `Compiler | MissingCompiler` in function `mesonbuild.cmake.tracetargets.resolve_cmake_trace_targets` [bad-argument-type]
+ ERROR mesonbuild/compilers/compilers.py:783:27-67: Object of class `NoneType` has no attribute `get_command` [missing-attribute]
- ERROR mesonbuild/compilers/compilers.py:787:50-57: Argument `list[str | None]` is not assignable to parameter `args` with type `list[str]` in function `mesonbuild.utils.universal.Popen_safe` [bad-argument-type]
+ ERROR mesonbuild/compilers/compilers.py:787:50-57: Argument `list[str | None] | Unknown` is not assignable to parameter `args` with type `list[str]` in function `mesonbuild.utils.universal.Popen_safe` [bad-argument-type]
- ERROR mesonbuild/dependencies/boost.py:371:21-56: `str | None` is not assignable to attribute `arch` with type `str` [bad-assignment]
+ ERROR mesonbuild/dependencies/boost.py:623:51-60: Argument `str | None` is not assignable to parameter `arch` with type `str` in function `BoostLibraryFile.arch_matches` [bad-argument-type]
+ ERROR mesonbuild/dependencies/pkgconfig.py:147:82-102: Object of class `NoneType` has no attribute `get_path` [missing-attribute]
- ERROR mesonbuild/dependencies/pkgconfig.py:241:23-27: `None` is not assignable to attribute `pkgbin` with type `ExternalProgram` [bad-assignment]
+ ERROR mesonbuild/dependencies/pkgconfig.py:288:42-65: Object of class `NoneType` has no attribute `get_command` [missing-attribute]
- ERROR mesonbuild/environment.py:230:32-36: `None` is not assignable to attribute `exe_wrapper` with type `ExternalProgram` [bad-assignment]
- ERROR mesonbuild/mconf.py:394:42-49: Argument `Unknown | None` is not assignable to parameter `builddata` with type `Build` in function `mesonbuild.mintro.write_meson_info_file` [bad-argument-type]
+ ERROR mesonbuild/mconf.py:394:42-49: Argument `Build | Unknown | None` is not assignable to parameter `builddata` with type `Build` in function `mesonbuild.mintro.write_meson_info_file` [bad-argument-type]
+ ERROR mesonbuild/modules/i18n.py:156:68-75: Argument `list[str | None] | list[str] | list[Any]` is not assignable to parameter `arguments` with type `list[str]` in function `XgettextProgram._get_rsp_file` [bad-argument-type]
+ ERROR mesonbuild/modules/python.py:557:21-558:44: Argument `list[str | None] | list[str] | list[Any]` is not assignable to parameter `args` with type `list[str]` in function `mesonbuild.utils.universal.Popen_safe` [bad-argument-type]
+ ERROR mesonbuild/modules/python.py:569:66-82: No matching overload found for function `str.join` called with arguments: (list[str | None] | list[str] | list[Any]) [no-matching-overload]
+ ERROR mesonbuild/programs.py:144:24-38: No matching overload found for function `str.join` called with arguments: (list[str | None] | list[str] | list[Any]) [no-matching-overload]
+ ERROR mesonbuild/programs.py:389:16-31: Returned type `list[str | None] | list[str] | list[Any]` is not assignable to declared return type `list[str]` [bad-return]
- ERROR mesonbuild/scripts/depfixer.py:500:48-55: Argument `Unknown | None` is not assignable to parameter `ifile` with type `BinaryIO` in function `SectionHeader.__init__` [bad-argument-type]
+ ERROR mesonbuild/scripts/depfixer.py:500:48-55: Argument `BufferedRandom | Unknown | None` is not assignable to parameter `ifile` with type `BinaryIO` in function `SectionHeader.__init__` [bad-argument-type]
- ERROR mesonbuild/scripts/depfixer.py:527:30-37: Argument `Unknown | None` is not assignable to parameter `ifile` with type `BinaryIO` in function `DynamicEntry.__init__` [bad-argument-type]
+ ERROR mesonbuild/scripts/depfixer.py:527:30-37: Argument `BufferedRandom | Unknown | None` is not assignable to parameter `ifile` with type `BinaryIO` in function `DynamicEntry.__init__` [bad-argument-type]
- ERROR mesonbuild/scripts/depfixer.py:538:29-36: Argument `Unknown | None` is not assignable to parameter `ifile` with type `BinaryIO` in function `DynsymEntry.__init__` [bad-argument-type]
+ ERROR mesonbuild/scripts/depfixer.py:538:29-36: Argument `BufferedRandom | Unknown | None` is not assignable to parameter `ifile` with type `BinaryIO` in function `DynsymEntry.__init__` [bad-argument-type]
- ERROR mesonbuild/scripts/depfixer.py:723:25-32: Argument `Unknown | None` is not assignable to parameter `ofile` with type `BinaryIO` in function `DynamicEntry.write` [bad-argument-type]
+ ERROR mesonbuild/scripts/depfixer.py:723:25-32: Argument `BufferedRandom | Unknown | None` is not assignable to parameter `ofile` with type `BinaryIO` in function `DynamicEntry.write` [bad-argument-type]

core (https://github.com/home-assistant/core)
- ERROR homeassistant/components/doods/image_processing.py:244:23-33: Argument `list[int]` is not assignable to parameter `box` with type `tuple[float, float, float, float]` in function `homeassistant.util.pil.draw_box` [bad-argument-type]
+ ERROR homeassistant/components/doods/image_processing.py:244:23-33: Argument `list[int] | list[Unknown]` is not assignable to parameter `box` with type `tuple[float, float, float, float]` in function `homeassistant.util.pil.draw_box` [bad-argument-type]
- ERROR homeassistant/components/influxdb/sensor.py:212:25-219:14: `InfluxQLSensorData` is not assignable to attribute `data` with type `InfluxFluxSensorData` [bad-assignment]
- ERROR homeassistant/components/kankun/switch.py:86:26-30: `None` is not assignable to attribute `_auth` with type `tuple[Unknown, Unknown]` [bad-assignment]
- ERROR homeassistant/components/modbus/cover.py:99:34-54: Argument `Any | None` is not assignable to parameter `value` with type `bool | int | str` in function `ModbusCover._set_attr_state` [bad-argument-type]
+ ERROR homeassistant/components/modbus/cover.py:99:34-54: Argument `bool | Unknown | None` is not assignable to parameter `value` with type `bool | int | str` in function `ModbusCover._set_attr_state` [bad-argument-type]
- ERROR homeassistant/components/neurio_energy/sensor.py:186:41-68: `Literal[UnitOfEnergy.KILO_WATT_HOUR]` is not assignable to attribute `_unit_of_measurement` with type `UnitOfPower` [bad-assignment]
- ERROR homeassistant/components/neurio_energy/sensor.py:196:41-68: `Literal[UnitOfEnergy.KILO_WATT_HOUR]` is not assignable to attribute `_unit_of_measurement` with type `UnitOfPower` [bad-assignment]

sphinx (https://github.com/sphinx-doc/sphinx)
+ ERROR sphinx/builders/changes.py:166:22-36: Argument `BuiltinTemplateLoader | Unknown` is not assignable to parameter `renderer` with type `BaseRenderer | None` in function `sphinx.util.fileutil.copy_asset_file` [bad-argument-type]
+ ERROR sphinx/builders/gettext.py:267:32-79: Object of class `SandboxedEnvironment` has no attribute `extract_translations` [missing-attribute]
+ ERROR sphinx/builders/html/__init__.py:863:30-44: Argument `BuiltinTemplateLoader | Unknown` is not assignable to parameter `renderer` with type `BaseRenderer | None` in function `sphinx.util.fileutil.copy_asset` [bad-argument-type]
+ ERROR sphinx/builders/html/__init__.py:890:26-40: Argument `BuiltinTemplateLoader | Unknown` is not assignable to parameter `renderer` with type `BaseRenderer | None` in function `sphinx.util.fileutil.copy_asset` [bad-argument-type]
+ ERROR sphinx/ext/napoleon/docstring.py:501:22-60: Object of class `Config` has no attribute `napoleon_preprocess_types` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:504:30-64: Object of class `Config` has no attribute `napoleon_type_aliases` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:510:41-53: Argument `sphinx.ext.napoleon.Config | sphinx.config.Config` is not assignable to parameter `config` with type `sphinx.config.Config | None` in function `GoogleDocstring.__init__` [bad-argument-type]
+ ERROR sphinx/ext/napoleon/docstring.py:533:41-53: Argument `sphinx.ext.napoleon.Config | sphinx.config.Config` is not assignable to parameter `config` with type `sphinx.config.Config | None` in function `GoogleDocstring.__init__` [bad-argument-type]
+ ERROR sphinx/ext/napoleon/docstring.py:552:47-85: Object of class `Config` has no attribute `napoleon_preprocess_types` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:555:34-68: Object of class `Config` has no attribute `napoleon_type_aliases` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:559:43-55: Argument `sphinx.ext.napoleon.Config | sphinx.config.Config` is not assignable to parameter `config` with type `sphinx.config.Config | None` in function `GoogleDocstring.__init__` [bad-argument-type]
+ ERROR sphinx/ext/napoleon/docstring.py:808:12-49: Object of class `Config` has no attribute `napoleon_custom_sections` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:809:26-63: Type `object` is not iterable [not-iterable]
+ ERROR sphinx/ext/napoleon/docstring.py:879:16-46: Object of class `Config` has no attribute `napoleon_use_ivar` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:896:12-42: Object of class `Config` has no attribute `napoleon_use_ivar` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:905:26-75: Object of class `Config` has no attribute `napoleon_use_admonition_for_examples` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:942:12-45: Object of class `Config` has no attribute `napoleon_use_keyword` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:961:26-72: Object of class `Config` has no attribute `napoleon_use_admonition_for_notes` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:965:12-43: Object of class `Config` has no attribute `napoleon_use_param` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:974:12-43: Object of class `Config` has no attribute `napoleon_use_param` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1001:12-43: Object of class `Config` has no attribute `napoleon_use_param` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1010:26-77: Object of class `Config` has no attribute `napoleon_use_admonition_for_references` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1016:41-72: Object of class `Config` has no attribute `napoleon_use_rtype` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1089:12-50: Object of class `Config` has no attribute `napoleon_attr_annotations` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1248:12-50: Object of class `Config` has no attribute `napoleon_preprocess_types` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1251:30-64: Object of class `Config` has no attribute `napoleon_type_aliases` [missing-attribute]
+ ERROR sphinx/ext/napoleon/docstring.py:1257:39-51: Argument `sphinx.ext.napoleon.Config | sphinx.config.Config` is not assignable to parameter `config` with type `sphinx.config.Config | None` in function `NumpyDocstring.__init__` [bad-argument-type]
+ ERROR sphinx/ext/napoleon/docstring.py:1370:28-62: Object of class `Config` has no attribute `napoleon_type_aliases` [missing-attribute]

parso (https://github.com/davidhalter/parso)
+ ERROR parso/python/errors.py:415:18-40: Object of class `NoneType` has no attribute `add_block` [missing-attribute]
+ ERROR parso/python/errors.py:416:24-43: Object of class `NoneType` has no attribute `blocks` [missing-attribute]
+ ERROR parso/python/errors.py:470:32-56: Object of class `NoneType` has no attribute `add_context` [missing-attribute]
+ ERROR parso/python/errors.py:489:9-30: Object of class `NoneType` has no attribute `finalize` [missing-attribute]

pydantic (https://github.com/pydantic/pydantic)
- ERROR pydantic/_internal/_validate_call.py:127:54-79: `((input: Any, *, strict: bool | None = None, extra: Literal['allow', 'forbid', 'ignore'] | None = None, from_attributes: bool | None = None, context: Any | None = None, self_instance: Any | None = None, allow_partial: Literal['off', 'on', 'trailing-strings'] | bool = False, by_alias: bool | None = None, by_name: bool | None = None) -> Any) | (self: SchemaValidator, input: Any, *, strict: bool | None = None, extra: Literal['allow', 'forbid', 'ignore'] | None = None, from_attributes: bool | None = None, context: Any | None = None, self_instance: Any | None = None, allow_partial: Literal['off', 'on', 'trailing-strings'] | bool = False, by_alias: bool | None = None, by_name: bool | None = None) -> Any` is not assignable to attribute `__return_pydantic_validator__` with type `(aw: Awaitable[Any]) -> Coroutine[Unknown, Unknown, None]` [bad-assignment]
- ERROR pydantic/_internal/_validate_call.py:129:50-54: `None` is not assignable to attribute `__return_pydantic_validator__` with type `(aw: Awaitable[Any]) -> Coroutine[Unknown, Unknown, None]` [bad-assignment]

discord.py (https://github.com/Rapptz/discord.py)
- ERROR discord/components.py:574:27-31: `None` is not assignable to attribute `_emoji` with type `PartialEmoji` [bad-assignment]
- ERROR discord/embeds.py:251:31-66: `datetime | None` is not assignable to attribute `_timestamp` with type `datetime` [bad-assignment]
- ERROR discord/embeds.py:362:31-35: `None` is not assignable to attribute `_timestamp` with type `datetime` [bad-assignment]

PyGithub (https://github.com/PyGithub/PyGithub)
+ ERROR github/Requester.py:932:57-71: Argument `list[str | Unknown | None] | list[str]` is not assignable to parameter `domain_or_domains` with type `list[str] | str` in function `Requester.__hostnameHasDomain` [bad-argument-type]

@github-actions

Copy link
Copy Markdown

Primer Diff Classification

❌ 4 regression(s) | ✅ 31 improvement(s) | ➖ 3 neutral | 38 project(s) total | +254, -208 errors

4 regression(s) across pip, apprise, beartype, aiohttp. error kinds: bad-return, Removed bad-assignment false positives, New bad-argument-type errors. caused by _get_repr(), record_self_attr_assign(). 31 improvement(s) across tornado, cki-lib, mitmproxy, steam.py, bokeh, jax, spark, prefect, aiortc, materialize, more-itertools, setuptools, comtypes, operator, mongo-python-driver, pandera, vision, zulip, sockeye, bandersnatch, ignite, websockets, dd-trace-py, pandas, meson, core, sphinx, parso, pydantic, discord.py, PyGithub.

Project Verdict Changes Error Kinds Root Cause
pip ❌ Regression -1 bad-return record_self_attr_assign()
tornado ✅ Improvement +1, -1 bad-assignment, missing-attribute pyrefly/lib/binding/scope.rs
cki-lib ✅ Improvement +10 missing-attribute on dict pyrefly/lib/binding/scope.rs
mitmproxy ✅ Improvement +1 bad-assignment record_self_attr_assign()
steam.py ✅ Improvement -2 bad-assignment record_self_attr_assign()
bokeh ✅ Improvement -2 bad-assignment record_self_attr_assign()
jax ✅ Improvement +4 missing-attribute record_self_attr_assign()
spark ✅ Improvement +66 missing-attribute on NoneType (65 errors) _do_init()
prefect ✅ Improvement +5, -6 Removed bad-assignment false positives record_self_attr_assign()
aiortc ✅ Improvement +1 bad-argument-type record_self_attr_assign()
materialize ✅ Improvement -3 bad-assignment record_self_attr_assign()
freqtrade ➖ Neutral +2, -2 bad-argument-type
more-itertools ✅ Improvement +1, -1 Removed false positive on seekable._cache pyrefly/lib/binding/scope.rs
pwndbg ➖ Neutral +3, -3 bad-argument-type
setuptools ✅ Improvement +2 bad-argument-type record_self_attr_assign()
comtypes ✅ Improvement -1 bad-assignment record_self_attr_assign()
apprise ❌ Regression +81, -102 Removed bad-assignment false positives record_self_attr_assign()
operator ✅ Improvement -1 bad-assignment record_self_attr_assign()
mongo-python-driver ✅ Improvement +2 bad-argument-type pyrefly/lib/binding/scope.rs
beartype ❌ Regression +1 bad-return _get_repr()
pandera ✅ Improvement +2, -3 bad-argument-type, bad-assignment record_self_attr_assign()
aiohttp ❌ Regression +1, -38 bad-override-mutable-attribute on HeadersMixin pyrefly/lib/binding/binding.rs
vision ✅ Improvement -1 bad-assignment record_self_attr_assign()
zulip ✅ Improvement -2 bad-assignment pyrefly/lib/binding/scope.rs
sockeye ✅ Improvement +4, -3 Removed bad-assignment false positive (unk_id_source) pyrefly/lib/binding/scope.rs
bandersnatch ✅ Improvement +3, -4 unsupported-operation: type[Any] is not subscriptable pyrefly/lib/binding/scope.rs
ignite ✅ Improvement +2 bad-argument-type record_self_attr_assign()
dragonchain ➖ Neutral +1, -1 bad-argument-type
websockets ✅ Improvement -3 bad-assignment record_self_attr_assign()
dd-trace-py ✅ Improvement +1, -4 bad-argument-type, bad-assignment record_self_attr_assign()
pandas ✅ Improvement +9, -3 bad-argument-type (7 new) pyrefly/lib/binding/scope.rs
meson ✅ Improvement +16, -10 New bad-argument-type errors pyrefly/lib/binding/scope.rs
core ✅ Improvement +2, -6 Removed bad-assignment errors (influxdb, kankun, neurio) pyrefly/lib/binding/scope.rs
sphinx ✅ Improvement +28 missing-attribute on Config (19 errors) pyrefly/lib/binding/scope.rs
parso ✅ Improvement +4 missing-attribute record_self_attr_assign()
pydantic ✅ Improvement -2 bad-assignment record_self_attr_assign()
discord.py ✅ Improvement -3 bad-assignment record_self_attr_assign()
PyGithub ✅ Improvement +1 bad-argument-type pyrefly/lib/alt/class/class_field.rs
Detailed analysis

❌ Regression (4)

pip (-1)

The old error dict[None, list[Unknown]] was a false positive caused by pyrefly's previous 'first-assignment-wins' inference model. It looked only at {None: []} and ignored the explicit annotation self.__dep_map: dict[str | None, list[Requirement]] on line 3374. The PR's accumulator-based model now correctly resolves the type, removing this incorrect error.
Attribution: The change in pyrefly/lib/binding/scope.rs in record_self_attr_assign() now accumulates all assignments to the same attribute within a method (lines 1980-2000), and the solver in pyrefly/lib/alt/class/class_field.rs unions all collected assignment types (lines 1600-1630). This means the explicit annotation on line 3374 of the source file is now properly considered alongside the initial value, fixing the false positive.

apprise (+81, -102)

Removed bad-assignment false positives: 55 removed errors were false positives from the first-assignment-wins model. Code like self.location = None in an else branch is valid — the attribute should be ContentLocation | None.
New bad-argument-type errors: 42 new errors from wider union types flowing into typed APIs (e.g., requests.get). 38/42 confirmed by pyright — these are real type issues.
New no-matching-overload errors: 16 new errors from attributes like self.__data_path being str | None and used in os.path.join(). 13/16 confirmed by pyright.
New unsupported-operation errors: 11 new errors from None-possible attributes used in string operations. 10/11 confirmed by pyright.
New bad-index/missing-attribute/bad-return/etc: Smaller categories of new errors from the same root cause — wider union types exposing real type issues. Most confirmed by pyright.
Removed bad-argument-type/no-matching-overload: 42 removed errors replaced by more precise versions with better type information (e.g., Unknown replaced by str | Unknown).

Overall: The analysis is factually accurate. Let me verify the key claims:

  1. Removed bad-assignment false positives (55 removed): The example self.location = None in apprise/apprise_attachment.py:123 is indeed a false positive under a first-assignment-wins model. The class AppriseAttachment sets self.location to a ContentLocation value in the if location: branch (line 108-111) and to None in the else branch (line 123). The correct inferred type should be ContentLocation | None, not just ContentLocation. Similarly, self.cache = None in apprise/attachment/base.py:172 — the cache attribute is set to bool | int in one branch and None in another, so the union type is correct.

  2. New bad-argument-type errors (42 added): The example shows tuple[str | Unknown, str | Unknown | None] | None being passed to auth parameter expecting tuple[str, str] | None (among other types). With the union model, self.user and self.password are inferred as str | Unknown (since they come from parent class attributes that may have multiple assignment types). The Unknown component makes the tuple incompatible with tuple[str, str]. The 38/42 pyright confirmation rate supports these being real type issues.

  3. New no-matching-overload errors (16 added): The example at apprise/persistent_store.py:467 shows os.path.join(self.__data_path, ...) where self.__data_path is str | None (set to os.path.join(...) in one branch and None in another, lines 412-418). os.path.join doesn't accept None as its first argument, so this is a real type error. The 13/16 pyright confirmation supports this.

  4. New unsupported-operation errors (11 added): Operations like + on None values are genuine type errors when attributes can be None.

  5. Removed bad-argument-type/no-matching-overload (42 removed): The claim that these are replaced by more precise versions is supported — the old errors had Unknown types while new errors have str | Unknown, showing better type inference.

  6. The overall assessment that this is a move from first-assignment-wins to union-based inference, reducing false positives while exposing real issues, is consistent with the error patterns observed.

All claims about Python types, the requests.get/requests.post auth parameter type, os.path.join overloads, and the source code behavior are accurate.

Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() accumulating assignments into a Vec<ExprOrBinding> instead of discarding subsequent ones, and the merging logic in method_defined_attributes) combined with pyrefly/lib/alt/class/class_field.rs (the solver now unions all collected assignment types via unions(union_types, self.heap)) directly caused both the removal of false-positive bad-assignment errors and the introduction of new errors from wider union types flowing into downstream operations.

beartype (+1)

This is a false positive. The _get_repr attribute is defined only in __slots__ without a type annotation, so pyrefly must infer its type from assignments. The attribute is assigned in two places: (1) in the property setter get_repr at line 302, where self._get_repr = get_repr assigns a value of type BeartypeValidatorRepresenter = (() -> str) | str, and (2) in __repr__ at line 322, where self._get_repr = self._get_repr() assigns the result of calling self._get_repr. In __repr__, the call happens inside if callable(self._get_repr):, so at runtime only the () -> str branch is called, yielding str. However, pyrefly apparently does not narrow the type after the callable() check, or its inference of calling a (() -> str) | str union produces object (possibly because calling str as a value — not as a constructor — is treated as calling object.__call__ or similar). This causes pyrefly to infer the backing field type as (() -> str) | object | str, which is wider than the property getter's declared return type (() -> str) | str, triggering the bad-return error. This is incorrect — the actual runtime values stored in _get_repr are always either callables returning str or str values, matching the declared return type BeartypeValidatorRepresenter.
Attribution: The change in pyrefly/lib/binding/scope.rs (the record_self_attr_assign function and the merging logic in method_defined_attributes) now accumulates all assignments to _get_repr across methods and unions their types. The _get_repr attribute is assigned in the property setter (a regular method) and in __repr__ (also not a constructor). Since neither is a constructor, pyrefly unions both assignments. The __repr__ method's self._get_repr = self.[_get_repr()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/scope.rs) call result gets inferred as object (since the callable's return type resolution may degrade), introducing object into the union. The solver in pyrefly/lib/alt/class/class_field.rs then produces (() -> str) | object | str instead of the correct (() -> str) | str.

aiohttp (+1, -38)

bad-override-mutable-attribute on HeadersMixin: New pyrefly-only error. StreamResponse._headers is CIMultiDict[str] while HeadersMixin._headers is Mapping[str, str]. Mutable attribute overrides technically require invariance, so this error has theoretical justification. However, in practice _headers is an internal attribute that subclasses are expected to provide with a concrete mutable mapping type, and neither mypy nor pyright flag this. This is a minor regression (1 debatable error added).
unsupported-operation on Mapping (28 removed): These were false positives caused by pyrefly incorrectly inferring _headers as Mapping[str, str] instead of CIMultiDict[str]. Mapping doesn't support __setitem__, but CIMultiDict does. Removing these is correct.
missing-attribute on Mapping (7 removed): False positives. pop, popall, setdefault exist on CIMultiDict but not on Mapping. Now that pyrefly correctly infers the type, these are gone.
bad-argument-type (2 removed): False positives from passing Mapping[str, str] where CIMultiDict[str] was expected. The actual type is CIMultiDict[str].
bad-return (1 removed): False positive from returning Mapping[str, str] where CIMultiDict[str] was declared. The actual type is CIMultiDict[str].

Overall: This is overwhelmingly an improvement. 38 false positive errors were removed because pyrefly now correctly infers self._headers as CIMultiDict[str] from the StreamResponse.__init__ constructor branches, instead of incorrectly using the parent class's Mapping[str, str] type. The removed errors were all cascading consequences of the wrong type inference.

The 1 new bad-override-mutable-attribute error is pyrefly-only and may be a false positive, though it has some theoretical justification. HeadersMixin defines _headers as a Mapping[str, str], and StreamResponse narrows it to CIMultiDict[str]. Since CIMultiDict[str] is a subtype of Mapping[str, str], covariant narrowing is safe for reads. However, for mutable (assignable) attributes, type theory requires invariance: code operating on a HeadersMixin reference could assign a plain Mapping[str, str] to _headers, which would violate the CIMultiDict[str] constraint in the subclass. That said, neither mypy nor pyright flag this, and in practice _headers is used as an internal implementation detail that subclasses provide. The net effect is strongly positive: -38 false positives, +1 debatable new error.

Per-category reasoning:

  • bad-override-mutable-attribute on HeadersMixin: New pyrefly-only error. StreamResponse._headers is CIMultiDict[str] while HeadersMixin._headers is Mapping[str, str]. Mutable attribute overrides technically require invariance, so this error has theoretical justification. However, in practice _headers is an internal attribute that subclasses are expected to provide with a concrete mutable mapping type, and neither mypy nor pyright flag this. This is a minor regression (1 debatable error added).
  • unsupported-operation on Mapping (28 removed): These were false positives caused by pyrefly incorrectly inferring _headers as Mapping[str, str] instead of CIMultiDict[str]. Mapping doesn't support __setitem__, but CIMultiDict does. Removing these is correct.
  • missing-attribute on Mapping (7 removed): False positives. pop, popall, setdefault exist on CIMultiDict but not on Mapping. Now that pyrefly correctly infers the type, these are gone.
  • bad-argument-type (2 removed): False positives from passing Mapping[str, str] where CIMultiDict[str] was expected. The actual type is CIMultiDict[str].
  • bad-return (1 removed): False positive from returning Mapping[str, str] where CIMultiDict[str] was declared. The actual type is CIMultiDict[str].

Attribution: The PR changed ClassFieldDefinition::DefinedInMethod in pyrefly/lib/binding/binding.rs from storing a single value: Box<ExprOrBinding> to values: Vec<ExprOrBinding>, and the solver in pyrefly/lib/alt/class/class_field.rs now unions all collected assignments. Previously, pyrefly used a 'first-assignment-wins' model. For StreamResponse._headers, the old behavior likely picked up the type from HeadersMixin (the parent class) as Mapping[str, str] rather than the CIMultiDict[str] from the constructor. The new accumulator-based model correctly resolves the type from all constructor branches, yielding CIMultiDict[str]. The new bad-override-mutable-attribute error is a side effect: now that pyrefly correctly sees StreamResponse._headers as CIMultiDict[str], it detects a mismatch with HeadersMixin._headers (typed as Mapping[str, str]). However, this is likely a false positive because HeadersMixin._headers is a property or abstract attribute that StreamResponse is meant to provide a concrete implementation for.

✅ Improvement (31)

tornado (+1, -1)

This is a mixed result. The removed error is clearly a false positive being fixed — the attribute address_family genuinely can be None. The new error at netutil.py:476 is debatable but leans toward being a true positive: pyrefly now correctly infers that self.executor can be None (because of the self.executor = None assignment on line 477, which has # type: ignore). The # type: ignore comment on line 477 indicates the developer knew this was type-unsafe. Pyright also flags this. The fact that shutdown() is called before the None assignment doesn't matter for type inference of the attribute's overall type — the attribute CAN be None at other call sites. This is a net improvement: one genuine false positive removed, one arguably-correct new error added (co-reported by pyright).
Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign method and the method_defined_attributes merging logic) now accumulate all assignments to the same attribute and union their types. This directly caused: (1) the removal of the false positive in httpserver.py where self.address_family = None was flagged because the type is now correctly inferred as AddressFamily | None, and (2) the new error in netutil.py where self.executor's type now includes None from the cleanup assignment self.executor = None on line 477, making self.executor.shutdown() on line 476 flag a missing-attribute error on NoneType.

cki-lib (+10)

missing-attribute on dict: Calling list methods (.append, .index) on dict objects. Confirmed by pyright (4/4). These are plausible real issues if a variable is assigned both dict and list values across branches, but could also be false positives if the branches are mutually exclusive at runtime and the new union inference is too conservative.
bad-index on dict: Invalid indexing into dict[str, Unknown]. Confirmed by pyright (2/2). Could reflect real type confusion or could be a consequence of imprecise type narrowing after the inference change.
no-matching-overload with @_ types: setdefault called with partially-inferred types. Confirmed by pyright (2/2). The @_ types indicate incomplete inference, making these more likely to be at least partially attributable to inference gaps rather than purely code bugs.
bad-assignment with @_ types: list[dict[@, @]] assigned to dict[str, Unknown]. Confirmed by pyright (1/1). The @_ types suggest inference gaps that may contribute to this error.
bad-argument-type: str | Unknown passed where dict[str, Any] expected. Confirmed by pyright (1/1). The union with Unknown suggests imprecise inference, but if the variable can genuinely be a string, this would be a real issue.

Overall: The PR improved type inference for unannotated attributes by unioning assignments across branches. This revealed 10 new type errors in cki_lib/kcidb/file.py. All 10 errors are also reported by pyright, which provides some corroboration. However, without source code access, it is difficult to definitively determine whether these are genuine bugs or false positives from overly conservative inference. The 3 errors containing @_ types indicate incomplete type inference, which could contribute to false positives. The remaining 7 errors without @_ types are more likely to reflect real issues, but even these could result from the inference engine incorrectly unioning types across branches where runtime control flow would prevent the problematic combinations. The fact that none of the errors appear in mypy is notable — mypy may handle these patterns differently or may not infer types for unannotated attributes at all, making the comparison less informative.

Per-category assessment:

  • missing-attribute on dict (4 errors): Calling list methods (.append, .index) on dict objects. Confirmed by pyright (4/4). These are plausible real issues if a variable is assigned both dict and list values across branches, but could also be false positives if the branches are mutually exclusive at runtime and the inference is too conservative.

  • bad-index on dict (2 errors): Invalid indexing into dict[str, Unknown]. Confirmed by pyright (2/2). Could reflect real type confusion or could be a consequence of imprecise type narrowing.

  • no-matching-overload with @_ types (2 errors): setdefault called with partially-inferred types. Confirmed by pyright (2/2). The @_ types indicate incomplete inference, which makes these more likely to be false positives or at least partially attributable to inference gaps rather than purely code bugs.

  • bad-assignment with @_ types (1 error): list[dict[@, @]] assigned to dict[str, Unknown]. Confirmed by pyright (1/1). The @_ types again suggest inference gaps that may contribute to this error.

  • bad-argument-type (1 error): str | Unknown passed where dict[str, Any] expected. Confirmed by pyright (1/1). The union with Unknown suggests the inference may be imprecise, but if the variable can genuinely be a string, this would be a real issue.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign method and the merge logic in the method_attrs processing) now accumulate all assignments to the same attribute and union their types. The solver changes in pyrefly/lib/alt/class/class_field.rs (the loop over values with unions()) compute the union type. This likely changed the inferred type of attributes in cki_lib/kcidb/file.py from a single type (first-assignment-wins) to a union type, revealing downstream type mismatches.

mitmproxy (+1)

The new error correctly identifies that self._get_content_view_message can be None (assigned on line 347) and therefore Message | None is not assignable to Message on line 346. Pyright agrees. The Unknown component in the type is slightly noisy but the core issue is real — the code uses # type: ignore on line 347 precisely because the author knows this is a type violation. The new union-based inference is more accurate than the old first-assignment-wins model.
Attribution: The change to pyrefly/lib/binding/scope.rs in record_self_attr_assign() now accumulates all assignments to the same attribute within a method (and across methods via method_defined_attributes). Previously, only the first assignment was kept. The solver change in pyrefly/lib/alt/class/class_field.rs then unions all collected assignment types. This causes _get_content_view_message to be inferred as Message | Unknown | None (from the Message assignment in content_view and the None assignment in _get_content_view) instead of just Message.

steam.py (-2)

Both removed errors were false positives caused by pyrefly's old 'first-assignment-wins' inference model. In both cases, an unannotated attribute is assigned different types in try/except branches of __init__. The correct behavior (now implemented) is to infer the union type (datetime | None and AppPriceOverview | None respectively), which makes the None assignments valid. This matches how mypy and pyright handle unannotated constructor attributes.
Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() and the merge logic in method_defined_attributes processing) now accumulate all assignments to the same attribute within and across constructor methods into a Vec<ExprOrBinding>. The changes in pyrefly/lib/alt/class/class_field.rs (the DefinedInMethod arm of the solver) then union all collected assignment types instead of using only the first one. This directly fixes the false positives where the except-branch None assignment was flagged as incompatible with the try-branch type.

bokeh (-2)

Both removed errors were false positives caused by pyrefly's old 'first-assignment-wins' model for unannotated attributes. In Server.__init__, self._port and self._address are assigned in two branches of an if/else: line 433 sets both to None (via tuple unpacking self._address, self._port = None, None), while line 439 sets _port to int (from the return of bind_sockets) and line 440 sets _address to str | None (from opts.address, which is typed as str | None in _ServerOpts). The old model inferred both attributes as None (from the first textual assignment on line 433) and then flagged the else branch assignments as bad-assignment. The new model correctly unions the types across branches, yielding int | None for _port and str | None for _address, which matches the property return type annotations on lines 581 and 595. Removing these false positives is an improvement.
Attribution: The changes to pyrefly/lib/binding/scope.rs in record_self_attr_assign() (now accumulating multiple assignments via attr.0.push(value) instead of discarding subsequent ones) and the merging logic in method_defined_attributes processing (the new existing_values.extend(values) path) caused pyrefly to union the types from both branches. The solver changes in pyrefly/lib/alt/class/class_field.rs (iterating over values and calling unions(union_types, self.heap)) then compute the union type int | None for _port and str | None for _address, making the assignments on lines 439-440 compatible with the inferred attribute types.

jax (+4)

The new errors are correct: self.gfile has type <module> | None because the constructor assigns it to either the gfile module or None depending on a condition. The code guards access with if self.use_gfile: but this doesn't narrow self.gfile's type. Pyright agrees (4/4). The previous behavior (no error) was due to a bug where pyrefly only considered the first assignment, ignoring the None branch. The PR correctly fixes this by unioning all branch assignments, which reveals a genuine (if benign) type safety gap in the user's code.
Attribution: The changes to pyrefly/lib/binding/scope.rs in record_self_attr_assign() (accumulating assignments into a Vec) and the merge logic in the method_attrs processing block now union all branch assignments. Previously 'first-assignment-wins' would pick the non-None branch type; now the union correctly includes None. The solver changes in pyrefly/lib/alt/class/class_field.rs (unions(union_types, self.heap)) produce the final union type.

spark (+66)

missing-attribute on NoneType (65 errors): These errors span two classes. In SparkContext (context.py), __init__ delegates to _do_init() which assigns self._jsc. The PR's constructor prioritization ignores _do_init assignments, and since stop() assigns self._jsc = None, the type collapses to NoneType. In Broadcast (broadcast.py), the errors are about self._python_broadcast, which is assigned to None in one branch of __init__ and to a non-None value in another branch. The constructor prioritization change causes the type to resolve as NoneType, producing errors like NoneType has no attribute setupDecryptionServer. While pyright also flags these 65 errors, mypy does not, and the previous pyrefly behavior was better.
bad-argument-type with Unknown (1 error): The Unknown type in the error message indicates an inference degradation. This is pyrefly-only (neither mypy nor pyright flag it), suggesting the PR's changes worsened type resolution for this attribute.

Overall: The PR's constructor prioritization logic causes a regression for two related patterns. For SparkContext, __init__ delegates to _do_init(), which assigns self._jsc = jsc or self._initialize_context(...). Since _do_init is not a recognized constructor, its assignments are ignored. Meanwhile, stop() assigns self._jsc = None, and since __init__ itself doesn't assign _jsc, the type collapses to NoneType. This produces false-positive missing-attribute errors in context.py and other files that access self._jsc. For the Broadcast class in broadcast.py, the errors are about self._python_broadcast, which is assigned to None in the else branch of __init__ (line 139) and to a non-None value in the if branch (line 119). The constructor prioritization change likely causes the inferred type to collapse to NoneType (possibly by narrowing based on the None assignment path), producing errors like NoneType has no attribute setupDecryptionServer. Together these produce 65 false-positive missing-attribute errors. The 1 bad-argument-type with Unknown is also an inference degradation.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the method_defined_attributes and field merging logic around line 2874+) implement constructor prioritization over helper methods. This causes _do_init()'s assignment of self._jsc to be ignored in favor of stop()'s self._jsc = None, collapsing the inferred type to NoneType. The accumulator model in record_self_attr_assign() collects assignments within a method, but the cross-method merging discards helper method assignments when a constructor exists.

prefect (+5, -6)

Removed bad-assignment false positives: 3 removed bad-assignment errors on cache_policy in tasks.py were false positives caused by the old first-assignment-wins model. The PR's union-based inference correctly handles multi-branch assignments. This is an improvement.
Swapped database.py errors (lateral): 2 bad-argument-type and 1 no-matching-overload were removed and replaced by equivalent errors with slightly different inferred types (URL | dict[str, Any] | Unknown vs dict[str, Any] | Any). Same fundamental issue, different type representation. Neutral.
New missing-attribute on NotSet: 2 new missing-attribute errors claiming NotSet has no isolation_level. The code guards with is not NotSet but pyrefly (and pyright) don't narrow through the chained and. These are confirmed by pyright (2/2). While the code is safe at runtime, the type-level issue is real — the type system cannot prove NotSet is excluded at that point in the expression.

Overall: Net effect: 3 genuine false positives removed (bad-assignment on cache_policy), 3 errors swapped for equivalent errors with updated types (database.py), and 2 new errors that are confirmed by pyright (missing-attribute on NotSet). The missing-attribute errors on NotSet at line 973 appear to be real type issues — the type narrowing from is not NotSet may not fully eliminate NotSet from the union in the chained and expression, and pyright agrees. Overall this is a net improvement: the core bug fix (removing false bad-assignment errors) is correct, and the new errors are co-reported by pyright.

Per-category reasoning:

  • Removed bad-assignment false positives: 3 removed bad-assignment errors on cache_policy in tasks.py were false positives caused by the old first-assignment-wins model. The PR's union-based inference correctly handles multi-branch assignments. This is an improvement.
  • Swapped database.py errors (lateral): 2 bad-argument-type and 1 no-matching-overload were removed and replaced by equivalent errors with slightly different inferred types (URL | dict[str, Any] | Unknown vs dict[str, Any] | Any). Same fundamental issue, different type representation. Neutral.
  • New missing-attribute on NotSet: 2 new missing-attribute errors claiming NotSet has no isolation_level. The code guards with is not NotSet but pyrefly (and pyright) don't narrow through the chained and. These are confirmed by pyright (2/2). While the code is safe at runtime, the type-level issue is real — the type system cannot prove NotSet is excluded at that point in the expression.

Attribution: The changes to pyrefly/lib/binding/scope.rs (in record_self_attr_assign() and the merge logic in method_defined_attributes) changed attribute type inference from first-assignment-wins to union-based. This directly caused: (1) removal of false-positive bad-assignment errors in tasks.py, (2) changed inferred types for _rendered_url in database.py (producing slightly different error messages), and (3) changed inferred type of cache_policy revealing new missing-attribute errors on NotSet.

aiortc (+1)

The PR's new branch-unioning behavior correctly computes None | RemoteBitrateEstimator for self.__remote_bitrate_estimator, but the downstream type inference for remb = self.__remote_bitrate_estimator.add(...) degrades — the Unknown component in int | list[int] | Unknown indicates an inference failure. The code at line 476 is correct (properly guarded by None checks), and neither mypy nor pyright flag it. This is a false positive introduced by the PR's changes.
Attribution: The change to pyrefly/lib/binding/scope.rs in the record_self_attr_assign() and field merging logic now unions all assignments across constructor branches. For RTCRtpReceiver.__init__, self.__remote_bitrate_estimator is assigned None in one branch and RemoteBitrateEstimator() in another. The new unioning in pyrefly/lib/alt/class/class_field.rs (the unions(union_types, self.heap) call) produces a broader type for the estimator, which cascades into the return type of .add() becoming int | list[int] | Unknown instead of a clean type, triggering the false positive bad-argument-type on pack_remb_fci.

materialize (-3)

The analysis is factually correct. In each case:

  1. For invalid_value (line 49): The if add_invalid_value: branch assigns self.invalid_value = invalid_value (type str), and the else: branch assigns self.invalid_value = None. The correct inferred type is str | None. The old behavior inferred str from the first branch and then flagged the None assignment as incompatible.

  2. For concerned_expression_str (line 129): The if concerned_expression is not None: branch assigns self.concerned_expression_str to the result of to_sql(...) which returns str, and the else: branch assigns None. The correct inferred type is str | None.

  3. For concerned_expression_hash (line 130): The if branch assigns self.concerned_expression_hash = concerned_expression.hash() which returns int, and the else: branch assigns None. The correct inferred type is int | None.

The analysis correctly identifies these as false positives caused by a first-assignment-wins inference model, and correctly states that the union-based accumulator model resolves them. The description of the code behavior, types, and branching logic is all accurate.

Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() and the merge logic in method_defined_attributes) now accumulate all assignments to the same attribute within a method into a Vec<ExprOrBinding> instead of keeping only the first one. The solver changes in pyrefly/lib/alt/class/class_field.rs then union all collected assignment types together via unions(union_types, self.heap). This means self.invalid_value assigned str in one branch and None in another now correctly resolves to str | None instead of just str, eliminating the false bad-assignment errors.

more-itertools (+1, -1)

Removed false positive on seekable._cache: The old error flagged deque assignment to an attribute first assigned as list — a false positive from first-assignment-wins inference. The @_ type in the error message confirms inference failure. Removing this is an improvement.
New unsupported-operation on _concurrent_tee.link: The new union inference resolves self.link as list[None] (from the else branch of __init__), which is too narrow for this self-referential linked-list node. The code uses the list heterogeneously ([value, [None, None]]). While pyright also flags this, the root cause is that the attribute needs a type annotation for correct inference. This is a minor regression — a false positive from overly narrow inference on an unannotated heterogeneous list.

Overall: Net effect is mixed but leans toward improvement. The removed error was clearly a false positive (inference failure with @_ type). The new error is borderline — pyright also flags it, suggesting there's a genuine type issue with the untyped heterogeneous list pattern (link holds both values and sub-lists). However, the list[None] inference is overly narrow due to the self-referential class pattern, making this more of an inference limitation than a real bug being caught. On balance, removing one clear false positive while adding one debatable error (co-reported by pyright) is a slight net improvement.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign method now accumulates assignments into a Vec instead of discarding subsequent ones) and pyrefly/lib/alt/class/class_field.rs (the solver now unions all collected assignment types) caused both changes. The removal of the seekable._cache false positive is a direct benefit of unioning list and deque. The new _concurrent_tee.link error is a side effect of the list[None] inference from the else branch being too narrow for the self-referential linked-list pattern.

setuptools (+2)

The PR's new union-across-methods behavior for unannotated attributes causes false positives when the same attribute is intentionally reassigned to a different type in a later method (a common pattern in distutils Command classes). The self.compiler attribute is set to None in the unannotated initialize_options() method, and then reassigned to a Compiler object (the return value of new_compiler()) in run(). Because initialize_options lacks type annotations, pyrefly infers Unknown for the attribute set there. When pyrefly unions the types across methods, it produces Compiler | Unknown. At the point where self.compiler is passed as the compiler=self.compiler argument to new_compiler() on line 91/327, pyrefly sees the unioned type Compiler | Unknown from the reassignment on the same line (or from the combined attribute type), which is not assignable to the expected parameter type str | None. The Unknown component in the union (originating from the unannotated initialize_options method) combined with the Compiler type (from the new_compiler() return value assignment) creates a type that doesn't match str | None. Before the reassignment on line 91/327, self.compiler should contextually be None or str | None (its value before run() executes), but pyrefly's union-across-methods approach conflates the pre-assignment and post-assignment types. Neither mypy nor pyright report these errors, as they handle unannotated method attribute inference differently.
Attribution: The change to pyrefly/lib/binding/scope.rs in record_self_attr_assign() now accumulates all assignments to the same attribute within a method (and across methods via method_defined_attributes merging in the same file). The change to pyrefly/lib/alt/class/class_field.rs in the DefinedInMethod arm unions all collected assignment types. Together, these cause self.compiler to be inferred as Compiler | Unknown (union of None from initialize_options and Compiler from run), which then fails the str | None parameter check.

comtypes (-1)

The analysis is factually correct. The attribute __keepref is indeed assigned in two mutually exclusive elif branches within the _set_value method: once at line 376 where value is of type _CArgObject (inside the elif isinstance(value, _CArgObject): branch), and once at line 398 where value is of type _Pointer (inside the elif isinstance(value, _Pointer): branch). The old type checker behavior of locking the type to _CArgObject from the first textual assignment and then flagging the second assignment of _Pointer[Unknown] as incompatible was indeed a false positive. The new accumulator-based model that unions the types from all assignments to produce _CArgObject | _Pointer[Unknown] correctly handles both assignments. The description of the branches being mutually exclusive (they are elif branches) and the characterization of the fix as an improvement in type inference accuracy are all correct.
Attribution: The change to record_self_attr_assign() in pyrefly/lib/binding/scope.rs now accumulates all assignments to the same attribute within a method (via attr.0.push(value) instead of ignoring subsequent assignments). The change to class_field.rs then unions all collected assignment types (let mut value_ty = unions(union_types, self.heap)). Together, these changes mean __keepref is now inferred as _CArgObject | _Pointer[Unknown] instead of just _CArgObject, eliminating the false bad-assignment error on line 398.

operator (-1)

The error on line 3159 was caused by line 3154 assigning the class object _TestingModelBackend (type type[_TestingModelBackend]) to self._backend, which then made the instance assignment on line 3159 incompatible. Line 3154 is almost certainly a bug in the source code — the _check_connection method (line 3171) calls self._backend._can_connect(self), an instance method, confirming _backend should hold an instance. The old type checker flagged the inconsistency (albeit on the wrong line — 3159 instead of 3154). The new union-based inference widens the type to type[_TestingModelBackend] | _TestingModelBackend, which makes both assignments valid but masks the accidental class assignment on line 3154. Whether this is an improvement depends on perspective: the old error was technically a true positive identifying a real code inconsistency, just reported on the second assignment rather than the first erroneous one.
Attribution: The change to analyze_class_field_value in pyrefly/lib/alt/class/class_field.rs and the accumulator logic in pyrefly/lib/binding/scope.rs (record_self_attr_assign()) now collects all assignments to the same attribute within a constructor and unions their types, rather than locking onto the first assignment. This directly removes the false positive bad-assignment error.

mongo-python-driver (+2)

These errors are legitimate type issues that pyright also flags (as indicated by the [mypy: no, pyright: yes] annotation). In the base class AsyncChangeStream/ChangeStream, the __init__ method accepts target as a union including AsyncMongoClient[_DocumentType]/MongoClient[_DocumentType]. The self._target attribute is assigned either from target.with_options(...) (line 137, which has a # type: ignore) or directly from target (line 141). This means self._target can be any of the three types in the union.

In _run_aggregation_cmd, self._aggregation_command_class returns type[_AggregationCommand] at the base class level. When self._target is passed as the first argument to _AggregationCommand.__init__, the type checker correctly identifies that self._target could be an AsyncMongoClient/MongoClient, which is not assignable to the target parameter of _AggregationCommand.__init__ that only accepts AsyncCollection[Any] | AsyncDatabase[Any] (or the synchronous equivalents).

The with_options branch also contributes additional type variants (e.g., AsyncCollection[Any]) to the union because with_options on a Collection returns Collection[Any] when called with document_class=RawBSONDocument.

The code works correctly at runtime because subclasses (AsyncCollectionChangeStream, AsyncDatabaseChangeStream, etc.) narrow _target to specific types and override _aggregation_command_class to return the appropriate subclass. But at the base class level, the type checker correctly identifies that the full union type of self._target includes MongoClient/AsyncMongoClient, which is not accepted by _AggregationCommand.__init__. This is a genuine type inconsistency in the base class that both pyrefly and pyright flag.

Attribution: The change in pyrefly/lib/binding/scope.rs (the record_self_attr_assign method and the merge logic in method_defined_attributes processing) now accumulates all assignments to self._target across the if/else branches in __init__. The solver in pyrefly/lib/alt/class/class_field.rs then unions these types via unions(union_types, self.heap). Previously, only one branch's type was used (first-assignment-wins). Now the union includes AsyncMongoClient[_DocumentType] from the else branch AND Unknown from the type-ignored if branch, making the type too broad for _AggregationCommand.__init__.

pandera (+2, -3)

Net improvement: 3 clear false positives removed, 2 new errors added that are co-reported by pyright. The removed errors were unambiguously wrong — the old checker was treating the unannotated attribute arg (initialized to None on line 237 via self.origin = self.arg = None) as having a fixed type of None, when in fact it is reassigned to various types throughout _parse_annotation. The 3 removed bad-assignment errors at lines 251, 277, and 280 are genuine false positives.

The 2 new errors are more nuanced:

  1. Line 696 (model.py): inner has the broader inferred type type[Any] | tuple[Any, ...] | Any | None from ann_info.arg. The code guards this with _is_dataset_model(inner) on line 695, which internally checks isinstance(cls, type), so at runtime inner is guaranteed to be type[Any]. However, since _is_dataset_model is a helper function rather than a direct isinstance check, pyrefly (and pyright) cannot narrow the type through it. This is a false positive caused by limited type narrowing through helper functions, not a genuine type incompatibility.

  2. Line 259 (common.py): self.arg has inferred type tuple[Any, ...] | Any. The Any component is assignable to (...) -> Any in most type systems, but the tuple[Any, ...] component is not callable and thus not assignable to inspect.signature's parameter type. At runtime, the code is guarded by if self.arg is not None and the broader context ensures self.arg is typically a type/class at this point. This error reflects the wider inferred union type surfacing a real type incompatibility in the static analysis, though at runtime the code works correctly.

Overall, the type inference for arg is more correct — it should not be locked to None. The 3 removed errors are clear false positives. The 2 new errors are consequences of the broader inferred type and limitations in type narrowing; they are co-reported by pyright but represent static analysis limitations rather than runtime bugs.

Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() accumulating assignments into a Vec<ExprOrBinding> instead of discarding subsequent ones) and pyrefly/lib/alt/class/class_field.rs (unioning all collected assignment types via unions(union_types, self.heap)) caused both the removal of the false bad-assignment errors and the introduction of the new bad-argument-type errors due to the broader inferred union type.

vision (-1)

This is a clear improvement. The RandomErasing.__init__ method assigns self.value in four different branches (lines 78-85) without an explicit type annotation. The old pyrefly behavior locked onto the first textual assignment (self.value = [float(value)] on line 79) and inferred list[float], then flagged the self.value = None assignment on line 81 as incompatible. This was wrong — the attribute is intentionally None when the erasing mode is 'random', and the code explicitly checks for None later (lines 101, 123). The new behavior correctly unions all branch types for unannotated attributes, eliminating this false positive.
Attribution: The change to record_self_attr_assign() in pyrefly/lib/binding/scope.rs now accumulates all assignments to the same attribute within a method (via attr.0.push(value)) instead of ignoring subsequent assignments. The solver changes in pyrefly/lib/alt/class/class_field.rs then iterate over all collected values, solve each one individually, and union them together (let mut value_ty = unions(union_types, self.heap)). This directly fixes the 'first-assignment-wins' behavior that caused the false positive on line 81.

zulip (-2)

The analysis is factually correct. Looking at the write_to_audit_log method in RealmBillingSession (lines 4417-4438), the audit_log_data dict is initialized with three key-value pairs: "realm" (type Realm), "event_type" (type int), and "event_time" (type datetime). Without explicit type annotation, pyrefly would infer the dict's value type from these initial assignments as Realm | int | datetime. Then on line 4433, extra_data of type dict[str, Any] is assigned to the "extra_data" key, and on line 4436, self.user of type UserProfile is assigned to the "acting_user" key. Both of these types fall outside the initially inferred Realm | int | datetime value type, producing the two reported errors. The PR fix of unioning all assignment types would correctly broaden the inferred value type to Realm | int | datetime | dict[str, Any] | UserProfile, eliminating both false positives.
Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically the record_self_attr_assign method and the method_defined_attributes merging logic) now accumulate all assignments to the same attribute within a method and union their types. Previously, pyrefly used a 'first-assignment-wins' model that locked the dict's value type to Realm | datetime | int from the initial literal. Now it unions all assignments, producing a broader type that correctly includes dict[str, Any] and UserProfile, eliminating the false bad-assignment errors.

sockeye (+4, -3)

Removed bad-assignment false positive (unk_id_source): The if/else branches assign int vs None to the same attribute. The old behavior locked onto int and flagged None. The new union behavior correctly infers int | None. This removal is an improvement.
Removed bad-argument-type false positives (bos_id/eos_id): The old errors used Unknown | None due to imprecise inference. Removing these is an improvement in type precision.
New tuple[Any, ...] vs list[str] errors (checkpoint_decoder.py): These arise because self.inputs_sentences is reassigned on line 120 via list(zip(self.inputs_sentences)) with a # type: ignore comment. The zip() call produces tuples, so list(zip(...)) produces list[tuple[Any, ...]]. The union behavior now propagates this tuple type to call sites where make_input_from_multiple_strings expects list[str]. Neither mypy nor pyright flag these. These are false positives — a regression.
New int | None vs int error (data_io.py:1281): self.bos_id is None | int due to branch union (set to None initially, then to C.BOS_ID when vocabulary is not None). The insert call is guarded by self.add_bos which is only True when vocabulary is not None (meaning bos_id is int). Pyrefly can't narrow this correlation. Neither mypy nor pyright flag this. This is a false positive — a regression.

Overall: The PR introduces 4 new false positives and removes 3 old false positives, making it slightly negative overall.

For the removed errors:

  • Removed bad-assignment false positive (unk_id_source): The if/else branches assign int vs None to the same attribute. The old behavior locked onto int and flagged None. The new union behavior correctly infers int | None. This removal is an improvement.
  • Removed bad-argument-type false positives (bos_id/eos_id): The old errors used Unknown | None due to imprecise inference. Removing these is an improvement in type precision.

For the new errors:

  • New tuple[Any, ...] vs list[str] errors (checkpoint_decoder.py): These arise because self.inputs_sentences is reassigned on line 120 via list(zip(*self.inputs_sentences)) with a # type: ignore comment. The zip(*) call produces tuples, so list(zip(...)) produces list[tuple[Any, ...]]. The union behavior now propagates this tuple type to call sites where make_input_from_multiple_strings expects list[str]. Neither mypy nor pyright flag these. These are false positives — a regression.
  • New int | None vs int error (data_io.py:1281): self.bos_id is None | int due to branch union (set to None initially, then to C.BOS_ID when vocabulary is not None). The insert call is guarded by self.add_bos which is only True when vocabulary is not None (meaning bos_id is int). Pyrefly can't narrow this correlation. Neither mypy nor pyright flag this. This is a false positive — a regression.

The overall change is mixed but slightly negative due to the 4 new false positives vs 3 removed false positives.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign and finish_class_scope methods) now accumulate all assignments to the same attribute and union them in pyrefly/lib/alt/class/class_field.rs (analyze_class_field_value loop). This union behavior correctly fixes the branch-union case (removing 3 false positives) but introduces 4 new false positives where the union includes types from # type: ignored reassignments or where correlated boolean guards would narrow the type.

bandersnatch (+3, -4)

unsupported-operation: type[Any] is not subscriptable: These 3 new errors are false positives — 0/3 co-reported by mypy/pyright. The PR's new union inference for unannotated constructor attributes is producing incorrect type[Any] types that then trigger spurious subscriptability errors.
Removed mirror.py errors (bad-return, bad-specialization, bad-argument-type): These were true positives catching real bugs: synced_serial is int | None at class level, but find_target_serial() returns int and passes self.synced_serial to max(). The PR's union logic changed how the attribute type is resolved, losing these valid error detections.
Removed bad-assignment in storage.py: This was a false positive — BandersnatchConfig is a subclass of ConfigParser, so assigning it to self.configuration is valid. Removing this error is an improvement.

Overall: Net effect is negative: 3 new false positive errors (pyrefly-only, no mypy/pyright agreement) and 4 removed errors that were catching real type issues (e.g., int | None passed where int expected). The new union inference model introduces spurious type[Any] is not subscriptable errors while simultaneously losing detection of genuine None-safety issues.

Per-category reasoning:

  • unsupported-operation: type[Any] is not subscriptable: These 3 new errors are false positives — 0/3 co-reported by mypy/pyright. The PR's new union inference for unannotated constructor attributes is producing incorrect type[Any] types that then trigger spurious subscriptability errors.
  • Removed mirror.py errors (bad-return, bad-specialization, bad-argument-type): These were true positives catching real bugs: synced_serial is int | None at class level, but find_target_serial() returns int and passes self.synced_serial to max(). The PR's union logic changed how the attribute type is resolved, losing these valid error detections.
  • Removed bad-assignment in storage.py: This was a false positive — BandersnatchConfig is a subclass of ConfigParser, so assigning it to self.configuration is valid. Removing this error is an improvement.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign and method_defined_attributes merge logic) and pyrefly/lib/alt/class/class_field.rs (the union of all collected assignment types) caused both the new false positive unsupported-operation errors and the loss of the true positive errors in mirror.py.

ignite (+2)

These are genuine type issues in the ignite codebase. The variable priority is assigned from two branches: (1) when self.score_function is not None (line 467): priority = self.score_function(engine) where score_function is typed as Callable[[Engine], int | float], giving type int | float. The subsequent isinstance(priority, numbers.Number) check on line 468 is a negative guard (raises if NOT a Number), which narrows the type to Number (since int and float are subclasses of numbers.Number). (2) when self.score_function is None (lines 470-473): priority = global_step, where global_step comes from self.global_step_transform(engine, engine.last_event_name) — an untyped Callable returning Unknown — or from engine.state.get_event_attrib_value(...). After merging both branches, the resulting type of priority is Number | int | Unknown, which is not assignable to int | float as required by _compare_fn (line 475) and Item.__new__ (line 516). numbers.Number is an abstract base class and is not equivalent to int | float in the type system — it's a broader type. The Unknown from the untyped callable also contributes to the mismatch. These are genuine type errors that the checker correctly identifies.
Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() and the merge logic in method_defined_attributes processing) now accumulate all assignments to the same attribute and union their types. This caused priority to be inferred as Number | int | Unknown (union of all branches) instead of a narrower type from just the first assignment. The solver changes in pyrefly/lib/alt/class/class_field.rs (unions(union_types, self.heap)) then produce the wider union type.

websockets (-3)

These three removed errors are all false positives caused by pyrefly's old 'first-assignment-wins' inference model for unannotated attributes. For example, in connection.py line 66-69: if isinstance(max_queue, int) or max_queue is None: self.max_queue_high, self.max_queue_low = max_queue, None sets max_queue_low to None in one branch, while else: self.max_queue_high, self.max_queue_low = max_queue unpacks a tuple[int | None, int | None], yielding int | None for max_queue_low. The correct inferred type is int | None, not just None. The old pyrefly locked onto the first textual assignment (None) and rejected the second. The same pattern applies to write_limit_low and max_fragment_size. Removing these false positives is an improvement.
Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() and the merge logic in the method_attrs processing) now accumulate all assignments to the same attribute within and across constructor methods into a Vec<ExprOrBinding> instead of keeping only the first. The solver changes in pyrefly/lib/alt/class/class_field.rs (the DefinedInMethod arm) then union all collected assignment types. This directly fixes the false positives where the first branch's None assignment was locking the attribute type.

dd-trace-py (+1, -4)

This PR fixes a real inference bug where pyrefly used 'first-assignment-wins' for unannotated attributes in constructors. The 3 removed errors were false positives caused by this bug:

  1. stack_context.py:68 - self.new_contexts is assigned a tuple on line 54 in __enter__, then assigned None on line 68 in __exit__. Under 'first-assignment-wins', the type was locked to tuple[Unknown, Self@TracerStackContext], making the None assignment a false positive. With the fix, the type becomes tuple[...] | None, and the assignment is valid.

  2. base.py:503 - self._flush_thread is assigned a Thread in one place and None in another. Same pattern - the old behavior locked the type to Thread, making None assignment a false positive.

  3. yacc.py:1437 - self.prod is assigned a list in one place and a tuple in another. The old behavior locked the type to list[Unknown], making the tuple assignment a false positive.

The 1 new error at subprocess/patch.py:327 replaces the old error at the same location. The attribute self.arguments is assigned [] (line 250), tokens[1:] (line 264, a list[str]), tokens[idx + 1:] (line 297, a list[str]), and from self._cache_entry.arguments which is Optional[list] (i.e., list[Unknown] | None). Under the old behavior, the type was inferred as list[Unknown] (first assignment wins from []), giving list[Unknown] | None when combined with the cache path. With the fix, all assignments are considered, yielding list[str] | list[Unknown] | None. Both old and new errors are true positives - None cannot be passed to deque() which expects Iterable[T]. The new error message is more accurate because it reflects the list[str] component from the non-cache branches.

Net result: 3 false positives removed, 1 error improved in accuracy. This is clearly an improvement.

Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() accumulating assignments into a Vec instead of discarding them, and the merge logic in method_defined_attributes) combined with pyrefly/lib/alt/class/class_field.rs (unioning all collected assignment types via unions()) caused the removed false positives. The new error's type changed from list[Unknown] | None to list[str] | list[Unknown] | None because the union now includes concrete list[str] assignments from other branches.

pandas (+9, -3)

bad-argument-type (7 new): All 7 are pyrefly-only false positives. The widened union types from the new inference cause type mismatches. For example, at line 245 in base_parser.py, names.insert(single_ic, single_ic)names is a list that gets inferred with element type including tuple[Any, ...] (from the zip result), and single_ic is int. The error reports int | integer not assignable to tuple[Any, ...], which is technically correct for the widened type but a false positive since the insert is only reached when single_ic is an int index being inserted at the right position.
bad-return (1 new, 1 removed): The removed error (Index | None not assignable to Index) was arguably a true positive since _categories can genuinely be None, but the property annotation says Index — this is a pre-existing annotation issue in pandas. The new error (Index | Any | None not assignable to Index) is a false positive: Index | Any | None simplifies to Any in Python's type system, and Any is assignable to any type including Index. The Any likely comes from __setstate__ using state.pop('categories', None) which returns Any. Net: slight regression since the new error is clearly wrong while the old one was at least debatable.
bad-assignment (2 removed): Both were false positives from the old 'first-assignment-wins' model. For example, in accessor.py line 201, self._index = data.index was erroring because _index was typed as None from its initialization at line 199. With union inference, _index is correctly typed as None | Index. Removing these is correct — improvement.
missing-attribute on list (1 new): Pyrefly-only false positive. The new union inference likely widened a variable's type to include list (in addition to str), causing a spurious startswith missing attribute error, since list does not have a startswith method while str does.

Overall: The PR correctly fixes 3 false positives by moving from 'first-assignment-wins' to union-based inference. However, it introduces 9 new false positives (all pyrefly-only) because the union inference is too aggressive — it unions assignments from methods like __setstate__ that produce overly broad types, leading to spurious bad-argument-type, bad-return, and missing-attribute errors. Net: 3 improvements, 9 regressions = net regression.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign and finalize_class_fields logic) now accumulate and union all assignments to the same attribute. This correctly fixes the false positives (removed errors) but also widens inferred types in ways that create new false positives — e.g., unioning types from __setstate__ with __init__ produces overly broad types like Index | Any | None that then fail return type checks. The class_field.rs changes in analyze_class_field_value union loop produce these broader types.

meson (+16, -10)

New bad-argument-type errors: The union-based inference now correctly includes the Compiler type from the successful dictionary lookup branch, producing Compiler | Unknown | None instead of the old Unknown | None. However, this wider union still doesn't match expected parameter types like Compiler | MissingCompiler. These are false positives because the code is correct — self.clib_compiler is set to a Compiler instance from compilers[lang] or remains None. The Unknown component comes from pyrefly's inability to fully resolve the dictionary value type. All pyrefly-only (0/10 in mypy, 0/10 in pyright).
New missing-attribute on NoneType: The wider union inference now includes None in attribute types where narrowing should exclude it (e.g., exe_wrapper after a need_exe_wrapper guard check that implies has_exe_wrapper() returned True). False positives — the code correctly guards against None before accessing attributes like get_command(). All pyrefly-only (0/3 in mypy, 0/3 in pyright).
New no-matching-overload and bad-return: Cascade effects of wider union inference producing list[str | None] types that fail str.join overload matching and return type checks. False positives. All pyrefly-only.
Removed bad-assignment errors: Genuine false positives removed. The old model locked onto the first assignment's type and flagged subsequent assignments in different branches. The PR correctly unions branch types. This is the intended improvement.
Removed bad-argument-type errors: These were also false positives that are now replaced by different false positives with slightly different type representations. The old errors had Unknown | None; the new errors have Compiler | Unknown | None. The PR now correctly captures the Compiler component from the successful branch, but the Unknown and None components still prevent type compatibility.

Overall: The PR fixes 3 genuine false positives (bad-assignment from first-assignment-wins) and removes 7 bad-argument-type errors that were also false positives. However, it introduces 16 new false positives (all pyrefly-only, 0/16 confirmed by mypy or pyright). The wider union inference produces types with Compiler, Unknown, and None components that cascade into bad-argument-type, missing-attribute, no-matching-overload, and bad-return errors on correct code. Net effect: +6 total errors (16 added - 10 removed).

Per-category reasoning:

  • New bad-argument-type errors: The union-based inference now correctly includes the Compiler type from the successful dictionary lookup branch, producing Compiler | Unknown | None instead of the old Unknown | None. However, this wider union still doesn't match expected parameter types like Compiler | MissingCompiler. These are false positives because the code is correct — self.clib_compiler is set to a Compiler instance from compilers[lang] or remains None. The Unknown component comes from pyrefly's inability to fully resolve the dictionary value type. All pyrefly-only.

  • New missing-attribute on NoneType: The wider union inference now includes None in attribute types where narrowing should exclude it (e.g., exe_wrapper after a need_exe_wrapper guard check that implies has_exe_wrapper() returned True). False positives — the code correctly guards against None before accessing attributes like get_command().

  • New no-matching-overload and bad-return: Cascade effects of wider union inference producing list[str | None] types that fail str.join overload matching and return type checks. False positives.

  • Removed bad-assignment errors: Genuine false positives removed. The old model locked onto the first assignment's type and flagged subsequent assignments in different branches. The PR correctly unions branch types. This is the intended improvement.

  • Removed bad-argument-type errors: These were also false positives that are now replaced by different false positives. The old errors had Unknown | None types; the new errors have Compiler | Unknown | None types. The underlying issue is the same — pyrefly's inference includes Unknown and None components that prevent type compatibility — but the specific type representations differ because the PR now correctly captures the Compiler component from the successful branch.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign and finish_class_body functions) accumulate multiple assignments into a Vec<ExprOrBinding> and union them in pyrefly/lib/alt/class/class_field.rs (analyze_class_field_value loop). This wider union inference causes attributes like clib_compiler to be inferred as Compiler | Unknown | None instead of Unknown | None, and list attributes to include None variants, producing the new false positive errors.

core (+2, -6)

Removed bad-assignment errors (influxdb, kankun, neurio): These were false positives from the first-assignment-wins model. The code legitimately assigns different types in different constructor branches (e.g., self.data is assigned InfluxFluxSensorData in one branch and InfluxQLSensorData in another). Removing them is an improvement.
Changed bad-argument-type errors (doods, modbus): The old errors reported simpler types (e.g., list[int]), the new errors report union types (e.g., list[int] | list[Unknown]). For the doods file, this is about self._area being a list passed where draw_box expects a tuple[float, float, float, float] — a real list-vs-tuple mismatch. For the modbus file at line 99, convert[state.state] is passed to _set_attr_state(value: str | bool | int), but the convert dict includes None values (for STATE_UNAVAILABLE and STATE_UNKNOWN), so the value could be None which doesn't match the parameter type. The union types in the new errors reflect constructor branch unioning producing noisier but still valid error messages. Both old and new errors flag real issues. Net neutral — same bugs caught with slightly different type representations.

Overall: Net result: 6 errors removed (4 false positive bad-assignments + 2 old bad-argument-type messages), 2 errors added (updated versions of bad-argument-type errors). The core fix correctly addresses issue #1159 — constructor branch unioning eliminates false positives. The new errors are slightly noisier (Unknown in union) but still flag real issues. Overall this is a clear improvement.

Per-category reasoning:

  • Removed bad-assignment errors (influxdb, kankun, neurio): These were false positives from the first-assignment-wins model. The code legitimately assigns different types in different constructor branches (e.g., self.data is assigned InfluxFluxSensorData in one branch and InfluxQLSensorData in another). Removing them is an improvement.
  • Changed bad-argument-type errors (doods, modbus): The old errors reported simpler types (e.g., list[int]), the new errors report union types (e.g., list[int] | list[Unknown]). For the doods file, this is about self._area being a list passed where draw_box expects a tuple[float, float, float, float] — a real type mismatch (list vs tuple). For the modbus file, the error at line 99 involves convert[state.state] being passed to _set_attr_state(value: str | bool | int), where the convert dict contains None values (for STATE_UNAVAILABLE and STATE_UNKNOWN keys), so the looked-up value could be None which is not assignable to str | bool | int. The union type in the new error likely reflects the constructor branch unioning producing a noisier but still valid type. Both old and new errors flag real issues. Net neutral — same bugs caught with slightly different type representations.

Attribution: The changes in pyrefly/lib/binding/scope.rs (the record_self_attr_assign function now accumulates assignments into a Vec instead of ignoring subsequent ones) and pyrefly/lib/alt/class/class_field.rs (the solver now unions all collected assignment types) directly caused: (1) removal of false bad-assignment errors by unioning branch types, and (2) the slightly changed error messages where inferred types now include Unknown from additional branches.

sphinx (+28)

missing-attribute on Config (19 errors): Sphinx's Config class dynamically registers attributes via app.add_config_value(). These attributes (napoleon_preprocess_types, napoleon_custom_sections, etc.) all exist at runtime. Neither mypy nor pyright flags these. The PR's union-based inference likely degraded the type of self.config or related objects, causing pyrefly to lose visibility into Config's attributes. These are false positives.
bad-argument-type with Unknown (7 errors): The error messages show types like BuiltinTemplateLoader | Unknown being rejected where BaseRenderer | None is expected. BuiltinTemplateLoader IS a BaseRenderer subclass. The Unknown component was introduced by the PR's new union-all-assignments logic — when some assignments resolve to Unknown (from untyped code), the union pollutes the type. These are false positives caused by inference degradation.
missing-attribute on SandboxedEnvironment (1 error): extract_translations is a method on Jinja2's Environment class, which SandboxedEnvironment inherits from. This is a valid attribute access. The error is likely caused by the PR's changes degrading the inferred type of self.templates.environment. False positive.
not-iterable object (1 error): A type that should be more specific was widened to object due to the union changes, making pyrefly think it's not iterable. This is a false positive from inference degradation.

Overall: All 28 new errors are pyrefly-only false positives caused by the PR's change from 'first-assignment-wins' to 'union all assignments' for unannotated attributes. The key issue is that when multiple assignments exist (including ones that resolve to Unknown due to dynamic/untyped code), the union now includes Unknown, which degrades type information. Specifically:

  1. missing-attribute on Config (19 errors): Sphinx's Config class uses dynamic attribute registration via app.add_config_value(). Attributes like napoleon_preprocess_types, napoleon_custom_sections, etc. are all valid runtime attributes. These were likely accessible before because self.config had a concrete Config type, but the new union-based inference may have degraded the type.

  2. bad-argument-type (7 errors): The error Argument 'BuiltinTemplateLoader | Unknown' is not assignable to parameter 'renderer' with type 'BaseRenderer | None' shows that self.templates is now inferred as BuiltinTemplateLoader | Unknown instead of just BuiltinTemplateLoader. The Unknown component comes from the union of multiple assignments. BuiltinTemplateLoader is a valid BaseRenderer subclass, so this is a false positive caused by the Unknown pollution.

  3. missing-attribute on SandboxedEnvironment (1 error): extract_translations is a method on Jinja2's Environment class. The SandboxedEnvironment inherits from Environment and has this method. This is likely a false positive from degraded type inference.

  4. not-iterable on object (1 error): Type object is not iterable — this suggests a type that should be more specific was widened to object due to the union changes.

The PR's intent was to fix issue #1159 by unioning branch types, but it has the side effect of introducing Unknown into unions when some assignments are untyped, which then cascades into false positives.

Attribution: The PR changed pyrefly/lib/binding/scope.rs to accumulate all assignments to the same attribute within a method (the record_self_attr_assign function now pushes to a Vec<ExprOrBinding> instead of keeping only the first assignment). It also changed pyrefly/lib/alt/class/class_field.rs to union all collected assignment types. This means that for classes like Builder where self.templates might be assigned in different branches or methods, the inferred type now becomes a union that includes Unknown (from untyped/dynamic assignments). The BuiltinTemplateLoader | Unknown type in the bad-argument-type errors is a direct consequence — previously pyrefly would have inferred a more specific type from the first assignment, but now it unions all assignments including ones that resolve to Unknown. Similarly, the Config missing-attribute errors likely stem from the changed inference affecting how self.config is typed (now including Unknown in the union), which prevents attribute resolution. The SandboxedEnvironment missing-attribute error follows the same pattern — self.templates.environment now resolves to SandboxedEnvironment (possibly via a union) where extract_translations isn't visible.

parso (+4)

These errors are legitimate type issues. The parent_context parameter of _Context.__init__ has a default of None, so context.parent_context has type _Context | None. At line 431, self.context = context.parent_context assigns a value of type _Context | None to self.context. This causes the type checker to infer self.context as _Context | None throughout the class (since instance attributes are typed based on all assignments). Consequently, accessing .add_block (line 415), .blocks (line 416), .add_context (line 470), and .finalize (line 489) on self.context is flagged because NoneType has none of these attributes. Pyright confirms all 4 errors. The code works at runtime because structural invariants ensure parent_context is not None when these code paths execute (e.g., a classdef/funcdef node always has a parent context), but the type system cannot verify these invariants. The new union-based inference correctly identifies that self.context has type _Context | None, and accessing .add_block, .blocks, .add_context, .finalize on None is indeed invalid.
Attribution: The changes in pyrefly/lib/binding/scope.rs (record_self_attr_assign() and the merge logic) now accumulate all assignments to self.context into a vector and union their types. Previously, only the first assignment's type was used (_Context). Now the union includes the context.parent_context assignment (line 431) which has type _Context | None, making the overall type _Context | None. This causes the 4 new missing-attribute errors.

pydantic (-2)

The removed errors were false positives caused by pyrefly's old 'first-assignment-wins' model for unannotated attributes. The __return_pydantic_validator__ attribute in ValidateCallWrapper is assigned in three different branches of _create_validators(): an async wrapper (line 125), validator.validate_python (line 127), and None (line 129). The old model locked the type to the first branch's type and flagged the other two as incompatible. The new union-based inference correctly computes the attribute's type as the union of all branch assignments, eliminating these spurious errors.
Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() which now accumulates multiple assignments via attr.0.push(value) instead of ignoring subsequent ones, and the merge logic in the method_attrs.[into_iter()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/scope.rs).for_each block) along with the solver changes in pyrefly/lib/alt/class/class_field.rs (which now iterates over values and unions their types via unions(union_types, self.heap)) are what fixed these false positives. Previously, only the first assignment was recorded, causing subsequent assignments to be checked against the first assignment's type.

discord.py (-3)

All three removed errors were false positives caused by pyrefly's old 'first-assignment-wins' inference model for unannotated class attributes. The code in discord.py intentionally assigns None to _emoji and _timestamp in certain branches, and the corresponding property getters return Optional[...] types, confirming these attributes are meant to hold None. The PR's accumulator-based model correctly unions all assignment types, eliminating these spurious errors.
Attribution: The changes in pyrefly/lib/binding/scope.rs (specifically record_self_attr_assign() and the merge logic in the method_attrs processing) changed from a 'first-assignment-wins' model to accumulating all assignments into a Vec<ExprOrBinding>. The solver changes in pyrefly/lib/alt/class/class_field.rs (the DefinedInMethod arm) now iterates over all collected values, solves each one, and unions them via unions(union_types, self.heap). This means attributes assigned different types across branches or different methods now get a union type instead of being locked to the first assignment's type, which eliminates the false bad-assignment errors.

PyGithub (+1)

The new error correctly identifies a real type inconsistency. In the else branch (line 418), o.hostname is str | None, so list({o.hostname, ...}) produces list[str | None]. The union with the if-branch's list[str] gives list[str | Unknown | None] | list[str], which is not assignable to list[str] due to list invariance. Pyright agrees this is an issue. The # type: ignore on line 418 was masking this problem. This is a genuine type safety concern that the PR's improved inference now surfaces.
Attribution: The change to pyrefly/lib/alt/class/class_field.rs in the AnswersSolver implementation now unions all collected assignment types via unions(union_types, self.heap) instead of using only the first assignment. Combined with the accumulation logic in pyrefly/lib/binding/scope.rs (record_self_attr_assign and method_defined_attributes), this causes self.__domains to be inferred as the union of both branches (list[str] | list[str | None]) rather than just the first branch's type.

➖ Neutral (3)

freqtrade (+2, -2)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

pwndbg (+3, -3)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

dragonchain (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

Suggested fixes

Summary: The PR's union-all-assignments model correctly fixes first-assignment-wins false positives in constructors, but causes ~80+ pyrefly-only false positives by (1) unioning assignments from non-constructor methods into the attribute type, polluting it with Unknown/unrelated types, (2) not respecting # type: ignore on assignments that widen types, and (3) triggering bad-override-mutable-attribute on internal implementation attributes.

1. In the method_defined_attributes merging logic in pyrefly/lib/binding/scope.rs (around line 2874+), when merging attributes across methods, only union assignments from recognized constructor methods (e.g., init, new, post_init). For non-constructor methods, do NOT merge their assignments into the attribute's values vec — instead, keep the existing constructor-prioritized behavior where non-constructor assignments are discarded if a constructor already defines the attribute. Currently the code extends existing_values with values from all methods; it should skip extending when the existing definition came from a constructor and the new one comes from a non-constructor.

Files: pyrefly/lib/binding/scope.rs
Confidence: high
Affected projects: beartype, sphinx, meson, pandas, setuptools, sockeye, bandersnatch, aiortc
Fixes: bad-argument-type, missing-attribute, bad-return, unsupported-operation, no-matching-overload, not-iterable
The beartype regression (object polluting union from repr method), sphinx (28 errors from Unknown in non-constructor assignments), meson (16 errors from Unknown), pandas (9 errors from setstate), setuptools (2 errors from run() method), sockeye (4 errors from reassignment in non-constructor), and bandersnatch (3 errors) are ALL caused by non-constructor method assignments being unioned into the attribute type. Constructor-only unioning would fix ~80 pyrefly-only false positives while preserving the branch-unioning improvement within constructors.

2. In the DefinedInMethod arm of analyze_class_field_value() in pyrefly/lib/alt/class/class_field.rs (around line 1600-1630), when computing the union of all assignment types, filter out or collapse Unknown before unioning. Specifically: if any concrete (non-Unknown) type exists in union_types, remove Unknown entries before calling unions(). This prevents Unknown from polluting otherwise-precise union types like BuiltinTemplateLoader | Unknown which should just be BuiltinTemplateLoader.

Files: pyrefly/lib/alt/class/class_field.rs
Confidence: medium
Affected projects: sphinx, meson, aiortc, apprise
Fixes: bad-argument-type, no-matching-overload
Many pyrefly-only false positives in sphinx (7 bad-argument-type with Unknown), meson (10 bad-argument-type with Unknown), and aiortc (1 bad-argument-type with Unknown) are caused by Unknown being included in the union alongside concrete types. Filtering Unknown when concrete types exist would eliminate these cascading errors. This is a secondary fix if the constructor-only restriction above isn't sufficient.

3. In the override checking logic (likely in the class field or override validation code), add a guard to suppress bad-override-mutable-attribute when the parent class attribute is defined via a property or abstract method (not a plain field). In aiohttp, HeadersMixin._headers is effectively an abstract/property-like attribute that subclasses are expected to provide with a concrete type. The check should only fire for plain mutable fields, not for attributes that serve as abstract interface points.

Files: pyrefly/lib/alt/class/class_field.rs
Confidence: medium
Affected projects: aiohttp
Fixes: bad-override-mutable-attribute
The aiohttp regression is 1 pyrefly-only bad-override-mutable-attribute error where StreamResponse._headers (CIMultiDict[str]) overrides HeadersMixin._headers (Mapping[str, str]). Neither mypy nor pyright flag this. The override is intentional — HeadersMixin defines the interface and StreamResponse provides the concrete implementation. Suppressing this for property/abstract attributes would eliminate this false positive while keeping the check valid for plain mutable fields.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (3 heuristic, 35 LLM)

@yangdanny97 yangdanny97 self-assigned this Jun 17, 2026
@meta-codesync

meta-codesync Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

@yangdanny97 has imported this pull request. If you are a Meta employee, you can view this in D108836452.

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.

use all usages within class (or within __init__) to infer type, not just initial assignment

2 participants