mirror of https://github.com/astral-sh/uv
Add support for pip-compile's `--unsafe-package` flag (#1889)
## Summary In uv, we're going to use `--no-emit-package` for this, to convey that the package will be included in the resolution but not in the output file. It also mirrors flags like `--emit-index-url`. We're also including an `--unsafe-package` alias. Closes https://github.com/astral-sh/uv/issues/1415.
This commit is contained in:
parent
9cf7d113bc
commit
eaf613ed31
|
|
@ -257,6 +257,13 @@ impl ResolutionGraph {
|
||||||
self.petgraph.node_count() == 0
|
self.petgraph.node_count() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the graph contains the given package.
|
||||||
|
pub fn contains(&self, name: &PackageName) -> bool {
|
||||||
|
self.petgraph
|
||||||
|
.node_indices()
|
||||||
|
.any(|index| self.petgraph[index].name() == name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the [`Diagnostic`]s that were encountered while building the graph.
|
/// Return the [`Diagnostic`]s that were encountered while building the graph.
|
||||||
pub fn diagnostics(&self) -> &[Diagnostic] {
|
pub fn diagnostics(&self) -> &[Diagnostic] {
|
||||||
&self.diagnostics
|
&self.diagnostics
|
||||||
|
|
@ -273,6 +280,8 @@ impl ResolutionGraph {
|
||||||
pub struct DisplayResolutionGraph<'a> {
|
pub struct DisplayResolutionGraph<'a> {
|
||||||
/// The underlying graph.
|
/// The underlying graph.
|
||||||
resolution: &'a ResolutionGraph,
|
resolution: &'a ResolutionGraph,
|
||||||
|
/// The packages to exclude from the output.
|
||||||
|
no_emit_packages: &'a [PackageName],
|
||||||
/// Whether to include hashes in the output.
|
/// Whether to include hashes in the output.
|
||||||
show_hashes: bool,
|
show_hashes: bool,
|
||||||
/// Whether to include annotations in the output, to indicate which dependency or dependencies
|
/// Whether to include annotations in the output, to indicate which dependency or dependencies
|
||||||
|
|
@ -285,7 +294,7 @@ pub struct DisplayResolutionGraph<'a> {
|
||||||
|
|
||||||
impl<'a> From<&'a ResolutionGraph> for DisplayResolutionGraph<'a> {
|
impl<'a> From<&'a ResolutionGraph> for DisplayResolutionGraph<'a> {
|
||||||
fn from(resolution: &'a ResolutionGraph) -> Self {
|
fn from(resolution: &'a ResolutionGraph) -> Self {
|
||||||
Self::new(resolution, false, true, AnnotationStyle::default())
|
Self::new(resolution, &[], false, true, AnnotationStyle::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,12 +302,14 @@ impl<'a> DisplayResolutionGraph<'a> {
|
||||||
/// Create a new [`DisplayResolutionGraph`] for the given graph.
|
/// Create a new [`DisplayResolutionGraph`] for the given graph.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
underlying: &'a ResolutionGraph,
|
underlying: &'a ResolutionGraph,
|
||||||
|
no_emit_packages: &'a [PackageName],
|
||||||
show_hashes: bool,
|
show_hashes: bool,
|
||||||
include_annotations: bool,
|
include_annotations: bool,
|
||||||
annotation_style: AnnotationStyle,
|
annotation_style: AnnotationStyle,
|
||||||
) -> DisplayResolutionGraph<'a> {
|
) -> DisplayResolutionGraph<'a> {
|
||||||
Self {
|
Self {
|
||||||
resolution: underlying,
|
resolution: underlying,
|
||||||
|
no_emit_packages,
|
||||||
show_hashes,
|
show_hashes,
|
||||||
include_annotations,
|
include_annotations,
|
||||||
annotation_style,
|
annotation_style,
|
||||||
|
|
@ -348,15 +359,19 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
||||||
.resolution
|
.resolution
|
||||||
.petgraph
|
.petgraph
|
||||||
.node_indices()
|
.node_indices()
|
||||||
.map(|index| {
|
.filter_map(|index| {
|
||||||
let dist = &self.resolution.petgraph[index];
|
let dist = &self.resolution.petgraph[index];
|
||||||
let name = dist.name();
|
let name = dist.name();
|
||||||
|
if self.no_emit_packages.contains(name) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let node = if let Some((editable, _)) = self.resolution.editables.get(name) {
|
let node = if let Some((editable, _)) = self.resolution.editables.get(name) {
|
||||||
Node::Editable(name, editable)
|
Node::Editable(name, editable)
|
||||||
} else {
|
} else {
|
||||||
Node::Distribution(name, dist)
|
Node::Distribution(name, dist)
|
||||||
};
|
};
|
||||||
(index, node)
|
Some((index, node))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ pub(crate) async fn pip_compile(
|
||||||
dependency_mode: DependencyMode,
|
dependency_mode: DependencyMode,
|
||||||
upgrade: Upgrade,
|
upgrade: Upgrade,
|
||||||
generate_hashes: bool,
|
generate_hashes: bool,
|
||||||
|
no_emit_packages: Vec<PackageName>,
|
||||||
include_annotations: bool,
|
include_annotations: bool,
|
||||||
include_header: bool,
|
include_header: bool,
|
||||||
include_index_url: bool,
|
include_index_url: bool,
|
||||||
|
|
@ -383,12 +384,30 @@ pub(crate) async fn pip_compile(
|
||||||
"{}",
|
"{}",
|
||||||
DisplayResolutionGraph::new(
|
DisplayResolutionGraph::new(
|
||||||
&resolution,
|
&resolution,
|
||||||
|
&no_emit_packages,
|
||||||
generate_hashes,
|
generate_hashes,
|
||||||
include_annotations,
|
include_annotations,
|
||||||
annotation_style,
|
annotation_style,
|
||||||
)
|
)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// If any "unsafe" packages were excluded, notify the user.
|
||||||
|
let excluded = no_emit_packages
|
||||||
|
.into_iter()
|
||||||
|
.filter(|name| resolution.contains(name))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if !excluded.is_empty() {
|
||||||
|
writeln!(writer)?;
|
||||||
|
writeln!(
|
||||||
|
writer,
|
||||||
|
"{}",
|
||||||
|
"# The following packages were included while generating the resolution:".green()
|
||||||
|
)?;
|
||||||
|
for package in excluded {
|
||||||
|
writeln!(writer, "# {package}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,6 @@ pub(crate) struct PipCompileCompatArgs {
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
no_emit_trusted_host: bool,
|
no_emit_trusted_host: bool,
|
||||||
|
|
||||||
#[clap(long, hide = true)]
|
|
||||||
unsafe_package: Vec<String>,
|
|
||||||
|
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
config: Option<String>,
|
config: Option<String>,
|
||||||
|
|
||||||
|
|
@ -171,12 +168,6 @@ impl CompatArgs for PipCompileCompatArgs {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.unsafe_package.is_empty() {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"pip-compile's `--unsafe-package` is not supported."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.is_some() {
|
if self.config.is_some() {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"pip-compile's `--config` is unsupported (uv does not use a configuration file)."
|
"pip-compile's `--config` is unsupported (uv does not use a configuration file)."
|
||||||
|
|
|
||||||
|
|
@ -352,6 +352,11 @@ struct PipCompileArgs {
|
||||||
#[arg(long, value_parser = date_or_datetime, hide = true)]
|
#[arg(long, value_parser = date_or_datetime, hide = true)]
|
||||||
exclude_newer: Option<DateTime<Utc>>,
|
exclude_newer: Option<DateTime<Utc>>,
|
||||||
|
|
||||||
|
/// Specify a package to omit from the output resolution. Its dependencies will still be
|
||||||
|
/// included in the resolution. Equivalent to pip-compile's `--unsafe-package` option.
|
||||||
|
#[clap(long, alias = "unsafe-package")]
|
||||||
|
no_emit_package: Vec<PackageName>,
|
||||||
|
|
||||||
/// Include `--index-url` and `--extra-index-url` entries in the generated output file.
|
/// Include `--index-url` and `--extra-index-url` entries in the generated output file.
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
emit_index_url: bool,
|
emit_index_url: bool,
|
||||||
|
|
@ -904,6 +909,7 @@ async fn run() -> Result<ExitStatus> {
|
||||||
dependency_mode,
|
dependency_mode,
|
||||||
upgrade,
|
upgrade,
|
||||||
args.generate_hashes,
|
args.generate_hashes,
|
||||||
|
args.no_emit_package,
|
||||||
!args.no_annotate,
|
!args.no_annotate,
|
||||||
!args.no_header,
|
!args.no_header,
|
||||||
args.emit_index_url,
|
args.emit_index_url,
|
||||||
|
|
|
||||||
|
|
@ -4225,3 +4225,46 @@ fn override_with_incompatible_constraint() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a package, marking a dependency as unsafe.
|
||||||
|
#[test]
|
||||||
|
fn unsafe_package() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
|
requirements_in.write_str("flask")?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.compile()
|
||||||
|
.arg("requirements.in")
|
||||||
|
.arg("--unsafe-package")
|
||||||
|
.arg("jinja2")
|
||||||
|
.arg("--unsafe-package")
|
||||||
|
.arg("pydantic"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in --unsafe-package jinja2 --unsafe-package pydantic
|
||||||
|
blinker==1.7.0
|
||||||
|
# via flask
|
||||||
|
click==8.1.7
|
||||||
|
# via flask
|
||||||
|
flask==3.0.0
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
# via flask
|
||||||
|
markupsafe==2.1.3
|
||||||
|
# via
|
||||||
|
# jinja2
|
||||||
|
# werkzeug
|
||||||
|
werkzeug==3.0.1
|
||||||
|
# via flask
|
||||||
|
|
||||||
|
# The following packages were included while generating the resolution:
|
||||||
|
# jinja2
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 7 packages in [TIME]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue