Skip to content

Commit 28e9c9a

Browse files
committed
only absolute gets extra paths
1 parent cd724e0 commit 28e9c9a

1 file changed

Lines changed: 56 additions & 2 deletions

File tree

  • crates/ty_python_semantic/src/module_resolver

crates/ty_python_semantic/src/module_resolver/resolver.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module<'_>> {
334334
db,
335335
file,
336336
path,
337-
desperate_search_paths(db, file).iter().flatten(),
337+
simple_desperate_search_paths(db, file).iter(),
338338
)
339339
})
340340
}
@@ -449,10 +449,64 @@ fn desperate_search_paths(db: &dyn Db, importing_file: File) -> Option<Vec<Searc
449449
if search_paths.is_empty() {
450450
None
451451
} else {
452-
search_paths.reverse();
453452
Some(search_paths)
454453
}
455454
}
455+
456+
/// Get the search-paths that should be used for desperate resolution of imports in this file
457+
///
458+
/// Currently this is "the closest ancestor dir that contains a pyproject.toml", which is
459+
/// a completely arbitrary decision. We could potentially change this to return an iterator
460+
/// of every ancestor with a pyproject.toml or every ancestor.
461+
///
462+
/// For now this works well in common cases where we have some larger workspace that contains
463+
/// one or more python projects in sub-directories, and those python projects assume that
464+
/// absolute imports resolve relative to the pyproject.toml they live under.
465+
///
466+
/// Being so strict minimizes concerns about this going off a lot and doing random
467+
/// chaotic things. In particular, all files under a given pyproject.toml will currently
468+
/// agree on this being their desperate search-path, which is really nice.
469+
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
470+
fn simple_desperate_search_paths(db: &dyn Db, importing_file: File) -> Option<SearchPath> {
471+
let system = db.system();
472+
let importing_path = importing_file.path(db).as_system_path()?;
473+
474+
// Only allow this if the importing_file is under the first-party search path
475+
let (base_path, rel_path) =
476+
search_paths(db, ModuleResolveMode::StubsAllowed).find_map(|search_path| {
477+
if !search_path.is_first_party() {
478+
return None;
479+
}
480+
Some((
481+
search_path.as_system_path()?,
482+
search_path.relativize_system_path_only(importing_path)?,
483+
))
484+
})?;
485+
486+
// Read the revision on the corresponding file root to
487+
// register an explicit dependency on this directory. When
488+
// the revision gets bumped, the cache that Salsa creates
489+
// for this routine will be invalidated.
490+
//
491+
// (This is conditional because ruff uses this code too and doesn't set roots)
492+
if let Some(root) = db.files().root(db, base_path) {
493+
let _ = root.revision(db);
494+
}
495+
496+
// Only allow searching up to the first-party path's root
497+
for rel_dir in rel_path.ancestors() {
498+
let candidate_path = base_path.join(rel_dir);
499+
// Any dir with a pyproject.toml or ty.toml might be a project root
500+
if system.is_file(&candidate_path.join("pyproject.toml"))
501+
|| system.is_file(&candidate_path.join("ty.toml"))
502+
{
503+
let search_path = SearchPath::first_party(system, candidate_path).ok()?;
504+
return Some(search_path);
505+
}
506+
}
507+
508+
None
509+
}
456510
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
457511
pub struct SearchPaths {
458512
/// Search paths that have been statically determined purely from reading

0 commit comments

Comments
 (0)