Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[ty] Treat custom enum __new__ values as dynamic#25136

Merged
charliermarsh merged 3 commits into
mainfrom
charlie/enum-new
May 13, 2026
Merged

[ty] Treat custom enum __new__ values as dynamic#25136
charliermarsh merged 3 commits into
mainfrom
charlie/enum-new

Conversation

@charliermarsh
Copy link
Copy Markdown
Member

Summary

For enums with a custom __new__, the member RHS is input to the constructor, but __new__ can assign _value_ independently. We now treat such values as dynamically typed, unless _value_ is explicitly annotated.

To quote @JelleZijlstra: "Looks right and fairly simple, behavior matches pyright and mypy."

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label May 13, 2026
@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented May 13, 2026

Typing conformance results

No changes detected ✅

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

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented May 13, 2026

Memory usage report

Summary

Project Old New Diff Outcome
prefect 682.62MB 682.67MB +0.01% (48.97kB)
trio 115.56MB 115.58MB +0.02% (17.96kB)
sphinx 256.21MB 256.23MB +0.01% (15.71kB)
flake8 47.42MB 47.43MB +0.01% (2.84kB)

Significant changes

Click to expand detailed breakdown

prefect

Name Old New Diff Outcome
enum_metadata 2.77MB 2.82MB +1.64% (46.63kB)
infer_definition_types 81.05MB 81.05MB +0.00% (1.36kB)
FunctionType 9.48MB 9.48MB +0.01% (672.00B)
OverloadLiteral 3.78MB 3.78MB +0.01% (336.00B)

trio

Name Old New Diff Outcome
Type<'db>::apply_specialization_::interned_arguments 625.08kB 630.31kB +0.84% (5.23kB)
enum_metadata 231.18kB 235.32kB +1.79% (4.15kB)
Type<'db>::apply_specialization_ 616.17kB 619.84kB +0.59% (3.66kB)
infer_scope_types_impl 4.02MB 4.02MB +0.05% (2.09kB)
infer_deferred_types 2.10MB 2.10MB +0.03% (676.00B)
infer_definition_types 7.24MB 7.24MB +0.01% (464.00B)
FunctionType<'db>::signature_ 1.11MB 1.11MB +0.03% (396.00B)
InferableTypeVarsInner 75.81kB 76.15kB +0.44% (344.00B)
FunctionType 1.53MB 1.53MB +0.01% (224.00B)
Specialization 457.11kB 457.33kB +0.05% (224.00B)
GenericContext 124.71kB 124.85kB +0.11% (140.00B)
inferable_typevars_inner 28.96kB 29.10kB +0.46% (136.00B)
bound_typevar_default_type 14.11kB 14.22kB +0.78% (112.00B)
OverloadLiteral 439.33kB 439.44kB +0.02% (112.00B)
BoundTypeVarInstance 162.63kB 162.70kB +0.04% (72.00B)

sphinx

Name Old New Diff Outcome
enum_metadata 704.03kB 715.90kB +1.69% (11.87kB)
infer_deferred_types 4.73MB 4.73MB +0.01% (676.00B)
Type<'db>::apply_specialization_::interned_arguments 1.44MB 1.44MB +0.04% (640.00B)
infer_definition_types 21.99MB 21.99MB +0.00% (464.00B)
Type<'db>::apply_specialization_ 1.41MB 1.41MB +0.03% (448.00B)
FunctionType<'db>::signature_ 2.38MB 2.38MB +0.02% (396.00B)
infer_scope_types_impl 12.97MB 12.97MB +0.00% (252.00B)
FunctionType 3.18MB 3.18MB +0.01% (224.00B)
InferableTypeVarsInner 84.40kB 84.57kB +0.20% (172.00B)
GenericContext 134.89kB 135.02kB +0.10% (140.00B)
Specialization 1018.34kB 1018.45kB +0.01% (112.00B)
OverloadLiteral 1019.27kB 1019.38kB +0.01% (112.00B)
TypeVarInstance 173.44kB 173.53kB +0.05% (96.00B)
BoundTypeVarInstance 624.73kB 624.80kB +0.01% (72.00B)
inferable_typevars_inner 34.97kB 35.04kB +0.19% (68.00B)
... 1 more

flake8

Name Old New Diff Outcome
enum_metadata 64.89kB 66.12kB +1.88% (1.22kB)
infer_definition_types 1.72MB 1.72MB +0.05% (928.00B)
FunctionType 451.45kB 451.89kB +0.10% (448.00B)
OverloadLiteral 120.94kB 121.16kB +0.18% (224.00B)
use_def_map 19.86kB 19.92kB +0.29% (60.00B)

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot Bot commented May 13, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-assignment 3 0 0
Total 3 0 0

Raw diff:

mypy (https://github.com/python/mypy)
+ mypy/typeshed/stdlib/ssl.pyi:268:5 error[invalid-assignment] Enum member `SERVER_AUTH` is incompatible with `__new__`
+ mypy/typeshed/stdlib/ssl.pyi:269:5 error[invalid-assignment] Enum member `CLIENT_AUTH` is incompatible with `__new__`

typeshed-stats (https://github.com/AlexWaygood/typeshed-stats)
+ tests/test_gather.py:114:13 error[invalid-assignment] Enum member `NOT_A_STRING` is incompatible with `__new__`

Full report with detailed diff (timing results)

@charliermarsh charliermarsh marked this pull request as ready for review May 13, 2026 03:08
reveal_type(Planet2.MERCURY._value_) # revealed: Any
```

### `__new__` without `_value_` annotation
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we test for __new__ coming from a parent class? The code makes explicit allowance for inherited __new__.

class Base(Enum):
def __new__(cls, value: str, connector_id: int = 0) -> "Base":
obj = object.__new__(cls)
obj._value_ = value
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we give any errors if this is set to a different type instead? Probably not essential but would be nice to note.

I tried this sample in multiplay, just changing the annotation on the __new__ to value: int, and only pyright detects the issue.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It appears not today, but I can fix that in a follow-up.

@charliermarsh charliermarsh enabled auto-merge (squash) May 13, 2026 23:14
@charliermarsh charliermarsh disabled auto-merge May 13, 2026 23:16
@charliermarsh charliermarsh merged commit ed819f9 into main May 13, 2026
58 checks passed
@charliermarsh charliermarsh deleted the charlie/enum-new branch May 13, 2026 23:47
thejchap pushed a commit to thejchap/ruff that referenced this pull request May 23, 2026
## Summary

For enums with a custom `__new__`, the member RHS is input to the
constructor, but `__new__` can assign `_value_` independently. We now
treat such values as dynamically typed, unless `_value_` is explicitly
annotated.

To quote @JelleZijlstra: "Looks right and fairly simple, behavior
matches pyright and mypy."
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.

3 participants