Skip to content

[ty] Add Flag and IntFlag support#26289

Closed
charliermarsh wants to merge 16 commits into
charlie/enum-member-exhaustivenessfrom
charlie/flag-support
Closed

[ty] Add Flag and IntFlag support#26289
charliermarsh wants to merge 16 commits into
charlie/enum-member-exhaustivenessfrom
charlie/flag-support

Conversation

@charliermarsh

Copy link
Copy Markdown
Member

Summary

This PR is stacked on #26277, which models Flag and IntFlag member sets as non-exhaustive. It adds the semantic support needed to reason about their runtime values without enumerating every possible flag combination.

We build a cached Flag profile containing the member type, declared integer masks, canonical iteration members, boundary policy, and the information needed to construct exact named results. We use that profile for auto() values, constructors, bitwise operations, inversion, truthiness, length, instance iteration, and subset membership, including classes created with the functional enum syntax.

For example, named results remain literals while unnamed combinations remain nominal Flag values:

from enum import Flag, auto

class Permission(Flag):
    READ = auto()
    WRITE = auto()
    READ_WRITE = READ | WRITE

reveal_type(Permission.READ | Permission.WRITE)  # Literal[Permission.READ_WRITE]
reveal_type(~Permission.READ)                    # Permission

The implementation also models STRICT, CONFORM, EJECT, and KEEP; preserves pre-3.11 auto(), inversion, negative-value, and inherited-operator behavior; and respects custom member types, metaclass calls, _missing_, reflected operators, and iteration hooks. When those hooks or member values make the runtime result unknowable, we retain the existing conservative fallback instead of applying the exact Flag path.

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Jun 23, 2026
@astral-sh-bot

astral-sh-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 94.47%. The percentage of expected errors that received a diagnostic held steady at 89.10%. The number of fully passing files held steady at 95/134.

@astral-sh-bot

astral-sh-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Memory usage report

Summary

Project Old New Diff Outcome
prefect 516.38MB 517.38MB +0.19% (1.01MB)
sphinx 193.43MB 193.69MB +0.14% (269.65kB)
trio 77.53MB 77.60MB +0.09% (73.32kB)
flake8 30.75MB 30.78MB +0.09% (29.40kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
enum_metadata 2.37MB 3.17MB +33.97% (823.19kB)
infer_expression_types_impl 55.93MB 56.01MB +0.14% (82.09kB)
infer_definition_types 69.15MB 69.23MB +0.11% (79.50kB)
infer_scope_types_impl 38.84MB 38.88MB +0.09% (34.15kB)
enum_class_literal 399.95kB 406.92kB +1.74% (6.97kB)
parsed_module 19.35MB 19.35MB -0.01% (1.79kB)
function_known_decorators 3.78MB 3.78MB +0.03% (1.34kB)
all_narrowing_constraints_for_expression 6.60MB 6.60MB +0.02% (1.08kB)
infer_statement_types_impl 1.51MB 1.52MB +0.06% (984.00B)
TupleType<'db>::to_class_type_ 476.76kB 477.72kB +0.20% (980.00B)
UnionType 1.06MB 1.06MB +0.07% (800.00B)
Specialization 2.66MB 2.66MB +0.02% (672.00B)
GenericAlias 1.10MB 1.10MB +0.04% (504.00B)
FunctionType<'db>::signature_ 2.91MB 2.91MB -0.01% (420.00B)
StaticClassLiteral<'db>::try_mro_ 4.54MB 4.54MB +0.01% (392.00B)
... 37 more

sphinx

Name Old New Diff Outcome
enum_metadata 592.20kB 798.73kB +34.87% (206.53kB)
infer_expression_types_impl 22.99MB 23.01MB +0.10% (24.40kB)
infer_definition_types 19.55MB 19.56MB +0.06% (12.93kB)
infer_scope_types_impl 10.51MB 10.52MB +0.07% (7.20kB)
parsed_module 18.37MB 18.36MB -0.03% (5.57kB)
StaticClassLiteral<'db>::try_mro_ 2.48MB 2.48MB +0.20% (5.02kB)
TupleType<'db>::to_class_type_ 161.49kB 165.81kB +2.68% (4.32kB)
Specialization 1.40MB 1.40MB +0.27% (3.83kB)
enum_class_literal 87.69kB 90.55kB +3.26% (2.86kB)
UnionType 567.89kB 570.59kB +0.48% (2.70kB)
GenericAlias 612.42kB 615.02kB +0.42% (2.60kB)
is_redundant_with_impl 901.58kB 904.14kB +0.28% (2.55kB)
is_redundant_with_impl::interned_arguments 1.13MB 1.13MB +0.16% (1.89kB)
StaticClassLiteral<'db>::try_mro_::interned_arguments 609.75kB 610.88kB +0.18% (1.12kB)
try_call_bin_op_return_type_impl 194.02kB 193.54kB -0.25% (496.00B)
... 39 more

trio

Name Old New Diff Outcome
enum_metadata 211.57kB 280.05kB +32.37% (68.49kB)
infer_expression_types_impl 6.51MB 6.51MB +0.10% (6.64kB)
infer_definition_types 5.59MB 5.60MB +0.08% (4.43kB)
infer_scope_types_impl 3.19MB 3.20MB +0.13% (4.16kB)
Type<'db>::class_member_with_policy_ 1.25MB 1.25MB -0.15% (1.87kB)
member_lookup_with_policy_inner 1.52MB 1.52MB -0.12% (1.83kB)
enum_class_literal 31.30kB 33.03kB +5.52% (1.73kB)
try_call_bin_op_return_type_impl 44.16kB 42.49kB -3.79% (1.68kB)
infer_deferred_types 1.80MB 1.80MB -0.07% (1.27kB)
FunctionType<'db>::signature_ 723.80kB 722.65kB -0.16% (1.15kB)
member_lookup_with_policy_inner::interned_arguments 765.12kB 764.18kB -0.12% (960.00B)
UnionType 142.09kB 142.97kB +0.62% (896.00B)
Type<'db>::class_member_with_policy_::interned_arguments 627.15kB 626.34kB -0.13% (832.00B)
Type<'db>::apply_specialization_inner_::interned_arguments 504.84kB 504.06kB -0.15% (800.00B)
TupleType<'db>::to_class_type_ 43.69kB 44.46kB +1.78% (796.00B)
... 26 more

flake8

Name Old New Diff Outcome
enum_metadata 58.49kB 81.33kB +39.05% (22.84kB)
parsed_module 9.77MB 9.77MB +0.02% (1.54kB)
infer_definition_types 1.35MB 1.35MB +0.10% (1.44kB)
infer_expression_types_impl 960.95kB 962.26kB +0.14% (1.31kB)
enum_class_literal 8.84kB 9.84kB +11.27% (1020.00B)
infer_scope_types_impl 672.01kB 672.61kB +0.09% (612.00B)
StaticClassLiteral<'db>::try_mro_ 294.78kB 294.96kB +0.06% (184.00B)
known_class_to_class_literal 5.04kB 5.18kB +2.71% (140.00B)
StaticClassLiteral<'db>::decorators_inner_ 5.59kB 5.70kB +1.89% (108.00B)
infer_statement_types_impl 54.41kB 54.48kB +0.13% (72.00B)
StaticClassLiteral<'db>::try_mro_::interned_arguments 73.41kB 73.48kB +0.10% (72.00B)
KnownClassArgument 1.91kB 1.97kB +2.86% (56.00B)
function_known_decorators 138.91kB 138.95kB +0.03% (36.00B)
infer_deferred_types 446.01kB 446.03kB +0.01% (24.00B)

@astral-sh-bot

astral-sh-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
unsupported-operator 0 0 2
Total 0 0 2

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Raw diff:

core (https://github.com/home-assistant/core)
- homeassistant/components/hunterdouglas_powerview/cover.py:368:9 error[unsupported-operator] Operator `|=` is not supported between objects of type `None` and `Literal[CoverEntityFeature.OPEN_TILT]`
+ homeassistant/components/hunterdouglas_powerview/cover.py:368:9 error[unsupported-operator] Operator `|=` is not supported between objects of type `None` and `CoverEntityFeature`
- homeassistant/components/velux/cover.py:242:9 error[unsupported-operator] Operator `|=` is not supported between objects of type `None` and `Literal[CoverEntityFeature.OPEN_TILT]`
+ homeassistant/components/velux/cover.py:242:9 error[unsupported-operator] Operator `|=` is not supported between objects of type `None` and `CoverEntityFeature`

Full report with detailed diff (timing results)

@charliermarsh charliermarsh force-pushed the charlie/enum-member-exhaustiveness branch 2 times, most recently from 9ca4e30 to b619ccc Compare June 24, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant