Skip to content

Commit a1535fb

Browse files
authored
[red-knot] Change venv discovery (#17099)
## Summary Rewrites the virtual env discovery to: * Only use of `System` APIs, this ensures that the discovery will also work when using a memory file system (testing or WASM) * Don't traverse ancestor directories. We're not convinced that this is necessary. Let's wait until someone shows us a use case where it is needed * Start from the project root and not from the current working directory. This ensures that Red Knot picks up the right venv even when using `knot --project ../other-dir` ## Test Plan Existing tests, @ntBre tested that the `file_watching` tests no longer pick up his virtual env in a parent directory
1 parent 4a6fa5f commit a1535fb

File tree

3 files changed

+15
-21
lines changed

3 files changed

+15
-21
lines changed

crates/red_knot_project/src/metadata/options.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ impl Options {
121121
.ok()
122122
.map(PythonPath::from_virtual_env_var)
123123
})
124-
.unwrap_or(PythonPath::Discover),
124+
.unwrap_or_else(|| PythonPath::Discover(project_root.to_path_buf())),
125125
}
126126
}
127127

crates/red_knot_python_semantic/src/module_resolver/resolver.rs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -133,20 +133,13 @@ pub(crate) fn search_paths(db: &dyn Db) -> SearchPathIterator {
133133
Program::get(db).search_paths(db).iter(db)
134134
}
135135

136-
/// Searches for a `.venv` directory in the current or any parent directory
137-
fn virtual_env_from_working_dir() -> Option<SystemPathBuf> {
138-
let current_dir = std::env::current_dir().ok()?;
139-
140-
for dir in current_dir.ancestors() {
141-
let dot_venv = dir.join(".venv");
142-
if dot_venv.is_dir() {
143-
if !dot_venv.join("pyvenv.cfg").is_file() {
144-
return None;
145-
}
146-
return SystemPathBuf::from_path_buf(dot_venv).ok();
147-
}
148-
}
149-
None
136+
/// Searches for a `.venv` directory in `project_root` that contains a `pyvenv.cfg` file.
137+
fn discover_venv_in(system: &dyn System, project_root: &SystemPath) -> Option<SystemPathBuf> {
138+
let virtual_env_directory = project_root.join(".venv");
139+
140+
system
141+
.is_file(&virtual_env_directory.join("pyvenv.cfg"))
142+
.then_some(virtual_env_directory)
150143
}
151144

152145
#[derive(Debug, PartialEq, Eq)]
@@ -251,15 +244,15 @@ impl SearchPaths {
251244
.and_then(|venv| venv.site_packages_directories(system))?
252245
}
253246

254-
PythonPath::Discover => {
255-
tracing::debug!("Discovering virtual environment");
256-
let virtual_env_path = virtual_env_from_working_dir();
247+
PythonPath::Discover(root) => {
248+
tracing::debug!("Discovering virtual environment in `{root}`");
249+
let virtual_env_path = discover_venv_in(db.system(), root);
257250
if let Some(virtual_env_path) = virtual_env_path {
258-
tracing::debug!("Found `.venv` folder at '{}'", virtual_env_path);
251+
tracing::debug!("Found `.venv` folder at `{}`", virtual_env_path);
259252

260253
let handle_invalid_virtual_env = |error: SitePackagesDiscoveryError| {
261254
tracing::debug!(
262-
"Ignoring automatically detected virtual environment at '{}': {}",
255+
"Ignoring automatically detected virtual environment at `{}`: {}",
263256
virtual_env_path,
264257
error
265258
);

crates/red_knot_python_semantic/src/program.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ pub enum PythonPath {
145145
/// [`sys.prefix`]: https://docs.python.org/3/library/sys.html#sys.prefix
146146
SysPrefix(SystemPathBuf, SysPrefixPathOrigin),
147147

148-
Discover,
148+
/// Tries to discover a virtual environment in the given path.
149+
Discover(SystemPathBuf),
149150

150151
/// Resolved site packages paths.
151152
///

0 commit comments

Comments
 (0)