-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
[red-knot] Understand typing.Callable
#16493
base: main
Are you sure you want to change the base?
Conversation
e71d87b
to
dbae500
Compare
dbae500
to
05a7a5b
Compare
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool! Great work.
crates/red_knot_python_semantic/resources/mdtest/annotations/callable.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/annotations/callable.md
Show resolved
Hide resolved
# TODO: This should be `() -> Unknown` which requires understanding that `int` is not a param spec variable | ||
reveal_type(c) # revealed: (*args: @Todo(todo signature *args), **kwargs: @Todo(todo signature **kwargs)) -> str |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually think inferring Callable[int, str]
(or Callable[42, str]
below, or any similar malformation) as equivalent to Callable[..., str]
(and of course also emitting an error) is probably better, since it will minimize cascading false positives. If you meant to say Callable[[int], str]
but you accidentally said Callable[int, str]
, ideally we would not emit a false positive on every subsequent attempt to call that callable with one argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, makes sense. Just want to confirm that we still do want to keep the return type intact i.e., use Callable[..., str]
and not Callable[..., Unknown]
which is what I've pushed but happy to change it back to using Unknown
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer Callable[..., Unknown]
here for the same reasons I stated in #16493 (comment) — it just seems like a simpler policy that involves less guesswork and that will be much easier to explain to users
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I'll also use Callable[..., Unknown]
for type forms with more than 2 arguments for similar reasons and the fact that we're at least sure that it's a callable object.
crates/red_knot_python_semantic/resources/mdtest/annotations/callable.md
Show resolved
Hide resolved
Or, when it's a literal type: | ||
|
||
```py | ||
# error: [invalid-type-form] "The first argument to `typing.Callable` must be either a list of types, parameter specification, `typing.Concatenate`, or `...`" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "ParamSpec" is probably clearer than "parameter specification", since it's a term that can be easily searched in the typing spec / docs -- the latter could be understand to have a broader meaning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I did a quick search on typing and Python docs and it seems that "parameter specification" would not give users much useful information. I'll change it to use typing.ParamSpec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For context, I mainly used this because ParamSpec
itself cannot be used directly as type expression similar to TypeVar
so suggesting ParamSpec
could be slightly confused by whether to directly use ParamSpec
v/s a variable that represents the parameter specification.
@@ -92,6 +101,23 @@ impl<'db> Parameters<'db> { | |||
]) | |||
} | |||
|
|||
/// Return parameters that accepts any arguments i.e., `(*args: Any, **kwargs: Any)` | |||
pub(crate) fn any() -> Self { | |||
// TODO: Should this be represented using `(*Any, **Any)` instead? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yeah, maybe we should ideally omit the names here? Not sure how much fallout that change would have
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we've decided to use (...) -> Unknown
I don't think this is relevant so I've avoided this.
@@ -2664,6 +2699,10 @@ impl<'db> Type<'db> { | |||
Type::Callable(CallableType::WrapperDescriptorDunderGet) => { | |||
KnownClass::WrapperDescriptorType.to_class_literal(db) | |||
} | |||
Type::Callable(CallableType::General(_)) => { | |||
// TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a small explanation of what needs to be done here or remove the comment if it's redundant to the todo_type description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll expand the comment. I avoided here as it would be similar to todo_type
but I still grep for TODO
comments 😅
Post review changes
Regarding the relation implementation, I think I'd prefer to add it in a follow-up PR to keep the changes separate and easier to review. |
Summary
Part of #15382
This PR implements a general callable type that wraps around a
Signature
and it uses that new type to representtyping.Callable
.It also implements
Display
support forCallable
. The format is as:The
/
and*
separators are added at the correct boundary for positional-only and keyword-only parameters. Now, astyping.Callable
only has positional-only parameters, the rendered signature would be:The
/
separator represents that all the arguments are positional-only.The relationship methods that check assignability, subtype relationship, etc. are not yet implemented and will be done so as a follow-up.
Test Plan
Add test cases for display support for
Signature
and various mdtest fortyping.Callable
.