Skip to content

Conversation

thejchap
Copy link
Contributor

@thejchap thejchap commented Sep 24, 2025

Summary

see astral-sh/ty#1241

  • support .value on single member enums
    • more context in this pr - for single-member enums, the type is an instance of the enum class. for multi-member enums, its a union of literals. handle the former
  • handle str, bytes, float as Enum mixins
    • this explicitly checks if the enum is a subclass of any of these and sets the type
  • add TODO test for tuple as an Enum mixin

i'm not sure this is the best implementation for auto for these mixin types - at runtime it looks like the counter gets coerced somehow into the mixin type (ie auto() -> (1,) for tuple mixins). could probably handle this in a more generic way but this seems like a decent step for now

Test Plan

new mdtests

Copy link
Contributor

github-actions bot commented Sep 24, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

Copy link
Contributor

github-actions bot commented Sep 24, 2025

mypy_primer results

Changes were detected when running on open source projects
django-stubs (https://github.com/typeddjango/django-stubs)
- tests/assert_type/db/models/test_enums.py:194:1: error[type-assertion-failure] Argument does not have asserted type `str`
- Found 468 diagnostics
+ Found 467 diagnostics
No memory usage changes detected ✅

@thejchap thejchap force-pushed the thejchap/enum-auto2 branch 2 times, most recently from 7208cf6 to 87548a3 Compare September 24, 2025 02:11
@AlexWaygood AlexWaygood added the ty Multi-file analysis & type inference label Sep 24, 2025
@thejchap thejchap force-pushed the thejchap/enum-auto2 branch 2 times, most recently from 184c132 to eb5cf24 Compare September 26, 2025 02:55
@thejchap thejchap marked this pull request as ready for review September 26, 2025 02:59
Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Thank you!

Comment on lines +157 to +168
} else if Type::ClassLiteral(class)
.is_subtype_of(db, KnownClass::Str.to_subclass_of(db))
{
KnownClass::Str.to_instance(db)
} else if Type::ClassLiteral(class)
.is_subtype_of(db, KnownClass::Bytes.to_subclass_of(db))
{
KnownClass::Bytes.to_instance(db)
} else if Type::ClassLiteral(class)
.is_subtype_of(db, KnownClass::Float.to_subclass_of(db))
{
KnownClass::Float.to_instance(db)
Copy link
Member

@AlexWaygood AlexWaygood Sep 26, 2025

Choose a reason for hiding this comment

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

hmm, I'm not sure we should be adding more special cases here. An enum class that uses bytes as a mixin isn't conceptually any different from an enum that uses tuple or list or dict as a mixin, or a custom dataclass or custom NamedTuple as a mixin. Why should we treat bytes and float specially here?

Rather than adding more special cases, I think we should have a generalised fallback logic that iterates through the MRO of the enum class to check whether it has a custom mixin that isn't str or int. (Those two make sense to special-case, I think, because of the presence of StrEnum and IntEnum in the stdlib. You can determine whether an element in the MRO is a "custom mixin" class by checking if it isn't object and isn't a subclass of enum.Enum -- if both those conditions hold, I think we can treat it as a mixin class.) If the enum class has a custom mixin and auto() is used to define the value of an enum member, we should probably infer the .value of that enum member as just being Any, which would still be more accurate than what we have on main (even if it isn't very precise!). It seems pretty hard to predict whether the .value property is going to give you an instance of the mixin or an integer:

>>> class Foo: ...
>>> import enum
>>> class FooEnum(Foo, enum.Enum):
...     X = enum.auto()
...     
>>> FooEnum.X.value
1
>>> class BEnum(bytes, enum.Enum):
...     X = enum.auto()
...     
>>> BEnum.X.value
b'\x00'
>>> class TEnum(tuple, enum.Enum):
...     X = enum.auto()
...     
>>> TEnum.X.value
(1,)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this makes a lot more sense - thanks - didn't feel great about the explicit special cases after going down that road...

@thejchap thejchap marked this pull request as draft September 26, 2025 16:31
@thejchap thejchap force-pushed the thejchap/enum-auto2 branch from eb5cf24 to fc0bd30 Compare October 14, 2025 02:05
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