diff --git a/crates/red_knot_project/src/metadata/options.rs b/crates/red_knot_project/src/metadata/options.rs index 8662172632..eaf19fe318 100644 --- a/crates/red_knot_project/src/metadata/options.rs +++ b/crates/red_knot_project/src/metadata/options.rs @@ -121,7 +121,7 @@ impl Options { .ok() .map(PythonPath::from_virtual_env_var) }) - .unwrap_or(PythonPath::Discover), + .unwrap_or_else(|| PythonPath::Discover(project_root.to_path_buf())), } } diff --git a/crates/red_knot_python_semantic/src/module_resolver/resolver.rs b/crates/red_knot_python_semantic/src/module_resolver/resolver.rs index 0d35cbd082..48e956c37d 100644 --- a/crates/red_knot_python_semantic/src/module_resolver/resolver.rs +++ b/crates/red_knot_python_semantic/src/module_resolver/resolver.rs @@ -133,20 +133,13 @@ pub(crate) fn search_paths(db: &dyn Db) -> SearchPathIterator { Program::get(db).search_paths(db).iter(db) } -/// Searches for a `.venv` directory in the current or any parent directory -fn virtual_env_from_working_dir() -> Option { - let current_dir = std::env::current_dir().ok()?; +/// Searches for a `.venv` directory in `project_root` that contains a `pyvenv.cfg` file. +fn discover_venv_in(system: &dyn System, project_root: &SystemPath) -> Option { + let virtual_env_directory = project_root.join(".venv"); - for dir in current_dir.ancestors() { - let dot_venv = dir.join(".venv"); - if dot_venv.is_dir() { - if !dot_venv.join("pyvenv.cfg").is_file() { - return None; - } - return SystemPathBuf::from_path_buf(dot_venv).ok(); - } - } - None + system + .is_file(&virtual_env_directory.join("pyvenv.cfg")) + .then_some(virtual_env_directory) } #[derive(Debug, PartialEq, Eq)] @@ -251,15 +244,15 @@ impl SearchPaths { .and_then(|venv| venv.site_packages_directories(system))? } - PythonPath::Discover => { - tracing::debug!("Discovering virtual environment"); - let virtual_env_path = virtual_env_from_working_dir(); + PythonPath::Discover(root) => { + tracing::debug!("Discovering virtual environment in `{root}`"); + let virtual_env_path = discover_venv_in(db.system(), root); if let Some(virtual_env_path) = virtual_env_path { - tracing::debug!("Found `.venv` folder at '{}'", virtual_env_path); + tracing::debug!("Found `.venv` folder at `{}`", virtual_env_path); let handle_invalid_virtual_env = |error: SitePackagesDiscoveryError| { tracing::debug!( - "Ignoring automatically detected virtual environment at '{}': {}", + "Ignoring automatically detected virtual environment at `{}`: {}", virtual_env_path, error ); diff --git a/crates/red_knot_python_semantic/src/program.rs b/crates/red_knot_python_semantic/src/program.rs index 73c553d14d..af3bbe0ce2 100644 --- a/crates/red_knot_python_semantic/src/program.rs +++ b/crates/red_knot_python_semantic/src/program.rs @@ -145,7 +145,8 @@ pub enum PythonPath { /// [`sys.prefix`]: https://docs.python.org/3/library/sys.html#sys.prefix SysPrefix(SystemPathBuf, SysPrefixPathOrigin), - Discover, + /// Tries to discover a virtual environment in the given path. + Discover(SystemPathBuf), /// Resolved site packages paths. ///