Compare commits

..

No commits in common. "main" and "0.9.18" have entirely different histories.
main ... 0.9.18

23 changed files with 81 additions and 661 deletions

View File

@ -1,125 +0,0 @@
# Contributor Covenant Code of Conduct
- [Our Pledge](#our-pledge)
- [Our Standards](#our-standards)
- [Enforcement Responsibilities](#enforcement-responsibilities)
- [Scope](#scope)
- [Enforcement](#enforcement)
- [Enforcement Guidelines](#enforcement-guidelines)
- [1. Correction](#1-correction)
- [2. Warning](#2-warning)
- [3. Temporary Ban](#3-temporary-ban)
- [4. Permanent Ban](#4-permanent-ban)
- [Attribution](#attribution)
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a
harassment-free experience for everyone, regardless of age, body size, visible or invisible
disability, ethnicity, sex characteristics, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance, race, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and
healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the
experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address, without their
explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior
and will take appropriate and fair corrective action in response to any behavior that they deem
inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits,
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and
will communicate reasons for moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is
officially representing the community in public spaces. Examples of representing our community
include using an official e-mail address, posting via an official social media account, or acting as
an appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community
leaders responsible for enforcement at <hey@astral.sh>. All complaints will be reviewed and
investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any
incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for
any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or
unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing clarity around the
nature of the violation and an explanation of why the behavior was inappropriate. A public apology
may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people
involved, including unsolicited interaction with those enforcing the Code of Conduct, for a
specified period of time. This includes avoiding interactions in community spaces as well as
external channels like social media. Violating these terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate
behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the
community for a specified period of time. No public or private interaction with the people involved,
including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this
period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including
sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement
of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available
[here](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
For answers to common questions about this code of conduct, see the
[FAQ](https://www.contributor-covenant.org/faq). Translations are available
[here](https://www.contributor-covenant.org/translations).
[homepage]: https://www.contributor-covenant.org

View File

@ -42,7 +42,7 @@ An extremely fast Python package and project manager, written in Rust.
- 🖥️ Supports macOS, Linux, and Windows.
uv is backed by [Astral](https://astral.sh), the creators of
[Ruff](https://github.com/astral-sh/ruff) and [ty](https://github.com/astral-sh/ty).
[Ruff](https://github.com/astral-sh/ruff).
## Installation
@ -268,6 +268,14 @@ Installed 43 packages in 208ms
See the [pip interface documentation](https://docs.astral.sh/uv/pip/index/) to get started.
## Platform support
See uv's [platform support](https://docs.astral.sh/uv/reference/platforms/) document.
## Versioning policy
See uv's [versioning policy](https://docs.astral.sh/uv/reference/versioning/) document.
## Contributing
We are passionate about supporting contributors of all levels of experience and would love to see
@ -284,15 +292,6 @@ It's pronounced as "you - vee" ([`/juː viː/`](https://en.wikipedia.org/wiki/He
Just "uv", please. See the [style guide](./STYLE.md#styling-uv) for details.
#### What platforms does uv support?
See uv's [platform support](https://docs.astral.sh/uv/reference/platforms/) document.
#### Is uv ready for production?
Yes, uv is stable and widely used in production. See uv's
[versioning policy](https://docs.astral.sh/uv/reference/versioning/) document for details.
## Acknowledgements
uv's dependency resolver uses [PubGrub](https://github.com/pubgrub-rs/pubgrub) under the hood. We're

View File

@ -3309,9 +3309,7 @@ pub struct InitArgs {
///
/// Disables creating extra files like `README.md`, the `src/` tree, `.python-version` files,
/// etc.
///
/// When combined with `--script`, the script will only contain the inline metadata header.
#[arg(long)]
#[arg(long, conflicts_with = "script")]
pub bare: bool,
/// Create a virtual project, rather than a package.
@ -5373,21 +5371,6 @@ pub struct ToolRunArgs {
#[arg(long)]
pub python_platform: Option<TargetTriple>,
/// The backend to use when fetching packages in the PyTorch ecosystem (e.g., `cpu`, `cu126`, or `auto`)
///
/// When set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,
/// and will instead use the defined backend.
///
/// For example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,
/// uv will use the PyTorch index for CUDA 12.6.
///
/// The `auto` mode will attempt to detect the appropriate PyTorch index based on the currently
/// installed CUDA drivers.
///
/// This option is in preview and may change in any future release.
#[arg(long, value_enum, env = EnvVars::UV_TORCH_BACKEND)]
pub torch_backend: Option<TorchMode>,
#[arg(long, hide = true)]
pub generate_shell_completion: Option<clap_complete_command::Shell>,
}
@ -5564,21 +5547,6 @@ pub struct ToolInstallArgs {
/// `--python-platform` option is intended for advanced use cases.
#[arg(long)]
pub python_platform: Option<TargetTriple>,
/// The backend to use when fetching packages in the PyTorch ecosystem (e.g., `cpu`, `cu126`, or `auto`)
///
/// When set, uv will ignore the configured index URLs for packages in the PyTorch ecosystem,
/// and will instead use the defined backend.
///
/// For example, when set to `cpu`, uv will use the CPU-only PyTorch index; when set to `cu126`,
/// uv will use the PyTorch index for CUDA 12.6.
///
/// The `auto` mode will attempt to detect the appropriate PyTorch index based on the currently
/// installed CUDA drivers.
///
/// This option is in preview and may change in any future release.
#[arg(long, value_enum, env = EnvVars::UV_TORCH_BACKEND)]
pub torch_backend: Option<TorchMode>,
}
#[derive(Args)]

View File

@ -366,7 +366,6 @@ pub fn resolver_options(
exclude_newer_package.unwrap_or_default(),
),
link_mode,
torch_backend: None,
no_build: flag(no_build, build, "build"),
no_build_package: Some(no_build_package),
no_binary: flag(no_binary, binary, "binary"),

View File

@ -335,7 +335,7 @@ impl RequirementsSpecification {
}
}
RequirementsSource::PylockToml(path) => {
if !(path.starts_with("http://") || path.starts_with("https://") || path.exists()) {
if !path.is_file() {
return Err(anyhow::anyhow!("File not found: `{}`", path.user_display()));
}

View File

@ -16,7 +16,7 @@ use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_platform_tags::Tags;
use uv_pypi_types::ResolverMarkerEnvironment;
use crate::lock::{HashedDist, LockErrorKind, Package, TagPolicy};
use crate::lock::{LockErrorKind, Package, TagPolicy};
use crate::{Lock, LockError};
pub trait Installable<'lock> {
@ -527,14 +527,18 @@ pub trait Installable<'lock> {
marker_env: &ResolverMarkerEnvironment,
build_options: &BuildOptions,
) -> Result<Node, LockError> {
let tag_policy = TagPolicy::Required(tags);
let HashedDist { dist, hashes } =
package.to_dist(self.install_path(), tag_policy, build_options, marker_env)?;
let dist = package.to_dist(
self.install_path(),
TagPolicy::Required(tags),
build_options,
marker_env,
)?;
let version = package.version().cloned();
let dist = ResolvedDist::Installable {
dist: Arc::new(dist),
version,
};
let hashes = package.hashes();
Ok(Node::Dist {
dist,
hashes,
@ -549,7 +553,7 @@ pub trait Installable<'lock> {
tags: &Tags,
marker_env: &ResolverMarkerEnvironment,
) -> Result<Node, LockError> {
let HashedDist { dist, .. } = package.to_dist(
let dist = package.to_dist(
self.install_path(),
TagPolicy::Preferred(tags),
&BuildOptions::default(),

View File

@ -171,15 +171,6 @@ static ANDROID_X86_MARKERS: LazyLock<UniversalMarker> = LazyLock::new(|| {
marker
});
/// A distribution with its associated hash.
///
/// This pairs a [`Dist`] with the [`HashDigests`] for the specific wheel or
/// sdist that would be installed.
pub(crate) struct HashedDist {
pub(crate) dist: Dist,
pub(crate) hashes: HashDigests,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
#[serde(try_from = "LockWire")]
pub struct Lock {
@ -1770,7 +1761,7 @@ impl Lock {
if let Some(version) = package.id.version.as_ref() {
// For a non-dynamic package, fetch the metadata from the distribution database.
let HashedDist { dist, .. } = package.to_dist(
let dist = package.to_dist(
root,
TagPolicy::Preferred(tags),
&BuildOptions::default(),
@ -1921,7 +1912,7 @@ impl Lock {
// exactly. For example, `hatchling` will flatten any recursive (or self-referential)
// extras, while `setuptools` will not.
if !satisfied {
let HashedDist { dist, .. } = package.to_dist(
let dist = package.to_dist(
root,
TagPolicy::Preferred(tags),
&BuildOptions::default(),
@ -2588,26 +2579,20 @@ impl Package {
Ok(())
}
/// Convert the [`Package`] to a [`Dist`] that can be used in installation, along with its hash.
/// Convert the [`Package`] to a [`Dist`] that can be used in installation.
fn to_dist(
&self,
workspace_root: &Path,
tag_policy: TagPolicy<'_>,
build_options: &BuildOptions,
markers: &MarkerEnvironment,
) -> Result<HashedDist, LockError> {
) -> Result<Dist, LockError> {
let no_binary = build_options.no_binary_package(&self.id.name);
let no_build = build_options.no_build_package(&self.id.name);
if !no_binary {
if let Some(best_wheel_index) = self.find_best_wheel(tag_policy) {
let hashes = self.wheels[best_wheel_index]
.hash
.as_ref()
.map(|hash| HashDigests::from(vec![hash.0.clone()]))
.unwrap_or_else(|| HashDigests::from(vec![]));
let dist = match &self.id.source {
return match &self.id.source {
Source::Registry(source) => {
let wheels = self
.wheels
@ -2619,7 +2604,7 @@ impl Package {
best_wheel_index,
sdist: None,
};
Dist::Built(BuiltDist::Registry(reg_built_dist))
Ok(Dist::Built(BuiltDist::Registry(reg_built_dist)))
}
Source::Path(path) => {
let filename: WheelFilename =
@ -2631,7 +2616,7 @@ impl Package {
install_path: absolute_path(workspace_root, path)?.into_boxed_path(),
};
let built_dist = BuiltDist::Path(path_dist);
Dist::Built(built_dist)
Ok(Dist::Built(built_dist))
}
Source::Direct(url, direct) => {
let filename: WheelFilename =
@ -2647,39 +2632,29 @@ impl Package {
url: VerbatimUrl::from_url(url),
};
let built_dist = BuiltDist::DirectUrl(direct_dist);
Dist::Built(built_dist)
Ok(Dist::Built(built_dist))
}
Source::Git(_, _) => {
return Err(LockErrorKind::InvalidWheelSource {
Source::Git(_, _) => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "Git",
}
.into());
}
Source::Directory(_) => {
return Err(LockErrorKind::InvalidWheelSource {
.into()),
Source::Directory(_) => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "directory",
}
.into());
}
Source::Editable(_) => {
return Err(LockErrorKind::InvalidWheelSource {
.into()),
Source::Editable(_) => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "editable",
}
.into());
}
Source::Virtual(_) => {
return Err(LockErrorKind::InvalidWheelSource {
.into()),
Source::Virtual(_) => Err(LockErrorKind::InvalidWheelSource {
id: self.id.clone(),
source_type: "virtual",
}
.into());
}
.into()),
};
return Ok(HashedDist { dist, hashes });
}
}
@ -2688,16 +2663,7 @@ impl Package {
// any local source tree, or at least editable source trees, which we allow in
// `uv pip`.)
if !no_build || sdist.is_virtual() {
let hashes = self
.sdist
.as_ref()
.and_then(|s| s.hash())
.map(|hash| HashDigests::from(vec![hash.0.clone()]))
.unwrap_or_else(|| HashDigests::from(vec![]));
return Ok(HashedDist {
dist: Dist::Source(sdist),
hashes,
});
return Ok(Dist::Source(sdist));
}
}

View File

@ -270,7 +270,6 @@ impl Pep723Script {
file: impl AsRef<Path>,
requires_python: &VersionSpecifiers,
existing_contents: Option<Vec<u8>>,
bare: bool,
) -> Result<(), Pep723Error> {
let file = file.as_ref();
@ -306,8 +305,6 @@ impl Pep723Script {
indoc::formatdoc! {r"
{shebang}{metadata}
{contents}" }
} else if bare {
metadata
} else {
indoc::formatdoc! {r#"
{metadata}

View File

@ -370,7 +370,6 @@ pub struct ResolverOptions {
pub config_settings_package: Option<PackageConfigSettings>,
pub exclude_newer: ExcludeNewer,
pub link_mode: Option<LinkMode>,
pub torch_backend: Option<TorchMode>,
pub upgrade: Option<Upgrade>,
pub build_isolation: Option<BuildIsolation>,
pub no_build: Option<bool>,
@ -405,7 +404,6 @@ pub struct ResolverInstallerOptions {
pub exclude_newer: Option<ExcludeNewerValue>,
pub exclude_newer_package: Option<ExcludeNewerPackage>,
pub link_mode: Option<LinkMode>,
pub torch_backend: Option<TorchMode>,
pub compile_bytecode: Option<bool>,
pub no_sources: Option<bool>,
pub upgrade: Option<Upgrade>,
@ -414,6 +412,7 @@ pub struct ResolverInstallerOptions {
pub no_build_package: Option<Vec<PackageName>>,
pub no_binary: Option<bool>,
pub no_binary_package: Option<Vec<PackageName>>,
pub torch_backend: Option<TorchMode>,
}
impl From<ResolverInstallerSchema> for ResolverInstallerOptions {
@ -439,7 +438,6 @@ impl From<ResolverInstallerSchema> for ResolverInstallerOptions {
exclude_newer,
exclude_newer_package,
link_mode,
torch_backend,
compile_bytecode,
no_sources,
upgrade,
@ -450,6 +448,7 @@ impl From<ResolverInstallerSchema> for ResolverInstallerOptions {
no_build_package,
no_binary,
no_binary_package,
torch_backend,
} = value;
Self {
index,
@ -474,7 +473,6 @@ impl From<ResolverInstallerSchema> for ResolverInstallerOptions {
exclude_newer,
exclude_newer_package,
link_mode,
torch_backend,
compile_bytecode,
no_sources,
upgrade: Upgrade::from_args(
@ -490,6 +488,7 @@ impl From<ResolverInstallerSchema> for ResolverInstallerOptions {
no_build_package,
no_binary,
no_binary_package,
torch_backend,
}
}
}
@ -1926,7 +1925,6 @@ impl From<ResolverInstallerSchema> for ResolverOptions {
extra_build_dependencies: value.extra_build_dependencies,
extra_build_variables: value.extra_build_variables,
no_sources: value.no_sources,
torch_backend: value.torch_backend,
}
}
}
@ -2006,7 +2004,6 @@ pub struct ToolOptions {
pub no_build_package: Option<Vec<PackageName>>,
pub no_binary: Option<bool>,
pub no_binary_package: Option<Vec<PackageName>>,
pub torch_backend: Option<TorchMode>,
}
impl From<ResolverInstallerOptions> for ToolOptions {
@ -2037,7 +2034,6 @@ impl From<ResolverInstallerOptions> for ToolOptions {
no_build_package: value.no_build_package,
no_binary: value.no_binary,
no_binary_package: value.no_binary_package,
torch_backend: value.torch_backend,
}
}
}
@ -2072,7 +2068,7 @@ impl From<ToolOptions> for ResolverInstallerOptions {
no_build_package: value.no_build_package,
no_binary: value.no_binary,
no_binary_package: value.no_binary_package,
torch_backend: value.torch_backend,
torch_backend: None,
}
}
}
@ -2154,7 +2150,7 @@ pub struct OptionsWire {
// `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
// They're respected in both `pyproject.toml` and `uv.toml` files.
override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
exclude_dependencies: Option<Vec<PackageName>>,
exclude_dependencies: Option<Vec<uv_normalize::PackageName>>,
constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
environments: Option<SupportedEnvironments>,

View File

@ -216,7 +216,6 @@ async fn build_impl(
upgrade: _,
build_options,
sources,
torch_backend: _,
} = settings;
// Determine the source to build.

View File

@ -500,28 +500,10 @@ pub(crate) async fn pip_install(
);
let (resolution, hasher) = if let Some(pylock) = pylock {
// Read the `pylock.toml` from disk or URL, and deserialize it from TOML.
let (install_path, content) =
if pylock.starts_with("http://") || pylock.starts_with("https://") {
// Fetch the `pylock.toml` over HTTP(S).
let url = uv_redacted::DisplaySafeUrl::parse(&pylock.to_string_lossy())?;
let client = client_builder.build();
let response = client
.for_host(&url)
.get(url::Url::from(url.clone()))
.send()
.await?;
response.error_for_status_ref()?;
let content = response.text().await?;
// Use the current working directory as the install path for remote lock files.
let install_path = std::env::current_dir()?;
(install_path, content)
} else {
// Read the `pylock.toml` from disk, and deserialize it from TOML.
let install_path = std::path::absolute(&pylock)?;
let install_path = install_path.parent().unwrap().to_path_buf();
let install_path = install_path.parent().unwrap();
let content = fs_err::tokio::read_to_string(&pylock).await?;
(install_path, content)
};
let lock = toml::from_str::<PylockToml>(&content).with_context(|| {
format!("Not a valid `pylock.toml` file: {}", pylock.user_display())
})?;
@ -555,7 +537,7 @@ pub(crate) async fn pip_install(
.collect::<Vec<_>>();
let resolution = lock.to_resolution(
&install_path,
install_path,
marker_env.markers(),
&extras,
&groups,

View File

@ -73,7 +73,6 @@ pub(crate) async fn init(
init_script(
path,
bare,
python,
install_mirrors,
client_builder,
@ -169,7 +168,7 @@ pub(crate) async fn init(
.await?;
// Create the `README.md` if it does not already exist.
if !no_readme && !bare {
if !no_readme {
let readme = path.join("README.md");
if !readme.exists() {
fs_err::write(readme, String::new())?;
@ -202,7 +201,6 @@ pub(crate) async fn init(
#[allow(clippy::fn_params_excessive_bools)]
async fn init_script(
script_path: &Path,
bare: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
client_builder: &BaseClientBuilder<'_>,
@ -277,7 +275,7 @@ async fn init_script(
fs_err::tokio::create_dir_all(parent).await?;
}
Pep723Script::create(script_path, requires_python.specifiers(), content, bare).await?;
Pep723Script::create(script_path, requires_python.specifiers(), content).await?;
Ok(())
}
@ -831,7 +829,7 @@ impl InitProjectKind {
author.as_ref(),
description,
no_description,
no_readme || bare,
no_readme,
);
// Include additional project configuration for packaged applications
@ -910,7 +908,7 @@ impl InitProjectKind {
author.as_ref(),
description,
no_description,
no_readme || bare,
no_readme,
);
// Always include a build system if the project is packaged.

View File

@ -470,7 +470,6 @@ async fn do_lock(
upgrade,
build_options,
sources,
torch_backend: _,
} = settings;
if !preview.is_enabled(PreviewFeatures::EXTRA_BUILD_DEPENDENCIES)

View File

@ -43,7 +43,6 @@ use uv_resolver::{
use uv_scripts::Pep723ItemRef;
use uv_settings::PythonInstallMirrors;
use uv_static::EnvVars;
use uv_torch::{TorchSource, TorchStrategy};
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_virtualenv::remove_virtualenv;
use uv_warnings::{warn_user, warn_user_once};
@ -279,9 +278,6 @@ pub(crate) enum ProjectError {
#[error(transparent)]
RetryParsing(#[from] uv_client::RetryParsingError),
#[error(transparent)]
Accelerator(#[from] uv_torch::AcceleratorError),
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
}
@ -1727,7 +1723,6 @@ pub(crate) async fn resolve_names(
prerelease: _,
resolution: _,
sources,
torch_backend,
upgrade: _,
},
compile_bytecode: _,
@ -1736,27 +1731,10 @@ pub(crate) async fn resolve_names(
let client_builder = client_builder.clone().keyring(*keyring_provider);
// Determine the PyTorch backend.
let torch_backend = torch_backend
.map(|mode| {
let source = if uv_auth::PyxTokenStore::from_settings()
.is_ok_and(|store| store.has_credentials())
{
TorchSource::Pyx
} else {
TorchSource::default()
};
TorchStrategy::from_mode(mode, source, interpreter.platform().os())
})
.transpose()
.ok()
.flatten();
// Initialize the registry client.
let client = RegistryClientBuilder::new(client_builder, cache.clone())
.index_locations(index_locations.clone())
.index_strategy(*index_strategy)
.torch_backend(torch_backend.clone())
.markers(interpreter.markers())
.platform(interpreter.platform())
.build();
@ -1902,7 +1880,6 @@ pub(crate) async fn resolve_environment(
upgrade: _,
build_options,
sources,
torch_backend,
} = settings;
// Respect all requirements from the provided sources.
@ -1923,33 +1900,10 @@ pub(crate) async fn resolve_environment(
let marker_env = pip::resolution_markers(None, python_platform, interpreter);
let python_requirement = PythonRequirement::from_interpreter(interpreter);
// Determine the PyTorch backend.
let torch_backend = torch_backend
.map(|mode| {
let source = if uv_auth::PyxTokenStore::from_settings()
.is_ok_and(|store| store.has_credentials())
{
TorchSource::Pyx
} else {
TorchSource::default()
};
TorchStrategy::from_mode(
mode,
source,
python_platform
.map(|t| t.platform())
.as_ref()
.unwrap_or(interpreter.platform())
.os(),
)
})
.transpose()?;
// Initialize the registry client.
let client = RegistryClientBuilder::new(client_builder, cache.clone())
.index_locations(index_locations.clone())
.index_strategy(*index_strategy)
.torch_backend(torch_backend.clone())
.markers(interpreter.markers())
.platform(interpreter.platform())
.build();
@ -2278,7 +2232,6 @@ pub(crate) async fn update_environment(
prerelease,
resolution,
sources,
torch_backend,
upgrade,
},
compile_bytecode,
@ -2349,33 +2302,10 @@ pub(crate) async fn update_environment(
}
}
// Determine the PyTorch backend.
let torch_backend = torch_backend
.map(|mode| {
let source = if uv_auth::PyxTokenStore::from_settings()
.is_ok_and(|store| store.has_credentials())
{
TorchSource::Pyx
} else {
TorchSource::default()
};
TorchStrategy::from_mode(
mode,
source,
python_platform
.map(|t| t.platform())
.as_ref()
.unwrap_or(interpreter.platform())
.os(),
)
})
.transpose()?;
// Initialize the registry client.
let client = RegistryClientBuilder::new(client_builder, cache.clone())
.index_locations(index_locations.clone())
.index_strategy(*index_strategy)
.torch_backend(torch_backend.clone())
.markers(interpreter.markers())
.platform(interpreter.platform())
.build();

View File

@ -676,7 +676,6 @@ pub(super) async fn do_sync(
prerelease: PrereleaseMode::default(),
resolution: ResolutionMode::default(),
sources,
torch_backend: None,
upgrade: Upgrade::default(),
};
script_extra_build_requires(

View File

@ -212,7 +212,6 @@ pub(crate) async fn tree(
upgrade: _,
build_options: _,
sources: _,
torch_backend: _,
} = &settings;
let capabilities = IndexCapabilities::default();

View File

@ -28,7 +28,7 @@ use uv_python::{
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_settings::{PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
use uv_tool::InstalledTools;
use uv_warnings::{warn_user, warn_user_once};
use uv_warnings::warn_user;
use uv_workspace::WorkspaceCache;
use crate::commands::ExitStatus;
@ -76,12 +76,6 @@ pub(crate) async fn install(
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
if settings.resolver.torch_backend.is_some() {
warn_user_once!(
"The `--torch-backend` option is experimental and may change without warning."
);
}
let reporter = PythonDownloadReporter::single(printer);
let python_request = python.as_deref().map(PythonRequest::parse);

View File

@ -129,12 +129,6 @@ pub(crate) async fn run(
.is_some_and(|ext| ext.eq_ignore_ascii_case("py") || ext.eq_ignore_ascii_case("pyw"))
}
if settings.resolver.torch_backend.is_some() {
warn_user_once!(
"The `--torch-backend` option is experimental and may change without warning."
);
}
// Read from the `.env` file, if necessary.
if !no_env_file {
for env_file_path in env_file.iter().rev().map(PathBuf::as_path) {

View File

@ -840,7 +840,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.combine(Refresh::from(args.settings.upgrade.clone())),
);
Box::pin(commands::pip_install(
commands::pip_install(
&requirements,
&constraints,
&overrides,
@ -892,7 +892,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.dry_run,
printer,
globals.preview,
))
)
.await
}
Commands::Pip(PipNamespace {

View File

@ -337,7 +337,7 @@ impl InitSettings {
no_description,
vcs: vcs.or(bare.then_some(VersionControlSystem::None)),
build_backend,
no_readme,
no_readme: no_readme || bare,
author_from,
pin_python: flag(pin_python, no_pin_python, "pin-python").unwrap_or(!bare),
no_workspace,
@ -586,7 +586,6 @@ impl ToolRunSettings {
lfs,
python,
python_platform,
torch_backend,
generate_shell_completion: _,
} = args;
@ -616,24 +615,21 @@ impl ToolRunSettings {
}
}
let filesystem_options = filesystem.map(FilesystemOptions::into_options);
let options =
resolver_installer_options(installer, build).combine(ResolverInstallerOptions::from(
filesystem_options
.as_ref()
.map(|options| options.top_level.clone())
filesystem
.clone()
.map(FilesystemOptions::into_options)
.map(|options| options.top_level)
.unwrap_or_default(),
));
let filesystem_install_mirrors = filesystem_options
.map(|options| options.install_mirrors.clone())
let filesystem_install_mirrors = filesystem
.map(FilesystemOptions::into_options)
.map(|options| options.install_mirrors)
.unwrap_or_default();
let mut settings = ResolverInstallerSettings::from(options.clone());
if torch_backend.is_some() {
settings.resolver.torch_backend = torch_backend;
}
let settings = ResolverInstallerSettings::from(options.clone());
let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);
Self {
@ -731,27 +727,23 @@ impl ToolInstallSettings {
refresh,
python,
python_platform,
torch_backend,
} = args;
let filesystem_options = filesystem.map(FilesystemOptions::into_options);
let options =
resolver_installer_options(installer, build).combine(ResolverInstallerOptions::from(
filesystem_options
.as_ref()
.map(|options| options.top_level.clone())
filesystem
.clone()
.map(FilesystemOptions::into_options)
.map(|options| options.top_level)
.unwrap_or_default(),
));
let filesystem_install_mirrors = filesystem_options
.map(|options| options.install_mirrors.clone())
let filesystem_install_mirrors = filesystem
.map(FilesystemOptions::into_options)
.map(|options| options.install_mirrors)
.unwrap_or_default();
let mut settings = ResolverInstallerSettings::from(options.clone());
if torch_backend.is_some() {
settings.resolver.torch_backend = torch_backend;
}
let settings = ResolverInstallerSettings::from(options.clone());
let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);
Self {
@ -3207,7 +3199,6 @@ pub(crate) struct ResolverSettings {
pub(crate) prerelease: PrereleaseMode,
pub(crate) resolution: ResolutionMode,
pub(crate) sources: SourceStrategy,
pub(crate) torch_backend: Option<TorchMode>,
pub(crate) upgrade: Upgrade,
}
@ -3262,7 +3253,6 @@ impl From<ResolverOptions> for ResolverSettings {
extra_build_variables: value.extra_build_variables.unwrap_or_default(),
exclude_newer: value.exclude_newer,
link_mode: value.link_mode.unwrap_or_default(),
torch_backend: value.torch_backend,
sources: SourceStrategy::from_args(value.no_sources.unwrap_or_default()),
upgrade: value.upgrade.unwrap_or_default(),
build_options: BuildOptions::new(
@ -3354,7 +3344,6 @@ impl From<ResolverInstallerOptions> for ResolverInstallerSettings {
prerelease: value.prerelease.unwrap_or_default(),
resolution: value.resolution.unwrap_or_default(),
sources: SourceStrategy::from_args(value.no_sources.unwrap_or_default()),
torch_backend: value.torch_backend,
upgrade: value.upgrade.unwrap_or_default(),
},
compile_bytecode: value.compile_bytecode.unwrap_or_default(),

View File

@ -700,42 +700,6 @@ fn init_script() -> Result<()> {
Ok(())
}
/// Using `--bare` with `--script` omits the default script content.
#[test]
fn init_script_bare() -> Result<()> {
let context = TestContext::new("3.12");
let child = context.temp_dir.child("foo");
child.create_dir_all()?;
let script = child.join("main.py");
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("--bare").arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Initialized script at `main.py`
"###);
let script = fs_err::read_to_string(&script)?;
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
script, @r###"
# /// script
# requires-python = ">=3.12"
# dependencies = []
# ///
"###
);
});
Ok(())
}
// Ensure python versions passed as arguments are present in file metadata
#[test]
fn init_script_python_version() -> Result<()> {

View File

@ -8231,6 +8231,7 @@ fn lock_invalid_hash() -> Result<()> {
Hash mismatch for `idna==3.6`
Expected:
sha256:aecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca
sha256:d05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
Computed:
@ -8241,225 +8242,6 @@ fn lock_invalid_hash() -> Result<()> {
Ok(())
}
/// Ensure that we can install from a lockfile when the index switches hash algorithms.
/// First lock and sync with SHA256 hashes, then switch to SHA512 and lock/sync again
/// without clearing the cache.
#[test]
fn lock_mixed_hashes() -> Result<()> {
let context = TestContext::new("3.13");
let root = context.temp_dir.child("simple-html");
fs_err::create_dir_all(&root)?;
let basic_package = root.child("basic-package");
fs_err::create_dir_all(&basic_package)?;
// Copy the wheel and sdist from `test/links`.
let wheel = basic_package.child("basic_package-0.1.0-py3-none-any.whl");
fs_err::copy(
context
.workspace_root
.join("test/links/basic_package-0.1.0-py3-none-any.whl"),
&wheel,
)?;
let sdist = basic_package.child("basic_package-0.1.0.tar.gz");
fs_err::copy(
context
.workspace_root
.join("test/links/basic_package-0.1.0.tar.gz"),
&sdist,
)?;
// Phase 1: Create an `index.html` with SHA256 hashes for both wheel and sdist.
let index = basic_package.child("index.html");
index.write_str(&formatdoc! {r#"
<!DOCTYPE html>
<html>
<head>
<meta name="pypi:repository-version" content="1.1" />
</head>
<body>
<h1>Links for basic-package</h1>
<a
href="{}#sha256=7b6229db79b5800e4e98a351b5628c1c8a944533a2d428aeeaa7275a30d4ea82"
>
basic_package-0.1.0-py3-none-any.whl
</a>
<a
href="{}#sha256=af478ff91ec60856c99a540b8df13d756513bebb65bc301fb27e0d1f974532b4"
>
basic_package-0.1.0.tar.gz
</a>
</body>
</html>
"#,
Url::from_file_path(&wheel).unwrap().as_str(),
Url::from_file_path(&sdist).unwrap().as_str(),
})?;
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(&formatdoc! { r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = ["basic-package"]
[tool.uv]
extra-index-url = ["{}"]
"#,
Url::from_file_path(&root).unwrap().as_str()
})?;
let index_url = Url::from_file_path(&root).unwrap().to_string();
let filters = [(index_url.as_str(), "file://[TMP]")]
.into_iter()
.chain(context.filters())
.collect::<Vec<_>>();
// Lock with SHA256 hashes.
uv_snapshot!(context.filters(), context.lock().env_remove(EnvVars::UV_EXCLUDE_NEWER), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
");
let lock = context.read("uv.lock");
insta::with_settings!({
filters => filters.clone(),
}, {
assert_snapshot!(
lock, @r#"
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "basic-package"
version = "0.1.0"
source = { registry = "simple-html" }
sdist = { path = "basic-package/basic_package-0.1.0.tar.gz", hash = "sha256:af478ff91ec60856c99a540b8df13d756513bebb65bc301fb27e0d1f974532b4" }
wheels = [
{ path = "basic-package/basic_package-0.1.0-py3-none-any.whl", hash = "sha256:7b6229db79b5800e4e98a351b5628c1c8a944533a2d428aeeaa7275a30d4ea82" },
]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "basic-package" },
]
[package.metadata]
requires-dist = [{ name = "basic-package" }]
"#
);
});
// Sync with SHA256 hashes to populate the cache.
uv_snapshot!(filters.clone(), context.sync().arg("--frozen"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ basic-package==0.1.0
");
// Phase 2: Update the index to use a SHA512 hash for the wheel instead.
index.write_str(&formatdoc! {r#"
<!DOCTYPE html>
<html>
<head>
<meta name="pypi:repository-version" content="1.1" />
</head>
<body>
<h1>Links for basic-package</h1>
<a
href="{}#sha512=765bde25938af485e492e25ee0e8cde262462565122c1301213a69bf9ceb2008e3997b652a604092a238c4b1a6a334e697ff3cee3c22f9a617cb14f34e26ef17"
>
basic_package-0.1.0-py3-none-any.whl
</a>
<a
href="{}#sha256=af478ff91ec60856c99a540b8df13d756513bebb65bc301fb27e0d1f974532b4"
>
basic_package-0.1.0.tar.gz
</a>
</body>
</html>
"#,
Url::from_file_path(&wheel).unwrap().as_str(),
Url::from_file_path(&sdist).unwrap().as_str(),
})?;
// Lock again with `--refresh` to pick up the SHA512 hash from the updated index.
uv_snapshot!(context.filters(), context.lock().arg("--refresh").env_remove(EnvVars::UV_EXCLUDE_NEWER), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 2 packages in [TIME]
");
let lock = context.read("uv.lock");
insta::with_settings!({
filters => filters.clone(),
}, {
assert_snapshot!(
lock, @r#"
version = 1
revision = 3
requires-python = ">=3.13"
[[package]]
name = "basic-package"
version = "0.1.0"
source = { registry = "simple-html" }
sdist = { path = "basic-package/basic_package-0.1.0.tar.gz", hash = "sha256:af478ff91ec60856c99a540b8df13d756513bebb65bc301fb27e0d1f974532b4" }
wheels = [
{ path = "basic-package/basic_package-0.1.0-py3-none-any.whl", hash = "sha512:765bde25938af485e492e25ee0e8cde262462565122c1301213a69bf9ceb2008e3997b652a604092a238c4b1a6a334e697ff3cee3c22f9a617cb14f34e26ef17" },
]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "basic-package" },
]
[package.metadata]
requires-dist = [{ name = "basic-package" }]
"#
);
});
// Reinstalling should re-compute the hash for the `basic-package` wheel to reflect SHA512.
uv_snapshot!(filters, context.sync().arg("--frozen").arg("--reinstall"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 1 package in [TIME]
Uninstalled 1 package in [TIME]
Installed 1 package in [TIME]
~ basic-package==0.1.0
");
Ok(())
}
/// Vary the `--resolution-mode`, and ensure that the lockfile is updated.
#[test]
fn lock_resolution_mode() -> Result<()> {

View File

@ -3565,7 +3565,6 @@ fn resolve_tool() -> anyhow::Result<()> {
link_mode: Some(
Clone,
),
torch_backend: None,
compile_bytecode: None,
no_sources: None,
upgrade: None,
@ -3574,6 +3573,7 @@ fn resolve_tool() -> anyhow::Result<()> {
no_build_package: None,
no_binary: None,
no_binary_package: None,
torch_backend: None,
},
settings: ResolverInstallerSettings {
resolver: ResolverSettings {
@ -3615,7 +3615,6 @@ fn resolve_tool() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: LowestDirect,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -7913,7 +7912,6 @@ fn preview_features() {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -8028,7 +8026,6 @@ fn preview_features() {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -8143,7 +8140,6 @@ fn preview_features() {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -8258,7 +8254,6 @@ fn preview_features() {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -8373,7 +8368,6 @@ fn preview_features() {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -8490,7 +8484,6 @@ fn preview_features() {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
compile_bytecode: false,
@ -9745,7 +9738,6 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
}
@ -9865,7 +9857,6 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: Packages(
{
PackageName(
@ -10008,7 +9999,6 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: All,
},
}
@ -10126,7 +10116,6 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: None,
},
}
@ -10234,7 +10223,6 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: All,
},
}
@ -10343,7 +10331,6 @@ fn upgrade_project_cli_config_interaction() -> anyhow::Result<()> {
prerelease: IfNecessaryOrExplicit,
resolution: Highest,
sources: Enabled,
torch_backend: None,
upgrade: Packages(
{
PackageName(