Skip to content

Commit

Permalink
Deduplicate search path roots
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Jul 12, 2024
1 parent 287fcec commit 645532b
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/red_knot_module_resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ruff_python_stdlib = { workspace = true }

compact_str = { workspace = true }
camino = { workspace = true }
ordermap = { workspace = true }
once_cell = { workspace = true }
rustc-hash = { workspace = true }
salsa = { workspace = true }
Expand Down
28 changes: 22 additions & 6 deletions crates/red_knot_module_resolver/src/resolver.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use std::hash::BuildHasherDefault;
use std::sync::Arc;

use ordermap::OrderSet;
use rustc_hash::FxHasher;

use ruff_db::files::{system_path_to_file, File, FilePath};
use ruff_db::source::{source_text, SourceText};
use ruff_db::system::{DirectoryEntry, SystemPath, SystemPathBuf};
Expand All @@ -12,6 +16,14 @@ use crate::resolver::internal::ModuleResolverSettings;
use crate::state::ResolverState;
use crate::supported_py_version::TargetVersion;

/// A sequence of search paths that maintains its insertion order, but never contains duplicates.
///
/// This follows the invariants maintained by [`sys.path` at runtime]:
/// "No item is added to `sys.path` more than once."
///
/// [`sys.path` at runtime]: https://docs.python.org/3/library/site.html#module-site
type OrderedSearchPaths = OrderSet<Arc<ModuleResolutionPathBuf>, BuildHasherDefault<FxHasher>>;

/// Configures the module resolver settings.
///
/// Must be called before calling any other module resolution functions.
Expand Down Expand Up @@ -193,20 +205,20 @@ pub(crate) struct ValidatedSearchPathSettings {
}

impl ValidatedSearchPathSettings {
fn search_paths(&self, db: &dyn Db) -> Vec<Arc<ModuleResolutionPathBuf>> {
fn search_paths(&self, db: &dyn Db) -> OrderedSearchPaths {
let ValidatedSearchPathSettings {
extra_paths,
workspace_root,
stdlib,
site_packages,
} = self;
let mut search_paths: Vec<Arc<ModuleResolutionPathBuf>> = extra_paths
let mut search_paths: OrderedSearchPaths = extra_paths
.iter()
.cloned()
.chain([workspace_root, stdlib].into_iter().cloned())
.collect();
if let Some(site_packages) = site_packages {
search_paths.push(Arc::clone(site_packages));
search_paths.insert(Arc::clone(site_packages));
let site_packages = site_packages
.as_system_path()
.expect("Expected site-packages never to be a VendoredPath!");
Expand Down Expand Up @@ -260,11 +272,15 @@ impl<'db> PthFile<'db> {
'a: 'db,
{
// Empty lines or lines starting with '#' are ignored by the Python interpreter.
// Lines that start with "import " do not represent editable installs at all;
// Lines that start with "import " or "import\t" do not represent editable installs at all;
// instead, these are files that are executed by Python at startup.
// https://docs.python.org/3/library/site.html#module-site
self.contents.lines().filter_map(|line| {
if line.is_empty() || line.starts_with('#') || line.starts_with("import ") {
if line.is_empty()
|| line.starts_with('#')
|| line.starts_with("import ")
|| line.starts_with("import\t")
{
return None;
}
let possible_editable_install = self.site_packages.join(line);
Expand Down Expand Up @@ -329,7 +345,7 @@ pub(crate) struct ModuleResolutionSettings {
}

impl ModuleResolutionSettings {
pub(crate) fn search_paths(&self, db: &dyn Db) -> Vec<Arc<ModuleResolutionPathBuf>> {
pub(crate) fn search_paths(&self, db: &dyn Db) -> OrderedSearchPaths {
self.search_path_settings.search_paths(db)
}

Expand Down

0 comments on commit 645532b

Please sign in to comment.