Skip to content

Commit 5c64c53

Browse files
committed
respect --project during uv python find (#11990)
## Summary this pr updates a couple discovery-related functions to accept a directory argument, used as the root directory when searching for a virtual environment ## Test Plan todo
1 parent 626fff1 commit 5c64c53

File tree

13 files changed

+234
-45
lines changed

13 files changed

+234
-45
lines changed

crates/uv-python/src/discovery.rs

+74-28
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::managed::ManagedPythonInstallations;
2929
use crate::microsoft_store::find_microsoft_store_pythons;
3030
use crate::virtualenv::Error as VirtualEnvError;
3131
use crate::virtualenv::{
32-
conda_environment_from_env, virtualenv_from_env, virtualenv_from_working_dir,
32+
conda_environment_from_env, virtualenv_from_dir, virtualenv_from_env,
3333
virtualenv_python_executable, CondaEnvironmentKind,
3434
};
3535
#[cfg(windows)]
@@ -250,8 +250,9 @@ pub enum Error {
250250
/// - Discovered virtual environment (e.g. `.venv` in a parent directory)
251251
///
252252
/// Notably, "system" environments are excluded. See [`python_executables_from_installed`].
253-
fn python_executables_from_virtual_environments<'a>(
254-
) -> impl Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a {
253+
fn python_executables_from_virtual_environments(
254+
discovery_root: &Path,
255+
) -> impl Iterator<Item = Result<(PythonSource, PathBuf), Error>> + '_ {
255256
let from_active_environment = iter::once_with(|| {
256257
virtualenv_from_env()
257258
.into_iter()
@@ -269,8 +270,8 @@ fn python_executables_from_virtual_environments<'a>(
269270
})
270271
.flatten();
271272

272-
let from_discovered_environment = iter::once_with(|| {
273-
virtualenv_from_working_dir()
273+
let from_discovered_environment = iter::once_with(move || {
274+
virtualenv_from_dir(discovery_root)
274275
.map(|path| {
275276
path.map(virtualenv_python_executable)
276277
.map(|path| (PythonSource::DiscoveredEnvironment, path))
@@ -420,6 +421,7 @@ fn python_executables<'a>(
420421
implementation: Option<&'a ImplementationName>,
421422
environments: EnvironmentPreference,
422423
preference: PythonPreference,
424+
discovery_root: &'a Path,
423425
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
424426
// Always read from `UV_INTERNAL__PARENT_INTERPRETER` — it could be a system interpreter
425427
let from_parent_interpreter = iter::once_with(|| {
@@ -438,7 +440,7 @@ fn python_executables<'a>(
438440
})
439441
.flatten();
440442

441-
let from_virtual_environments = python_executables_from_virtual_environments();
443+
let from_virtual_environments = python_executables_from_virtual_environments(discovery_root);
442444
let from_installed = python_executables_from_installed(version, implementation, preference);
443445

444446
// Limit the search to the relevant environment preference; this avoids unnecessary work like
@@ -615,16 +617,22 @@ fn python_interpreters<'a>(
615617
environments: EnvironmentPreference,
616618
preference: PythonPreference,
617619
cache: &'a Cache,
620+
discovery_root: &'a Path,
618621
) -> impl Iterator<Item = Result<(PythonSource, Interpreter), Error>> + 'a {
619622
python_interpreters_from_executables(
620623
// Perform filtering on the discovered executables based on their source. This avoids
621624
// unnecessary interpreter queries, which are generally expensive. We'll filter again
622625
// with `interpreter_satisfies_environment_preference` after querying.
623-
python_executables(version, implementation, environments, preference).filter_ok(
624-
move |(source, path)| {
625-
source_satisfies_environment_preference(*source, path, environments)
626-
},
627-
),
626+
python_executables(
627+
version,
628+
implementation,
629+
environments,
630+
preference,
631+
discovery_root,
632+
)
633+
.filter_ok(move |(source, path)| {
634+
source_satisfies_environment_preference(*source, path, environments)
635+
}),
628636
cache,
629637
)
630638
.filter_ok(move |(source, interpreter)| {
@@ -862,6 +870,7 @@ pub fn find_python_installations<'a>(
862870
environments: EnvironmentPreference,
863871
preference: PythonPreference,
864872
cache: &'a Cache,
873+
discovery_root: &'a Path,
865874
) -> Box<dyn Iterator<Item = Result<FindPythonResult, Error>> + 'a> {
866875
let sources = DiscoveryPreferences {
867876
python_preference: preference,
@@ -942,8 +951,15 @@ pub fn find_python_installations<'a>(
942951
}
943952
PythonRequest::Any => Box::new({
944953
debug!("Searching for any Python interpreter in {sources}");
945-
python_interpreters(&VersionRequest::Any, None, environments, preference, cache)
946-
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
954+
python_interpreters(
955+
&VersionRequest::Any,
956+
None,
957+
environments,
958+
preference,
959+
cache,
960+
discovery_root,
961+
)
962+
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
947963
}),
948964
PythonRequest::Default => Box::new({
949965
debug!("Searching for default Python interpreter in {sources}");
@@ -953,6 +969,7 @@ pub fn find_python_installations<'a>(
953969
environments,
954970
preference,
955971
cache,
972+
discovery_root,
956973
)
957974
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
958975
}),
@@ -962,8 +979,15 @@ pub fn find_python_installations<'a>(
962979
};
963980
Box::new({
964981
debug!("Searching for {request} in {sources}");
965-
python_interpreters(version, None, environments, preference, cache)
966-
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
982+
python_interpreters(
983+
version,
984+
None,
985+
environments,
986+
preference,
987+
cache,
988+
discovery_root,
989+
)
990+
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
967991
})
968992
}
969993
PythonRequest::Implementation(implementation) => Box::new({
@@ -974,6 +998,7 @@ pub fn find_python_installations<'a>(
974998
environments,
975999
preference,
9761000
cache,
1001+
discovery_root,
9771002
)
9781003
.filter_ok(|(_source, interpreter)| {
9791004
interpreter
@@ -994,6 +1019,7 @@ pub fn find_python_installations<'a>(
9941019
environments,
9951020
preference,
9961021
cache,
1022+
discovery_root,
9971023
)
9981024
.filter_ok(|(_source, interpreter)| {
9991025
interpreter
@@ -1017,6 +1043,7 @@ pub fn find_python_installations<'a>(
10171043
environments,
10181044
preference,
10191045
cache,
1046+
discovery_root,
10201047
)
10211048
.filter_ok(|(_source, interpreter)| request.satisfied_by_interpreter(interpreter))
10221049
.map_ok(|tuple| Ok(PythonInstallation::from_tuple(tuple)))
@@ -1034,8 +1061,10 @@ pub(crate) fn find_python_installation(
10341061
environments: EnvironmentPreference,
10351062
preference: PythonPreference,
10361063
cache: &Cache,
1064+
discovery_root: &Path,
10371065
) -> Result<FindPythonResult, Error> {
1038-
let installations = find_python_installations(request, environments, preference, cache);
1066+
let installations =
1067+
find_python_installations(request, environments, preference, cache, discovery_root);
10391068
let mut first_prerelease = None;
10401069
let mut first_error = None;
10411070
for result in installations {
@@ -1136,7 +1165,13 @@ pub fn find_best_python_installation(
11361165

11371166
// First, check for an exact match (or the first available version if no Python version was provided)
11381167
debug!("Looking for exact match for request {request}");
1139-
let result = find_python_installation(request, environments, preference, cache);
1168+
let result = find_python_installation(
1169+
request,
1170+
environments,
1171+
preference,
1172+
cache,
1173+
crate::current_dir()?.as_path(),
1174+
);
11401175
match result {
11411176
Ok(Ok(installation)) => {
11421177
warn_on_unsupported_python(installation.interpreter());
@@ -1164,7 +1199,13 @@ pub fn find_best_python_installation(
11641199
_ => None,
11651200
} {
11661201
debug!("Looking for relaxed patch version {request}");
1167-
let result = find_python_installation(&request, environments, preference, cache);
1202+
let result = find_python_installation(
1203+
&request,
1204+
environments,
1205+
preference,
1206+
cache,
1207+
crate::current_dir()?.as_path(),
1208+
);
11681209
match result {
11691210
Ok(Ok(installation)) => {
11701211
warn_on_unsupported_python(installation.interpreter());
@@ -1180,16 +1221,21 @@ pub fn find_best_python_installation(
11801221
// If a Python version was requested but cannot be fulfilled, just take any version
11811222
debug!("Looking for a default Python installation");
11821223
let request = PythonRequest::Default;
1183-
Ok(
1184-
find_python_installation(&request, environments, preference, cache)?.map_err(|err| {
1185-
// Use a more general error in this case since we looked for multiple versions
1186-
PythonNotFound {
1187-
request,
1188-
python_preference: err.python_preference,
1189-
environment_preference: err.environment_preference,
1190-
}
1191-
}),
1192-
)
1224+
Ok(find_python_installation(
1225+
&request,
1226+
environments,
1227+
preference,
1228+
cache,
1229+
crate::current_dir()?.as_path(),
1230+
)?
1231+
.map_err(|err| {
1232+
// Use a more general error in this case since we looked for multiple versions
1233+
PythonNotFound {
1234+
request,
1235+
python_preference: err.python_preference,
1236+
environment_preference: err.environment_preference,
1237+
}
1238+
}))
11931239
}
11941240

11951241
/// Display a warning if the Python version of the [`Interpreter`] is unsupported by uv.

crates/uv-python/src/environment.rs

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ impl PythonEnvironment {
153153
// Ignore managed installations when looking for environments
154154
PythonPreference::OnlySystem,
155155
cache,
156+
crate::current_dir()?.as_path(),
156157
)? {
157158
Ok(installation) => installation,
158159
Err(err) => return Err(EnvironmentNotFound::from(err).into()),

crates/uv-python/src/installation.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::fmt;
2+
use std::path::Path;
23
use std::str::FromStr;
34

45
use tracing::{debug, info};
@@ -54,8 +55,10 @@ impl PythonInstallation {
5455
environments: EnvironmentPreference,
5556
preference: PythonPreference,
5657
cache: &Cache,
58+
discovery_root: &Path,
5759
) -> Result<Self, Error> {
58-
let installation = find_python_installation(request, environments, preference, cache)??;
60+
let installation =
61+
find_python_installation(request, environments, preference, cache, discovery_root)??;
5962
Ok(installation)
6063
}
6164

@@ -90,9 +93,16 @@ impl PythonInstallation {
9093
pypy_install_mirror: Option<&str>,
9194
) -> Result<Self, Error> {
9295
let request = request.unwrap_or(&PythonRequest::Default);
96+
let root_directory = crate::current_dir()?;
9397

9498
// Search for the installation
95-
let err = match Self::find(request, environments, preference, cache) {
99+
let err = match Self::find(
100+
request,
101+
environments,
102+
preference,
103+
cache,
104+
root_directory.as_path(),
105+
) {
96106
Ok(installation) => return Ok(installation),
97107
Err(err) => err,
98108
};

0 commit comments

Comments
 (0)