mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 21:23:54 +00:00
Update preview installation of Python executables to be non-fatal (#14612)
Some checks are pending
CI / check system | alpine (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
Some checks are pending
CI / check system | alpine (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
Previously, if installation of executables into the bin directory failed we'd with a non-zero code. However, if we make this behavior the default we don't want it to be fatal. There's a `--bin` opt-in to _require_ successful executable installation and a `--no-bin` opt-out to silence the warning / opt-out of installation entirely. Part of https://github.com/astral-sh/uv/issues/14296 — we need this before we can stabilize the behavior. In #14614 we do the same for writing entries to the Windows registry.
This commit is contained in:
parent
cd0d5d4748
commit
bb1e9a247c
8 changed files with 212 additions and 40 deletions
|
|
@ -4941,6 +4941,19 @@ pub struct PythonInstallArgs {
|
|||
#[arg(long, short, env = EnvVars::UV_PYTHON_INSTALL_DIR)]
|
||||
pub install_dir: Option<PathBuf>,
|
||||
|
||||
/// Install a Python executable into the `bin` directory.
|
||||
///
|
||||
/// This is the default behavior. If this flag is provided explicitly, uv will error if the
|
||||
/// executable cannot be installed.
|
||||
///
|
||||
/// See `UV_PYTHON_BIN_DIR` to customize the target directory.
|
||||
#[arg(long, overrides_with("no_bin"), hide = true)]
|
||||
pub bin: bool,
|
||||
|
||||
/// Do not install a Python executable into the `bin` directory.
|
||||
#[arg(long, overrides_with("bin"), conflicts_with("default"))]
|
||||
pub no_bin: bool,
|
||||
|
||||
/// The Python version(s) to install.
|
||||
///
|
||||
/// If not provided, the requested Python version(s) will be read from the `UV_PYTHON`
|
||||
|
|
@ -5003,7 +5016,7 @@ pub struct PythonInstallArgs {
|
|||
/// and `python`.
|
||||
///
|
||||
/// If multiple Python versions are requested, uv will exit with an error.
|
||||
#[arg(long)]
|
||||
#[arg(long, conflicts_with("no_bin"))]
|
||||
pub default: bool,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,12 +129,13 @@ fn read_registry_entry(company: &str, tag: &str, tag_key: &Key) -> Option<Window
|
|||
pub enum ManagedPep514Error {
|
||||
#[error("Windows has an unknown pointer width for arch: `{_0}`")]
|
||||
InvalidPointerSize(Arch),
|
||||
#[error("Failed to write registry entry: {0}")]
|
||||
WriteError(#[from] windows_result::Error),
|
||||
}
|
||||
|
||||
/// Register a managed Python installation in the Windows registry following PEP 514.
|
||||
pub fn create_registry_entry(
|
||||
installation: &ManagedPythonInstallation,
|
||||
errors: &mut Vec<(PythonInstallationKey, anyhow::Error)>,
|
||||
) -> Result<(), ManagedPep514Error> {
|
||||
let pointer_width = match installation.key().arch().family().pointer_width() {
|
||||
Ok(PointerWidth::U32) => 32,
|
||||
|
|
@ -146,9 +147,7 @@ pub fn create_registry_entry(
|
|||
}
|
||||
};
|
||||
|
||||
if let Err(err) = write_registry_entry(installation, pointer_width) {
|
||||
errors.push((installation.key().clone(), err.into()));
|
||||
}
|
||||
write_registry_entry(installation, pointer_width)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,14 @@ impl Changelog {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum InstallErrorKind {
|
||||
DownloadUnpack,
|
||||
Bin,
|
||||
#[cfg(windows)]
|
||||
Registry,
|
||||
}
|
||||
|
||||
/// Download and install Python versions.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub(crate) async fn install(
|
||||
|
|
@ -143,6 +151,7 @@ pub(crate) async fn install(
|
|||
targets: Vec<String>,
|
||||
reinstall: bool,
|
||||
upgrade: bool,
|
||||
bin: Option<bool>,
|
||||
force: bool,
|
||||
python_install_mirror: Option<String>,
|
||||
pypy_install_mirror: Option<String>,
|
||||
|
|
@ -432,12 +441,16 @@ pub(crate) async fn install(
|
|||
downloaded.push(installation.clone());
|
||||
}
|
||||
Err(err) => {
|
||||
errors.push((download.key().clone(), anyhow::Error::new(err)));
|
||||
errors.push((
|
||||
InstallErrorKind::DownloadUnpack,
|
||||
download.key().clone(),
|
||||
anyhow::Error::new(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let bin = if preview.is_enabled() {
|
||||
let bin_dir = if matches!(bin, Some(true)) || preview.is_enabled() {
|
||||
Some(python_executable_dir()?)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -460,7 +473,7 @@ pub(crate) async fn install(
|
|||
continue;
|
||||
}
|
||||
|
||||
let bin = bin
|
||||
let bin_dir = bin_dir
|
||||
.as_ref()
|
||||
.expect("We should have a bin directory with preview enabled")
|
||||
.as_path();
|
||||
|
|
@ -468,9 +481,10 @@ pub(crate) async fn install(
|
|||
let upgradeable = (default || is_default_install)
|
||||
|| requested_minor_versions.contains(&installation.key().version().python_version());
|
||||
|
||||
if !matches!(bin, Some(false)) {
|
||||
create_bin_links(
|
||||
installation,
|
||||
bin,
|
||||
bin_dir,
|
||||
reinstall,
|
||||
force,
|
||||
default,
|
||||
|
|
@ -483,12 +497,22 @@ pub(crate) async fn install(
|
|||
&mut changelog,
|
||||
&mut errors,
|
||||
preview,
|
||||
)?;
|
||||
);
|
||||
}
|
||||
|
||||
if preview.is_enabled() {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
uv_python::windows_registry::create_registry_entry(installation, &mut errors)?;
|
||||
match uv_python::windows_registry::create_registry_entry(installation) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
errors.push((
|
||||
InstallErrorKind::Registry,
|
||||
installation.key().clone(),
|
||||
err.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -636,24 +660,47 @@ pub(crate) async fn install(
|
|||
}
|
||||
}
|
||||
|
||||
if preview.is_enabled() {
|
||||
let bin = bin
|
||||
if preview.is_enabled() && !matches!(bin, Some(false)) {
|
||||
let bin_dir = bin_dir
|
||||
.as_ref()
|
||||
.expect("We should have a bin directory with preview enabled")
|
||||
.as_path();
|
||||
warn_if_not_on_path(bin);
|
||||
warn_if_not_on_path(bin_dir);
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
for (key, err) in errors
|
||||
// If there are only bin install errors and the user didn't opt-in, we're only going to warn
|
||||
let fatal = errors
|
||||
.iter()
|
||||
.all(|(kind, _, _)| matches!(kind, InstallErrorKind::Bin))
|
||||
&& bin.is_none();
|
||||
|
||||
for (kind, key, err) in errors
|
||||
.into_iter()
|
||||
.sorted_unstable_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b))
|
||||
.sorted_unstable_by(|(_, key_a, _), (_, key_b, _)| key_a.cmp(key_b))
|
||||
{
|
||||
let (level, verb) = match kind {
|
||||
InstallErrorKind::DownloadUnpack => ("error".red().bold().to_string(), "install"),
|
||||
InstallErrorKind::Bin => {
|
||||
let level = match bin {
|
||||
None => "warning".yellow().bold().to_string(),
|
||||
Some(false) => continue,
|
||||
Some(true) => "error".red().bold().to_string(),
|
||||
};
|
||||
(level, "install executable for")
|
||||
}
|
||||
#[cfg(windows)]
|
||||
InstallErrorKind::Registry => (
|
||||
"error".red().bold().to_string(),
|
||||
"install registry entry for",
|
||||
),
|
||||
};
|
||||
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}: Failed to install {}",
|
||||
"error".red().bold(),
|
||||
"{level}{} Failed to {verb} {}",
|
||||
":".bold(),
|
||||
key.green()
|
||||
)?;
|
||||
for err in err.chain() {
|
||||
|
|
@ -665,6 +712,11 @@ pub(crate) async fn install(
|
|||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if fatal {
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
||||
|
|
@ -672,6 +724,8 @@ pub(crate) async fn install(
|
|||
}
|
||||
|
||||
/// Link the binaries of a managed Python installation to the bin directory.
|
||||
///
|
||||
/// This function is fallible, but errors are pushed to `errors` instead of being thrown.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
fn create_bin_links(
|
||||
installation: &ManagedPythonInstallation,
|
||||
|
|
@ -686,9 +740,9 @@ fn create_bin_links(
|
|||
existing_installations: &[ManagedPythonInstallation],
|
||||
installations: &[&ManagedPythonInstallation],
|
||||
changelog: &mut Changelog,
|
||||
errors: &mut Vec<(PythonInstallationKey, Error)>,
|
||||
errors: &mut Vec<(InstallErrorKind, PythonInstallationKey, Error)>,
|
||||
preview: PreviewMode,
|
||||
) -> Result<(), Error> {
|
||||
) {
|
||||
let targets =
|
||||
if (default || is_default_install) && first_request.matches_installation(installation) {
|
||||
vec![
|
||||
|
|
@ -773,6 +827,7 @@ fn create_bin_links(
|
|||
);
|
||||
} else {
|
||||
errors.push((
|
||||
InstallErrorKind::Bin,
|
||||
installation.key().clone(),
|
||||
anyhow::anyhow!(
|
||||
"Executable already exists at `{}` but is not managed by uv; use `--force` to replace it",
|
||||
|
|
@ -848,7 +903,17 @@ fn create_bin_links(
|
|||
}
|
||||
|
||||
// Replace the existing link
|
||||
fs_err::remove_file(&to)?;
|
||||
if let Err(err) = fs_err::remove_file(&to) {
|
||||
errors.push((
|
||||
InstallErrorKind::Bin,
|
||||
installation.key().clone(),
|
||||
anyhow::anyhow!(
|
||||
"Executable already exists at `{}` but could not be removed: {err}",
|
||||
to.simplified_display()
|
||||
),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(existing) = existing {
|
||||
// Ensure we do not report installation of this executable for an existing
|
||||
|
|
@ -860,7 +925,18 @@ fn create_bin_links(
|
|||
.remove(&target);
|
||||
}
|
||||
|
||||
create_link_to_executable(&target, executable)?;
|
||||
if let Err(err) = create_link_to_executable(&target, executable) {
|
||||
errors.push((
|
||||
InstallErrorKind::Bin,
|
||||
installation.key().clone(),
|
||||
anyhow::anyhow!(
|
||||
"Failed to create link at `{}`: {err}",
|
||||
target.simplified_display()
|
||||
),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Updated executable at `{}` to {}",
|
||||
target.simplified_display(),
|
||||
|
|
@ -874,11 +950,14 @@ fn create_bin_links(
|
|||
.insert(target.clone());
|
||||
}
|
||||
Err(err) => {
|
||||
errors.push((installation.key().clone(), anyhow::Error::new(err)));
|
||||
errors.push((
|
||||
InstallErrorKind::Bin,
|
||||
installation.key().clone(),
|
||||
anyhow::Error::new(err),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn format_executables(
|
||||
|
|
|
|||
|
|
@ -1402,6 +1402,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.targets,
|
||||
args.reinstall,
|
||||
upgrade,
|
||||
args.bin,
|
||||
args.force,
|
||||
args.python_install_mirror,
|
||||
args.pypy_install_mirror,
|
||||
|
|
@ -1430,6 +1431,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.targets,
|
||||
reinstall,
|
||||
upgrade,
|
||||
args.bin,
|
||||
args.force,
|
||||
args.python_install_mirror,
|
||||
args.pypy_install_mirror,
|
||||
|
|
|
|||
|
|
@ -933,6 +933,7 @@ pub(crate) struct PythonInstallSettings {
|
|||
pub(crate) targets: Vec<String>,
|
||||
pub(crate) reinstall: bool,
|
||||
pub(crate) force: bool,
|
||||
pub(crate) bin: Option<bool>,
|
||||
pub(crate) python_install_mirror: Option<String>,
|
||||
pub(crate) pypy_install_mirror: Option<String>,
|
||||
pub(crate) python_downloads_json_url: Option<String>,
|
||||
|
|
@ -961,6 +962,8 @@ impl PythonInstallSettings {
|
|||
install_dir,
|
||||
targets,
|
||||
reinstall,
|
||||
bin,
|
||||
no_bin,
|
||||
force,
|
||||
mirror: _,
|
||||
pypy_mirror: _,
|
||||
|
|
@ -973,6 +976,7 @@ impl PythonInstallSettings {
|
|||
targets,
|
||||
reinstall,
|
||||
force,
|
||||
bin: flag(bin, no_bin, "bin"),
|
||||
python_install_mirror: python_mirror,
|
||||
pypy_install_mirror: pypy_mirror,
|
||||
python_downloads_json_url,
|
||||
|
|
@ -992,6 +996,7 @@ pub(crate) struct PythonUpgradeSettings {
|
|||
pub(crate) pypy_install_mirror: Option<String>,
|
||||
pub(crate) python_downloads_json_url: Option<String>,
|
||||
pub(crate) default: bool,
|
||||
pub(crate) bin: Option<bool>,
|
||||
}
|
||||
|
||||
impl PythonUpgradeSettings {
|
||||
|
|
@ -1013,6 +1018,7 @@ impl PythonUpgradeSettings {
|
|||
args.python_downloads_json_url.or(python_downloads_json_url);
|
||||
let force = false;
|
||||
let default = false;
|
||||
let bin = None;
|
||||
|
||||
let PythonUpgradeArgs {
|
||||
install_dir,
|
||||
|
|
@ -1030,6 +1036,7 @@ impl PythonUpgradeSettings {
|
|||
pypy_install_mirror: pypy_mirror,
|
||||
python_downloads_json_url,
|
||||
default,
|
||||
bin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -504,6 +504,9 @@ fn help_subsubcommand() {
|
|||
|
||||
[env: UV_PYTHON_INSTALL_DIR=]
|
||||
|
||||
--no-bin
|
||||
Do not install a Python executable into the `bin` directory
|
||||
|
||||
--mirror <MIRROR>
|
||||
Set the URL to use as the source for downloading Python installations.
|
||||
|
||||
|
|
@ -790,6 +793,8 @@ fn help_flag_subsubcommand() {
|
|||
Options:
|
||||
-i, --install-dir <INSTALL_DIR>
|
||||
The directory to store the Python installation in [env: UV_PYTHON_INSTALL_DIR=]
|
||||
--no-bin
|
||||
Do not install a Python executable into the `bin` directory
|
||||
--mirror <MIRROR>
|
||||
Set the URL to use as the source for downloading Python installations [env:
|
||||
UV_PYTHON_INSTALL_MIRROR=]
|
||||
|
|
|
|||
|
|
@ -430,15 +430,35 @@ fn python_install_preview() {
|
|||
bin_python.touch().unwrap();
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: Failed to install executable for cpython-3.13.5-[PLATFORM]
|
||||
Caused by: Executable already exists at `[BIN]/python3.13` but is not managed by uv; use `--force` to replace it
|
||||
");
|
||||
|
||||
// With `--bin`, this should error instead of warn
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("--bin").arg("3.13"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to install cpython-3.13.5-[PLATFORM]
|
||||
error: Failed to install executable for cpython-3.13.5-[PLATFORM]
|
||||
Caused by: Executable already exists at `[BIN]/python3.13` but is not managed by uv; use `--force` to replace it
|
||||
");
|
||||
|
||||
// With `--no-bin`, this should be silent
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("--no-bin").arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("--force").arg("3.13"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
|
|
@ -565,6 +585,52 @@ fn python_install_preview() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_install_preview_no_bin() {
|
||||
let context: TestContext = TestContext::new_with_versions(&[])
|
||||
.with_filtered_python_keys()
|
||||
.with_filtered_exe_suffix()
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install the latest version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("--no-bin"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Installed Python 3.13.5 in [TIME]
|
||||
+ cpython-3.13.5-[PLATFORM]
|
||||
");
|
||||
|
||||
let bin_python = context
|
||||
.bin_dir
|
||||
.child(format!("python3.13{}", std::env::consts::EXE_SUFFIX));
|
||||
|
||||
// The executable should not be installed in the bin directory
|
||||
bin_python.assert(predicate::path::missing());
|
||||
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("--no-bin").arg("--default"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: the argument '--no-bin' cannot be used with '--default'
|
||||
|
||||
Usage: uv python install --no-bin --install-dir <INSTALL_DIR> [TARGETS]...
|
||||
|
||||
For more information, try '--help'.
|
||||
");
|
||||
|
||||
let bin_python = context
|
||||
.bin_dir
|
||||
.child(format!("python{}", std::env::consts::EXE_SUFFIX));
|
||||
|
||||
// The executable should not be installed in the bin directory
|
||||
bin_python.assert(predicate::path::missing());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_install_preview_upgrade() {
|
||||
let context = TestContext::new_with_versions(&[])
|
||||
|
|
|
|||
|
|
@ -2795,7 +2795,8 @@ uv python install [OPTIONS] [TARGETS]...
|
|||
<p>May also be set with the <code>UV_PYTHON_INSTALL_MIRROR</code> environment variable.</p></dd><dt id="uv-python-install--native-tls"><a href="#uv-python-install--native-tls"><code>--native-tls</code></a></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>
|
||||
<p>However, in some cases, you may want to use the platform's native certificate store, especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's included in your system's certificate store.</p>
|
||||
<p>May also be set with the <code>UV_NATIVE_TLS</code> environment variable.</p></dd><dt id="uv-python-install--no-cache"><a href="#uv-python-install--no-cache"><code>--no-cache</code></a>, <code>--no-cache-dir</code>, <code>-n</code></dt><dd><p>Avoid reading from or writing to the cache, instead using a temporary directory for the duration of the operation</p>
|
||||
<p>May also be set with the <code>UV_NATIVE_TLS</code> environment variable.</p></dd><dt id="uv-python-install--no-bin"><a href="#uv-python-install--no-bin"><code>--no-bin</code></a></dt><dd><p>Do not install a Python executable into the <code>bin</code> directory</p>
|
||||
</dd><dt id="uv-python-install--no-cache"><a href="#uv-python-install--no-cache"><code>--no-cache</code></a>, <code>--no-cache-dir</code>, <code>-n</code></dt><dd><p>Avoid reading from or writing to the cache, instead using a temporary directory for the duration of the operation</p>
|
||||
<p>May also be set with the <code>UV_NO_CACHE</code> environment variable.</p></dd><dt id="uv-python-install--no-config"><a href="#uv-python-install--no-config"><code>--no-config</code></a></dt><dd><p>Avoid discovering configuration files (<code>pyproject.toml</code>, <code>uv.toml</code>).</p>
|
||||
<p>Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.</p>
|
||||
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p></dd><dt id="uv-python-install--no-managed-python"><a href="#uv-python-install--no-managed-python"><code>--no-managed-python</code></a></dt><dd><p>Disable use of uv-managed Python versions.</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue