mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[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
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:
parent
036f3616a1
commit
ef4df34652
3 changed files with 84 additions and 8 deletions
|
@ -267,6 +267,11 @@ reveal_type(Color.red)
|
||||||
|
|
||||||
### Using `auto()`
|
### Using `auto()`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[environment]
|
||||||
|
python-version = "3.11"
|
||||||
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from ty_extensions import enum_members
|
from ty_extensions import enum_members
|
||||||
|
@ -277,6 +282,50 @@ class Answer(Enum):
|
||||||
|
|
||||||
# revealed: tuple[Literal["YES"], Literal["NO"]]
|
# revealed: tuple[Literal["YES"], Literal["NO"]]
|
||||||
reveal_type(enum_members(Answer))
|
reveal_type(enum_members(Answer))
|
||||||
|
|
||||||
|
reveal_type(Answer.YES.value) # revealed: Literal[1]
|
||||||
|
reveal_type(Answer.NO.value) # revealed: Literal[2]
|
||||||
|
```
|
||||||
|
|
||||||
|
Usages of `auto()` can be combined with manual value assignments:
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Mixed(Enum):
|
||||||
|
MANUAL_1 = -1
|
||||||
|
AUTO_1 = auto()
|
||||||
|
MANUAL_2 = -2
|
||||||
|
AUTO_2 = auto()
|
||||||
|
|
||||||
|
reveal_type(Mixed.MANUAL_1.value) # revealed: Literal[-1]
|
||||||
|
reveal_type(Mixed.AUTO_1.value) # revealed: Literal[1]
|
||||||
|
reveal_type(Mixed.MANUAL_2.value) # revealed: Literal[-2]
|
||||||
|
reveal_type(Mixed.AUTO_2.value) # revealed: Literal[2]
|
||||||
|
```
|
||||||
|
|
||||||
|
When using `auto()` with `StrEnum`, the value is the lowercase name of the member:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from enum import StrEnum, auto
|
||||||
|
|
||||||
|
class Answer(StrEnum):
|
||||||
|
YES = auto()
|
||||||
|
NO = auto()
|
||||||
|
|
||||||
|
reveal_type(Answer.YES.value) # revealed: Literal["yes"]
|
||||||
|
reveal_type(Answer.NO.value) # revealed: Literal["no"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `auto()` with `IntEnum` also works as expected:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from enum import IntEnum, auto
|
||||||
|
|
||||||
|
class Answer(IntEnum):
|
||||||
|
YES = auto()
|
||||||
|
NO = auto()
|
||||||
|
|
||||||
|
reveal_type(Answer.YES.value) # revealed: Literal[1]
|
||||||
|
reveal_type(Answer.NO.value) # revealed: Literal[2]
|
||||||
```
|
```
|
||||||
|
|
||||||
Combining aliases with `auto()`:
|
Combining aliases with `auto()`:
|
||||||
|
|
|
@ -3664,6 +3664,7 @@ pub enum KnownClass {
|
||||||
Auto,
|
Auto,
|
||||||
Member,
|
Member,
|
||||||
Nonmember,
|
Nonmember,
|
||||||
|
StrEnum,
|
||||||
// abc
|
// abc
|
||||||
ABCMeta,
|
ABCMeta,
|
||||||
// Types
|
// Types
|
||||||
|
@ -3804,6 +3805,7 @@ impl KnownClass {
|
||||||
| Self::Auto
|
| Self::Auto
|
||||||
| Self::Member
|
| Self::Member
|
||||||
| Self::Nonmember
|
| Self::Nonmember
|
||||||
|
| Self::StrEnum
|
||||||
| Self::ABCMeta
|
| Self::ABCMeta
|
||||||
| Self::Iterable
|
| Self::Iterable
|
||||||
| Self::Iterator
|
| Self::Iterator
|
||||||
|
@ -3864,6 +3866,7 @@ impl KnownClass {
|
||||||
| KnownClass::Auto
|
| KnownClass::Auto
|
||||||
| KnownClass::Member
|
| KnownClass::Member
|
||||||
| KnownClass::Nonmember
|
| KnownClass::Nonmember
|
||||||
|
| KnownClass::StrEnum
|
||||||
| KnownClass::ABCMeta
|
| KnownClass::ABCMeta
|
||||||
| KnownClass::GenericAlias
|
| KnownClass::GenericAlias
|
||||||
| KnownClass::ModuleType
|
| KnownClass::ModuleType
|
||||||
|
@ -3944,6 +3947,7 @@ impl KnownClass {
|
||||||
| KnownClass::Auto
|
| KnownClass::Auto
|
||||||
| KnownClass::Member
|
| KnownClass::Member
|
||||||
| KnownClass::Nonmember
|
| KnownClass::Nonmember
|
||||||
|
| KnownClass::StrEnum
|
||||||
| KnownClass::ABCMeta
|
| KnownClass::ABCMeta
|
||||||
| KnownClass::GenericAlias
|
| KnownClass::GenericAlias
|
||||||
| KnownClass::ModuleType
|
| KnownClass::ModuleType
|
||||||
|
@ -4024,6 +4028,7 @@ impl KnownClass {
|
||||||
| KnownClass::Auto
|
| KnownClass::Auto
|
||||||
| KnownClass::Member
|
| KnownClass::Member
|
||||||
| KnownClass::Nonmember
|
| KnownClass::Nonmember
|
||||||
|
| KnownClass::StrEnum
|
||||||
| KnownClass::ABCMeta
|
| KnownClass::ABCMeta
|
||||||
| KnownClass::GenericAlias
|
| KnownClass::GenericAlias
|
||||||
| KnownClass::ModuleType
|
| KnownClass::ModuleType
|
||||||
|
@ -4142,6 +4147,7 @@ impl KnownClass {
|
||||||
| Self::Auto
|
| Self::Auto
|
||||||
| Self::Member
|
| Self::Member
|
||||||
| Self::Nonmember
|
| Self::Nonmember
|
||||||
|
| Self::StrEnum
|
||||||
| Self::ABCMeta
|
| Self::ABCMeta
|
||||||
| Self::Super
|
| Self::Super
|
||||||
| Self::StdlibAlias
|
| Self::StdlibAlias
|
||||||
|
@ -4226,6 +4232,7 @@ impl KnownClass {
|
||||||
Self::Auto => "auto",
|
Self::Auto => "auto",
|
||||||
Self::Member => "member",
|
Self::Member => "member",
|
||||||
Self::Nonmember => "nonmember",
|
Self::Nonmember => "nonmember",
|
||||||
|
Self::StrEnum => "StrEnum",
|
||||||
Self::ABCMeta => "ABCMeta",
|
Self::ABCMeta => "ABCMeta",
|
||||||
Self::Super => "super",
|
Self::Super => "super",
|
||||||
Self::Iterable => "Iterable",
|
Self::Iterable => "Iterable",
|
||||||
|
@ -4462,9 +4469,12 @@ impl KnownClass {
|
||||||
| Self::Property => KnownModule::Builtins,
|
| Self::Property => KnownModule::Builtins,
|
||||||
Self::VersionInfo => KnownModule::Sys,
|
Self::VersionInfo => KnownModule::Sys,
|
||||||
Self::ABCMeta => KnownModule::Abc,
|
Self::ABCMeta => KnownModule::Abc,
|
||||||
Self::Enum | Self::EnumType | Self::Auto | Self::Member | Self::Nonmember => {
|
Self::Enum
|
||||||
KnownModule::Enum
|
| Self::EnumType
|
||||||
}
|
| Self::Auto
|
||||||
|
| Self::Member
|
||||||
|
| Self::Nonmember
|
||||||
|
| Self::StrEnum => KnownModule::Enum,
|
||||||
Self::GenericAlias
|
Self::GenericAlias
|
||||||
| Self::ModuleType
|
| Self::ModuleType
|
||||||
| Self::FunctionType
|
| Self::FunctionType
|
||||||
|
@ -4591,6 +4601,7 @@ impl KnownClass {
|
||||||
| Self::Auto
|
| Self::Auto
|
||||||
| Self::Member
|
| Self::Member
|
||||||
| Self::Nonmember
|
| Self::Nonmember
|
||||||
|
| Self::StrEnum
|
||||||
| Self::ABCMeta
|
| Self::ABCMeta
|
||||||
| Self::Super
|
| Self::Super
|
||||||
| Self::NewType
|
| Self::NewType
|
||||||
|
@ -4675,6 +4686,7 @@ impl KnownClass {
|
||||||
| Self::Auto
|
| Self::Auto
|
||||||
| Self::Member
|
| Self::Member
|
||||||
| Self::Nonmember
|
| Self::Nonmember
|
||||||
|
| Self::StrEnum
|
||||||
| Self::ABCMeta
|
| Self::ABCMeta
|
||||||
| Self::Super
|
| Self::Super
|
||||||
| Self::UnionType
|
| Self::UnionType
|
||||||
|
@ -4762,6 +4774,9 @@ impl KnownClass {
|
||||||
"EnumType" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
|
"EnumType" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
|
||||||
Self::EnumType
|
Self::EnumType
|
||||||
}
|
}
|
||||||
|
"StrEnum" if Program::get(db).python_version(db) >= PythonVersion::PY311 => {
|
||||||
|
Self::StrEnum
|
||||||
|
}
|
||||||
"auto" => Self::Auto,
|
"auto" => Self::Auto,
|
||||||
"member" => Self::Member,
|
"member" => Self::Member,
|
||||||
"nonmember" => Self::Nonmember,
|
"nonmember" => Self::Nonmember,
|
||||||
|
@ -4835,6 +4850,7 @@ impl KnownClass {
|
||||||
| Self::Auto
|
| Self::Auto
|
||||||
| Self::Member
|
| Self::Member
|
||||||
| Self::Nonmember
|
| Self::Nonmember
|
||||||
|
| Self::StrEnum
|
||||||
| Self::ABCMeta
|
| Self::ABCMeta
|
||||||
| Self::Super
|
| Self::Super
|
||||||
| Self::NotImplementedType
|
| Self::NotImplementedType
|
||||||
|
@ -5387,7 +5403,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
KnownClass::GenericAlias => PythonVersion::PY39,
|
KnownClass::GenericAlias => PythonVersion::PY39,
|
||||||
KnownClass::KwOnly => PythonVersion::PY310,
|
KnownClass::KwOnly => PythonVersion::PY310,
|
||||||
KnownClass::Member | KnownClass::Nonmember => PythonVersion::PY311,
|
KnownClass::Member | KnownClass::Nonmember | KnownClass::StrEnum => {
|
||||||
|
PythonVersion::PY311
|
||||||
|
}
|
||||||
_ => PythonVersion::PY37,
|
_ => PythonVersion::PY37,
|
||||||
};
|
};
|
||||||
(class, version_added)
|
(class, version_added)
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::{
|
||||||
place::{Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
place::{Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
||||||
semantic_index::{place_table, use_def_map},
|
semantic_index::{place_table, use_def_map},
|
||||||
types::{
|
types::{
|
||||||
ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy, Type,
|
ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy,
|
||||||
TypeQualifiers,
|
StringLiteralType, Type, TypeQualifiers,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,12 +77,14 @@ pub(crate) fn enum_metadata<'db>(
|
||||||
return None;
|
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 scope_id = class.body_scope(db);
|
||||||
let use_def_map = use_def_map(db, scope_id);
|
let use_def_map = use_def_map(db, scope_id);
|
||||||
let table = place_table(db, scope_id);
|
let table = place_table(db, scope_id);
|
||||||
|
|
||||||
let mut enum_values: FxHashMap<Type<'db>, Name> = FxHashMap::default();
|
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 mut auto_counter = 0;
|
||||||
|
|
||||||
let ignored_names: Option<Vec<&str>> = if let Some(ignore) = table.symbol_id("_ignore_") {
|
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
|
// enum.auto
|
||||||
Some(KnownClass::Auto) => {
|
Some(KnownClass::Auto) => {
|
||||||
auto_counter += 1;
|
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,
|
_ => None,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue