Add --package to uv sync (#5656)

## Summary

Closes https://github.com/astral-sh/uv/issues/5008.
This commit is contained in:
Charlie Marsh 2024-07-31 11:16:48 -04:00 committed by GitHub
parent 37a60a57e8
commit 176e9c4deb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 89 additions and 4 deletions

View file

@ -2008,6 +2008,10 @@ pub struct SyncArgs {
#[command(flatten)] #[command(flatten)]
pub refresh: RefreshArgs, pub refresh: RefreshArgs,
/// Sync a specific package in the workspace.
#[arg(long)]
pub package: Option<PackageName>,
/// The Python interpreter to use to build the run environment. /// The Python interpreter to use to build the run environment.
/// ///
/// By default, uv uses the virtual environment in the current working directory or any parent /// By default, uv uses the virtual environment in the current working directory or any parent

View file

@ -1,4 +1,4 @@
use anyhow::Result; use anyhow::{Context, Result};
use uv_auth::store_credentials_from_url; use uv_auth::store_credentials_from_url;
use uv_cache::Cache; use uv_cache::Cache;
@ -10,11 +10,12 @@ use uv_dispatch::BuildDispatch;
use uv_distribution::DEV_DEPENDENCIES; use uv_distribution::DEV_DEPENDENCIES;
use uv_fs::CWD; use uv_fs::CWD;
use uv_installer::SitePackages; use uv_installer::SitePackages;
use uv_normalize::PackageName;
use uv_python::{PythonEnvironment, PythonFetch, PythonPreference, PythonRequest}; use uv_python::{PythonEnvironment, PythonFetch, PythonPreference, PythonRequest};
use uv_resolver::{FlatIndex, Lock}; use uv_resolver::{FlatIndex, Lock};
use uv_types::{BuildIsolation, HashStrategy}; use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
use uv_workspace::{DiscoveryOptions, VirtualProject}; use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace};
use crate::commands::pip::operations::Modifications; use crate::commands::pip::operations::Modifications;
use crate::commands::project::lock::do_safe_lock; use crate::commands::project::lock::do_safe_lock;
@ -28,6 +29,7 @@ use crate::settings::{InstallerSettingsRef, ResolverInstallerSettings};
pub(crate) async fn sync( pub(crate) async fn sync(
locked: bool, locked: bool,
frozen: bool, frozen: bool,
package: Option<PackageName>,
extras: ExtrasSpecification, extras: ExtrasSpecification,
dev: bool, dev: bool,
modifications: Modifications, modifications: Modifications,
@ -46,8 +48,17 @@ pub(crate) async fn sync(
warn_user_once!("`uv sync` is experimental and may change without warning"); warn_user_once!("`uv sync` is experimental and may change without warning");
} }
// Identify the project // Identify the project.
let project = VirtualProject::discover(&CWD, &DiscoveryOptions::default()).await?; let project = if let Some(package) = package {
VirtualProject::Project(
Workspace::discover(&CWD, &DiscoveryOptions::default())
.await?
.with_current_project(package.clone())
.with_context(|| format!("Package `{package}` not found in workspace"))?,
)
} else {
VirtualProject::discover(&CWD, &DiscoveryOptions::default()).await?
};
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::get_or_init_environment( let venv = project::get_or_init_environment(

View file

@ -979,6 +979,7 @@ async fn run_project(
commands::sync( commands::sync(
args.locked, args.locked,
args.frozen, args.frozen,
args.package,
args.extras, args.extras,
args.dev, args.dev,
args.modifications, args.modifications,

View file

@ -543,6 +543,7 @@ pub(crate) struct SyncSettings {
pub(crate) extras: ExtrasSpecification, pub(crate) extras: ExtrasSpecification,
pub(crate) dev: bool, pub(crate) dev: bool,
pub(crate) modifications: Modifications, pub(crate) modifications: Modifications,
pub(crate) package: Option<PackageName>,
pub(crate) python: Option<String>, pub(crate) python: Option<String>,
pub(crate) refresh: Refresh, pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings, pub(crate) settings: ResolverInstallerSettings,
@ -564,6 +565,7 @@ impl SyncSettings {
installer, installer,
build, build,
refresh, refresh,
package,
python, python,
} = args; } = args;
@ -582,6 +584,7 @@ impl SyncSettings {
), ),
dev: flag(dev, no_dev).unwrap_or(true), dev: flag(dev, no_dev).unwrap_or(true),
modifications, modifications,
package,
python, python,
refresh: Refresh::from(refresh), refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine( settings: ResolverInstallerSettings::combine(

View file

@ -205,3 +205,69 @@ fn empty() -> Result<()> {
Ok(()) Ok(())
} }
/// Sync an individual package within a workspace.
#[test]
fn package() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "root"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["child", "anyio>3"]
[tool.uv.sources]
child = { workspace = true }
[tool.uv.workspace]
members = ["child"]
"#,
)?;
let src = context.temp_dir.child("src").child("albatross");
src.create_dir_all()?;
let init = src.child("__init__.py");
init.touch()?;
let child = context.temp_dir.child("child");
fs_err::create_dir_all(&child)?;
let pyproject_toml = child.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "child"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig>1"]
"#,
)?;
let src = child.child("src").child("albatross");
src.create_dir_all()?;
let init = src.child("__init__.py");
init.touch()?;
uv_snapshot!(context.filters(), context.sync().arg("--package").arg("child"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: `uv sync` is experimental and may change without warning
warning: `uv.sources` is experimental and may change without warning
Resolved 6 packages in [TIME]
Prepared 2 packages in [TIME]
Installed 2 packages in [TIME]
+ child==0.1.0 (from file://[TEMP_DIR]/child)
+ iniconfig==2.0.0
"###);
Ok(())
}