Add --no-hashes to uv export (#6954)

## Summary

Closes https://github.com/astral-sh/uv/issues/6944.
This commit is contained in:
Charlie Marsh 2024-09-02 22:12:29 -04:00 committed by GitHub
parent ad82b94856
commit ccdf2d793b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 76 additions and 11 deletions

View file

@ -2802,6 +2802,14 @@ pub struct ExportArgs {
#[arg(long, overrides_with("dev"))]
pub no_dev: bool,
/// Include hashes for all dependencies.
#[arg(long, overrides_with("no_hashes"), hide = true)]
pub hashes: bool,
/// Omit hashes in the generated output.
#[arg(long, overrides_with("hashes"))]
pub no_hashes: bool,
/// Assert that the `uv.lock` will remain unchanged.
///
/// Requires that the lockfile is up-to-date. If the lockfile is missing or

View file

@ -24,7 +24,10 @@ type LockGraph<'lock> = Graph<Node<'lock>, Edge, Directed>;
/// An export of a [`Lock`] that renders in `requirements.txt` format.
#[derive(Debug)]
pub struct RequirementsTxtExport<'lock>(LockGraph<'lock>);
pub struct RequirementsTxtExport<'lock> {
graph: LockGraph<'lock>,
hashes: bool,
}
impl<'lock> RequirementsTxtExport<'lock> {
pub fn from_lock(
@ -32,6 +35,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
root_name: &PackageName,
extras: &ExtrasSpecification,
dev: &[GroupName],
hashes: bool,
) -> Result<Self, LockError> {
let size_guess = lock.packages.len();
let mut petgraph = LockGraph::with_capacity(size_guess, size_guess);
@ -114,7 +118,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
let graph = propagate_markers(petgraph);
Ok(Self(graph))
Ok(Self { graph, hashes })
}
}
@ -122,7 +126,7 @@ impl std::fmt::Display for RequirementsTxtExport<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// Collect all packages.
let mut nodes = self
.0
.graph
.raw_nodes()
.iter()
.map(|node| &node.weight)
@ -197,12 +201,14 @@ impl std::fmt::Display for RequirementsTxtExport<'_> {
write!(f, " ; {contents}")?;
}
let hashes = package.hashes();
if !hashes.is_empty() {
for hash in &hashes {
writeln!(f, " \\")?;
write!(f, " --hash=")?;
write!(f, "{hash}")?;
if self.hashes {
let hashes = package.hashes();
if !hashes.is_empty() {
for hash in &hashes {
writeln!(f, " \\")?;
write!(f, " --hash=")?;
write!(f, "{hash}")?;
}
}
}

View file

@ -22,6 +22,7 @@ use crate::settings::ResolverSettings;
pub(crate) async fn export(
format: ExportFormat,
package: Option<PackageName>,
hashes: bool,
extras: ExtrasSpecification,
dev: bool,
locked: bool,
@ -112,8 +113,13 @@ pub(crate) async fn export(
// Generate the export.
match format {
ExportFormat::RequirementsTxt => {
let export =
RequirementsTxtExport::from_lock(&lock, project.project_name(), &extras, &dev)?;
let export = RequirementsTxtExport::from_lock(
&lock,
project.project_name(),
&extras,
&dev,
hashes,
)?;
anstream::println!(
"{}",
"# This file was autogenerated via `uv export`.".green()

View file

@ -1273,6 +1273,7 @@ async fn run_project(
commands::export(
args.format,
args.package,
args.hashes,
args.extras,
args.dev,
args.locked,

View file

@ -944,6 +944,7 @@ pub(crate) struct ExportSettings {
pub(crate) package: Option<PackageName>,
pub(crate) extras: ExtrasSpecification,
pub(crate) dev: bool,
pub(crate) hashes: bool,
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) python: Option<String>,
@ -963,6 +964,8 @@ impl ExportSettings {
no_all_extras,
dev,
no_dev,
hashes,
no_hashes,
locked,
frozen,
resolver,
@ -979,6 +982,7 @@ impl ExportSettings {
extra.unwrap_or_default(),
),
dev: flag(dev, no_dev).unwrap_or(true),
hashes: flag(hashes, no_hashes).unwrap_or(true),
locked,
frozen,
python,

View file

@ -632,3 +632,41 @@ fn dev() -> Result<()> {
Ok(())
}
#[test]
fn no_hashes() -> 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 = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"
"#,
)?;
context.lock().assert().success();
uv_snapshot!(context.filters(), context.export().arg("--no-hashes"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated via `uv export`.
-e .
anyio==3.7.0
idna==3.6
sniffio==1.3.1
----- stderr -----
Resolved 4 packages in [TIME]
"###);
Ok(())
}

View file

@ -1738,6 +1738,8 @@ uv export [OPTIONS]
</dd><dt><code>--no-dev</code></dt><dd><p>Omit development dependencies</p>
</dd><dt><code>--no-hashes</code></dt><dd><p>Omit hashes in the generated output</p>
</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>
</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>