Add `--python-platform` to `uv sync` (#14320)

## Summary

Closes https://github.com/astral-sh/uv/issues/14273.
This commit is contained in:
Charlie Marsh 2025-07-10 21:38:28 -04:00 committed by GitHub
parent 1b2ac40568
commit 0fb8c2b1d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 123 additions and 10 deletions

View File

@ -3439,6 +3439,23 @@ pub struct SyncArgs {
)]
pub python: Option<Maybe<String>>,
/// The platform for which requirements should be installed.
///
/// Represented as a "target triple", a string that describes the target platform in terms of
/// its CPU, vendor, and operating system name, like `x86_64-unknown-linux-gnu` or
/// `aarch64-apple-darwin`.
///
/// When targeting macOS (Darwin), the default minimum version is `12.0`. Use
/// `MACOSX_DEPLOYMENT_TARGET` to specify a different minimum version, e.g., `13.0`.
///
/// WARNING: When specified, uv will select wheels that are compatible with the _target_
/// platform; as a result, the installed distributions may not be compatible with the _current_
/// platform. Conversely, any distributions that are built from source may be incompatible with
/// the _target_ platform, as they will be built for the _current_ platform. The
/// `--python-platform` option is intended for advanced use cases.
#[arg(long)]
pub python_platform: Option<TargetTriple>,
/// Check if the Python environment is synchronized with the project.
///
/// If the environment is not up to date, uv will exit with an error.

View File

@ -1080,6 +1080,7 @@ async fn lock_and_sync(
EditableMode::Editable,
InstallOptions::default(),
Modifications::Sufficient,
None,
settings.into(),
network_settings,
&sync_state,

View File

@ -357,6 +357,7 @@ pub(crate) async fn remove(
EditableMode::Editable,
InstallOptions::default(),
Modifications::Exact,
None,
(&settings).into(),
&network_settings,
&state,

View File

@ -305,6 +305,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
editable,
install_options,
modifications,
None,
(&settings).into(),
&network_settings,
&sync_state,
@ -816,6 +817,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
editable,
install_options,
modifications,
None,
(&settings).into(),
&network_settings,
&sync_state,

View File

@ -13,7 +13,7 @@ use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
Concurrency, Constraints, DependencyGroups, DependencyGroupsWithDefaults, DryRun, EditableMode,
ExtrasSpecification, ExtrasSpecificationWithDefaults, HashCheckingMode, InstallOptions,
PreviewMode,
PreviewMode, TargetTriple,
};
use uv_dispatch::BuildDispatch;
use uv_distribution_types::{
@ -34,8 +34,9 @@ use uv_workspace::pyproject::Source;
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache};
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
use crate::commands::pip::operations;
use crate::commands::pip::operations::Modifications;
use crate::commands::pip::resolution_markers;
use crate::commands::pip::{operations, resolution_tags};
use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::lock::{LockMode, LockOperation, LockResult};
use crate::commands::project::lock_target::LockTarget;
@ -63,6 +64,7 @@ pub(crate) async fn sync(
install_options: InstallOptions,
modifications: Modifications,
python: Option<String>,
python_platform: Option<TargetTriple>,
install_mirrors: PythonInstallMirrors,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
@ -453,6 +455,7 @@ pub(crate) async fn sync(
editable,
install_options,
modifications,
python_platform.as_ref(),
(&settings).into(),
&network_settings,
&state,
@ -589,6 +592,7 @@ pub(super) async fn do_sync(
editable: EditableMode,
install_options: InstallOptions,
modifications: Modifications,
python_platform: Option<&TargetTriple>,
settings: InstallerSettingsRef<'_>,
network_settings: &NetworkSettings,
state: &PlatformState,
@ -644,7 +648,7 @@ pub(super) async fn do_sync(
target.validate_groups(groups)?;
// Determine the markers to use for resolution.
let marker_env = venv.interpreter().resolver_marker_environment();
let marker_env = resolution_markers(None, python_platform, venv.interpreter());
// Validate that the platform is supported by the lockfile.
let environments = target.lock().supported_environments();
@ -670,13 +674,13 @@ pub(super) async fn do_sync(
}
}
// Determine the tags to use for resolution.
let tags = venv.interpreter().tags()?;
// Determine the tags to use for the resolution.
let tags = resolution_tags(None, python_platform, venv.interpreter())?;
// Read the lockfile.
let resolution = target.to_resolution(
&marker_env,
tags,
&tags,
extras,
groups,
build_options,
@ -728,7 +732,7 @@ pub(super) async fn do_sync(
let entries = client
.fetch_all(index_locations.flat_indexes().map(Index::url))
.await?;
FlatIndex::from_entries(entries, Some(tags), &hasher, build_options)
FlatIndex::from_entries(entries, Some(&tags), &hasher, build_options)
};
// Create a build dispatch.
@ -768,7 +772,7 @@ pub(super) async fn do_sync(
index_locations,
config_setting,
&hasher,
tags,
&tags,
&client,
state.in_flight(),
concurrency,
@ -847,7 +851,7 @@ fn apply_editable_mode(resolution: Resolution, editable: EditableMode) -> Resolu
/// These credentials can come from any of `tool.uv.sources`, `tool.uv.dev-dependencies`,
/// `project.dependencies`, and `project.optional-dependencies`.
fn store_credentials_from_target(target: InstallTarget<'_>) {
// Iterate over any idnexes in the target.
// Iterate over any indexes in the target.
for index in target.indexes() {
if let Some(credentials) = index.credentials() {
let credentials = Arc::new(credentials);

View File

@ -634,6 +634,7 @@ async fn lock_and_sync(
EditableMode::Editable,
install_options,
Modifications::Sufficient,
None,
settings.into(),
&network_settings,
&state,

View File

@ -1802,6 +1802,7 @@ async fn run_project(
args.install_options,
args.modifications,
args.python,
args.python_platform,
args.install_mirrors,
globals.python_preference,
globals.python_downloads,

View File

@ -1150,6 +1150,7 @@ pub(crate) struct SyncSettings {
pub(crate) all_packages: bool,
pub(crate) package: Option<PackageName>,
pub(crate) python: Option<String>,
pub(crate) python_platform: Option<TargetTriple>,
pub(crate) install_mirrors: PythonInstallMirrors,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
@ -1190,6 +1191,7 @@ impl SyncSettings {
package,
script,
python,
python_platform,
check,
no_check,
} = args;
@ -1249,6 +1251,7 @@ impl SyncSettings {
all_packages,
package,
python: python.and_then(Maybe::into_option),
python_platform,
refresh: Refresh::from(refresh),
settings,
install_mirrors,

View File

@ -10026,3 +10026,42 @@ fn read_only() -> Result<()> {
Ok(())
}
#[test]
fn sync_python_platform() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["black"]
"#,
)?;
// Lock the project
context.lock().assert().success();
// Sync with a specific platform should filter packages
uv_snapshot!(context.filters(), context.sync().arg("--python-platform").arg("linux"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 8 packages in [TIME]
Prepared 6 packages in [TIME]
Installed 6 packages in [TIME]
+ black==24.3.0
+ click==8.1.7
+ mypy-extensions==1.0.0
+ packaging==24.0
+ pathspec==0.12.1
+ platformdirs==4.2.0
");
Ok(())
}

View File

@ -1138,7 +1138,51 @@ used.</p>
synced to the given environment. The interpreter will be used to create a virtual
environment in the project.</p>
<p>See <a href="#uv-python">uv python</a> for details on Python discovery and supported request formats.</p>
<p>May also be set with the <code>UV_PYTHON</code> environment variable.</p></dd><dt id="uv-sync--quiet"><a href="#uv-sync--quiet"><code>--quiet</code></a>, <code>-q</code></dt><dd><p>Use quiet output.</p>
<p>May also be set with the <code>UV_PYTHON</code> environment variable.</p></dd><dt id="uv-sync--python-platform"><a href="#uv-sync--python-platform"><code>--python-platform</code></a> <i>python-platform</i></dt><dd><p>The platform for which requirements should be installed.</p>
<p>Represented as a &quot;target triple&quot;, a string that describes the target platform in terms of its CPU, vendor, and operating system name, like <code>x86_64-unknown-linux-gnu</code> or <code>aarch64-apple-darwin</code>.</p>
<p>When targeting macOS (Darwin), the default minimum version is <code>12.0</code>. Use <code>MACOSX_DEPLOYMENT_TARGET</code> to specify a different minimum version, e.g., <code>13.0</code>.</p>
<p>WARNING: When specified, uv will select wheels that are compatible with the <em>target</em> platform; as a result, the installed distributions may not be compatible with the <em>current</em> platform. Conversely, any distributions that are built from source may be incompatible with the <em>target</em> platform, as they will be built for the <em>current</em> platform. The <code>--python-platform</code> option is intended for advanced use cases.</p>
<p>Possible values:</p>
<ul>
<li><code>windows</code>: An alias for <code>x86_64-pc-windows-msvc</code>, the default target for Windows</li>
<li><code>linux</code>: An alias for <code>x86_64-unknown-linux-gnu</code>, the default target for Linux</li>
<li><code>macos</code>: An alias for <code>aarch64-apple-darwin</code>, the default target for macOS</li>
<li><code>x86_64-pc-windows-msvc</code>: A 64-bit x86 Windows target</li>
<li><code>i686-pc-windows-msvc</code>: A 32-bit x86 Windows target</li>
<li><code>x86_64-unknown-linux-gnu</code>: An x86 Linux target. Equivalent to <code>x86_64-manylinux_2_17</code></li>
<li><code>aarch64-apple-darwin</code>: An ARM-based macOS target, as seen on Apple Silicon devices</li>
<li><code>x86_64-apple-darwin</code>: An x86 macOS target</li>
<li><code>aarch64-unknown-linux-gnu</code>: An ARM64 Linux target. Equivalent to <code>aarch64-manylinux_2_17</code></li>
<li><code>aarch64-unknown-linux-musl</code>: An ARM64 Linux target</li>
<li><code>x86_64-unknown-linux-musl</code>: An <code>x86_64</code> Linux target</li>
<li><code>x86_64-manylinux2014</code>: An <code>x86_64</code> target for the <code>manylinux2014</code> platform. Equivalent to <code>x86_64-manylinux_2_17</code></li>
<li><code>x86_64-manylinux_2_17</code>: An <code>x86_64</code> target for the <code>manylinux_2_17</code> platform</li>
<li><code>x86_64-manylinux_2_28</code>: An <code>x86_64</code> target for the <code>manylinux_2_28</code> platform</li>
<li><code>x86_64-manylinux_2_31</code>: An <code>x86_64</code> target for the <code>manylinux_2_31</code> platform</li>
<li><code>x86_64-manylinux_2_32</code>: An <code>x86_64</code> target for the <code>manylinux_2_32</code> platform</li>
<li><code>x86_64-manylinux_2_33</code>: An <code>x86_64</code> target for the <code>manylinux_2_33</code> platform</li>
<li><code>x86_64-manylinux_2_34</code>: An <code>x86_64</code> target for the <code>manylinux_2_34</code> platform</li>
<li><code>x86_64-manylinux_2_35</code>: An <code>x86_64</code> target for the <code>manylinux_2_35</code> platform</li>
<li><code>x86_64-manylinux_2_36</code>: An <code>x86_64</code> target for the <code>manylinux_2_36</code> platform</li>
<li><code>x86_64-manylinux_2_37</code>: An <code>x86_64</code> target for the <code>manylinux_2_37</code> platform</li>
<li><code>x86_64-manylinux_2_38</code>: An <code>x86_64</code> target for the <code>manylinux_2_38</code> platform</li>
<li><code>x86_64-manylinux_2_39</code>: An <code>x86_64</code> target for the <code>manylinux_2_39</code> platform</li>
<li><code>x86_64-manylinux_2_40</code>: An <code>x86_64</code> target for the <code>manylinux_2_40</code> platform</li>
<li><code>aarch64-manylinux2014</code>: An ARM64 target for the <code>manylinux2014</code> platform. Equivalent to <code>aarch64-manylinux_2_17</code></li>
<li><code>aarch64-manylinux_2_17</code>: An ARM64 target for the <code>manylinux_2_17</code> platform</li>
<li><code>aarch64-manylinux_2_28</code>: An ARM64 target for the <code>manylinux_2_28</code> platform</li>
<li><code>aarch64-manylinux_2_31</code>: An ARM64 target for the <code>manylinux_2_31</code> platform</li>
<li><code>aarch64-manylinux_2_32</code>: An ARM64 target for the <code>manylinux_2_32</code> platform</li>
<li><code>aarch64-manylinux_2_33</code>: An ARM64 target for the <code>manylinux_2_33</code> platform</li>
<li><code>aarch64-manylinux_2_34</code>: An ARM64 target for the <code>manylinux_2_34</code> platform</li>
<li><code>aarch64-manylinux_2_35</code>: An ARM64 target for the <code>manylinux_2_35</code> platform</li>
<li><code>aarch64-manylinux_2_36</code>: An ARM64 target for the <code>manylinux_2_36</code> platform</li>
<li><code>aarch64-manylinux_2_37</code>: An ARM64 target for the <code>manylinux_2_37</code> platform</li>
<li><code>aarch64-manylinux_2_38</code>: An ARM64 target for the <code>manylinux_2_38</code> platform</li>
<li><code>aarch64-manylinux_2_39</code>: An ARM64 target for the <code>manylinux_2_39</code> platform</li>
<li><code>aarch64-manylinux_2_40</code>: An ARM64 target for the <code>manylinux_2_40</code> platform</li>
<li><code>wasm32-pyodide2024</code>: A wasm32 target using the the Pyodide 2024 platform. Meant for use with Python 3.12</li>
</ul></dd><dt id="uv-sync--quiet"><a href="#uv-sync--quiet"><code>--quiet</code></a>, <code>-q</code></dt><dd><p>Use quiet output.</p>
<p>Repeating this option, e.g., <code>-qq</code>, will enable a silent mode in which uv will write no output to stdout.</p>
</dd><dt id="uv-sync--refresh"><a href="#uv-sync--refresh"><code>--refresh</code></a></dt><dd><p>Refresh all cached data</p>
</dd><dt id="uv-sync--refresh-package"><a href="#uv-sync--refresh-package"><code>--refresh-package</code></a> <i>refresh-package</i></dt><dd><p>Refresh cached data for a specific package</p>