Skip to content

fix Cannot infer set type from usage #2693#2785

Open
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2693
Open

fix Cannot infer set type from usage #2693#2785
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2693

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #2693

Adjusted first-use binding so an initial narrowing read no longer permanently disables deferred inference. In practice, if key not in seen: no longer forces seen off the first-use path, so the later seen.add(key) can infer set[str] as intended.

Test Plan

add test

@meta-cla meta-cla bot added the cla signed label Mar 12, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 12, 2026 18:32
Copilot AI review requested due to automatic review settings March 12, 2026 18:32
@github-actions

This comment has been minimized.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a delayed/first-use inference edge case where an initial narrowing-style read (e.g. if key not in seen:) incorrectly disabled later inference from mutating uses (e.g. seen.add(key)), preventing set() from being inferred as set[str] in common dedup patterns.

Changes:

  • Adjusted deferred bound-name finalization so narrowing reads no longer consume/disable first-use inference.
  • Added a regression test covering set() inference inside a loop with an in membership check before .add(...).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pyrefly/lib/binding/bindings.rs Updates first-use/deferred inference side-effects for narrowing reads during deferred bound-name finalization.
pyrefly/lib/test/delayed_inference.rs Adds a regression testcase ensuring seen = set() infers set[str] in the reported loop pattern.

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 1301 to 1305
} else {
// Narrowing or other reads: mark as DoesNotPin if still undetermined.
if matches!(first_use, FirstUse::Undetermined) {
self.mark_does_not_pin_if_first_use(def_idx);
}
// Narrowing reads should not pin partial types, but they also should not
// consume first-use inference. A later ordinary read may still determine the
// type, such as `if key not in seen: seen.add(key)`.
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

This else branch is intentionally a no-op but is an empty block (comment-only). If cargo clippy is run with clippy::all, this can trigger clippy::empty_else warnings. Consider removing the else block and moving the comment to a standalone comment after the if/else if chain (or otherwise ensuring the branch isn’t syntactically empty).

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions
Copy link

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

rotki (https://github.com/rotki/rotki)
+ ERROR rotkehlchen/data_import/utils.py:75:21-53: Object of class `bool` has no attribute `append` [missing-attribute]

ibis (https://github.com/ibis-project/ibis)
+ ERROR ibis/expr/types/relations.py:514:12-18: Returned type `dict[object, ibis.expr.operations.core.Value[Unknown]]` is not assignable to declared return type `Mapping[str, ibis.expr.types.generic.Value]` [bad-return]

pwndbg (https://github.com/pwndbg/pwndbg)
- ERROR pwndbg/commands/vmmap.py:326:77-87: Object of class `NoneType` has no attribute `vaddr` [missing-attribute]
- ERROR pwndbg/commands/vmmap.py:328:29-39: Object of class `NoneType` has no attribute `vaddr` [missing-attribute]

apprise (https://github.com/caronc/apprise)
+ ERROR apprise/cli.py:1040:17-44: Object of class `int` has no attribute `append`
+ Object of class `str` has no attribute `append` [missing-attribute]
+ ERROR apprise/cli.py:1089:30-45: Type `int` is not iterable [not-iterable]
+ ERROR apprise/cli.py:1090:27-36: Object of class `str` has no attribute `url` [missing-attribute]
+ ERROR apprise/cli.py:1102:24-34: Object of class `str` has no attribute `tags` [missing-attribute]
+ ERROR apprise/cli.py:1104:66-78: No matching overload found for function `str.join` called with arguments: (object | set[Unknown] | Unknown) [no-matching-overload]
+ ERROR apprise/config/base.py:788:43-53: Argument `dict[object, set[Unknown]]` is not assignable to parameter `group_tags` with type `dict[str, set[str]]` in function `ConfigBase.__normalize_tag_groups` [bad-argument-type]
+ ERROR apprise/config/base.py:1300:43-53: Argument `dict[object | Unknown, set[object] | set[Unknown]]` is not assignable to parameter `group_tags` with type `dict[str, set[str]]` in function `ConfigBase.__normalize_tag_groups` [bad-argument-type]

cloud-init (https://github.com/canonical/cloud-init)
+ ERROR cloudinit/net/eni.py:442:9-40: Object of class `str` has no attribute `append` [missing-attribute]
+ ERROR cloudinit/netinfo.py:355:16-20: Returned type `dict[Unknown, dict[str, Unknown]] | dict[Unknown, dict[str, bool | list[Unknown] | str]] | dict[Unknown, Unknown] | Unknown` is not assignable to declared return type `dict[str, dict[str, Interface]]` [bad-return]
+ ERROR cloudinit/netinfo.py:376:12-16: Returned type `dict[Unknown, dict[str, Unknown]] | dict[Unknown, dict[str, bool | list[Unknown] | str]] | dict[Unknown, Unknown] | Unknown` is not assignable to declared return type `dict[str, dict[str, Interface]]` [bad-return]

hydpy (https://github.com/hydpy-dev/hydpy)
+ ERROR hydpy/models/kinw/kinw_model.py:3505:38-40: Argument `list[Parameter | float | Unknown]` is not assignable to parameter `hvector` with type `Iterable[float]` in function `BaseModelProfile.calculate_qgvector` [bad-argument-type]

prefect (https://github.com/PrefectHQ/prefect)
+ ERROR src/prefect/server/models/block_schemas.py:575:21-85: Object of class `dict` has no attribute `append` [missing-attribute]
+ ERROR src/prefect/server/models/block_schemas.py:584:81-587:22: Cannot set item in `dict[str | None, dict[str, str]]` [unsupported-operation]

@github-actions
Copy link

Primer Diff Classification

❌ 6 regression(s) | ✅ 1 improvement(s) | 7 project(s) total | +14, -2 errors

6 regression(s) across rotki, pwndbg, apprise, cloud-init, hydpy, prefect. error kinds: missing-attribute, False positive on dict value type inference, not-iterable on meta['plugins']. 1 improvement(s) across ibis.

Project Verdict Changes Error Kinds Root Cause
rotki ❌ Regression +1 False positive on dict value type inference pyrefly/lib/binding/bindings.rs
ibis ✅ Improvement +1 bad-return pyrefly/lib/binding/bindings.rs
pwndbg ❌ Regression -2 missing-attribute pyrefly/lib/binding/bindings.rs
apprise ❌ Regression +6 not-iterable on meta['plugins'] pyrefly/lib/binding/bindings.rs
cloud-init ❌ Regression +3 missing-attribute on str (eni.py) pyrefly/lib/binding/bindings.rs
hydpy ❌ Regression +1 false positive from inference change pyrefly/lib/binding/bindings.rs
prefect ❌ Regression +2 missing-attribute, unsupported-operation pyrefly/lib/binding/bindings.rs
Detailed analysis

❌ Regression (6)

rotki (+1)

False positive on dict value type inference: Pyrefly incorrectly infers the type of grouped_msgs[msg]['rows'] as including bool due to heterogeneous dict value types in the literal on lines 69-73. The 'rows' key always maps to a list, but pyrefly unions all value types and reports bool has no append. This is a regression in inference quality caused by the first-use binding change.

Overall: This is a false positive. Looking at the code:

  1. Line 64: grouped_msgs = {} creates an empty dict.
  2. Line 69-73: grouped_msgs[msg] is assigned a dict literal with key 'rows' mapped to [row] (a list[int] since row comes from import_msg['row'] where import_msg is from self.import_msgs: list[dict]), key 'msg' mapped to msg (a string), and optionally 'is_error' mapped to True (a bool) via the **({'is_error': True} if ... else {}) spread.
  3. Line 75: grouped_msgs[msg]['rows'] accesses the 'rows' key, which is always a list. Calling .append(row) on it is perfectly valid.

Pyrefly is incorrectly inferring the type of grouped_msgs[msg]['rows'] as bool (or a union including bool). This happens because the dict literal on lines 69-73 has heterogeneous value types (str for 'msg', list for 'rows', bool for 'is_error'), and pyrefly appears to be unifying all value types and then returning the union when indexing with a string key. The **({'is_error': True} if ... else {}) spread adds a bool value type to the union. When pyrefly resolves grouped_msgs[msg]['rows'], it gets str | list[int] | bool instead of list[int], and then reports that bool has no append.

This is a type inference limitation/regression. The code is correct at runtime, and neither mypy nor pyright flag it.

Attribution: The change in pyrefly/lib/binding/bindings.rs removed the mark_does_not_pin_if_first_use call for narrowing reads. This was intended to fix set inference for patterns like if key not in seen: seen.add(key). However, the change appears to have a side effect on how pyrefly infers types for dictionary literals. Specifically, on line 69, the dict literal {'msg': msg, 'rows': [row], **({'is_error': True} if 'is_error' in import_msg else {})} contains values of types str (for 'msg'), list[int] (for 'rows'), and bool (for 'is_error'). Pyrefly appears to be inferring the value type of grouped_msgs[msg] incorrectly — it seems to be collapsing the dict value type to str | list[int] | bool or similar, and then when accessing ['rows'], it returns the union type including bool, hence the Object of class 'bool' has no attribute 'append' error. The change to first-use binding inference in bindings.rs likely altered how grouped_msgs (initialized as {} on line 64) gets its type inferred from subsequent usage.

pwndbg (-2)

The removed errors were false positives. page is iterated from total_pages on line 304, which contains Page objects. Line 309 has if page is not None and page.in_darwin_shared_cache and not expand_shared_cache: — this is a compound condition. When this condition is false and execution falls through (no continue), page could still be a non-None Page instance (e.g., if in_darwin_shared_cache was false or expand_shared_cache was true). Pyrefly was incorrectly narrowing page to NoneType in the fall-through path after this compound and condition, treating the page is not None sub-check as the sole discriminator. This caused spurious missing-attribute errors on lines 326 and 328 where page.vaddr is accessed. The PR fixes this narrowing bug, correctly removing these false positive errors.
Attribution: The change in pyrefly/lib/binding/bindings.rs in the BindingsBuilder removed the call to mark_does_not_pin_if_first_use(def_idx) for narrowing reads. Previously, when page is not None was checked on line 309, this narrowing read would mark the variable as DoesNotPin, which could cause pyrefly to incorrectly infer page as NoneType in certain code paths. By removing this behavior, the later ordinary reads of page.vaddr can correctly resolve the type.

apprise (+6)

not-iterable on meta['plugins']: False positive. meta['plugins'] is always a list (assigned as [plugin] or []). Pyrefly appears to be inferring the wrong type from the dict literal's mixed values.
missing-attribute .url and .tags on entry: Cascade false positives from the not-iterable error. Since pyrefly mis-infers the iteration variable type, it then can't find .url() and .tags on what it thinks is str.
bad-argument-type for group_tags: False positive. group_tags is built as dict[str, set[str]] throughout the code, but pyrefly now infers dict[object, set[Unknown]] due to the changed inference behavior.
no-matching-overload for str.join: Cascade false positive. ', '.join(entry.tags) fails because pyrefly thinks entry is str (which has no .tags), and the resulting type doesn't match any str.join overload.

Overall: All 6 new errors are false positives caused by the PR's change to first-use binding inference. The uids dict is constructed with {"plugins": [plugin], "state": ..., "size": 0} — its values include lists of plugin objects. Pyrefly now incorrectly infers the element type when iterating over meta["plugins"], producing int (from the "size": 0 value) or str instead of the actual plugin type. Similarly, group_tags is correctly typed as dict[str, set[str]] but pyrefly now infers dict[object, set[Unknown]]. Neither mypy nor pyright flag any of these.

Per-category reasoning:

  • not-iterable on meta['plugins']: False positive. meta['plugins'] is always a list (assigned as [plugin] or []). Pyrefly appears to be inferring the wrong type from the dict literal's mixed values.
  • missing-attribute .url and .tags on entry: Cascade false positives from the not-iterable error. Since pyrefly mis-infers the iteration variable type, it then can't find .url() and .tags on what it thinks is str.
  • bad-argument-type for group_tags: False positive. group_tags is built as dict[str, set[str]] throughout the code, but pyrefly now infers dict[object, set[Unknown]] due to the changed inference behavior.
  • no-matching-overload for str.join: Cascade false positive. ', '.join(entry.tags) fails because pyrefly thinks entry is str (which has no .tags), and the resulting type doesn't match any str.join overload.

Attribution: The change in pyrefly/lib/binding/bindings.rs removed the mark_does_not_pin_if_first_use call for narrowing reads. While this fixed the set() inference test case, it appears to have changed how inference propagates for other patterns (dict literals with heterogeneous values), causing pyrefly to mis-infer types for uids dict values and group_tags dict in the apprise project.

cloud-init (+3)

missing-attribute on str (eni.py): False positive. devs[devname] is always a dict with a "subnets" key mapped to a list (line 420). The inner dict has values of type str (for keys "type", "name", and potentially "mac_address" on line 424) and list (for key "subnets"). Pyrefly incorrectly infers the inner dict's value type as the union str | list[...] and applies this union when accessing devs[devname]["subnets"], causing it to think .append() might be called on a str. A correct type checker should narrow based on the specific key or use TypedDict-like inference. This is an inference regression from the PR's first-use binding change.
bad-return in netdev_info (netinfo.py): False positive. The devs variable is assigned from untyped helper functions. The PR's inference change causes pyrefly to compute a more complex union type for devs that doesn't match the declared return type. The return type annotation Dict[str, Dict[str, Interface]] is itself arguably incorrect for the actual data structure, but neither mypy nor pyright flags this, and the error is caused by pyrefly's changed inference behavior, not by a real bug in the code.

Overall: All three errors are false positives introduced by the PR's change to first-use binding inference.

eni.py:442devs[devname] is always initialized on line 420 as {"type": dtype, "name": devname, "subnets": []}, so devs[devname]["subnets"] is always a list. The error claims it's a str, which means pyrefly's inference of the dict value type went wrong — it's inferring the inner dict's value type as a union of all value types in the dict literal (str | list[...]), since "type" and "name" map to str while "subnets" maps to list. Additionally, line 424 assigns devs[devname]["mac_address"] = data["hwaddress"] (another str value). When pyrefly resolves devs[devname]["subnets"], it uses the union value type str | list[...] rather than narrowing based on the specific key, causing it to think .append() might be called on a str. This is an inference failure — a type checker should either use a TypedDict-like inference for the inner dict or at minimum not flag this given the literal key access pattern.

netinfo.py:355,376 — The devs variable is assigned from multiple helper functions (_netdev_info_ifconfig_netbsd, _netdev_info_iproute_json, _netdev_info_iproute, _netdev_info_ifconfig), all of which are untyped (return type not annotated). The return type annotation Dict[str, Dict[str, Interface]] is itself arguably incorrect for the actual runtime values (the actual structure is more like Dict[str, Interface] where each device name maps to an Interface-like dict, not Dict[str, Dict[str, Interface]]). Regardless, this is a pre-existing annotation issue. The error message shows dict[Unknown, dict[str, Unknown]] | dict[Unknown, dict[str, bool | list[Unknown] | str]] | ... — the Unknown types indicate pyrefly is now propagating untyped return values differently. Previously these were not flagged; the PR's inference change caused pyrefly to compute a more complex (but still imprecise) type for devs, triggering the bad-return error. Neither mypy nor pyright flags these.

Attribution: The change in pyrefly/lib/binding/bindings.rs at the mark_does_not_pin_if_first_use removal altered how first-use binding works for narrowing reads. Previously, a narrowing read (like if key not in seen) would mark the binding as DoesNotPin, which apparently had a side effect on how dict literal types were inferred. Now that narrowing reads no longer consume first-use inference, the inference for devs = {} (an empty dict literal) in both _ifaces_to_net_config_data and netdev_info is being resolved differently — likely picking up incorrect or overly complex union types from the various assignment branches, leading to the missing-attribute error on str (pyrefly incorrectly infers devs[devname]["subnets"] as str in some branch) and the bad-return errors (pyrefly infers devs as a complex union type instead of a simple dict).

hydpy (+1)

false positive from inference change: The PR changed narrowing-read handling in first-use binding, causing pyrefly to infer temp as list[Parameter | float | Unknown] instead of a float-compatible type. The Unknown indicates inference uncertainty, and the code is valid — hydpy Parameters are numeric values used as floats throughout.

Overall: The analysis is factually correct. The error is at line 3505 where self.calculate_qgvector(hs) is called. Looking at the code:

  1. hs is built by starting as an empty list temp = [] (line 3500), then appending float values from the hs list (which contains results of arithmetic on con.hm, der.hv[0], der.hv[1], and hmax — all of which are Parameter or float types), then sorting with sorted(temp) (line 3504).

  2. The calculate_qgvector method signature at line 3557 declares hvector: Iterable[float].

  3. The error says list[Parameter | float | Unknown] is not assignable to Iterable[float]. This indicates pyrefly inferred the elements of temp (and thus hs) as Parameter | float | Unknown rather than just float. This is because values like con.hm / 2.0, con.hm + der.hv[0] / 2.0, etc. involve Parameter objects in arithmetic, and pyrefly's inference (changed by the PR) couldn't resolve these to float.

  4. The code is valid Python — hydpy Parameter objects support arithmetic operations that produce float-compatible results, and the list elements are used as floats throughout. Neither mypy nor pyright would flag this as an error.

  5. The Unknown in the inferred type indicates an inference failure/uncertainty in pyrefly's type narrowing, consistent with the analysis's claim about the PR's first-use binding inference change causing this regression.

The analysis correctly identifies this as a false positive caused by the PR's inference changes.

Attribution: The change to BindingsBuilder in pyrefly/lib/binding/bindings.rs removed the mark_does_not_pin_if_first_use call for narrowing reads. This changed how temp = [] gets its element type inferred — previously the narrowing read if h not in temp would mark the binding as DoesNotPin, allowing a different inference path. Now the inference resolves differently, producing list[Parameter | float | Unknown] instead of a type compatible with Iterable[float].

prefect (+2)

Both errors are false positives caused by pyrefly over-constraining the inferred value type of the inner dict block_schema_fields_copy["block_schema_references"]. This dict is initialized as {} on line 543. Pyrefly infers its value type as dict[str, str] based on the first assignment at line 561, where new_block_schema_reference (a dict[str, str]) is stored as a value. However, the code intentionally builds a heterogeneous dict where values can be either dict[str, str] or list[dict[str, str]].

For error 1 (line 575): The isinstance(..., list) check on lines 568-571 should narrow the value type to list within that branch, allowing .append(). But since pyrefly has inferred the dict's value type as dict[str, str], the isinstance check against list would narrow to Never (since dict[str, str] and list don't overlap), so pyrefly doesn't recognize .append() as valid.

For error 2 (line 584): Pyrefly rejects assigning a list[dict[str, str]] value to a position typed as dict[str, str] in the dict.

Both are false positives because the outer dict block_schema_fields_copy has type dict[str, Any] (from the spread of parent_block_schema.fields), so block_schema_fields_copy["block_schema_references"] should be Any, which would allow all these operations. The root cause is that pyrefly is inferring a more specific type for the "block_schema_references" key's value from the {} literal and subsequent assignments rather than treating it as Any. Neither mypy nor pyright flags these.

Attribution: The change in pyrefly/lib/binding/bindings.rs removed the mark_does_not_pin_if_first_use call for narrowing reads. This changed how first-use inference works for variables involved in narrowing conditions. The block_schema_fields_copy["block_schema_references"] dict is initialized as {} and its type is now being inferred too narrowly from the first assignment (line 561, which assigns a dict[str, str]), causing pyrefly to lock the value type to dict[str, str] and reject both the .append() call (after isinstance narrowing to list) and the list assignment on line 584.

✅ Improvement (1)

ibis (+1)

The analysis correctly identifies that unwrap_aliases declares -> Mapping[str, ir.Value] but actually returns a dict whose values are ops.Value (operation nodes) rather than ir.Value (expression wrappers). The code at line 513 stores unwrap_alias(node) which returns ops.Value, not ir.Value. The claim that these are different types in ibis's hierarchy is correct — ops.Value is an operation/node type while ir.Value is an expression wrapper type. The annotation [mypy: yes] confirms mypy also flags this. The callers of unwrap_aliases (e.g., ops.Sort, ops.Project, ops.Aggregate) all consume the values as operation nodes, confirming the runtime code is correct but the return type annotation is wrong. Pyrefly catching this is a valid finding.
Attribution: The change in pyrefly/lib/binding/bindings.rs adjusted first-use binding so narrowing reads no longer permanently disable deferred inference. This allowed pyrefly to more precisely infer the type of the result dict in unwrap_aliases, revealing that its actual type (dict[object, ops.core.Value]) doesn't match the declared return type (Mapping[str, ir.Value]). Previously, the less precise inference may have masked this mismatch.

Suggested fixes

Summary: The PR's removal of mark_does_not_pin_if_first_use for narrowing reads fixes set inference but breaks dict inference for heterogeneous dict literals across 6 projects, causing 13 pyrefly-only false positives.

**1. In BindingsBuilder::[ensure_first_use_forwarding()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/binding/bindings.rs) (or the equivalent first-use handling block) in pyrefly/lib/binding/bindings.rs, instead of completely removing the mark_does_not_pin_if_first_use call for narrowing reads, add a more targeted condition: only skip the DoesNotPin marking when the narrowing read involves a membership/containment test (like not in) on the same variable being inferred. For other narrowing reads (like isinstance checks or general dict key accesses), preserve the previous behavior of marking DoesNotPin when still Undetermined. Pseudo-code:

} else {
    // Narrowing reads: only skip DoesNotPin for containment checks
    // (e.g., `key not in seen`) to allow later .add() to pin the type.
    // For other narrowing patterns, preserve DoesNotPin to avoid
    // over-constraining dict value types from heterogeneous literals.
    if matches!(first_use, FirstUse::Undetermined) && !is_containment_narrowing_on_self(current_idx) {
        self.mark_does_not_pin_if_first_use(def_idx);
    }
}

Alternatively, a simpler fix: instead of distinguishing narrowing kinds, change the approach so that narrowing reads neither pin NOR consume first-use inference AND also don't mark DoesNotPin — but ensure that when the empty container's type is resolved, heterogeneous dict literal values are handled by inferring the value type from ALL assignments to the dict (not just the first one that pins). The core issue is that without DoesNotPin, the first ordinary read after x = {} pins the type too eagerly from a single assignment branch.**

Files: pyrefly/lib/binding/bindings.rs
Confidence: medium
Affected projects: rotki, apprise, cloud-init, hydpy, prefect
Fixes: missing-attribute, not-iterable, bad-argument-type, no-matching-overload, bad-return
The regression pattern is consistent: all 6 projects involve empty dict/container literals (x = {} or x = []) whose types are inferred from subsequent assignments. The PR removed DoesNotPin marking for narrowing reads, which was intended to fix set() inference for if key not in seen: seen.add(key). But this has a side effect: for dicts with heterogeneous value types (e.g., {'msg': str_val, 'rows': list_val, 'is_error': bool_val}), the inference now pins the value type too eagerly or incorrectly, producing union types like str | list | bool instead of properly tracking per-key types. The fix should be more surgical — only skip DoesNotPin for the specific pattern the test case targets (containment checks on sets/lists), not for all narrowing reads.

2. As an alternative/complementary fix in the type inference layer (likely in the dict literal type resolution or the first-use type solver): when resolving the type of an empty dict x = {} that is later assigned heterogeneous values via x[key] = {k1: v1, k2: v2, ...}, ensure that accessing x[key][specific_literal_key] narrows to the type of that specific key's value rather than the union of all value types. This is a TypedDict-like inference for dict literals with string literal keys. This would fix the rotki, apprise, cloud-init, and prefect regressions even if the binding change remains as-is.

Files: pyrefly/lib/binding/bindings.rs
Confidence: low
Affected projects: rotki, apprise, cloud-init, prefect
Fixes: missing-attribute, not-iterable, bad-argument-type, no-matching-overload, bad-return
This is a deeper fix that addresses the root cause for dict-heavy patterns. Even before the PR, pyrefly likely had imprecise dict value type inference for heterogeneous literals, but the DoesNotPin marking was masking it. The PR exposed the underlying issue. However, this is a larger change and may not be feasible as a quick fix.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (7 LLM)

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.

Cannot infer set type from usage

2 participants