mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Add more tests for special-cased builtin functions and methods (#20329)
This commit is contained in:
parent
a1fdd66f10
commit
ffead90410
2 changed files with 214 additions and 12 deletions
|
@ -586,5 +586,157 @@ reveal_type(C.f2(1)) # revealed: str
|
||||||
reveal_type(C().f2(1)) # revealed: str
|
reveal_type(C().f2(1)) # revealed: str
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Builtin functions and methods
|
||||||
|
|
||||||
|
Some builtin functions and methods are heavily special-cased by ty. This mdtest checks that various
|
||||||
|
properties are understood correctly for these functions and methods.
|
||||||
|
|
||||||
|
```py
|
||||||
|
import types
|
||||||
|
from typing import Callable
|
||||||
|
from ty_extensions import static_assert, CallableTypeOf, is_assignable_to, TypeOf
|
||||||
|
|
||||||
|
def f(obj: type) -> None: ...
|
||||||
|
|
||||||
|
class MyClass:
|
||||||
|
@property
|
||||||
|
def my_property(self) -> int:
|
||||||
|
return 42
|
||||||
|
|
||||||
|
@my_property.setter
|
||||||
|
def my_property(self, value: int | str) -> None: ...
|
||||||
|
|
||||||
|
static_assert(is_assignable_to(types.FunctionType, Callable))
|
||||||
|
|
||||||
|
# revealed: <wrapper-descriptor `__get__` of `function` objects>
|
||||||
|
reveal_type(types.FunctionType.__get__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[types.FunctionType.__get__], Callable))
|
||||||
|
|
||||||
|
# revealed: def f(obj: type) -> None
|
||||||
|
reveal_type(f)
|
||||||
|
static_assert(is_assignable_to(TypeOf[f], Callable))
|
||||||
|
|
||||||
|
# revealed: <method-wrapper `__get__` of `f`>
|
||||||
|
reveal_type(f.__get__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[f.__get__], Callable))
|
||||||
|
|
||||||
|
# revealed: def __call__(self, *args: Any, **kwargs: Any) -> Any
|
||||||
|
reveal_type(types.FunctionType.__call__)
|
||||||
|
static_assert(is_assignable_to(TypeOf[types.FunctionType.__call__], Callable))
|
||||||
|
|
||||||
|
# revealed: <method-wrapper `__call__` of `f`>
|
||||||
|
reveal_type(f.__call__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[f.__call__], Callable))
|
||||||
|
|
||||||
|
# revealed: <wrapper-descriptor `__get__` of `property` objects>
|
||||||
|
reveal_type(property.__get__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[property.__get__], Callable))
|
||||||
|
|
||||||
|
# revealed: property
|
||||||
|
reveal_type(MyClass.my_property)
|
||||||
|
static_assert(is_assignable_to(TypeOf[property], Callable))
|
||||||
|
static_assert(not is_assignable_to(TypeOf[MyClass.my_property], Callable))
|
||||||
|
|
||||||
|
# revealed: <method-wrapper `__get__` of `property` object>
|
||||||
|
reveal_type(MyClass.my_property.__get__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[MyClass.my_property.__get__], Callable))
|
||||||
|
|
||||||
|
# revealed: <wrapper-descriptor `__set__` of `property` objects>
|
||||||
|
reveal_type(property.__set__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[property.__set__], Callable))
|
||||||
|
|
||||||
|
# revealed: <method-wrapper `__set__` of `property` object>
|
||||||
|
reveal_type(MyClass.my_property.__set__)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf[MyClass.my_property.__set__], Callable))
|
||||||
|
|
||||||
|
# revealed: def startswith(self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
|
||||||
|
reveal_type(str.startswith)
|
||||||
|
static_assert(is_assignable_to(TypeOf[str.startswith], Callable))
|
||||||
|
|
||||||
|
# revealed: <method-wrapper `startswith` of `str` object>
|
||||||
|
reveal_type("foo".startswith)
|
||||||
|
|
||||||
|
# TODO: should pass
|
||||||
|
# error: [static-assert-error]
|
||||||
|
static_assert(is_assignable_to(TypeOf["foo".startswith], Callable))
|
||||||
|
|
||||||
|
def _(
|
||||||
|
a: CallableTypeOf[types.FunctionType.__get__],
|
||||||
|
b: CallableTypeOf[f],
|
||||||
|
c: CallableTypeOf[f.__get__],
|
||||||
|
d: CallableTypeOf[types.FunctionType.__call__],
|
||||||
|
# TODO: false-positive diagnostic
|
||||||
|
e: CallableTypeOf[f.__call__], # error: [invalid-type-form]
|
||||||
|
f: CallableTypeOf[property],
|
||||||
|
g: CallableTypeOf[property.__get__],
|
||||||
|
h: CallableTypeOf[MyClass.my_property.__get__],
|
||||||
|
i: CallableTypeOf[property.__set__],
|
||||||
|
j: CallableTypeOf[MyClass.my_property.__set__],
|
||||||
|
k: CallableTypeOf[str.startswith],
|
||||||
|
l: CallableTypeOf["foo".startswith],
|
||||||
|
):
|
||||||
|
# revealed: Overload[(self: FunctionType, instance: None, owner: type, /) -> Unknown, (self: FunctionType, instance: object, owner: type | None = None, /) -> Unknown]
|
||||||
|
reveal_type(a)
|
||||||
|
|
||||||
|
# revealed: (obj: type) -> None
|
||||||
|
reveal_type(b)
|
||||||
|
|
||||||
|
# TODO: ideally this would have precise return types rather than `Unknown`
|
||||||
|
# revealed: Overload[(instance: None, owner: type, /) -> Unknown, (instance: object, owner: type | None = None, /) -> Unknown]
|
||||||
|
reveal_type(c)
|
||||||
|
|
||||||
|
# revealed: (self, *args: Any, **kwargs: Any) -> Any
|
||||||
|
reveal_type(d)
|
||||||
|
|
||||||
|
# TODO: this should be `(obj: type) -> None`
|
||||||
|
# revealed: Unknown
|
||||||
|
reveal_type(e)
|
||||||
|
|
||||||
|
# revealed: (fget: ((Any, /) -> Any) | None = None, fset: ((Any, Any, /) -> None) | None = None, fdel: ((Any, /) -> Any) | None = None, doc: str | None = None) -> Unknown
|
||||||
|
reveal_type(f)
|
||||||
|
|
||||||
|
# revealed: Overload[(self: property, instance: None, owner: type, /) -> Unknown, (self: property, instance: object, owner: type | None = None, /) -> Unknown]
|
||||||
|
reveal_type(g)
|
||||||
|
|
||||||
|
# TODO: ideally this would have precise return types rather than `Unknown`
|
||||||
|
# revealed: Overload[(instance: None, owner: type, /) -> Unknown, (instance: object, owner: type | None = None, /) -> Unknown]
|
||||||
|
reveal_type(h)
|
||||||
|
|
||||||
|
# TODO: ideally this would have `-> None` rather than `-> Unknown`
|
||||||
|
# revealed: (self: property, instance: object, value: object, /) -> Unknown
|
||||||
|
reveal_type(i)
|
||||||
|
|
||||||
|
# TODO: ideally this would have a more precise input type and `-> None` rather than `-> Unknown`
|
||||||
|
# revealed: (instance: object, value: object, /) -> Unknown
|
||||||
|
reveal_type(j)
|
||||||
|
|
||||||
|
# revealed: (self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
|
||||||
|
reveal_type(k)
|
||||||
|
|
||||||
|
# revealed: (prefix: str | tuple[str, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> bool
|
||||||
|
reveal_type(l)
|
||||||
|
```
|
||||||
|
|
||||||
[functions and methods]: https://docs.python.org/3/howto/descriptor.html#functions-and-methods
|
[functions and methods]: https://docs.python.org/3/howto/descriptor.html#functions-and-methods
|
||||||
[`__init_subclass__`]: https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__
|
[`__init_subclass__`]: https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__
|
||||||
|
|
|
@ -534,6 +534,49 @@ impl<'db> PropertyInstanceType<'db> {
|
||||||
ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn when_equivalent_to<C: Constraints<'db>>(self, db: &'db dyn Db, other: Self) -> C {
|
||||||
|
self.is_equivalent_to_impl(
|
||||||
|
db,
|
||||||
|
other,
|
||||||
|
&IsEquivalentVisitor::new(C::always_satisfiable(db)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_equivalent_to_impl<C: Constraints<'db>>(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
other: Self,
|
||||||
|
visitor: &IsEquivalentVisitor<'db, C>,
|
||||||
|
) -> C {
|
||||||
|
let getter_equivalence = if let Some(getter) = self.getter(db) {
|
||||||
|
let Some(other_getter) = other.getter(db) else {
|
||||||
|
return C::unsatisfiable(db);
|
||||||
|
};
|
||||||
|
getter.is_equivalent_to_impl(db, other_getter, visitor)
|
||||||
|
} else {
|
||||||
|
if other.getter(db).is_some() {
|
||||||
|
return C::unsatisfiable(db);
|
||||||
|
}
|
||||||
|
C::always_satisfiable(db)
|
||||||
|
};
|
||||||
|
|
||||||
|
let setter_equivalence = || {
|
||||||
|
if let Some(setter) = self.setter(db) {
|
||||||
|
let Some(other_setter) = other.setter(db) else {
|
||||||
|
return C::unsatisfiable(db);
|
||||||
|
};
|
||||||
|
setter.is_equivalent_to_impl(db, other_setter, visitor)
|
||||||
|
} else {
|
||||||
|
if other.setter(db).is_some() {
|
||||||
|
return C::unsatisfiable(db);
|
||||||
|
}
|
||||||
|
C::always_satisfiable(db)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getter_equivalence.and(db, setter_equivalence)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -1931,6 +1974,11 @@ impl<'db> Type<'db> {
|
||||||
let class_literal = instance.class(db).class_literal(db).0;
|
let class_literal = instance.class(db).class_literal(db).0;
|
||||||
C::from_bool(db, is_single_member_enum(db, class_literal))
|
C::from_bool(db, is_single_member_enum(db, class_literal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Type::PropertyInstance(left), Type::PropertyInstance(right)) => {
|
||||||
|
left.is_equivalent_to_impl(db, right, visitor)
|
||||||
|
}
|
||||||
|
|
||||||
_ => C::unsatisfiable(db),
|
_ => C::unsatisfiable(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9251,14 +9299,15 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
) => self_function.has_relation_to_impl(db, other_function, relation, visitor),
|
) => self_function.has_relation_to_impl(db, other_function, relation, visitor),
|
||||||
|
|
||||||
(
|
(
|
||||||
KnownBoundMethodType::PropertyDunderGet(_),
|
KnownBoundMethodType::PropertyDunderGet(self_property),
|
||||||
KnownBoundMethodType::PropertyDunderGet(_),
|
KnownBoundMethodType::PropertyDunderGet(other_property),
|
||||||
)
|
)
|
||||||
| (
|
| (
|
||||||
KnownBoundMethodType::PropertyDunderSet(_),
|
KnownBoundMethodType::PropertyDunderSet(self_property),
|
||||||
KnownBoundMethodType::PropertyDunderSet(_),
|
KnownBoundMethodType::PropertyDunderSet(other_property),
|
||||||
)
|
) => self_property.when_equivalent_to(db, other_property),
|
||||||
| (KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => {
|
|
||||||
|
(KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => {
|
||||||
C::from_bool(db, self == other)
|
C::from_bool(db, self == other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9295,14 +9344,15 @@ impl<'db> KnownBoundMethodType<'db> {
|
||||||
) => self_function.is_equivalent_to_impl(db, other_function, visitor),
|
) => self_function.is_equivalent_to_impl(db, other_function, visitor),
|
||||||
|
|
||||||
(
|
(
|
||||||
KnownBoundMethodType::PropertyDunderGet(_),
|
KnownBoundMethodType::PropertyDunderGet(self_property),
|
||||||
KnownBoundMethodType::PropertyDunderGet(_),
|
KnownBoundMethodType::PropertyDunderGet(other_property),
|
||||||
)
|
)
|
||||||
| (
|
| (
|
||||||
KnownBoundMethodType::PropertyDunderSet(_),
|
KnownBoundMethodType::PropertyDunderSet(self_property),
|
||||||
KnownBoundMethodType::PropertyDunderSet(_),
|
KnownBoundMethodType::PropertyDunderSet(other_property),
|
||||||
)
|
) => self_property.is_equivalent_to_impl(db, other_property, visitor),
|
||||||
| (KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => {
|
|
||||||
|
(KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => {
|
||||||
C::from_bool(db, self == other)
|
C::from_bool(db, self == other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue