-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[red-knot] Class literal __new__ function callable subtyping
#17533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ba52d1d
3c17670
e4bf1f7
519dacb
24e63fa
f5c0a8e
1fc7bf0
a3ab945
c3dd673
0745192
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1175,25 +1175,9 @@ impl<'db> Type<'db> { | |
| self_subclass_ty.is_subtype_of(db, target_subclass_ty) | ||
| } | ||
|
|
||
| (Type::ClassLiteral(_), Type::Callable(_)) => { | ||
| let metaclass_call_function_symbol = self | ||
| .member_lookup_with_policy( | ||
| db, | ||
| "__call__".into(), | ||
| MemberLookupPolicy::NO_INSTANCE_FALLBACK | ||
| | MemberLookupPolicy::META_CLASS_NO_TYPE_FALLBACK, | ||
| ) | ||
| .symbol; | ||
|
|
||
| if let Symbol::Type(Type::BoundMethod(metaclass_call_function), _) = | ||
| metaclass_call_function_symbol | ||
| { | ||
| // TODO: this intentionally diverges from step 1 in | ||
| // https://typing.python.org/en/latest/spec/constructors.html#converting-a-constructor-to-callable | ||
| // by always respecting the signature of the metaclass `__call__`, rather than | ||
| // using a heuristic which makes unwarranted assumptions to sometimes ignore it. | ||
| let metaclass_call_function = metaclass_call_function.into_callable_type(db); | ||
| return metaclass_call_function.is_subtype_of(db, target); | ||
| (Type::ClassLiteral(class_literal), Type::Callable(_)) => { | ||
| if let Some(callable) = class_literal.into_callable(db) { | ||
| return callable.is_subtype_of(db, target); | ||
| } | ||
| false | ||
| } | ||
|
|
@@ -5961,6 +5945,15 @@ impl<'db> FunctionType<'db> { | |
| )) | ||
| } | ||
|
|
||
| /// Convert the `FunctionType` into a [`Type::BoundMethod`]. | ||
| pub(crate) fn into_bound_method_type( | ||
MatthewMckee4 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self, | ||
| db: &'db dyn Db, | ||
| self_instance: Type<'db>, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter name is a bit misleading because in this PR above where this is being called it's not an instance of the class but the class itself. This is also the case for the field name on Regardless, this shouldn't block this PR and something that could be done in a follow-up instead. |
||
| ) -> Type<'db> { | ||
| Type::BoundMethod(BoundMethodType::new(db, self, self_instance)) | ||
| } | ||
|
|
||
| /// Returns the [`FileRange`] of the function's name. | ||
| pub fn focus_range(self, db: &dyn Db) -> FileRange { | ||
| FileRange::new( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -818,6 +818,43 @@ impl<'db> ClassLiteralType<'db> { | |
| )) | ||
| } | ||
|
|
||
| pub(super) fn into_callable(self, db: &'db dyn Db) -> Option<Type<'db>> { | ||
| let self_ty = Type::from(self); | ||
| let metaclass_call_function_symbol = self_ty | ||
| .member_lookup_with_policy( | ||
| db, | ||
| "__call__".into(), | ||
| MemberLookupPolicy::NO_INSTANCE_FALLBACK | ||
| | MemberLookupPolicy::META_CLASS_NO_TYPE_FALLBACK, | ||
| ) | ||
| .symbol; | ||
|
Comment on lines
+822
to
+830
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some reason this wasnt working let metaclass_call_function_symbol = self
.class_member(
db,
"__call__",
MemberLookupPolicy::NO_INSTANCE_FALLBACK
| MemberLookupPolicy::META_CLASS_NO_TYPE_FALLBACK,
)
.symbol;It couldn't find
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect it found it, but it wasn't a |
||
|
|
||
| if let Symbol::Type(Type::BoundMethod(metaclass_call_function), _) = | ||
| metaclass_call_function_symbol | ||
| { | ||
| // TODO: this intentionally diverges from step 1 in | ||
| // https://typing.python.org/en/latest/spec/constructors.html#converting-a-constructor-to-callable | ||
| // by always respecting the signature of the metaclass `__call__`, rather than | ||
| // using a heuristic which makes unwarranted assumptions to sometimes ignore it. | ||
| return Some(metaclass_call_function.into_callable_type(db)); | ||
| } | ||
|
|
||
| let new_function_symbol = self_ty | ||
| .member_lookup_with_policy( | ||
| db, | ||
| "__new__".into(), | ||
| MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK | ||
| | MemberLookupPolicy::META_CLASS_NO_TYPE_FALLBACK, | ||
| ) | ||
| .symbol; | ||
|
|
||
| if let Symbol::Type(Type::FunctionLiteral(new_function), _) = new_function_symbol { | ||
| return Some(new_function.into_bound_method_type(db, self.into())); | ||
| } | ||
| // TODO handle `__init__` also | ||
| None | ||
| } | ||
|
|
||
| /// Returns the class member of this class named `name`. | ||
| /// | ||
| /// The member resolves to a member on the class itself or any of its proper superclasses. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.