allow `from` imports in nonglobal scopes to add available submodule attributes

This commit is contained in:
Alex Waygood 2025-11-23 22:51:34 +00:00 committed by Aria Desires
parent dbe5696f8f
commit e82cb13384
2 changed files with 42 additions and 1 deletions

View File

@ -116,3 +116,43 @@ b = 1
```py ```py
``` ```
## Submodule is loaded in a non-global scope
We recognise submodules as being available as attributes even if they are loaded in a function
scope. The function might never be executed, which means that the submodule might never be loaded;
however, we prefer to prioritise avoiding false positives over catching all possible errors here.
`a/b.py`:
```py
```
`a/c.py`:
```py
d = 42
```
`a/e/f.py`:
```py
```
`main.py`:
```py
import a
def f():
import a.b
from a.c import d
from a.e import f
f()
reveal_type(a.b) # revealed: <module 'a.b'>
reveal_type(a.c) # revealed: <module 'a.c'>
reveal_type(a.e) # revealed: <module 'a.e'>
reveal_type(a.e.f) # revealed: <module 'a.e.f'>
```

View File

@ -1535,11 +1535,12 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
is_self_import = &module_name == thispackage; is_self_import = &module_name == thispackage;
} }
if self.current_scope().is_global() && node.module.is_some() { if node.module.is_some() {
if let Ok(thispackage) = this_package if let Ok(thispackage) = this_package
&& let Some(relative_submodule) = module_name.relative_to(&thispackage) && let Some(relative_submodule) = module_name.relative_to(&thispackage)
{ {
if is_package if is_package
&& self.current_scope().is_global()
&& let Some(direct_submodule) = && let Some(direct_submodule) =
relative_submodule.components().next() relative_submodule.components().next()
&& !self.seen_submodule_imports.contains(direct_submodule) && !self.seen_submodule_imports.contains(direct_submodule)