mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Add --install-dir
arg to uv python install
and uninstall
(#7920)
## Summary This PR adds `--install-dir` argument for the following commands: - `uv python install` - `uv python uninstall` The `UV_PYTHON_INSTALL_DIR` env variable can be used to set it (previously it was also used internally). Any more commands we would want to add this to? ## Test Plan For now just manual test (works on my machine hehe) ``` ❯ ./target/debug/uv python install --install-dir /tmp/pythons 3.8.12 Searching for Python versions matching: Python 3.8.12 Installed Python 3.8.12 in 4.31s + cpython-3.8.12-linux-x86_64-gnu ❯ /tmp/pythons/cpython-3.8.12-linux-x86_64-gnu/bin/python --help usage: /tmp/pythons/cpython-3.8.12-linux-x86_64-gnu/bin/python [option] ... [-c cmd | -m mod | file | -] [arg] ... ``` Open to add some tests after the initial feedback. --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
parent
b751648bfe
commit
d0ccc9a16f
13 changed files with 76 additions and 19 deletions
|
@ -4250,6 +4250,16 @@ pub struct PythonDirArgs {
|
|||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct PythonInstallArgs {
|
||||
/// The directory to store the Python installation in.
|
||||
///
|
||||
/// If provided, `UV_PYTHON_INSTALL_DIR` will need to be set for subsequent operations for
|
||||
/// uv to discover the Python installation.
|
||||
///
|
||||
/// See `uv python dir` to view the current Python installation directory. Defaults to
|
||||
/// `~/.local/share/uv/python`.
|
||||
#[arg(long, short, env = "UV_PYTHON_INSTALL_DIR")]
|
||||
pub install_dir: Option<PathBuf>,
|
||||
|
||||
/// The Python version(s) to install.
|
||||
///
|
||||
/// If not provided, the requested Python version(s) will be read from the
|
||||
|
@ -4310,6 +4320,10 @@ pub struct PythonInstallArgs {
|
|||
#[derive(Args)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct PythonUninstallArgs {
|
||||
/// The directory where the Python was installed.
|
||||
#[arg(long, short, env = "UV_PYTHON_INSTALL_DIR")]
|
||||
pub install_dir: Option<PathBuf>,
|
||||
|
||||
/// The Python version(s) to uninstall.
|
||||
///
|
||||
/// See `uv help python` to view supported request formats.
|
||||
|
|
|
@ -302,7 +302,7 @@ fn python_executables_from_installed<'a>(
|
|||
preference: PythonPreference,
|
||||
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
|
||||
let from_managed_installations = std::iter::once_with(move || {
|
||||
ManagedPythonInstallations::from_settings()
|
||||
ManagedPythonInstallations::from_settings(None)
|
||||
.map_err(Error::from)
|
||||
.and_then(|installed_installations| {
|
||||
debug!(
|
||||
|
|
|
@ -135,7 +135,7 @@ impl PythonInstallation {
|
|||
python_install_mirror: Option<&str>,
|
||||
pypy_install_mirror: Option<&str>,
|
||||
) -> Result<Self, Error> {
|
||||
let installations = ManagedPythonInstallations::from_settings()?.init()?;
|
||||
let installations = ManagedPythonInstallations::from_settings(None)?.init()?;
|
||||
let installations_dir = installations.root();
|
||||
let scratch_dir = installations.scratch();
|
||||
let _lock = installations.lock().await?;
|
||||
|
|
|
@ -107,11 +107,15 @@ impl ManagedPythonInstallations {
|
|||
}
|
||||
|
||||
/// Prefer, in order:
|
||||
/// 1. The specific Python directory specified by the user, i.e., `UV_PYTHON_INSTALL_DIR`
|
||||
/// 2. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/python`
|
||||
/// 3. A directory in the local data directory, e.g., `./.uv/python`
|
||||
pub fn from_settings() -> Result<Self, Error> {
|
||||
if let Some(install_dir) = std::env::var_os(EnvVars::UV_PYTHON_INSTALL_DIR) {
|
||||
///
|
||||
/// 1. The specific Python directory passed via the `install_dir` argument.
|
||||
/// 2. The specific Python directory specified with the `UV_PYTHON_INSTALL_DIR` environment variable.
|
||||
/// 3. A directory in the system-appropriate user-level data directory, e.g., `~/.local/uv/python`.
|
||||
/// 4. A directory in the local data directory, e.g., `./.uv/python`.
|
||||
pub fn from_settings(install_dir: Option<PathBuf>) -> Result<Self, Error> {
|
||||
if let Some(install_dir) = install_dir {
|
||||
Ok(Self::from_path(install_dir))
|
||||
} else if let Some(install_dir) = std::env::var_os(EnvVars::UV_PYTHON_INSTALL_DIR) {
|
||||
Ok(Self::from_path(install_dir))
|
||||
} else {
|
||||
Ok(Self::from_path(
|
||||
|
@ -227,7 +231,7 @@ impl ManagedPythonInstallations {
|
|||
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation>, Error> {
|
||||
let platform_key = platform_key_from_env()?;
|
||||
|
||||
let iter = ManagedPythonInstallations::from_settings()?
|
||||
let iter = ManagedPythonInstallations::from_settings(None)?
|
||||
.find_all()?
|
||||
.filter(move |installation| {
|
||||
installation
|
||||
|
|
|
@ -11,7 +11,7 @@ pub(crate) fn dir(bin: bool) -> anyhow::Result<()> {
|
|||
let bin = python_executable_dir()?;
|
||||
println!("{}", bin.simplified_display().cyan());
|
||||
} else {
|
||||
let installed_toolchains = ManagedPythonInstallations::from_settings()
|
||||
let installed_toolchains = ManagedPythonInstallations::from_settings(None)
|
||||
.context("Failed to initialize toolchain settings")?;
|
||||
println!(
|
||||
"{}",
|
||||
|
|
|
@ -121,6 +121,7 @@ impl Changelog {
|
|||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub(crate) async fn install(
|
||||
project_dir: &Path,
|
||||
install_dir: Option<PathBuf>,
|
||||
targets: Vec<String>,
|
||||
reinstall: bool,
|
||||
force: bool,
|
||||
|
@ -178,7 +179,7 @@ pub(crate) async fn install(
|
|||
};
|
||||
|
||||
// Read the existing installations, lock the directory for the duration
|
||||
let installations = ManagedPythonInstallations::from_settings()?.init()?;
|
||||
let installations = ManagedPythonInstallations::from_settings(install_dir)?.init()?;
|
||||
let installations_dir = installations.root();
|
||||
let scratch_dir = installations.scratch();
|
||||
let _lock = installations.lock().await?;
|
||||
|
|
|
@ -22,12 +22,14 @@ use crate::printer::Printer;
|
|||
|
||||
/// Uninstall managed Python versions.
|
||||
pub(crate) async fn uninstall(
|
||||
install_dir: Option<PathBuf>,
|
||||
targets: Vec<String>,
|
||||
all: bool,
|
||||
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
let installations = ManagedPythonInstallations::from_settings()?.init()?;
|
||||
let installations = ManagedPythonInstallations::from_settings(install_dir)?.init()?;
|
||||
|
||||
let _lock = installations.lock().await?;
|
||||
|
||||
// Perform the uninstallation.
|
||||
|
|
|
@ -1104,6 +1104,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
|
||||
commands::python_install(
|
||||
&project_dir,
|
||||
args.install_dir,
|
||||
args.targets,
|
||||
args.reinstall,
|
||||
args.force,
|
||||
|
@ -1127,7 +1128,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
let args = settings::PythonUninstallSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
commands::python_uninstall(args.targets, args.all, printer).await
|
||||
commands::python_uninstall(args.install_dir, args.targets, args.all, printer).await
|
||||
}
|
||||
Commands::Python(PythonNamespace {
|
||||
command: PythonCommand::Find(args),
|
||||
|
|
|
@ -760,6 +760,7 @@ impl PythonDirSettings {
|
|||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PythonInstallSettings {
|
||||
pub(crate) install_dir: Option<PathBuf>,
|
||||
pub(crate) targets: Vec<String>,
|
||||
pub(crate) reinstall: bool,
|
||||
pub(crate) force: bool,
|
||||
|
@ -784,6 +785,7 @@ impl PythonInstallSettings {
|
|||
let pypy_mirror = args.pypy_mirror.or(pypy_mirror);
|
||||
|
||||
let PythonInstallArgs {
|
||||
install_dir,
|
||||
targets,
|
||||
reinstall,
|
||||
force,
|
||||
|
@ -793,6 +795,7 @@ impl PythonInstallSettings {
|
|||
} = args;
|
||||
|
||||
Self {
|
||||
install_dir,
|
||||
targets,
|
||||
reinstall,
|
||||
force,
|
||||
|
@ -807,6 +810,7 @@ impl PythonInstallSettings {
|
|||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PythonUninstallSettings {
|
||||
pub(crate) install_dir: Option<PathBuf>,
|
||||
pub(crate) targets: Vec<String>,
|
||||
pub(crate) all: bool,
|
||||
}
|
||||
|
@ -818,9 +822,17 @@ impl PythonUninstallSettings {
|
|||
args: PythonUninstallArgs,
|
||||
_filesystem: Option<FilesystemOptions>,
|
||||
) -> Self {
|
||||
let PythonUninstallArgs { targets, all } = args;
|
||||
let PythonUninstallArgs {
|
||||
install_dir,
|
||||
targets,
|
||||
all,
|
||||
} = args;
|
||||
|
||||
Self { targets, all }
|
||||
Self {
|
||||
install_dir,
|
||||
targets,
|
||||
all,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1075,7 +1075,7 @@ pub fn venv_to_interpreter(venv: &Path) -> PathBuf {
|
|||
|
||||
/// Get the path to the python interpreter for a specific python version.
|
||||
pub fn get_python(version: &PythonVersion) -> PathBuf {
|
||||
ManagedPythonInstallations::from_settings()
|
||||
ManagedPythonInstallations::from_settings(None)
|
||||
.map(|installed_pythons| {
|
||||
installed_pythons
|
||||
.find_version(version)
|
||||
|
|
|
@ -450,7 +450,7 @@ fn help_subcommand() {
|
|||
fn help_subsubcommand() {
|
||||
let context = TestContext::new_with_versions(&[]);
|
||||
|
||||
uv_snapshot!(context.filters(), context.help().arg("python").arg("install"), @r##"
|
||||
uv_snapshot!(context.filters(), context.help().arg("python").arg("install"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -483,6 +483,17 @@ fn help_subsubcommand() {
|
|||
See `uv help python` to view supported request formats.
|
||||
|
||||
Options:
|
||||
-i, --install-dir <INSTALL_DIR>
|
||||
The directory to store the Python installation in.
|
||||
|
||||
If provided, `UV_PYTHON_INSTALL_DIR` will need to be set for subsequent operations for uv
|
||||
to discover the Python installation.
|
||||
|
||||
See `uv python dir` to view the current Python installation directory. Defaults to
|
||||
`~/.local/share/uv/python`.
|
||||
|
||||
[env: UV_PYTHON_INSTALL_DIR=]
|
||||
|
||||
--mirror <MIRROR>
|
||||
Set the URL to use as the source for downloading Python installations.
|
||||
|
||||
|
@ -673,7 +684,7 @@ fn help_subsubcommand() {
|
|||
|
||||
|
||||
----- stderr -----
|
||||
"##);
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -759,6 +770,8 @@ fn help_flag_subsubcommand() {
|
|||
[TARGETS]... The Python version(s) to install
|
||||
|
||||
Options:
|
||||
-i, --install-dir <INSTALL_DIR> The directory to store the Python installation in [env:
|
||||
UV_PYTHON_INSTALL_DIR=]
|
||||
--mirror <MIRROR> Set the URL to use as the source for downloading Python
|
||||
installations [env: UV_PYTHON_INSTALL_MIRROR=]
|
||||
--pypy-mirror <PYPY_MIRROR> Set the URL to use as the source for downloading PyPy
|
||||
|
|
|
@ -74,7 +74,7 @@ fn python_install() {
|
|||
error: the following required arguments were not provided:
|
||||
<TARGETS>...
|
||||
|
||||
Usage: uv python uninstall <TARGETS>...
|
||||
Usage: uv python uninstall --install-dir <INSTALL_DIR> <TARGETS>...
|
||||
|
||||
For more information, try '--help'.
|
||||
"###);
|
||||
|
@ -209,7 +209,7 @@ fn python_install_preview() {
|
|||
error: the following required arguments were not provided:
|
||||
<TARGETS>...
|
||||
|
||||
Usage: uv python uninstall <TARGETS>...
|
||||
Usage: uv python uninstall --install-dir <INSTALL_DIR> <TARGETS>...
|
||||
|
||||
For more information, try '--help'.
|
||||
"###);
|
||||
|
|
|
@ -4553,6 +4553,13 @@ uv python install [OPTIONS] [TARGETS]...
|
|||
|
||||
</dd><dt><code>--help</code>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
|
||||
|
||||
</dd><dt><code>--install-dir</code>, <code>-i</code> <i>install-dir</i></dt><dd><p>The directory to store the Python installation in.</p>
|
||||
|
||||
<p>If provided, <code>UV_PYTHON_INSTALL_DIR</code> will need to be set for subsequent operations for uv to discover the Python installation.</p>
|
||||
|
||||
<p>See <code>uv python dir</code> to view the current Python installation directory. Defaults to <code>~/.local/share/uv/python</code>.</p>
|
||||
|
||||
<p>May also be set with the <code>UV_PYTHON_INSTALL_DIR</code> environment variable.</p>
|
||||
</dd><dt><code>--mirror</code> <i>mirror</i></dt><dd><p>Set the URL to use as the source for downloading Python installations.</p>
|
||||
|
||||
<p>The provided URL will replace <code>https://github.com/indygreg/python-build-standalone/releases/download</code> in, e.g., <code>https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz</code>.</p>
|
||||
|
@ -5114,6 +5121,9 @@ uv python uninstall [OPTIONS] <TARGETS>...
|
|||
|
||||
</dd><dt><code>--help</code>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
|
||||
|
||||
</dd><dt><code>--install-dir</code>, <code>-i</code> <i>install-dir</i></dt><dd><p>The directory where the Python was installed</p>
|
||||
|
||||
<p>May also be set with the <code>UV_PYTHON_INSTALL_DIR</code> environment variable.</p>
|
||||
</dd><dt><code>--native-tls</code></dt><dd><p>Whether to load TLS certificates from the platform’s native certificate store.</p>
|
||||
|
||||
<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue