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

Skip to content

[ty] Treat assigned enum hooks conservatively#25958

Merged
charliermarsh merged 1 commit into
mainfrom
charlie/conservative-enum-construction-hooks
Jun 13, 2026
Merged

[ty] Treat assigned enum hooks conservatively#25958
charliermarsh merged 1 commit into
mainfrom
charlie/conservative-enum-construction-hooks

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

Enum metadata currently recognizes __init__, __new__, and _generate_next_value_ only when the hook is defined as a function within the class body. If a class instead assigns or reassigns one of these hooks, we (incorrectly) fall through and infer / validate member values against a shadowed method:

class Token(Enum):
    _value_: int

    def __new__(cls, value: int): ...
    __new__ = cast(Any, external_new)

    TEXT = "text"

reveal_type(Token.TEXT.value)  # int

This PR resolves each hook from its final binding. When the binding is a single function definition, we can inspect its parameters and return type. Everything else is considered "opaque" (e.g., an assignment through Any, a descriptor, or multiple possible bindings). An opaque binding prevents validation against a hook (like __new__) and suppresses the raw _value_ annotation fallback, while a separate function binding still validates the original member payload. Without an explicit _value_ annotation, any opaque construction hook makes .value dynamic; with one, the annotation remains authoritative. An opaque _generate_next_value_ binding affects only auto() members.

Custom metaclass __prepare__ and assigned __new__ hooks are both now treated like a directly defined metaclass __new__, because they can rewrite the class namespace before enum members are constructed. Alias detection is also conservative whenever a construction hook can change stored values:

class Kind(Enum):
    _generate_next_value_ = cast(Any, external_generator)

    A = auto()
    B = auto()
    EXPLICIT = 1

reveal_type(Kind.A.value)         # Any
reveal_type(Kind.EXPLICIT.value)  # Literal[1]

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

astral-sh-bot Bot commented Jun 13, 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.36%. The percentage of expected errors that received a diagnostic held steady at 88.82%. The number of fully passing files held steady at 93/134.

@astral-sh-bot

astral-sh-bot Bot commented Jun 13, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@charliermarsh charliermarsh force-pushed the charlie/conservative-enum-construction-hooks branch from c2a4ca5 to 6428b17 Compare June 13, 2026 15:48
@astral-sh-bot

astral-sh-bot Bot commented Jun 13, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

No diagnostic changes detected ✅

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

Full report with detailed diff (timing results)

@charliermarsh charliermarsh force-pushed the charlie/conservative-enum-construction-hooks branch 6 times, most recently from 5fdebb1 to 055291f Compare June 13, 2026 18:38
@charliermarsh charliermarsh marked this pull request as ready for review June 13, 2026 18:47
@astral-sh-bot astral-sh-bot Bot requested a review from dhruvmanila June 13, 2026 18:47
@charliermarsh charliermarsh force-pushed the charlie/conservative-enum-construction-hooks branch from 055291f to 2e0ccbb Compare June 13, 2026 18:49
## Summary

Enum metadata currently recognizes `__init__`, `__new__`, and `_generate_next_value_` only when the hook is a directly defined function. If a class assigns or reassigns one of these hooks, we can incorrectly fall through to a shadowed method and infer or validate member values against code that will not run.

```python
class Token(Enum):
    _value_: int

    def __new__(cls, value: int): ...
    __new__ = cast(Any, external_new)

    TEXT = "text"

reveal_type(Token.TEXT.value)  # int
```

This resolves each hook from its effective final binding. When the final binding is a single function definition, we can inspect its parameters and return type. Other final bindings are opaque to this analysis, as with an assignment through `Any`, a descriptor, or multiple possible bindings. An opaque binding still shadows definitions later in the MRO, so we must not fall through to a function that will not run.

`__init__` and `__new__` are tracked independently: an opaque binding prevents validation against that hook and suppresses the raw `_value_` annotation fallback, while a separate function binding still validates the original member payload. Without an explicit `_value_` annotation, any opaque construction hook makes `.value` dynamic; with one, the annotation remains authoritative. An opaque `_generate_next_value_` binding affects only `auto()` members.

Custom metaclass `__prepare__` and assigned `__new__` hooks are now treated like a directly defined metaclass `__new__`, because they can rewrite the class namespace before enum members are constructed. Alias detection is also conservative whenever a construction hook can change stored values.

```python
class Kind(Enum):
    _generate_next_value_ = cast(Any, external_generator)

    A = auto()
    B = auto()
    EXPLICIT = 1

reveal_type(Kind.A.value)         # Any
reveal_type(Kind.EXPLICIT.value)  # Literal[1]
```
@charliermarsh charliermarsh force-pushed the charlie/conservative-enum-construction-hooks branch from 2e0ccbb to 9ae4931 Compare June 13, 2026 19:04
@charliermarsh charliermarsh enabled auto-merge (squash) June 13, 2026 19:05
@charliermarsh charliermarsh merged commit 4863e91 into main Jun 13, 2026
58 checks passed
@charliermarsh charliermarsh deleted the charlie/conservative-enum-construction-hooks branch June 13, 2026 19:10
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.

2 participants