-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prioritize attribute in from/import statement (#15041)
This tweaks the new semantics from #15026 a bit when a symbol could be interpreted both as an attribute and a submodule of a package. For `from...import`, we should actually prioritize the attribute, because of how the statement itself is implemented [1]. > 1. check if the imported module has an attribute by that name > 2. if not, attempt to import a submodule with that name and then check the imported module again for that attribute [1] https://docs.python.org/3/reference/simple_stmts.html#the-import-statement
- Loading branch information
Showing
2 changed files
with
101 additions
and
25 deletions.
There are no files selected for viewing
75 changes: 75 additions & 0 deletions
75
crates/red_knot_python_semantic/resources/mdtest/import/conflicts.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Conflicting attributes and submodules | ||
|
||
## Via import | ||
|
||
```py | ||
import a.b | ||
|
||
reveal_type(a.b) # revealed: <module 'a.b'> | ||
``` | ||
|
||
```py path=a/__init__.py | ||
b = 42 | ||
``` | ||
|
||
```py path=a/b.py | ||
``` | ||
|
||
## Via from/import | ||
|
||
```py | ||
from a import b | ||
|
||
reveal_type(b) # revealed: Literal[42] | ||
``` | ||
|
||
```py path=a/__init__.py | ||
b = 42 | ||
``` | ||
|
||
```py path=a/b.py | ||
``` | ||
|
||
## Via both | ||
|
||
```py | ||
import a.b | ||
from a import b | ||
|
||
reveal_type(b) # revealed: <module 'a.b'> | ||
reveal_type(a.b) # revealed: <module 'a.b'> | ||
``` | ||
|
||
```py path=a/__init__.py | ||
b = 42 | ||
``` | ||
|
||
```py path=a/b.py | ||
``` | ||
|
||
## Via both (backwards) | ||
|
||
In this test, we infer a different type for `b` than the runtime behavior of the Python interpreter. | ||
The interpreter will not load the submodule `a.b` during the `from a import b` statement, since `a` | ||
contains a non-module attribute named `b`. (See the [definition][from-import] of a `from...import` | ||
statement for details.) However, because our import tracking is flow-insensitive, we will see that | ||
`a.b` is imported somewhere in the file, and therefore assume that the `from...import` statement | ||
sees the submodule as the value of `b` instead of the integer. | ||
|
||
```py | ||
from a import b | ||
import a.b | ||
|
||
# Python would say `Literal[42]` for `b` | ||
reveal_type(b) # revealed: <module 'a.b'> | ||
reveal_type(a.b) # revealed: <module 'a.b'> | ||
``` | ||
|
||
```py path=a/__init__.py | ||
b = 42 | ||
``` | ||
|
||
```py path=a/b.py | ||
``` | ||
|
||
[from-import]: https://docs.python.org/3/reference/simple_stmts.html#the-import-statement |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters