Skip to content

Commit 312a487

Browse files
authored
[red-knot] Add some knowledge of __all__ to *-import machinery (#17373)
1 parent cf8dc60 commit 312a487

File tree

2 files changed

+189
-44
lines changed

2 files changed

+189
-44
lines changed

crates/red_knot_python_semantic/resources/mdtest/import/star.md

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,30 @@ reveal_type(X) # revealed: Unknown
626626
reveal_type(Y) # revealed: bool
627627
```
628628

629+
### An implicit import in a `.pyi` file later overridden by another assignment
630+
631+
`a.pyi`:
632+
633+
```pyi
634+
X: bool = True
635+
```
636+
637+
`b.pyi`:
638+
639+
```pyi
640+
from a import X
641+
642+
X: bool = False
643+
```
644+
645+
`c.py`:
646+
647+
```py
648+
from b import *
649+
650+
reveal_type(X) # revealed: bool
651+
```
652+
629653
## Visibility constraints
630654

631655
If an `importer` module contains a `from exporter import *` statement in its global namespace, the
@@ -865,15 +889,10 @@ from exporter import *
865889

866890
reveal_type(X) # revealed: bool
867891

868-
# TODO none of these should error, should all reveal `bool`
869-
# error: [unresolved-reference]
870-
reveal_type(_private) # revealed: Unknown
871-
# error: [unresolved-reference]
872-
reveal_type(__protected) # revealed: Unknown
873-
# error: [unresolved-reference]
874-
reveal_type(__dunder__) # revealed: Unknown
875-
# error: [unresolved-reference]
876-
reveal_type(___thunder___) # revealed: Unknown
892+
reveal_type(_private) # revealed: bool
893+
reveal_type(__protected) # revealed: bool
894+
reveal_type(__dunder__) # revealed: bool
895+
reveal_type(___thunder___) # revealed: bool
877896

878897
# TODO: should emit [unresolved-reference] diagnostic & reveal `Unknown`
879898
reveal_type(Y) # revealed: bool
@@ -1072,6 +1091,44 @@ reveal_type(Y) # revealed: bool
10721091
reveal_type(Z) # revealed: Unknown
10731092
```
10741093

1094+
### `__all__` conditionally defined in a statically known branch (2)
1095+
1096+
The same example again, but with a different `python-version` set:
1097+
1098+
```toml
1099+
[environment]
1100+
python-version = "3.10"
1101+
```
1102+
1103+
`exporter.py`:
1104+
1105+
```py
1106+
import sys
1107+
1108+
X: bool = True
1109+
1110+
if sys.version_info >= (3, 11):
1111+
__all__ = ["X", "Y"]
1112+
Y: bool = True
1113+
else:
1114+
__all__ = ("Z",)
1115+
Z: bool = True
1116+
```
1117+
1118+
`importer.py`:
1119+
1120+
```py
1121+
from exporter import *
1122+
1123+
# TODO: should reveal `Unknown` and emit `[unresolved-reference]`
1124+
reveal_type(X) # revealed: bool
1125+
1126+
# error: [unresolved-reference]
1127+
reveal_type(Y) # revealed: Unknown
1128+
1129+
reveal_type(Z) # revealed: bool
1130+
```
1131+
10751132
### `__all__` conditionally mutated in a statically known branch
10761133

10771134
```toml
@@ -1084,11 +1141,11 @@ python-version = "3.11"
10841141
```py
10851142
import sys
10861143

1087-
__all__ = ["X"]
1144+
__all__ = []
10881145
X: bool = True
10891146

10901147
if sys.version_info >= (3, 11):
1091-
__all__.append("Y")
1148+
__all__.extend(["X", "Y"])
10921149
Y: bool = True
10931150
else:
10941151
__all__.append("Z")
@@ -1107,6 +1164,45 @@ reveal_type(Y) # revealed: bool
11071164
reveal_type(Z) # revealed: Unknown
11081165
```
11091166

1167+
### `__all__` conditionally mutated in a statically known branch (2)
1168+
1169+
The same example again, but with a different `python-version` set:
1170+
1171+
```toml
1172+
[environment]
1173+
python-version = "3.10"
1174+
```
1175+
1176+
`exporter.py`:
1177+
1178+
```py
1179+
import sys
1180+
1181+
__all__ = []
1182+
X: bool = True
1183+
1184+
if sys.version_info >= (3, 11):
1185+
__all__.extend(["X", "Y"])
1186+
Y: bool = True
1187+
else:
1188+
__all__.append("Z")
1189+
Z: bool = True
1190+
```
1191+
1192+
`importer.py`:
1193+
1194+
```py
1195+
from exporter import *
1196+
1197+
# TODO: should reveal `Unknown` & emit `[unresolved-reference]
1198+
reveal_type(X) # revealed: bool
1199+
1200+
# error: [unresolved-reference]
1201+
reveal_type(Y) # revealed: Unknown
1202+
1203+
reveal_type(Z) # revealed: bool
1204+
```
1205+
11101206
### Empty `__all__`
11111207

11121208
An empty `__all__` is valid, but a `*` import from a module with an empty `__all__` results in 0
@@ -1166,6 +1262,7 @@ from b import *
11661262

11671263
# TODO: should not error, should reveal `bool`
11681264
# (`X` is re-exported from `b.pyi` due to presence in `__all__`)
1265+
# See https://github.com/astral-sh/ruff/issues/16159
11691266
#
11701267
# error: [unresolved-reference]
11711268
reveal_type(X) # revealed: Unknown

0 commit comments

Comments
 (0)