Skip to content

Commit 2bc6c78

Browse files
authored
[ty] introduce local variables for from imports of submodules in __init__.py(i) (#21173)
This rips out the previous implementation in favour of a new implementation with 3 rules: - **froms are locals**: a `from..import` can only define locals, it does not have global side-effects. Specifically any submodule attribute `a` that's implicitly introduced by either `from .a import b` or `from . import a as b` (in an `__init__.py(i)`) is a local and not a global. If you do such an import at the top of a file you won't notice this. However if you do such an import in a function, that means it will only be function-scoped (so you'll need to do it in every function that wants to access it, making your code less sensitive to execution order). - **first from first serve**: only the *first* `from..import` in an `__init__.py(i)` that imports a particular direct submodule of the current package introduces that submodule as a local. Subsequent imports of the submodule will not introduce that local. This reflects the fact that in actual python only the first import of a submodule (in the entire execution of the program) introduces it as an attribute of the package. By "first" we mean "the first time in this scope (or any parent scope)". This pairs well with the fact that we are specifically introducing a local (as long as you don't accidentally shadow or overwrite the local). - **dot re-exports**: `from . import a` in an `__init__.pyi` is considered a re-export of `a` (equivalent to `from . import a as a`). This is required to properly handle many stubs in the wild. Currently it must be *exactly* `from . import ...`. This implementation is intentionally limited/conservative (notably, often requiring a from import to be relative). I'm going to file a ton of followups for improvements so that their impact can be evaluated separately. Fixes astral-sh/ty#133
1 parent 1fd852f commit 2bc6c78

File tree

9 files changed

+663
-198
lines changed

9 files changed

+663
-198
lines changed

crates/ruff_db/src/files.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,12 @@ impl File {
475475
self.path(db).as_str().ends_with("__init__.pyi")
476476
}
477477

478+
/// Returns `true` if the file is an `__init__.pyi`
479+
pub fn is_package(self, db: &dyn Db) -> bool {
480+
let path = self.path(db).as_str();
481+
path.ends_with("__init__.pyi") || path.ends_with("__init__.py")
482+
}
483+
478484
pub fn source_type(self, db: &dyn Db) -> PySourceType {
479485
match self.path(db) {
480486
FilePath::System(path) => path

0 commit comments

Comments
 (0)