-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Allow assigning ellipsis literal as parameter default value #14982
base: main
Are you sure you want to change the base?
Conversation
|
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
The pre-commit failure will go away if you merge in |
8157331
to
a7e7723
Compare
Wow there's a bug in the new github UI for PRs it was not showing the rebase button I thought it's already up to date. |
Oh, that only appears for a repository if the maintainers have checked a certain box in the repository settings on GitHub. We haven't enabled it for Astral repositories because the rate of development is quite rapid, and some folks found it annoying to be constantly told that all their PRs were out of date with the |
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.
Thank you!!
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
Thanks for the review. |
4d835da
to
06e1551
Compare
Ah, I think there's a small misunderstanding here. There are two halves of typing.readthedocs.io:
The two pages you link to there are both in the "second half" of the website: those paragraphs describe best practices for people writing stub files, but these aren't necessarily rules that type checkers have to enforce regarding stub files. The typing spec part of typing.readthedocs.io is here: https://typing.readthedocs.io/en/latest/spec/distributing.html#value-expressions. It states that type checkers should allow
And later:
|
So I think we should allow |
Thanks for that clarification, Alex! I was surprised that I feel like the wording of the spec:
Suggests a totally different implementation approach here, which is that in a stub file we should simply infer literal |
That's very interesting, good observation! I don't actually know why that is. It may be in order to support ellipsis defaults in overloads and protocol methods. I'm a little surprised that this appears to be handled by allowing it based on the body being I'm not convinced we want to follow suit here; it seems incorrect to do this based on a body of So I would say let's implement this only for stubs for now, and consider the best approach when we add overloads/protocols support. |
That seems okay to me as long as we don't start inferring unions with |
Yes, that's right. |
Thanks let me do try to change this to infer the type of ellipsis literal as Any instead of adding this special case. |
Ok, after further discussion (thanks @AlexWaygood!) I think this is not a good idea, and might cause undesirable behavior in examples like this: class Foo: ...
X: int = ...
Y: Foo = X Where this should be flagged as an error, even in a type stub, but my suggestion (combined with our current handling of inferred dynamic type) would not consider it an error. (It might be worth including this as a test case.) So I think the better approach is in fact to special-case handling of literal In the first two cases, if we see a literal |
06e1551
to
9689544
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.
Thank you! A few comments.
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
crates/red_knot_python_semantic/resources/mdtest/stubs/ellipsis.md
Outdated
Show resolved
Hide resolved
TargetKind::Name => value_ty, | ||
TargetKind::Name => { | ||
if self.file().is_stub(self.db().upcast()) && value.is_ellipsis_literal_expr() { | ||
Type::Any |
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.
We should use Type::Unknown
rather than Type::Any
here. In our model, Type::Any
is reserved for explicit uses of typing.Any
as an annotation. Any dynamic type arising from lack of an annotation uses Type::Unknown
.
Also we should add a test for this case (un-annotated assignment in a stub file, with ...
as RHS.)
@@ -1846,7 +1852,13 @@ impl<'db> TypeInferenceBuilder<'db> { | |||
|
|||
unpacked.get(name_ast_id).unwrap_or(Type::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.
It would be unusual, but I think valid according to the spec to do FOO, BAR = ...
in a stub file. So I think we also should add a special case for ...
(only if file.is_stub(db)
of course) in infer_unpack_types
(where we infer the value_ty
from the RHS expression). And a test for this.
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 applied this change inside the unpacker since that was the first place that value type was being inferred. Also there was a match to check the expression being used. I hope this is what you meant as well.
But if I understand correctly we don't want the unpacking to happen in for loops so I'm checking that unpack value is of type UnpackerValue::Assign
.
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 spent some time to see if I can pull out the code for special case to infer_unpack_types
itself. But I think we want the whole unpacking behavior to happen.
This could be done if we only want to treat the case where LHS is a tuple and RHS is ...
. But I feel this behavior would not be consistent. I also checked Pyright.
For the following .pyi
file Pyright infers all variables the same.
a, b = ...
reveal_type(a)
reveal_type(b)
[c] = ...
reveal_type(c)
I can't decide if we should only allow a, b = ...
or other forms of unpacking as well so please let me know if you think the other way is better.
annotation_ty, | ||
value_ty, | ||
); | ||
if self.file().is_stub(self.db().upcast()) { |
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.
It seems we've forgotten the condition "and the value
expression is a literal ...
" here. If that doesn't fail any test, we should definitely add a test with e.g. x: int = 1
in a stub file showing we still infer Literal[1]
for x
, not 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.
Yes it was not failing. Added a new test for it in mdtest/assignment/annotations.md
.
@@ -1846,7 +1852,13 @@ impl<'db> TypeInferenceBuilder<'db> { | |||
|
|||
unpacked.get(name_ast_id).unwrap_or(Type::Unknown) | |||
} | |||
TargetKind::Name => value_ty, | |||
TargetKind::Name => { | |||
if self.file().is_stub(self.db().upcast()) && value.is_ellipsis_literal_expr() { |
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: It might be worth introducing a is_stub
function on InferenceBuilder
that does the self.file().is_stub(self.db().upcast())
acf3072
to
d30649c
Compare
Out of curiosity I checked what pyright and mypy do when they encounter With this x, y = ... Pyright:
Mypy:
|
@@ -76,6 +76,11 @@ impl<'db> UnpackValue<'db> { | |||
matches!(self, UnpackValue::Iterable(_)) | |||
} | |||
|
|||
/// Returns `true` if the value is being assigned to a target. | |||
pub(crate) const fn is_assign(self) -> bool { |
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 wanted to keep this consistent with is_iterable
so I created this. I think https://docs.rs/is-macro/latest/is_macro/ also works here.
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
…s.md Co-authored-by: Carl Meyer <carl@oddbird.net>
Co-authored-by: Carl Meyer <carl@oddbird.net>
d499b43
to
2be530d
Compare
Resolves #14840
Summary
Usage of ellipsis literal as default argument is allowed in stub files.
Test Plan
Added mdtest for both python files and stub files.