Skip to content

Commit 218e122

Browse files
committed
Support import namespace
1 parent 9875a5a commit 218e122

File tree

16 files changed

+230
-125
lines changed

16 files changed

+230
-125
lines changed

crates/ruff_db/src/files.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl Files {
7979
///
8080
/// The operation always succeeds even if the path doesn't exist on disk, isn't accessible or if the path points to a directory.
8181
/// In these cases, a file with status [`FileStatus::NotFound`] is returned.
82-
fn system(&self, db: &dyn Db, path: &SystemPath) -> File {
82+
pub fn system(&self, db: &dyn Db, path: &SystemPath) -> File {
8383
let absolute = SystemPath::absolute(path, db.system().current_directory());
8484

8585
*self

crates/ruff_graph/src/resolver.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ impl<'a> Resolver<'a> {
1818
/// Resolve the [`CollectedImport`] into a [`FilePath`].
1919
pub(crate) fn resolve(&self, import: CollectedImport) -> Option<&'a FilePath> {
2020
match import {
21-
CollectedImport::Import(import) => {
22-
resolve_module(self.db, &import).map(|module| module.file().path(self.db))
23-
}
21+
CollectedImport::Import(import) => resolve_module(self.db, &import)
22+
.and_then(|module| Some(module.file()?.path(self.db))),
2423
CollectedImport::ImportFrom(import) => {
2524
// Attempt to resolve the member (e.g., given `from foo import bar`, look for `foo.bar`).
2625
let parent = import.parent();
2726

2827
resolve_module(self.db, &import)
29-
.map(|module| module.file().path(self.db))
28+
.and_then(|module| Some(module.file()?.path(self.db)))
3029
.or_else(|| {
3130
// Attempt to resolve the module (e.g., given `from foo import bar`, look for `foo`).
3231

33-
resolve_module(self.db, &parent?).map(|module| module.file().path(self.db))
32+
resolve_module(self.db, &parent?)
33+
.and_then(|module| Some(module.file()?.path(self.db)))
3434
})
3535
}
3636
}

crates/ty/tests/file_watching.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,13 +1444,11 @@ mod unix {
14441444
)
14451445
.expect("Expected bar.baz to exist in site-packages.");
14461446
let baz_project = case.project_path("bar/baz.py");
1447+
let baz_file = baz.file().unwrap();
14471448

1449+
assert_eq!(source_text(case.db(), baz_file).as_str(), "def baz(): ...");
14481450
assert_eq!(
1449-
source_text(case.db(), baz.file()).as_str(),
1450-
"def baz(): ..."
1451-
);
1452-
assert_eq!(
1453-
baz.file().path(case.db()).as_system_path(),
1451+
baz_file.path(case.db()).as_system_path(),
14541452
Some(&*baz_project)
14551453
);
14561454

@@ -1465,7 +1463,7 @@ mod unix {
14651463
case.apply_changes(changes);
14661464

14671465
assert_eq!(
1468-
source_text(case.db(), baz.file()).as_str(),
1466+
source_text(case.db(), baz_file).as_str(),
14691467
"def baz(): print('Version 2')"
14701468
);
14711469

@@ -1478,7 +1476,7 @@ mod unix {
14781476
case.apply_changes(changes);
14791477

14801478
assert_eq!(
1481-
source_text(case.db(), baz.file()).as_str(),
1479+
source_text(case.db(), baz_file).as_str(),
14821480
"def baz(): print('Version 3')"
14831481
);
14841482

@@ -1524,6 +1522,7 @@ mod unix {
15241522
&ModuleName::new_static("bar.baz").unwrap(),
15251523
)
15261524
.expect("Expected bar.baz to exist in site-packages.");
1525+
let baz_file = baz.file().unwrap();
15271526
let bar_baz = case.project_path("bar/baz.py");
15281527

15291528
let patched_bar_baz = case.project_path("patched/bar/baz.py");
@@ -1534,11 +1533,8 @@ mod unix {
15341533
"def baz(): ..."
15351534
);
15361535

1537-
assert_eq!(
1538-
source_text(case.db(), baz.file()).as_str(),
1539-
"def baz(): ..."
1540-
);
1541-
assert_eq!(baz.file().path(case.db()).as_system_path(), Some(&*bar_baz));
1536+
assert_eq!(source_text(case.db(), baz_file).as_str(), "def baz(): ...");
1537+
assert_eq!(baz_file.path(case.db()).as_system_path(), Some(&*bar_baz));
15421538

15431539
case.assert_indexed_project_files([patched_bar_baz_file]);
15441540

@@ -1567,7 +1563,7 @@ mod unix {
15671563
let patched_baz_text = source_text(case.db(), patched_bar_baz_file);
15681564
let did_update_patched_baz = patched_baz_text.as_str() == "def baz(): print('Version 2')";
15691565

1570-
let bar_baz_text = source_text(case.db(), baz.file());
1566+
let bar_baz_text = source_text(case.db(), baz_file);
15711567
let did_update_bar_baz = bar_baz_text.as_str() == "def baz(): print('Version 2')";
15721568

15731569
assert!(
@@ -1650,7 +1646,7 @@ mod unix {
16501646
"def baz(): ..."
16511647
);
16521648
assert_eq!(
1653-
baz.file().path(case.db()).as_system_path(),
1649+
baz.file().unwrap().path(case.db()).as_system_path(),
16541650
Some(&*baz_original)
16551651
);
16561652

crates/ty_ide/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ impl HasNavigationTargets for Type<'_> {
188188

189189
impl HasNavigationTargets for TypeDefinition<'_> {
190190
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
191-
let full_range = self.full_range(db.upcast());
191+
let Some(full_range) = self.full_range(db.upcast()) else {
192+
return NavigationTargets::empty();
193+
};
194+
192195
NavigationTargets::single(NavigationTarget {
193196
file: full_range.file(),
194197
focus_range: self.focus_range(db.upcast()).unwrap_or(full_range).range(),

crates/ty_python_semantic/resources/mdtest/import/namespace.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import parent.child.two
2929
`from.py`
3030

3131
```py
32-
# TODO: This should not be an error
33-
from parent.child import one, two # error: [unresolved-import]
32+
from parent.child import one, two
33+
34+
reveal_type(one) # revealed: <module 'parent.child.one'>
35+
reveal_type(two) # revealed: <module 'parent.child.two'>
3436
```
3537

3638
## Regular package in namespace package

crates/ty_python_semantic/src/dunder_all.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,10 @@ impl<'db> DunderAllNamesCollector<'db> {
109109
else {
110110
return false;
111111
};
112-
let Some(module_dunder_all_names) =
113-
dunder_all_names(self.db, module_literal.module(self.db).file())
112+
let Some(module_dunder_all_names) = module_literal
113+
.module(self.db)
114+
.file()
115+
.and_then(|file| dunder_all_names(self.db, file))
114116
else {
115117
// The module either does not have a `__all__` variable or it is invalid.
116118
return false;
@@ -179,7 +181,12 @@ impl<'db> DunderAllNamesCollector<'db> {
179181
let module_name =
180182
ModuleName::from_import_statement(self.db, self.file, import_from).ok()?;
181183
let module = resolve_module(self.db, &module_name)?;
182-
dunder_all_names(self.db, module.file())
184+
185+
if let Some(file) = module.file() {
186+
dunder_all_names(self.db, file)
187+
} else {
188+
None
189+
}
183190
}
184191

185192
/// Infer the type of a standalone expression.

crates/ty_python_semantic/src/module_resolver/module.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ impl Module {
1717
pub(crate) fn new(
1818
name: ModuleName,
1919
kind: ModuleKind,
20-
search_path: SearchPath,
21-
file: File,
20+
search_path: Option<SearchPath>,
21+
file: Option<File>,
2222
) -> Self {
23-
let known = KnownModule::try_from_search_path_and_name(&search_path, &name);
23+
let known = search_path
24+
.as_ref()
25+
.and_then(|search_path| KnownModule::try_from_search_path_and_name(search_path, &name));
26+
2427
Self {
2528
inner: Arc::new(ModuleInner {
2629
name,
@@ -38,7 +41,9 @@ impl Module {
3841
}
3942

4043
/// The file to the source code that defines this module
41-
pub fn file(&self) -> File {
44+
///
45+
/// This is `None` for namespace packages.
46+
pub fn file(&self) -> Option<File> {
4247
self.inner.file
4348
}
4449

@@ -53,8 +58,8 @@ impl Module {
5358
}
5459

5560
/// The search path from which the module was resolved.
56-
pub(crate) fn search_path(&self) -> &SearchPath {
57-
&self.inner.search_path
61+
pub(crate) fn search_path(&self) -> Option<&SearchPath> {
62+
self.inner.search_path.as_ref()
5863
}
5964

6065
/// Determine whether this module is a single-file module or a package
@@ -78,8 +83,8 @@ impl std::fmt::Debug for Module {
7883
struct ModuleInner {
7984
name: ModuleName,
8085
kind: ModuleKind,
81-
search_path: SearchPath,
82-
file: File,
86+
search_path: Option<SearchPath>,
87+
file: Option<File>,
8388
known: Option<KnownModule>,
8489
}
8590

0 commit comments

Comments
 (0)