[ty] implement auto() for StrEnum (#20524)
Some checks are pending
CI / cargo build (release) (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

## Summary
see discussion here:
https://github.com/astral-sh/ty/issues/876#issuecomment-3310130167

https://docs.python.org/3/library/enum.html#enum.StrEnum

> Note Using
[auto](https://docs.python.org/3/library/enum.html#enum.auto) with
[StrEnum](https://docs.python.org/3/library/enum.html#enum.StrEnum)
results in the lower-cased member name as the value.

## Test Plan
- new mdtest
- also, added a test to assert the (already correct) behavior for
`IntEnum`

---------

Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
justin 2025-09-23 06:22:59 -04:00 committed by GitHub
parent 036f3616a1
commit ef4df34652
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 8 deletions

View file

@ -3664,6 +3664,7 @@ pub enum KnownClass {
Auto,
Member,
Nonmember,
StrEnum,
// abc
ABCMeta,
// Types
@ -3804,6 +3805,7 @@ impl KnownClass {
| Self::Auto
| Self::Member
| Self::Nonmember
| Self::StrEnum
| Self::ABCMeta
| Self::Iterable
| Self::Iterator
@ -3864,6 +3866,7 @@ impl KnownClass {
| KnownClass::Auto
| KnownClass::Member
| KnownClass::Nonmember
| KnownClass::StrEnum
| KnownClass::ABCMeta
| KnownClass::GenericAlias
| KnownClass::ModuleType
@ -3944,6 +3947,7 @@ impl KnownClass {
| KnownClass::Auto
| KnownClass::Member
| KnownClass::Nonmember
| KnownClass::StrEnum
| KnownClass::ABCMeta
| KnownClass::GenericAlias
| KnownClass::ModuleType
@ -4024,6 +4028,7 @@ impl KnownClass {
| KnownClass::Auto
| KnownClass::Member
| KnownClass::Nonmember
| KnownClass::StrEnum
| KnownClass::ABCMeta
| KnownClass::GenericAlias
| KnownClass::ModuleType
@ -4142,6 +4147,7 @@ impl KnownClass {
| Self::Auto
| Self::Member
| Self::Nonmember
| Self::StrEnum
| Self::ABCMeta
| Self::Super
| Self::StdlibAlias
@ -4226,6 +4232,7 @@ impl KnownClass {
Self::Auto => "auto",
Self::Member => "member",
Self::Nonmember => "nonmember",
Self::StrEnum => "StrEnum",
Self::ABCMeta => "ABCMeta",
Self::Super => "super",
Self::Iterable => "Iterable",
@ -4462,9 +4469,12 @@ impl KnownClass {
| Self::Property => KnownModule::Builtins,
Self::VersionInfo => KnownModule::Sys,
Self::ABCMeta => KnownModule::Abc,
Self::Enum | Self::EnumType | Self::Auto | Self::Member | Self::Nonmember => {
KnownModule::Enum
}
Self::Enum
| Self::EnumType
| Self::Auto
| Self::Member
| Self::Nonmember
| Self::StrEnum => KnownModule::Enum,
Self::GenericAlias
| Self::ModuleType
| Self::FunctionType
@ -4591,6 +4601,7 @@ impl KnownClass {
| Self::Auto
| Self::Member
| Self::Nonmember
| Self::StrEnum
| Self::ABCMeta
| Self::Super
| Self::NewType
@ -4675,6 +4686,7 @@ impl KnownClass {
| Self::Auto
| Self::Member
| Self::Nonmember
| Self::StrEnum
| Self::ABCMeta
| Self::Super
| Self::UnionType
@ -4762,6 +4774,9 @@ impl KnownClass {
"EnumType" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
Self::EnumType
}
"StrEnum" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
Self::StrEnum
}
"auto" => Self::Auto,
"member" => Self::Member,
"nonmember" => Self::Nonmember,
@ -4835,6 +4850,7 @@ impl KnownClass {
| Self::Auto
| Self::Member
| Self::Nonmember
| Self::StrEnum
| Self::ABCMeta
| Self::Super
| Self::NotImplementedType
@ -5387,7 +5403,9 @@ mod tests {
}
KnownClass::GenericAlias => PythonVersion::PY39,
KnownClass::KwOnly => PythonVersion::PY310,
KnownClass::Member | KnownClass::Nonmember => PythonVersion::PY311,
KnownClass::Member | KnownClass::Nonmember | KnownClass::StrEnum => {
PythonVersion::PY311
}
_ => PythonVersion::PY37,
};
(class, version_added)

View file

@ -6,8 +6,8 @@ use crate::{
place::{Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
semantic_index::{place_table, use_def_map},
types::{
ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy, Type,
TypeQualifiers,
ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy,
StringLiteralType, Type, TypeQualifiers,
},
};
@ -77,12 +77,14 @@ pub(crate) fn enum_metadata<'db>(
return None;
}
let is_str_enum =
Type::ClassLiteral(class).is_subtype_of(db, KnownClass::StrEnum.to_subclass_of(db));
let scope_id = class.body_scope(db);
let use_def_map = use_def_map(db, scope_id);
let table = place_table(db, scope_id);
let mut enum_values: FxHashMap<Type<'db>, Name> = FxHashMap::default();
// TODO: handle `StrEnum` which uses lowercase names as values when using `auto()`.
let mut auto_counter = 0;
let ignored_names: Option<Vec<&str>> = if let Some(ignore) = table.symbol_id("_ignore_") {
@ -148,7 +150,14 @@ pub(crate) fn enum_metadata<'db>(
// enum.auto
Some(KnownClass::Auto) => {
auto_counter += 1;
Some(Type::IntLiteral(auto_counter))
Some(if is_str_enum {
Type::StringLiteral(StringLiteralType::new(
db,
name.to_lowercase().as_str(),
))
} else {
Type::IntLiteral(auto_counter)
})
}
_ => None,