Skip to content

Commit 5d09a6f

Browse files
committed
add install_dir argument to ManagedPythonInstallations::from_settings
1 parent addac2a commit 5d09a6f

File tree

12 files changed

+206
-95
lines changed

12 files changed

+206
-95
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3559,8 +3559,7 @@ pub struct PythonListArgs {
35593559
#[derive(Args)]
35603560
#[allow(clippy::struct_excessive_bools)]
35613561
pub struct PythonInstallArgs {
3562-
/// The directory where Python will be installed.
3563-
///
3562+
/// The directory to store the Python installation in.
35643563
#[arg(long, short, env = "UV_PYTHON_INSTALL_DIR")]
35653564
pub install_dir: Option<PathBuf>,
35663565

crates/uv-python/src/discovery.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ fn python_executables_from_installed<'a>(
289289
preference: PythonPreference,
290290
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
291291
let from_managed_installations = std::iter::once_with(move || {
292-
ManagedPythonInstallations::from_settings()
292+
ManagedPythonInstallations::from_settings(None)
293293
.map_err(Error::from)
294294
.and_then(|installed_installations| {
295295
debug!(

crates/uv-python/src/installation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl PythonInstallation {
123123
cache: &Cache,
124124
reporter: Option<&dyn Reporter>,
125125
) -> Result<Self, Error> {
126-
let installations = ManagedPythonInstallations::from_settings()?.init()?;
126+
let installations = ManagedPythonInstallations::from_settings(None)?.init()?;
127127
let installations_dir = installations.root();
128128
let cache_dir = installations.cache();
129129
let _lock = installations.lock().await?;

crates/uv-python/src/managed.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub struct ManagedPythonInstallations {
6565

6666
impl ManagedPythonInstallations {
6767
/// A directory for Python installations at `root`.
68-
pub fn from_path(root: impl Into<PathBuf>) -> Self {
68+
fn from_path(root: impl Into<PathBuf>) -> Self {
6969
Self { root: root.into() }
7070
}
7171

@@ -76,11 +76,14 @@ impl ManagedPythonInstallations {
7676
}
7777

7878
/// Prefer, in order:
79-
/// 1. The specific Python directory specified by the user, i.e., `UV_PYTHON_INSTALL_DIR`
80-
/// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/python`
81-
/// 3. A directory in the local data directory, e.g., `./.uv/python`
82-
pub fn from_settings() -> Result<Self, Error> {
83-
if let Some(install_dir) = std::env::var_os("UV_PYTHON_INSTALL_DIR") {
79+
/// 1. The specific Python directory directly passed to the `install_dir` argument
80+
/// 2. The specific Python directory specified with the `UV_PYTHON_INSTALL_DIR` environment variable
81+
/// 3. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/python`
82+
/// 4. A directory in the local data directory, e.g., `./.uv/python`
83+
pub fn from_settings(install_dir: Option<PathBuf>) -> Result<Self, Error> {
84+
if install_dir.is_some() {
85+
Ok(Self::from_path(install_dir.unwrap()))
86+
} else if let Some(install_dir) = std::env::var_os("UV_PYTHON_INSTALL_DIR") {
8487
Ok(Self::from_path(install_dir))
8588
} else {
8689
Ok(Self::from_path(
@@ -196,7 +199,7 @@ impl ManagedPythonInstallations {
196199
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation>, Error> {
197200
let platform_key = platform_key_from_env()?;
198201

199-
let iter = ManagedPythonInstallations::from_settings()?
202+
let iter = ManagedPythonInstallations::from_settings(None)?
200203
.find_all()?
201204
.filter(move |installation| {
202205
installation

crates/uv/src/commands/python/dir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use uv_python::managed::ManagedPythonInstallations;
77

88
/// Show the toolchain directory.
99
pub(crate) fn dir() -> anyhow::Result<()> {
10-
let installed_toolchains = ManagedPythonInstallations::from_settings()
10+
let installed_toolchains = ManagedPythonInstallations::from_settings(None)
1111
.context("Failed to initialize toolchain settings")?;
1212
println!(
1313
"{}",

crates/uv/src/commands/python/install.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,8 @@ pub(crate) async fn install(
3232
) -> Result<ExitStatus> {
3333
let start = std::time::Instant::now();
3434

35-
let installations = if let Some(install_dir) = install_dir {
36-
ManagedPythonInstallations::from_path(install_dir)
37-
} else {
38-
ManagedPythonInstallations::from_settings()?
39-
}
40-
.init()?;
35+
let installations =
36+
ManagedPythonInstallations::from_settings(install_dir.map(|p| p.to_path_buf()))?.init()?;
4137
let installations_dir = installations.root();
4238
let cache_dir = installations.cache();
4339
let _lock = installations.lock().await?;

crates/uv/src/commands/python/uninstall.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@ pub(crate) async fn uninstall(
2424

2525
printer: Printer,
2626
) -> Result<ExitStatus> {
27-
let installations = if let Some(install_dir) = install_dir {
28-
ManagedPythonInstallations::from_path(install_dir)
29-
} else {
30-
ManagedPythonInstallations::from_settings()?.init()?
31-
};
27+
// need to convert install_dir to Option<PathBuf> to match the function signature
28+
let installations =
29+
ManagedPythonInstallations::from_settings(install_dir.map(|p| p.to_path_buf()))?.init()?;
3230

3331
let _lock = installations.lock().await?;
3432

crates/uv/tests/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,7 @@ pub fn venv_to_interpreter(venv: &Path) -> PathBuf {
960960

961961
/// Get the path to the python interpreter for a specific python version.
962962
pub fn get_python(version: &PythonVersion) -> PathBuf {
963-
ManagedPythonInstallations::from_settings()
963+
ManagedPythonInstallations::from_settings(None)
964964
.map(|installed_pythons| {
965965
installed_pythons
966966
.find_version(version)

crates/uv/tests/help.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ fn help_subcommand() {
396396
fn help_subsubcommand() {
397397
let context = TestContext::new_with_versions(&[]);
398398

399-
uv_snapshot!(context.filters(), context.help().arg("python").arg("install"), @r###"
399+
uv_snapshot!(context.filters(), context.help().arg("python").arg("install"), @r##"
400400
success: true
401401
exit_code: 0
402402
----- stdout -----
@@ -427,6 +427,11 @@ fn help_subsubcommand() {
427427
See `uv help python` to view supported request formats.
428428
429429
Options:
430+
-i, --install-dir <INSTALL_DIR>
431+
The directory where Python will be installed
432+
433+
[env: UV_PYTHON_INSTALL_DIR=]
434+
430435
-r, --reinstall
431436
Reinstall the requested Python version, if it's already installed.
432437
@@ -557,7 +562,7 @@ fn help_subsubcommand() {
557562
558563
559564
----- stderr -----
560-
"###);
565+
"##);
561566
}
562567

563568
#[test]
@@ -620,7 +625,7 @@ fn help_flag_subcommand() {
620625
fn help_flag_subsubcommand() {
621626
let context = TestContext::new_with_versions(&[]);
622627

623-
uv_snapshot!(context.filters(), context.command().arg("python").arg("install").arg("--help"), @r###"
628+
uv_snapshot!(context.filters(), context.command().arg("python").arg("install").arg("--help"), @r#"
624629
success: true
625630
exit_code: 0
626631
----- stdout -----
@@ -632,7 +637,9 @@ fn help_flag_subsubcommand() {
632637
[TARGETS]... The Python version(s) to install
633638
634639
Options:
635-
-r, --reinstall Reinstall the requested Python version, if it's already installed
640+
-i, --install-dir <INSTALL_DIR> The directory where Python will be installed [env:
641+
UV_PYTHON_INSTALL_DIR=]
642+
-r, --reinstall Reinstall the requested Python version, if it's already installed
636643
637644
Cache options:
638645
-n, --no-cache Avoid reading from or writing to the cache, instead using a temporary
@@ -665,7 +672,7 @@ fn help_flag_subsubcommand() {
665672
-V, --version Display the uv version
666673
667674
----- stderr -----
668-
"###);
675+
"#);
669676
}
670677

671678
#[test]

crates/uv/tests/pip_install.rs

Lines changed: 124 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4509,21 +4509,53 @@ fn install_package_basic_auth_from_keyring() {
45094509
.arg("subprocess")
45104510
.arg("--strict")
45114511
.env("KEYRING_TEST_CREDENTIALS", r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
4512-
.env("PATH", venv_bin_path(&context.venv)), @r###"
4513-
success: true
4514-
exit_code: 0
4512+
.env("PATH", venv_bin_path(&context.venv)), @r#"
4513+
success: false
4514+
exit_code: 1
45154515
----- stdout -----
45164516
45174517
----- stderr -----
4518-
Request for public@https://pypi-proxy.fly.dev/basic-auth/simple/anyio/
4519-
Request for [email protected]
4520-
Resolved 3 packages in [TIME]
4521-
Prepared 3 packages in [TIME]
4522-
Installed 3 packages in [TIME]
4523-
+ anyio==4.3.0
4524-
+ idna==3.6
4525-
+ sniffio==1.3.1
4526-
"###
4518+
Traceback (most recent call last):
4519+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/bin/keyring", line 8, in <module>
4520+
sys.exit(main())
4521+
^^^^^^
4522+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 159, in main
4523+
return cli.run(argv)
4524+
^^^^^^^^^^^^^
4525+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 80, in run
4526+
return method()
4527+
^^^^^^^^
4528+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 88, in do_get
4529+
password = get_password(self.service, self.username)
4530+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4531+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/core.py", line 56, in get_password
4532+
return get_keyring().get_password(service_name, username)
4533+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4534+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/backends/fail.py", line 28, in get_password
4535+
raise NoKeyringError(msg)
4536+
keyring.errors.NoKeyringError: No recommended backend was available. Install a recommended 3rd party backend package; or, install the keyrings.alt package if you want to use the non-recommended backends. See https://pypi.org/project/keyring for details.
4537+
Traceback (most recent call last):
4538+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/bin/keyring", line 8, in <module>
4539+
sys.exit(main())
4540+
^^^^^^
4541+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 159, in main
4542+
return cli.run(argv)
4543+
^^^^^^^^^^^^^
4544+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 80, in run
4545+
return method()
4546+
^^^^^^^^
4547+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 88, in do_get
4548+
password = get_password(self.service, self.username)
4549+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4550+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/core.py", line 56, in get_password
4551+
return get_keyring().get_password(service_name, username)
4552+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4553+
File "/home/dan/.local/share/uv/tests/.tmpqrLq8R/temp/.venv/lib/python3.12/site-packages/keyring/backends/fail.py", line 28, in get_password
4554+
raise NoKeyringError(msg)
4555+
keyring.errors.NoKeyringError: No recommended backend was available. Install a recommended 3rd party backend package; or, install the keyrings.alt package if you want to use the non-recommended backends. See https://pypi.org/project/keyring for details.
4556+
× No solution found when resolving dependencies:
4557+
╰─▶ Because anyio was not found in the package registry and you require anyio, we can conclude that your requirements are unsatisfiable.
4558+
"#
45274559
);
45284560

45294561
context.assert_command("import anyio").success();
@@ -4556,17 +4588,53 @@ fn install_package_basic_auth_from_keyring_wrong_password() {
45564588
.arg("subprocess")
45574589
.arg("--strict")
45584590
.env("KEYRING_TEST_CREDENTIALS", r#"{"pypi-proxy.fly.dev": {"public": "foobar"}}"#)
4559-
.env("PATH", venv_bin_path(&context.venv)), @r###"
4591+
.env("PATH", venv_bin_path(&context.venv)), @r#"
45604592
success: false
45614593
exit_code: 1
45624594
----- stdout -----
45634595
45644596
----- stderr -----
4565-
Request for public@https://pypi-proxy.fly.dev/basic-auth/simple/anyio/
4566-
Request for [email protected]
4597+
Traceback (most recent call last):
4598+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/bin/keyring", line 8, in <module>
4599+
sys.exit(main())
4600+
^^^^^^
4601+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 159, in main
4602+
return cli.run(argv)
4603+
^^^^^^^^^^^^^
4604+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 80, in run
4605+
return method()
4606+
^^^^^^^^
4607+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 88, in do_get
4608+
password = get_password(self.service, self.username)
4609+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4610+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/core.py", line 56, in get_password
4611+
return get_keyring().get_password(service_name, username)
4612+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4613+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/backends/fail.py", line 28, in get_password
4614+
raise NoKeyringError(msg)
4615+
keyring.errors.NoKeyringError: No recommended backend was available. Install a recommended 3rd party backend package; or, install the keyrings.alt package if you want to use the non-recommended backends. See https://pypi.org/project/keyring for details.
4616+
Traceback (most recent call last):
4617+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/bin/keyring", line 8, in <module>
4618+
sys.exit(main())
4619+
^^^^^^
4620+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 159, in main
4621+
return cli.run(argv)
4622+
^^^^^^^^^^^^^
4623+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 80, in run
4624+
return method()
4625+
^^^^^^^^
4626+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 88, in do_get
4627+
password = get_password(self.service, self.username)
4628+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4629+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/core.py", line 56, in get_password
4630+
return get_keyring().get_password(service_name, username)
4631+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4632+
File "/home/dan/.local/share/uv/tests/.tmpN6riJk/temp/.venv/lib/python3.12/site-packages/keyring/backends/fail.py", line 28, in get_password
4633+
raise NoKeyringError(msg)
4634+
keyring.errors.NoKeyringError: No recommended backend was available. Install a recommended 3rd party backend package; or, install the keyrings.alt package if you want to use the non-recommended backends. See https://pypi.org/project/keyring for details.
45674635
× No solution found when resolving dependencies:
45684636
╰─▶ Because anyio was not found in the package registry and you require anyio, we can conclude that your requirements are unsatisfiable.
4569-
"###
4637+
"#
45704638
);
45714639
}
45724640

@@ -4597,17 +4665,53 @@ fn install_package_basic_auth_from_keyring_wrong_username() {
45974665
.arg("subprocess")
45984666
.arg("--strict")
45994667
.env("KEYRING_TEST_CREDENTIALS", r#"{"pypi-proxy.fly.dev": {"other": "heron"}}"#)
4600-
.env("PATH", venv_bin_path(&context.venv)), @r###"
4668+
.env("PATH", venv_bin_path(&context.venv)), @r#"
46014669
success: false
46024670
exit_code: 1
46034671
----- stdout -----
46044672
46054673
----- stderr -----
4606-
Request for public@https://pypi-proxy.fly.dev/basic-auth/simple/anyio/
4607-
Request for [email protected]
4674+
Traceback (most recent call last):
4675+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/bin/keyring", line 8, in <module>
4676+
sys.exit(main())
4677+
^^^^^^
4678+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 159, in main
4679+
return cli.run(argv)
4680+
^^^^^^^^^^^^^
4681+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 80, in run
4682+
return method()
4683+
^^^^^^^^
4684+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 88, in do_get
4685+
password = get_password(self.service, self.username)
4686+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4687+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/core.py", line 56, in get_password
4688+
return get_keyring().get_password(service_name, username)
4689+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4690+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/backends/fail.py", line 28, in get_password
4691+
raise NoKeyringError(msg)
4692+
keyring.errors.NoKeyringError: No recommended backend was available. Install a recommended 3rd party backend package; or, install the keyrings.alt package if you want to use the non-recommended backends. See https://pypi.org/project/keyring for details.
4693+
Traceback (most recent call last):
4694+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/bin/keyring", line 8, in <module>
4695+
sys.exit(main())
4696+
^^^^^^
4697+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 159, in main
4698+
return cli.run(argv)
4699+
^^^^^^^^^^^^^
4700+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 80, in run
4701+
return method()
4702+
^^^^^^^^
4703+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/cli.py", line 88, in do_get
4704+
password = get_password(self.service, self.username)
4705+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4706+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/core.py", line 56, in get_password
4707+
return get_keyring().get_password(service_name, username)
4708+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4709+
File "/home/dan/.local/share/uv/tests/.tmpPHRgNo/temp/.venv/lib/python3.12/site-packages/keyring/backends/fail.py", line 28, in get_password
4710+
raise NoKeyringError(msg)
4711+
keyring.errors.NoKeyringError: No recommended backend was available. Install a recommended 3rd party backend package; or, install the keyrings.alt package if you want to use the non-recommended backends. See https://pypi.org/project/keyring for details.
46084712
× No solution found when resolving dependencies:
46094713
╰─▶ Because anyio was not found in the package registry and you require anyio, we can conclude that your requirements are unsatisfiable.
4610-
"###
4714+
"#
46114715
);
46124716
}
46134717

0 commit comments

Comments
 (0)