Skip to content

fix Narrow types based on collection containment #2706#2710

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

fix Narrow types based on collection containment #2706#2710
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:2706

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #2706

containment narrowing now derives finite membership domains directly from typed RHS containers instead of relying only on the generic iterable/mapping decomposition.

That preserves literal precision for typed dict, tuple, list, set, and frozenset values, and the literal fast path now also recognizes inline frozenset(...) calls

Test Plan

add the test in issue

@meta-cla meta-cla bot added the cla signed label Mar 8, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 8, 2026 11:14
Copilot AI review requested due to automatic review settings March 8, 2026 11:14
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

Improves containment-based type narrowing so that x in <typed collection> can narrow x to a finite domain derived from the RHS container’s typed element/key domain (preserving literal precision for typed dict, tuple, list, set, and frozenset, including frozenset(...) calls).

Changes:

  • Add helper routines to derive a finite “membership domain” type from RHS container types (including TypedDict keys and typed container element types).
  • Extend the syntactic fast-path for membership narrowing to recognize frozenset(<literal container>).
  • Add a regression test covering narrowing for in checks against typed collection variables and inline frozenset(...).

Reviewed changes

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

File Description
pyrefly/lib/alt/narrow.rs Implements finite-domain membership narrowing from typed RHS containers and recognizes inline frozenset(...) for the literal fast-path.
pyrefly/lib/test/narrow.rs Adds a test ensuring in-based narrowing works for typed dict/tuple/list/set/frozenset and frozenset(("a",)).

💡 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.

@yangdanny97
Copy link
Contributor

it's weird that mypy primer timed out, i wonder if there is a perf regression somewhere

@rchen152
Copy link
Contributor

rchen152 commented Mar 8, 2026

it's weird that mypy primer timed out, i wonder if there is a perf regression somewhere

@yangdanny97 I've also started seeing mypy_primer timeouts in the past day or so (but only with the GitHub workflow).

containment narrowing now derives finite membership domains directly
from typed RHS containers instead of relying only on the generic
iterable/mapping decomposition. That preserves literal precision for
typed dict, tuple, list, set, and frozenset values, and the literal fast
path now also recognizes inline frozenset(...) calls
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

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

urllib3 (https://github.com/urllib3/urllib3)
- ERROR src/urllib3/util/ssl_.py:235:66-237:14: No matching overload found for function `dict.get` called with arguments: (int | None, Literal[TLSVersion.MINIMUM_SUPPORTED]) [no-matching-overload]
- ERROR src/urllib3/util/ssl_.py:238:66-240:14: No matching overload found for function `dict.get` called with arguments: (int | None, Literal[TLSVersion.MAXIMUM_SUPPORTED]) [no-matching-overload]
- ERROR src/urllib3/util/ssl_.py:254:35-54: `int | Unknown` is not assignable to attribute `minimum_version` with type `TLSVersion` [bad-assignment]
+ ERROR src/urllib3/util/ssl_.py:254:35-54: `int` is not assignable to attribute `minimum_version` with type `TLSVersion` [bad-assignment]
- ERROR src/urllib3/util/ssl_.py:259:35-54: `int | Unknown` is not assignable to attribute `maximum_version` with type `TLSVersion` [bad-assignment]
+ ERROR src/urllib3/util/ssl_.py:259:35-54: `int` is not assignable to attribute `maximum_version` with type `TLSVersion` [bad-assignment]

pydantic (https://github.com/pydantic/pydantic)
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `allow_inf_nan` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `allowed_schemes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `arguments_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `auto_collapse` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `choices` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `cls` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `cls_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `cls_repr` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `coerce_numbers_to_str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `collect_init_only` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `computed_fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `config` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_error_context` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_error_message` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_error_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `custom_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `dataclass_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `decimal_places` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_factory` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_factory_takes_data` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_host` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_path` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `default_port` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `definitions` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `discriminator` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `expected` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `extra_behavior` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `extras_keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `extras_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `fail_fast` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `from_attributes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `frozen` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `function_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `ge` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `generic_origin` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `gt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `host_required` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `items_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `json_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `json_schema_input_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `lax_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `le` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `lt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `max_digits` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `max_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `members` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `microseconds_precision` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `min_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `missing` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `model_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `multiple_of` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `now_op` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `now_utc_offset` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `on_error` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `pattern` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `post_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `python_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `regex_engine` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `return_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `revalidate_instances` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `root_model` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `schema_ref` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `slots` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `steps` [bad-typed-dict-key]
- ERROR pydantic/json_schema.py:2380:60-68: Invalid key for TypedDict `AfterValidatorFunctionSchema`, got `str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `strict` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `strict_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `strip_whitespace` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `sub_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `to_lower` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `to_upper` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `total` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `tz_constraint` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `validate_by_alias` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `validate_by_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `validate_default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `values_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `var_args_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `var_kwargs_mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `var_kwargs_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `variadic_item_index` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AfterValidatorFunctionSchema` does not have key `version` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `allow_inf_nan` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `allowed_schemes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `arguments_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `auto_collapse` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `choices` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `cls` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `cls_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `cls_repr` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `coerce_numbers_to_str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `collect_init_only` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `computed_fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `config` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `custom_error_context` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `custom_error_message` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `custom_error_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `custom_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `dataclass_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `decimal_places` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `default_factory` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `default_factory_takes_data` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `default_host` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `default_path` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `default_port` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `definitions` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `discriminator` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `expected` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `extra_behavior` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `extras_keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `extras_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `fail_fast` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `from_attributes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `frozen` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `function` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `function_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `ge` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `generic_origin` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `gt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `host_required` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `items_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `json_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `json_schema_input_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `keys_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `lax_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `le` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `lt` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `max_digits` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `max_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `members` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `microseconds_precision` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `min_length` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `missing` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `model_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `multiple_of` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `now_op` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `now_utc_offset` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `on_error` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `pattern` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `post_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `python_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `regex_engine` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `return_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `revalidate_instances` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `root_model` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `schema_ref` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `slots` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `steps` [bad-typed-dict-key]
- ERROR pydantic/json_schema.py:2380:60-68: Invalid key for TypedDict `AnySchema`, got `str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `strict` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `strict_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `strip_whitespace` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `sub_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `to_lower` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `to_upper` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `total` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `tz_constraint` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `validate_by_alias` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `validate_by_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `validate_default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `values_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `var_args_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `var_kwargs_mode` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `var_kwargs_schema` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `variadic_item_index` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `AnySchema` does not have key `version` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `allow_inf_nan` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `allowed_schemes` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `auto_collapse` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `choices` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `cls` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `cls_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `cls_repr` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `coerce_numbers_to_str` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `collect_init_only` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `computed_fields` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `config` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `custom_error_context` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `custom_error_message` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `custom_error_type` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `custom_init` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `dataclass_name` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `decimal_places` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `default` [bad-typed-dict-key]
+ ERROR pydantic/json_schema.py:2380:60-68: TypedDict `ArgumentsSchema` does not have key `default_factory` [bad-typed-dict-key]

... (truncated 4159 lines) ...

pylint (https://github.com/pycqa/pylint)
- ERROR pylint/checkers/stdlib.py:703:71-85: Argument `str | Unknown | None` is not assignable to parameter `func_name` with type `str` in function `StdlibChecker._check_open_call` [bad-argument-type]

schema_salad (https://github.com/common-workflow-language/schema_salad)
- ERROR schema_salad/avro/schema.py:712:36-50: Argument `Unknown | None` is not assignable to parameter `atype` with type `str` in function `PrimitiveSchema.__init__` [bad-argument-type]

pip (https://github.com/pypa/pip)
+  WARN iterative_resolve_scc: SCC CalcId(pip._vendor.msgpack.fallback, /home/runner/work/pyrefly/pyrefly/primer_base/projects/pip/src/pip/_vendor/msgpack/fallback.py, KeyClassField(class1, _unpack)) exceeded 5 iterations; committing last answers

streamlit (https://github.com/streamlit/streamlit)
+  WARN lib/streamlit/elements/widgets/button.py:111:16-47: Redundant cast: `Literal['left', 'right']` is the same type as `Literal['left', 'right']` [redundant-cast]

meson (https://github.com/mesonbuild/meson)
+  WARN mesonbuild/interpreter/interpreter.py:1164:36-54: Redundant cast: `Literal['c', 'cpp', 'cs', 'cuda', 'cython', 'd', 'fortran', 'java', 'linearasm', 'masm', 'nasm', 'objc', 'objcpp', 'rust', 'swift', 'vala']` is the same type as `Literal['c', 'cpp', 'cs', 'cuda', 'cython', 'd', 'fortran', 'java', 'linearasm', 'masm', 'nasm', 'objc', 'objcpp', 'rust', 'swift', 'vala']` [redundant-cast]
+  WARN mesonbuild/interpreter/mesonmain.py:294:22-40: Redundant cast: `Literal['c', 'cpp', 'cs', 'cuda', 'cython', 'd', 'fortran', 'java', 'linearasm', 'masm', 'nasm', 'objc', 'objcpp', 'rust', 'swift', 'vala']` is the same type as `Literal['c', 'cpp', 'cs', 'cuda', 'cython', 'd', 'fortran', 'java', 'linearasm', 'masm', 'nasm', 'objc', 'objcpp', 'rust', 'swift', 'vala']` [redundant-cast]

static-frame (https://github.com/static-frame/static-frame)
+ ERROR static_frame/core/interface.py:1577:35-48: Argument `type[InterfaceBatchDatetime] | type[InterfaceBatchString] | type[InterfaceBatchTranspose] | type[InterfaceBatchValues] | type[InterfaceDatetime[Unknown]] | type[InterfaceHashlib] | type[InterfaceString[Unknown]] | type[InterfaceTranspose[Unknown]] | type[InterfaceValues[Unknown]] | type[TypeClinic]` is not assignable to parameter `cls_interface` with type `type[Interface]` in function `InterfaceRecord.gen_from_accessor` [bad-argument-type]

mypy (https://github.com/python/mypy)
- ERROR mypyc/irbuild/util.py:142:13-23: Cannot set item in `MypycAttrs` [unsupported-operation]
- ERROR mypyc/irbuild/util.py:143:19-22: Cannot set item in `dict[MypycAttr, int]` [unsupported-operation]

Tanjun (https://github.com/FasterSpeeding/Tanjun)
- ERROR tanjun/context/menu.py:144:16-28: Returned type `CommandType` is not assignable to declared return type `Literal[CommandType.MESSAGE, CommandType.USER]` [bad-return]

cloud-init (https://github.com/canonical/cloud-init)
-  WARN iterative_resolve_scc: SCC CalcId(cloudinit.sources.helpers.ec2, /home/runner/work/pyrefly/pyrefly/primer_base/projects/cloud-init/cloudinit/sources/helpers/ec2.py, KeyClassField(class1, _materialize)) exceeded 5 iterations; committing last answers
-  WARN iterative_resolve_scc: SCC CalcId(cloudinit.sources.helpers.aliyun, /home/runner/work/pyrefly/pyrefly/primer_base/projects/cloud-init/cloudinit/sources/helpers/aliyun.py, Key::Definition(_process_dict_values 85:9-29)) exceeded 5 iterations; committing last answers

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ ERROR pymongo/common.py:386:12-17: Returned type `Literal['nearest', 'primary', 'primaryPreferred', 'secondary', 'secondaryPreferred']` is not assignable to declared return type `_ServerMode` [bad-return]

@github-actions
Copy link

github-actions bot commented Mar 9, 2026

Primer Diff Classification

❌ 1 regression(s) | ✅ 8 improvement(s) | ➖ 1 neutral | 10 project(s) total

1 regression(s) across static-frame. error kinds: bad-argument-type. caused by membership_narrow_container_type(). 8 improvement(s) across urllib3, pylint, schema_salad, streamlit, meson, mypy, Tanjun, mongo-python-driver.

Project Verdict Changes Error Kinds Root Cause
urllib3 ✅ Improvement +2, -4 bad-assignment, no-matching-overload membership_narrow_type()
pydantic ➖ Neutral +1, -1 bad-typed-dict-key
pylint ✅ Improvement -1 bad-argument-type membership_narrow_type()
schema_salad ✅ Improvement -1 bad-argument-type membership_narrow_type()
streamlit ✅ Improvement +1 redundant-cast membership_narrow_type()
meson ✅ Improvement +2 redundant-cast membership_narrow_container_type()
static-frame ❌ Regression +1 bad-argument-type membership_narrow_container_type()
mypy ✅ Improvement -2 unsupported-operation membership_narrow_container_type()
Tanjun ✅ Improvement -1 bad-return membership_narrow_container_type()
mongo-python-driver ✅ Improvement +1 bad-return membership_narrow_container_type()
Detailed analysis

❌ Regression (1)

static-frame (+1)

This is a REGRESSION. The PR improved pyrefly's type inference precision, but this exposed an overly strict type check. The union of interface subclass types should be acceptable where type[Interface] is expected, since all members of the union are valid Interface subclasses. Mypy/pyright would likely accept this pattern. The code is correct at runtime - the method can handle any Interface subclass. Pyrefly is being too strict about the type[X] invariance rule in a context where contravariance would be more practical.
Attribution: The changes to membership_narrow_container_type() and related functions in pyrefly/lib/alt/narrow.rs improved type inference for containment operations. This made pyrefly more precise at inferring that obj.__class__ (derived from checking membership in INTERFACE_ATTRIBUTE_CLS) has the specific union type rather than a more general type, exposing the existing type annotation issue.

✅ Improvement (8)

urllib3 (+2, -4)

This is an improvement. The PR enhanced pyrefly's containment narrowing to preserve literal precision for typed containers. The removed errors were false positives:

  1. The 'no-matching-overload' errors on lines 235-237 and 238-240 incorrectly flagged _SSL_VERSION_TO_TLS_VERSION.get(ssl_version, TLSVersion.MINIMUM_SUPPORTED) calls. The dict.get() method has a proper overload for get(key, default) that returns the union of the value type and default type. Since the default is a TLSVersion enum member, the return type should be TLSVersion, not cause an overload error.

  2. The 'bad-assignment' errors claiming int | Unknown is incompatible with TLSVersion were wrong. The dict.get() calls return TLSVersion values because the fallback defaults (TLSVersion.MINIMUM_SUPPORTED, TLSVersion.MAXIMUM_SUPPORTED) are TLSVersion enum members.

The new 'bad-assignment' errors claiming int is incompatible with TLSVersion on lines 254 and 259 appear to be revealing actual type issues where integer values are being assigned to attributes expecting TLSVersion enums. However, examining the code shows these assignments use variables (ssl_minimum_version, ssl_maximum_version) that should be TLSVersion values from the dict.get() calls above. The improved narrowing likely revealed that these variables weren't being properly inferred as TLSVersion types.

Overall, pyrefly got better at type narrowing and removed false positives while potentially catching real type inconsistencies.

Attribution: The changes to membership_narrow_type() and membership_narrow_container_type() in pyrefly/lib/alt/narrow.rs improved type narrowing for containment checks. This better preserved literal types in dict.get() calls, resolving the type inference that was causing the false positive overload and assignment errors.

pylint (-1)

This is a genuine type bug that pyrefly is now correctly catching. The variable open_func_name is declared as str | None, initialized to None, and only conditionally assigned string values. However, it's passed to _check_open_call() which expects a str parameter. The improved narrowing in this PR allows pyrefly to correctly track that the variable can still be None at the call site, revealing a real type safety issue in the pylint codebase.
Attribution: The changes to membership_narrow_type() and related functions in pyrefly/lib/alt/narrow.rs improved pyrefly's type narrowing capabilities. The enhanced narrowing now correctly identifies that open_func_name can be None after the conditional assignments, whereas previously pyrefly may have been less precise about tracking the possible None value through the control flow.

schema_salad (-1)

This is an improvement. The removed error was a false positive caused by pyrefly's failure to properly narrow types after containment checks. The code is correct: json_data.get('type') returns str | None, but the if atype in PRIMITIVE_TYPES: check narrows this to str since PRIMITIVE_TYPES contains only strings. The PR's enhanced containment narrowing logic now correctly handles this pattern, eliminating the incorrect Unknown | None type inference.
Attribution: The change to membership_narrow_type() and related functions in pyrefly/lib/alt/narrow.rs improved type narrowing for containment checks. The new membership_narrow_container_type() function and enhanced AtomicNarrowOp::In handling now correctly narrow types based on container membership, fixing the false positive where atype in PRIMITIVE_TYPES failed to narrow atype from str | None to str.

streamlit (+1)

Looking at the code, icon_position has type IconPosition | str | None initially. After the containment check if icon_position not in _VALID_ICON_POSITIONS (where _VALID_ICON_POSITIONS is ('left', 'right')), pyrefly now correctly narrows the type to Literal['left', 'right'] due to the improved containment narrowing logic. The cast cast('IconPosition', icon_position) is therefore redundant since IconPosition is defined as Literal['left', 'right'] on line 93. This is a legitimate improvement - pyrefly is now smart enough to detect unnecessary casts that were previously hidden by imprecise type narrowing.
Attribution: The change to containment narrowing in pyrefly/lib/alt/narrow.rs improved type inference precision. The new membership_narrow_type() and related functions now better preserve literal types when checking containment, which allows pyrefly to more accurately infer that icon_position after the containment check if icon_position not in _VALID_ICON_POSITIONS is already narrowed to the exact Literal['left', 'right'] type, making the subsequent cast redundant.

meson (+2)

This is an IMPROVEMENT. The PR enhanced pyrefly's containment narrowing to work with typed containers, not just literal expressions. Now when code checks lang in compilers.all_languages where all_languages has a type like set[Language], pyrefly can narrow lang to Language, making the subsequent T.cast('Language', lang) redundant. These are legitimate redundant cast detections - the casts are no longer needed because pyrefly's improved type inference already provides the correct narrow type. This represents better type checking capability, correctly identifying unnecessary type annotations.
Attribution: The changes to membership_narrow_container_type() and related functions in pyrefly/lib/alt/narrow.rs enhanced containment narrowing to work with typed containers (like set[Language]), allowing pyrefly to narrow the type of lang after lang in compilers.all_languages checks, making the subsequent T.cast('Language', lang) redundant

mypy (-2)

The removed errors were false positives. After key in MYPYC_ATTRS where MYPYC_ATTRS: frozenset[MypycAttr], the variable key should be narrowed from str to MypycAttr. This makes both attrs[key] = value (on MypycAttrs TypedDict) and lines[key] = line (on dict[MypycAttr, int]) valid operations. The PR improved pyrefly's containment narrowing to correctly handle typed containers per the narrowing spec, removing these incorrect errors.
Attribution: The change to membership_narrow_container_type() in pyrefly/lib/alt/narrow.rs added precise narrowing for typed containers like frozenset[MypycAttr]. This allows pyrefly to correctly narrow key from str to MypycAttr after the containment check, removing the false positive unsupported-operation errors.

Tanjun (-1)

This removal is an improvement. The error was a false positive caused by pyrefly's previous inability to properly narrow types based on containment checks with typed containers. Looking at the code:

  1. Line 142: command_type = hikari.CommandType(self._interaction.command_type) - this creates a CommandType enum value
  2. Line 143: assert command_type in _VALID_TYPES - this should narrow the type
  3. Lines 57-59: _VALID_TYPES is defined as frozenset([hikari.CommandType.USER, hikari.CommandType.MESSAGE])
  4. Line 144: return command_type - should now be narrowed to the literal union

The PR improved containment narrowing to derive finite membership domains from typed containers like frozenset, allowing pyrefly to correctly understand that after the assertion, command_type can only be one of the two specific enum values, making it compatible with the Literal[CommandType.MESSAGE, CommandType.USER] return type. The old pyrefly incorrectly failed to perform this narrowing, producing a false positive.

Attribution: The change to membership_narrow_container_type() in pyrefly/lib/alt/narrow.rs improved containment narrowing for typed containers. The new logic in membership_narrow_container_type() now properly handles frozenset types and derives finite membership domains from typed containers, allowing pyrefly to correctly narrow command_type after the assert command_type in _VALID_TYPES check.

mongo-python-driver (+1)

This is a genuine type error that pyrefly is now correctly catching. The function validate_read_preference_mode is annotated to return _ServerMode but actually returns a string literal. The PR improved pyrefly's containment narrowing, allowing it to precisely track that after value not in _MONGOS_MODES fails (meaning value IS in _MONGOS_MODES), the value is narrowed to the union of those specific string literals. When the function returns this narrowed value, pyrefly correctly identifies the type mismatch with the declared return type _ServerMode. The docstring even confirms this returns 'the original value' rather than a _ServerMode instance, indicating this is a real bug in the type annotation.
Attribution: The change to membership_narrow_container_type() in pyrefly/lib/alt/narrow.rs improved containment narrowing to preserve literal precision for typed containers. This allowed pyrefly to correctly narrow value to Literal['nearest', 'primary', 'primaryPreferred', 'secondary', 'secondaryPreferred'] after the not in _MONGOS_MODES check, revealing the existing type annotation bug.

➖ Neutral (1)

pydantic (+1, -1)

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


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (1 heuristic, 9 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.

Narrow types based on collection containment

4 participants