Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3401,6 +3401,10 @@ pub struct ExportArgs {
#[arg(long, conflicts_with = "all_packages")]
pub package: Option<PackageName>,

/// Prune the given package from the dependency tree.
#[arg(long, conflicts_with = "all_packages")]
pub prune: Vec<PackageName>,

/// Include optional dependencies from the specified extra name.
///
/// May be provided more than once.
Expand Down
15 changes: 14 additions & 1 deletion crates/uv-resolver/src/lock/requirements_txt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use uv_configuration::{DevGroupsManifest, EditableMode, ExtrasSpecification, Ins
use uv_distribution_filename::{DistExtension, SourceDistExtension};
use uv_fs::Simplified;
use uv_git::GitReference;
use uv_normalize::ExtraName;
use uv_normalize::{ExtraName, PackageName};
use uv_pep508::MarkerTree;
use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl};

Expand All @@ -33,6 +33,7 @@ pub struct RequirementsTxtExport<'lock> {
impl<'lock> RequirementsTxtExport<'lock> {
pub fn from_lock(
target: InstallTarget<'lock>,
prune: &[PackageName],
extras: &ExtrasSpecification,
dev: &DevGroupsManifest,
editable: EditableMode,
Expand All @@ -50,6 +51,10 @@ impl<'lock> RequirementsTxtExport<'lock> {

// Add the workspace package to the queue.
for root_name in target.packages() {
if prune.contains(root_name) {
continue;
}

let dist = target
.lock()
.find_by_name(root_name)
Expand Down Expand Up @@ -96,6 +101,10 @@ impl<'lock> RequirementsTxtExport<'lock> {
})
.flatten()
{
if prune.contains(&dep.package_id.name) {
continue;
}

let dep_dist = target.lock().find_by_id(&dep.package_id);

// Add the dependency to the graph.
Expand Down Expand Up @@ -142,6 +151,10 @@ impl<'lock> RequirementsTxtExport<'lock> {
};

for dep in deps {
if prune.contains(&dep.package_id.name) {
continue;
}

let dep_dist = target.lock().find_by_id(&dep.package_id);

// Add the dependency to the graph.
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/src/commands/project/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub(crate) async fn export(
format: ExportFormat,
all_packages: bool,
package: Option<PackageName>,
prune: Vec<PackageName>,
hashes: bool,
install_options: InstallOptions,
output_file: Option<PathBuf>,
Expand Down Expand Up @@ -178,6 +179,7 @@ pub(crate) async fn export(
ExportFormat::RequirementsTxt => {
let export = RequirementsTxtExport::from_lock(
target,
&prune,
&extras,
&dev.with_defaults(defaults),
editable,
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,7 @@ async fn run_project(
args.format,
args.all_packages,
args.package,
args.prune,
args.hashes,
args.install_options,
args.output_file,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,7 @@ pub(crate) struct ExportSettings {
pub(crate) format: ExportFormat,
pub(crate) all_packages: bool,
pub(crate) package: Option<PackageName>,
pub(crate) prune: Vec<PackageName>,
pub(crate) extras: ExtrasSpecification,
pub(crate) dev: DevGroupsSpecification,
pub(crate) editable: EditableMode,
Expand All @@ -1302,6 +1303,7 @@ impl ExportSettings {
format,
all_packages,
package,
prune,
extra,
all_extras,
no_all_extras,
Expand Down Expand Up @@ -1337,6 +1339,7 @@ impl ExportSettings {
format,
all_packages,
package,
prune,
extras: ExtrasSpecification::from_args(
flag(all_extras, no_all_extras).unwrap_or_default(),
extra.unwrap_or_default(),
Expand Down
57 changes: 57 additions & 0 deletions crates/uv/tests/it/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,63 @@ fn project_extra() -> Result<()> {
Ok(())
}

#[test]
fn prune() -> 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 = [
"jupyter-client"
]
"#,
)?;

// project v0.1.0
// └── jupyter-client v8.6.1
// ├── jupyter-core v5.7.2
// │ ├── platformdirs v4.2.0
// │ └── traitlets v5.14.2
// ├── python-dateutil v2.9.0.post0
// │ └── six v1.16.0
// ├── pyzmq v25.1.2
// ├── tornado v6.4
// └── traitlets v5.14.2

uv_snapshot!(
context.filters(),
context.export()
.arg("--no-hashes")
.arg("--prune")
.arg("jupyter-core"),
@r"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv export --cache-dir [CACHE_DIR] --no-hashes --prune jupyter-core
cffi==1.16.0 ; implementation_name == 'pypy'
jupyter-client==8.6.1
pycparser==2.21 ; implementation_name == 'pypy'
python-dateutil==2.9.0.post0
pyzmq==25.1.2
six==1.16.0
tornado==6.4
traitlets==5.14.2

----- stderr -----
Resolved 12 packages in [TIME]
"
);

Ok(())
}

#[test]
fn dependency_marker() -> Result<()> {
let context = TestContext::new("3.12");
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -2355,6 +2355,8 @@ uv export [OPTIONS]

<p>This setting has no effect when used in the <code>uv pip</code> interface.</p>

</dd><dt><code>--prune</code> <i>prune</i></dt><dd><p>Prune the given package from the dependency tree</p>

</dd><dt><code>--python</code>, <code>-p</code> <i>python</i></dt><dd><p>The Python interpreter to use during resolution.</p>

<p>A Python interpreter is required for building source distributions to determine package metadata when there are not wheels.</p>
Expand Down