mirror of https://github.com/astral-sh/ruff
[red-knot] Three-argument type-calls take 'str' as the first argument (#17168)
## Summary Similar to #17163, a minor fix in the signature of `type(…)`. ## Test Plan New MD tests
This commit is contained in:
parent
d401a5440e
commit
3f00010a7a
|
|
@ -26,7 +26,11 @@ reveal_type(type(1)) # revealed: Literal[int]
|
||||||
But a three-argument call to type creates a dynamic instance of the `type` class:
|
But a three-argument call to type creates a dynamic instance of the `type` class:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
class Base: ...
|
||||||
|
|
||||||
reveal_type(type("Foo", (), {})) # revealed: type
|
reveal_type(type("Foo", (), {})) # revealed: type
|
||||||
|
|
||||||
|
reveal_type(type("Foo", (Base,), {"attr": 1})) # revealed: type
|
||||||
```
|
```
|
||||||
|
|
||||||
Other numbers of arguments are invalid
|
Other numbers of arguments are invalid
|
||||||
|
|
@ -39,6 +43,24 @@ type("Foo", ())
|
||||||
type("Foo", (), {}, weird_other_arg=42)
|
type("Foo", (), {}, weird_other_arg=42)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The following calls are also invalid, due to incorrect argument types:
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Base: ...
|
||||||
|
|
||||||
|
# error: [no-matching-overload] "No overload of class `type` matches arguments"
|
||||||
|
type(b"Foo", (), {})
|
||||||
|
|
||||||
|
# error: [no-matching-overload] "No overload of class `type` matches arguments"
|
||||||
|
type("Foo", Base, {})
|
||||||
|
|
||||||
|
# TODO: this should be an error
|
||||||
|
type("Foo", (1, 2), {})
|
||||||
|
|
||||||
|
# TODO: this should be an error
|
||||||
|
type("Foo", (Base,), {b"attr": 1})
|
||||||
|
```
|
||||||
|
|
||||||
## Calls to `str()`
|
## Calls to `str()`
|
||||||
|
|
||||||
### Valid calls
|
### Valid calls
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ No narrowing should occur if `type` is used to dynamically create a class:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def _(x: str | int):
|
def _(x: str | int):
|
||||||
|
# The following diagnostic is valid, since the three-argument form of `type`
|
||||||
|
# can only be called with `str` as the first argument.
|
||||||
|
# error: [no-matching-overload] "No overload of class `type` matches arguments"
|
||||||
if type(x, (), {}) is str:
|
if type(x, (), {}) is str:
|
||||||
reveal_type(x) # revealed: str | int
|
reveal_type(x) # revealed: str | int
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,9 @@ static_assert(is_assignable_to(tuple[int, str], tuple[int, str]))
|
||||||
static_assert(is_assignable_to(tuple[Literal[1], Literal[2]], tuple[int, int]))
|
static_assert(is_assignable_to(tuple[Literal[1], Literal[2]], tuple[int, int]))
|
||||||
static_assert(is_assignable_to(tuple[Any, Literal[2]], tuple[int, int]))
|
static_assert(is_assignable_to(tuple[Any, Literal[2]], tuple[int, int]))
|
||||||
static_assert(is_assignable_to(tuple[Literal[1], Any], tuple[int, int]))
|
static_assert(is_assignable_to(tuple[Literal[1], Any], tuple[int, int]))
|
||||||
|
static_assert(is_assignable_to(tuple[()], tuple))
|
||||||
|
static_assert(is_assignable_to(tuple[int, str], tuple))
|
||||||
|
static_assert(is_assignable_to(tuple[Any], tuple))
|
||||||
|
|
||||||
static_assert(not is_assignable_to(tuple[()], tuple[int]))
|
static_assert(not is_assignable_to(tuple[()], tuple[int]))
|
||||||
static_assert(not is_assignable_to(tuple[int], tuple[str]))
|
static_assert(not is_assignable_to(tuple[int], tuple[str]))
|
||||||
|
|
|
||||||
|
|
@ -950,6 +950,13 @@ impl<'db> Type<'db> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This special case is required because the left-hand side tuple might be a
|
||||||
|
// gradual type, so we can not rely on subtyping. This allows us to assign e.g.
|
||||||
|
// `tuple[Any, int]` to `tuple`.
|
||||||
|
(Type::Tuple(_), _) => KnownClass::Tuple
|
||||||
|
.to_instance(db)
|
||||||
|
.is_assignable_to(db, target),
|
||||||
|
|
||||||
// `type[Any]` is assignable to any `type[...]` type, because `type[Any]` can
|
// `type[Any]` is assignable to any `type[...]` type, because `type[Any]` can
|
||||||
// materialize to any `type[...]` type.
|
// materialize to any `type[...]` type.
|
||||||
(Type::SubclassOf(subclass_of_ty), Type::SubclassOf(_))
|
(Type::SubclassOf(subclass_of_ty), Type::SubclassOf(_))
|
||||||
|
|
@ -2903,12 +2910,14 @@ impl<'db> Type<'db> {
|
||||||
),
|
),
|
||||||
Signature::new(
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
Parameter::positional_only(Some(Name::new_static("o")))
|
Parameter::positional_only(Some(Name::new_static("name")))
|
||||||
.with_annotated_type(Type::any()),
|
.with_annotated_type(KnownClass::Str.to_instance(db)),
|
||||||
Parameter::positional_only(Some(Name::new_static("bases")))
|
Parameter::positional_only(Some(Name::new_static("bases")))
|
||||||
.with_annotated_type(Type::any()),
|
// TODO: Should be tuple[type, ...] once we have support for homogenous tuples
|
||||||
|
.with_annotated_type(KnownClass::Tuple.to_instance(db)),
|
||||||
Parameter::positional_only(Some(Name::new_static("dict")))
|
Parameter::positional_only(Some(Name::new_static("dict")))
|
||||||
.with_annotated_type(Type::any()),
|
// TODO: Should be `dict[str, Any]` once we have support for generics
|
||||||
|
.with_annotated_type(KnownClass::Dict.to_instance(db)),
|
||||||
]),
|
]),
|
||||||
Some(KnownClass::Type.to_instance(db)),
|
Some(KnownClass::Type.to_instance(db)),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue