-
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
[red-knot] Port type inference tests to new test framework #13719
Conversation
I've changed parser's CODE_RE regex, cause without that fix it was not correctly parsing code snippets with empty files, like: ```py path=package/__init__.py
``` Of course feel free to comment on this fix... |
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.
This is awesome, thank you for taking this on!
I'm going to wait for removal of the extraneous quadruple-backtick markdown fences before reviewing the tests in detail, because removing those will make them a lot easier to read :)
````markdown | ||
```py path=a.py | ||
from b import C as D; E = D | ||
reveal_type(E) # revealed: Literal[C] | ||
``` | ||
|
||
```py path=b.py | ||
class C: pass | ||
``` | ||
```` |
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, the outer markdown fenced code block is just how the mdtest README handles showing an example markdown document within a markdown README, it's not needed in the tests and should be removed here and in all other cases:
````markdown | |
```py path=a.py | |
from b import C as D; E = D | |
reveal_type(E) # revealed: Literal[C] | |
``` | |
```py path=b.py | |
class C: pass | |
``` | |
```` | |
```py path=a.py | |
from b import C as D; E = D | |
reveal_type(E) # revealed: Literal[C] | |
``` | |
```py path=b.py | |
class C: pass | |
``` |
It's funny that it still works either way, because of our naive regex parsing :)
I'll look at updating the README to address this potential confusion.
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.
Fixed, my bad. Once again - thanks for clarifying
We can follow import to class: | ||
|
||
````markdown | ||
```py path=a.py |
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 say it's probably better style in general if we don't specify the path on the "main" test file, if it isn't imported by another file and its location/name don't actually matter to the test. (This wasn't possible in the previous test format.) I don't care too much about this, though, not a blocking issue.
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.
Fixed it
This fix looks good, thank you! |
Also it looks like the mdformat pre-commit check is failing, you can check the ruff CONTRIBUTING doc for info on how to run pre-commit locally so you can catch those issues without having to push and wait for CI. |
|
Oh, ok, understood, I will redo that, thanks for clarifing
Got it, totally forgot about it |
Address a potential point of confusion that bit a contributor in #13719 Also remove a no-longer-accurate line about bare `error: ` assertions (which are no longer allowed) and clarify another point about which kinds of error assertions to use.
6114ee7
to
6ac74d3
Compare
Rebased on the current I have problems with porting few tests so far:
|
6ac74d3
to
5215c97
Compare
Most of the work is done. Now I’m just waiting for feedback on what’s finished and looking for some help with the issues I’m facing before I can keep going. |
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 so much, this is a lot of good work! Excellent translation of the tests to the new assertion style.
A few general comments, which I commented partway through but not everywhere they occur:
- Avoid
TODO
in test titles, and keep TODO comments minimal in length and as close as possible to the specific line/assertion that isn't quite right yet. - Avoid boilerplate phrases like "This test ensures that" or "Check that" or "We can infer the type when ..." or anything similar -- minimize the words that are not adding specific value to the test in question.
- Break up into more smaller files; I commented on this throughout to try to give a clear idea of an organization that I think would work well.
I appreciate your quick work on this PR and would like to get it landed ASAP to minimize conflicts as new tests are added in other PRs! If you won't have time to update it in the next day-ish, please just let me know, I'm also happy to make the updates and get it landed!
Thanks again.
### TODO: Not function | ||
|
||
Unknown should not be part of the type of typing.reveal_type | ||
|
||
```py | ||
from typing import reveal_type | ||
|
||
def f(): | ||
return 1 | ||
|
||
a = not f | ||
b = not reveal_type | ||
|
||
reveal_type(a) # revealed: Literal[False] | ||
# reveal_type(b) # TODO: revealed: Literal[False] |
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.
Let's consolidate the stuff that's about the TODO to just the one line where it's relevant, that's not the main point of this test.
### TODO: Not function | |
Unknown should not be part of the type of typing.reveal_type | |
```py | |
from typing import reveal_type | |
def f(): | |
return 1 | |
a = not f | |
b = not reveal_type | |
reveal_type(a) # revealed: Literal[False] | |
# reveal_type(b) # TODO: revealed: Literal[False] | |
### Not function | |
```py | |
from typing import reveal_type | |
def f(): | |
return 1 | |
a = not f | |
b = not reveal_type | |
reveal_type(a) # revealed: Literal[False] | |
# TODO Unknown should not be part of the type of typing.reveal_type | |
# reveal_type(b) # revealed: Literal[False] |
reveal_type(f) # revealed: Literal[False] | ||
``` | ||
|
||
## Comparison |
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'd like to break up this large file a bit, lets move out this whole section into a set of files, e.g. comparison/integers.md
, comparison/non_boolean_returns.md
, comparison/strings.md
, comparison/unsupported.md
. There's probably more that we could break out as well, but that's probably good enough for now.
|
||
## Cyclical class definition | ||
|
||
Python supports classes that can reference themselves in their base class definitions. Although it may seem unusual, such a structure is not uncommon, particularly in type hinting systems like `typeshed`, where base classes can be self-referential: `class str(Sequence[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.
Let's generally hard-wrap lines at 80 columns in text, and also edit this text a bit for clarity/conciseness:
Python supports classes that can reference themselves in their base class definitions. Although it may seem unusual, such a structure is not uncommon, particularly in type hinting systems like `typeshed`, where base classes can be self-referential: `class str(Sequence[str]): ...`. | |
In type stubs, classes can reference themselves in their base class definitions. For example, in `typeshed`, we have `class str(Sequence[str]): ...`. |
NOTE: `j = "ab" < "ab_cd"` is a very cornercase test ensuring we're not comparing the interned salsa symbols, which compare by order of declaration. | ||
|
||
```py | ||
def str_instance() -> str: ... | ||
a = "abc" == "abc" | ||
b = "ab_cd" <= "ab_ce" | ||
c = "abc" in "ab cd" | ||
d = "" not in "hello" | ||
e = "--" is "--" | ||
f = "A" is "B" | ||
g = "--" is not "--" | ||
h = "A" is not "B" | ||
i = str_instance() < "..." | ||
j = "ab" < "ab_cd" |
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: I'd rather put notes like this as Python comments right next to the relevant line
NOTE: `j = "ab" < "ab_cd"` is a very cornercase test ensuring we're not comparing the interned salsa symbols, which compare by order of declaration. | |
```py | |
def str_instance() -> str: ... | |
a = "abc" == "abc" | |
b = "ab_cd" <= "ab_ce" | |
c = "abc" in "ab cd" | |
d = "" not in "hello" | |
e = "--" is "--" | |
f = "A" is "B" | |
g = "--" is not "--" | |
h = "A" is not "B" | |
i = str_instance() < "..." | |
j = "ab" < "ab_cd" | |
```py | |
def str_instance() -> str: ... | |
a = "abc" == "abc" | |
b = "ab_cd" <= "ab_ce" | |
c = "abc" in "ab cd" | |
d = "" not in "hello" | |
e = "--" is "--" | |
f = "A" is "B" | |
g = "--" is not "--" | |
h = "A" is not "B" | |
i = str_instance() < "..." | |
# ensure we're not comparing the interned salsa symbols, which compare by order of declaration. | |
j = "ab" < "ab_cd" |
TODO: `d = 5 < object()` should be `Unknown` but we don't check if __lt__ signature is valid for right operand type. | ||
|
||
```py | ||
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`" | ||
b = 0 not in 10 # error: "Operator `not in` is not supported for types `Literal[0]` and `Literal[10]`" | ||
c = object() < 5 # error: "Operator `<` is not supported for types `object` and `Literal[5]`" | ||
d = 5 < object() | ||
|
||
reveal_type(a) # revealed: bool | ||
reveal_type(b) # revealed: bool | ||
reveal_type(c) # revealed: Unknown | ||
reveal_type(d) # revealed: 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.
Same here:
TODO: `d = 5 < object()` should be `Unknown` but we don't check if __lt__ signature is valid for right operand type. | |
```py | |
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`" | |
b = 0 not in 10 # error: "Operator `not in` is not supported for types `Literal[0]` and `Literal[10]`" | |
c = object() < 5 # error: "Operator `<` is not supported for types `object` and `Literal[5]`" | |
d = 5 < object() | |
reveal_type(a) # revealed: bool | |
reveal_type(b) # revealed: bool | |
reveal_type(c) # revealed: Unknown | |
reveal_type(d) # revealed: bool | |
```py | |
a = 1 in 7 # error: "Operator `in` is not supported for types `Literal[1]` and `Literal[7]`" | |
b = 0 not in 10 # error: "Operator `not in` is not supported for types `Literal[0]` and `Literal[10]`" | |
c = object() < 5 # error: "Operator `<` is not supported for types `object` and `Literal[5]`" | |
d = 5 < object() | |
reveal_type(a) # revealed: bool | |
reveal_type(b) # revealed: bool | |
reveal_type(c) # revealed: Unknown | |
# TODO should be `Unknown` but we don't yet check if `__lt__` signature is valid for right operand | |
reveal_type(d) # revealed: bool |
reveal_type(a) # revealed: str | ||
``` | ||
|
||
## Subscript |
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.
These belong in subscript/string.md
reveal_type(a) # revealed: str | ||
``` | ||
|
||
## Bytes |
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.
Bytes are a separate type from strings, this should go in bytes.md
or literal/bytes.md
reveal_type(x) # revealed: Unbound | ||
``` | ||
|
||
### Annotation only transparent to local inference |
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.
All the tests from here onward should go in shadowing.md
along with some other tests I commented above.
reveal_type(x) # revealed: Literal[1, 2] | ||
``` | ||
|
||
## Assignment |
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.
These could go in assignment.md
@@ -0,0 +1,134 @@ | |||
# Variables | |||
|
|||
## Union resolution |
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.
This test can go with some others in conditional/if_statement.md
All clear. Will try to push fixes as soon as possible |
2cdafef
to
44d6372
Compare
I tried to follow the style you described. Please take a look at the tests with the new structure. Sorry for all the issues! |
…ype guards (#13758) ## Summary - Fix a bug with `… is not …` type guards. Previously, in an example like ```py x = [1] y = [1] if x is not y: reveal_type(x) ``` we would infer a type of `list[int] & ~list[int] == Never` for `x` inside the conditional (instead of `list[int]`), since we built a (negative) intersection with the type of the right hand side (`y`). However, as this example shows, this assumption can only be made for singleton types (types with a single inhabitant) such as `None`. - Add support for `… is …` type guards. closes #13715 ## Test Plan Moved existing `narrow_…` tests to Markdown-based tests and added new ones (including a regression test for the bug described above). Note that will create some conflicts with #13719. I tried to establish the correct organizational structure as proposed in #13719 (comment)
Except multiplied_string, multiplied_literal_string, truncated_string_literals_become_literal_string, adding_string_literals_and_literal_string
Except scope tests (unbound_function_local, implicit_global_in_function, conditionally_global_or_builtin, nonlocal_name_reference, nonlocal_name_reference_multi_level, nonlocal_name_reference_skips_class_scope, nonlocal_name_reference_skips_annotation_only_assignment)
Except exception_handler_with_invalid_syntax
9f27116
to
1b657b2
Compare
@sharkdp Thanks for letting me know! I rebased on the current main, but somehow |
Yep, I checked, actually on @sharkdp pr merge commit (74bf4b) test is running fine, but on 5fa82f it started to fail |
Yes -- unfortunately I had to change the assertion the test was making as part of that PR (#13753). This is because typeshed now declares |
Ok, i see. Thanks for the info! |
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.
This is great!! Thank you so much for the quick and thorough work. I'm going to make a few minor cosmetic tweaks, push those, wait for CI, and then merge.
Thanks so much for taking this on @Lexxxzy! A big first contribution, and a really helpful one for us!! |
Big thanks @carljm for reviewing my first PR to ruff! Learned a lot about that project while porting tests |
Summary
Porting infer tests to new markdown tests framework.
Link to the corresponding issue: #13696