mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-29 08:03:50 +00:00
feat: Implement --annotation-style
parameter for uv pip compile
(#1679)
## Summary Hello there! The motivation for this feature is described here #1678 ## Test Plan I've added unit tests and also tested this manually on my work project by comparing it to the original `pip-compile` output - it looks much like the `pip-compile` generated lock file.
This commit is contained in:
parent
3cd51ffc92
commit
31752bf4be
7 changed files with 193 additions and 53 deletions
|
@ -4,7 +4,7 @@ pub use finder::{DistFinder, Reporter as FinderReporter};
|
||||||
pub use manifest::Manifest;
|
pub use manifest::Manifest;
|
||||||
pub use options::{Options, OptionsBuilder};
|
pub use options::{Options, OptionsBuilder};
|
||||||
pub use prerelease_mode::PreReleaseMode;
|
pub use prerelease_mode::PreReleaseMode;
|
||||||
pub use resolution::{Diagnostic, DisplayResolutionGraph, ResolutionGraph};
|
pub use resolution::{AnnotationStyle, Diagnostic, DisplayResolutionGraph, ResolutionGraph};
|
||||||
pub use resolution_mode::ResolutionMode;
|
pub use resolution_mode::ResolutionMode;
|
||||||
pub use resolver::{
|
pub use resolver::{
|
||||||
BuildId, DefaultResolverProvider, InMemoryIndex, Reporter as ResolverReporter, Resolver,
|
BuildId, DefaultResolverProvider, InMemoryIndex, Reporter as ResolverReporter, Resolver,
|
||||||
|
|
|
@ -9,7 +9,6 @@ use petgraph::Direction;
|
||||||
use pubgrub::range::Range;
|
use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{Kind, State};
|
use pubgrub::solver::{Kind, State};
|
||||||
use pubgrub::type_aliases::SelectedDependencies;
|
use pubgrub::type_aliases::SelectedDependencies;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -23,9 +22,20 @@ use uv_normalize::{ExtraName, PackageName};
|
||||||
use crate::pins::FilePins;
|
use crate::pins::FilePins;
|
||||||
use crate::pubgrub::{PubGrubDistribution, PubGrubPackage, PubGrubPriority};
|
use crate::pubgrub::{PubGrubDistribution, PubGrubPackage, PubGrubPriority};
|
||||||
use crate::resolver::VersionsResponse;
|
use crate::resolver::VersionsResponse;
|
||||||
|
|
||||||
use crate::ResolveError;
|
use crate::ResolveError;
|
||||||
|
|
||||||
|
/// Indicate the style of annotation comments, used to indicate the dependencies that requested each
|
||||||
|
/// package.
|
||||||
|
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||||
|
pub enum AnnotationStyle {
|
||||||
|
/// Render the annotations on a single, comma-separated line.
|
||||||
|
Line,
|
||||||
|
/// Render each annotation on its own line.
|
||||||
|
#[default]
|
||||||
|
Split,
|
||||||
|
}
|
||||||
|
|
||||||
/// A complete resolution graph in which every node represents a pinned package and every edge
|
/// A complete resolution graph in which every node represents a pinned package and every edge
|
||||||
/// represents a dependency between two pinned packages.
|
/// represents a dependency between two pinned packages.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -256,11 +266,14 @@ pub struct DisplayResolutionGraph<'a> {
|
||||||
/// 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
|
||||||
/// requested each package.
|
/// requested each package.
|
||||||
include_annotations: bool,
|
include_annotations: bool,
|
||||||
|
/// The style of annotation comments, used to indicate the dependencies that requested each
|
||||||
|
/// package.
|
||||||
|
annotation_style: AnnotationStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
Self::new(resolution, false, true, AnnotationStyle::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,11 +283,13 @@ impl<'a> DisplayResolutionGraph<'a> {
|
||||||
underlying: &'a ResolutionGraph,
|
underlying: &'a ResolutionGraph,
|
||||||
show_hashes: bool,
|
show_hashes: bool,
|
||||||
include_annotations: bool,
|
include_annotations: bool,
|
||||||
|
annotation_style: AnnotationStyle,
|
||||||
) -> DisplayResolutionGraph<'a> {
|
) -> DisplayResolutionGraph<'a> {
|
||||||
Self {
|
Self {
|
||||||
resolution: underlying,
|
resolution: underlying,
|
||||||
show_hashes,
|
show_hashes,
|
||||||
include_annotations,
|
include_annotations,
|
||||||
|
annotation_style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,16 +354,13 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
||||||
// Print out the dependency graph.
|
// Print out the dependency graph.
|
||||||
for (index, node) in nodes {
|
for (index, node) in nodes {
|
||||||
// Display the node itself.
|
// Display the node itself.
|
||||||
match node {
|
let mut line = match node {
|
||||||
Node::Distribution(_, dist) => {
|
Node::Distribution(_, dist) => format!("{}", dist.verbatim()),
|
||||||
write!(f, "{}", dist.verbatim())?;
|
Node::Editable(_, editable) => format!("-e {}", editable.verbatim()),
|
||||||
}
|
};
|
||||||
Node::Editable(_, editable) => {
|
|
||||||
write!(f, "-e {}", editable.verbatim())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the distribution hashes, if any.
|
// Display the distribution hashes, if any.
|
||||||
|
let mut has_hashes = false;
|
||||||
if self.show_hashes {
|
if self.show_hashes {
|
||||||
if let Some(hashes) = self
|
if let Some(hashes) = self
|
||||||
.resolution
|
.resolution
|
||||||
|
@ -358,13 +370,17 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
||||||
{
|
{
|
||||||
for hash in hashes {
|
for hash in hashes {
|
||||||
if let Some(hash) = hash.to_string() {
|
if let Some(hash) = hash.to_string() {
|
||||||
writeln!(f, " \\")?;
|
has_hashes = true;
|
||||||
write!(f, " --hash={hash}")?;
|
line.push_str(" \\\n");
|
||||||
|
line.push_str(" --hash=");
|
||||||
|
line.push_str(&hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
|
||||||
|
// Determine the annotation comment and separator (between comment and requirement).
|
||||||
|
let mut annotation = None;
|
||||||
|
|
||||||
if self.include_annotations {
|
if self.include_annotations {
|
||||||
// Display all dependencies.
|
// Display all dependencies.
|
||||||
|
@ -376,21 +392,50 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
edges.sort_unstable_by_key(|package| package.name());
|
edges.sort_unstable_by_key(|package| package.name());
|
||||||
|
|
||||||
match edges.len() {
|
match self.annotation_style {
|
||||||
0 => {}
|
AnnotationStyle::Line => {
|
||||||
1 => {
|
if !edges.is_empty() {
|
||||||
for dependency in edges {
|
let separator = if has_hashes { "\n " } else { " " };
|
||||||
writeln!(f, "{}", format!(" # via {}", dependency.name()).green())?;
|
let deps = edges
|
||||||
|
.into_iter()
|
||||||
|
.map(|dependency| dependency.name().to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
let comment = format!("# via {deps}").green().to_string();
|
||||||
|
annotation = Some((separator, comment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
AnnotationStyle::Split => match edges.as_slice() {
|
||||||
writeln!(f, "{}", " # via".green())?;
|
[] => {}
|
||||||
for dependency in edges {
|
[edge] => {
|
||||||
writeln!(f, "{}", format!(" # {}", dependency.name()).green())?;
|
let separator = "\n";
|
||||||
|
let comment = format!(" # via {}", edge.name()).green().to_string();
|
||||||
|
annotation = Some((separator, comment));
|
||||||
}
|
}
|
||||||
}
|
edges => {
|
||||||
|
let separator = "\n";
|
||||||
|
let deps = edges
|
||||||
|
.iter()
|
||||||
|
.map(|dependency| format!(" # {}", dependency.name()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
let comment = format!(" # via\n{deps}").green().to_string();
|
||||||
|
annotation = Some((separator, comment));
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some((separator, comment)) = annotation {
|
||||||
|
// Assemble the line with the annotations and remove trailing whitespaces.
|
||||||
|
for line in format!("{line:24}{separator}{comment}").lines() {
|
||||||
|
let line = line.trim_end();
|
||||||
|
writeln!(f, "{line}")?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Write the line as is.
|
||||||
|
writeln!(f, "{line}")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -28,8 +28,8 @@ use uv_installer::{Downloader, NoBinary};
|
||||||
use uv_interpreter::{Interpreter, PythonVersion};
|
use uv_interpreter::{Interpreter, PythonVersion};
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
use uv_resolver::{
|
use uv_resolver::{
|
||||||
DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest, OptionsBuilder,
|
AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest,
|
||||||
PreReleaseMode, ResolutionMode, Resolver,
|
OptionsBuilder, PreReleaseMode, ResolutionMode, Resolver,
|
||||||
};
|
};
|
||||||
use uv_traits::{InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{InFlight, NoBuild, SetupPyStrategy};
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
|
@ -62,6 +62,7 @@ pub(crate) async fn pip_compile(
|
||||||
no_build: &NoBuild,
|
no_build: &NoBuild,
|
||||||
python_version: Option<PythonVersion>,
|
python_version: Option<PythonVersion>,
|
||||||
exclude_newer: Option<DateTime<Utc>>,
|
exclude_newer: Option<DateTime<Utc>>,
|
||||||
|
annotation_style: AnnotationStyle,
|
||||||
cache: Cache,
|
cache: Cache,
|
||||||
mut printer: Printer,
|
mut printer: Printer,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
|
@ -391,7 +392,12 @@ pub(crate) async fn pip_compile(
|
||||||
write!(
|
write!(
|
||||||
writer,
|
writer,
|
||||||
"{}",
|
"{}",
|
||||||
DisplayResolutionGraph::new(&resolution, generate_hashes, include_annotations)
|
DisplayResolutionGraph::new(
|
||||||
|
&resolution,
|
||||||
|
generate_hashes,
|
||||||
|
include_annotations,
|
||||||
|
annotation_style
|
||||||
|
)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
|
|
|
@ -36,9 +36,6 @@ pub(crate) struct PipCompileCompatArgs {
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
resolver: Option<Resolver>,
|
resolver: Option<Resolver>,
|
||||||
|
|
||||||
#[clap(long, hide = true)]
|
|
||||||
annotation_style: Option<AnnotationStyle>,
|
|
||||||
|
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
max_rounds: Option<usize>,
|
max_rounds: Option<usize>,
|
||||||
|
|
||||||
|
@ -144,21 +141,6 @@ impl CompatArgs for PipCompileCompatArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(annotation_style) = self.annotation_style {
|
|
||||||
match annotation_style {
|
|
||||||
AnnotationStyle::Split => {
|
|
||||||
warn_user!(
|
|
||||||
"pip-compile's `--annotation-style=split` has no effect (uv always emits split annotations)."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
AnnotationStyle::Line => {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"pip-compile's `--annotation-style=line` is unsupported (uv always emits split annotations)."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.max_rounds.is_some() {
|
if self.max_rounds.is_some() {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"pip-compile's `--max-rounds` is unsupported (uv always resolves until convergence)."
|
"pip-compile's `--max-rounds` is unsupported (uv always resolves until convergence)."
|
||||||
|
@ -346,12 +328,6 @@ enum Resolver {
|
||||||
Legacy,
|
Legacy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, ValueEnum)]
|
|
||||||
enum AnnotationStyle {
|
|
||||||
Line,
|
|
||||||
Split,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Arguments for `venv` compatibility.
|
/// Arguments for `venv` compatibility.
|
||||||
///
|
///
|
||||||
/// These represent a subset of the `virtualenv` interface that uv supports by default.
|
/// These represent a subset of the `virtualenv` interface that uv supports by default.
|
||||||
|
|
|
@ -19,7 +19,7 @@ use uv_client::Connectivity;
|
||||||
use uv_installer::{NoBinary, Reinstall};
|
use uv_installer::{NoBinary, Reinstall};
|
||||||
use uv_interpreter::PythonVersion;
|
use uv_interpreter::PythonVersion;
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
use uv_resolver::{DependencyMode, PreReleaseMode, ResolutionMode};
|
use uv_resolver::{AnnotationStyle, DependencyMode, PreReleaseMode, ResolutionMode};
|
||||||
use uv_traits::{NoBuild, PackageNameSpecifier, SetupPyStrategy};
|
use uv_traits::{NoBuild, PackageNameSpecifier, SetupPyStrategy};
|
||||||
|
|
||||||
use crate::commands::{extra_name_with_clap_error, ExitStatus, Upgrade};
|
use crate::commands::{extra_name_with_clap_error, ExitStatus, Upgrade};
|
||||||
|
@ -354,6 +354,10 @@ struct PipCompileArgs {
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
emit_find_links: bool,
|
emit_find_links: bool,
|
||||||
|
|
||||||
|
/// Choose the style of the annotation comments, which indicate the source of each package.
|
||||||
|
#[clap(long, default_value_t=AnnotationStyle::Split, value_enum)]
|
||||||
|
annotation_style: AnnotationStyle,
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
compat_args: compat::PipCompileCompatArgs,
|
compat_args: compat::PipCompileCompatArgs,
|
||||||
}
|
}
|
||||||
|
@ -898,6 +902,7 @@ async fn run() -> Result<ExitStatus> {
|
||||||
&no_build,
|
&no_build,
|
||||||
args.python_version,
|
args.python_version,
|
||||||
args.exclude_newer,
|
args.exclude_newer,
|
||||||
|
args.annotation_style,
|
||||||
cache,
|
cache,
|
||||||
printer,
|
printer,
|
||||||
)
|
)
|
||||||
|
|
|
@ -266,7 +266,9 @@ pub fn run_and_format<'a>(
|
||||||
// The optional leading +/- is for install logs, the optional next line is for lock files
|
// The optional leading +/- is for install logs, the optional next line is for lock files
|
||||||
let windows_only_deps = [
|
let windows_only_deps = [
|
||||||
("( [+-] )?colorama==\\d+(\\.[\\d+])+\n( # via .*\n)?"),
|
("( [+-] )?colorama==\\d+(\\.[\\d+])+\n( # via .*\n)?"),
|
||||||
|
("( [+-] )?colorama==\\d+(\\.[\\d+])+\\s+(# via .*\n)?"),
|
||||||
("( [+-] )?tzdata==\\d+(\\.[\\d+])+\n( # via .*\n)?"),
|
("( [+-] )?tzdata==\\d+(\\.[\\d+])+\n( # via .*\n)?"),
|
||||||
|
("( [+-] )?tzdata==\\d+(\\.[\\d+])+\\s+(# via .*\n)?"),
|
||||||
];
|
];
|
||||||
let mut removed_packages = 0;
|
let mut removed_packages = 0;
|
||||||
for windows_only_dep in windows_only_deps {
|
for windows_only_dep in windows_only_deps {
|
||||||
|
|
|
@ -47,6 +47,33 @@ fn compile_requirements_in() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a specific version of Django from a `requirements.in` file with a `--annotation-style=line` flag.
|
||||||
|
#[test]
|
||||||
|
fn compile_requirements_in_annotation_line() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
|
requirements_in.write_str("django==5.0b1")?;
|
||||||
|
|
||||||
|
uv_snapshot!(context
|
||||||
|
.compile()
|
||||||
|
.arg("--annotation-style=line")
|
||||||
|
.arg("requirements.in"), @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 --annotation-style=line requirements.in
|
||||||
|
asgiref==3.7.2 # via django
|
||||||
|
django==5.0b1
|
||||||
|
sqlparse==0.4.4 # via django
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve a specific version of Django from a `requirements.in` file on stdin
|
/// Resolve a specific version of Django from a `requirements.in` file on stdin
|
||||||
/// when passed a path of `-`.
|
/// when passed a path of `-`.
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -550,6 +577,38 @@ fn compile_python_312() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a specific version of Black at Python 3.12 with `--annotation-style=line`.
|
||||||
|
#[test]
|
||||||
|
fn compile_python_312_annotation_line() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let requirements_in = context.temp_dir.child("requirements.in");
|
||||||
|
requirements_in.write_str("black==23.10.1")?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.compile()
|
||||||
|
.arg("--annotation-style=line")
|
||||||
|
.arg("requirements.in")
|
||||||
|
.arg("--python-version")
|
||||||
|
.arg("3.12"), @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 --annotation-style=line requirements.in --python-version 3.12
|
||||||
|
black==23.10.1
|
||||||
|
click==8.1.7 # via black
|
||||||
|
mypy-extensions==1.0.0 # via black
|
||||||
|
packaging==23.2 # via black
|
||||||
|
pathspec==0.11.2 # via black
|
||||||
|
platformdirs==4.0.0 # via black
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 6 packages in [TIME]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve a specific version of Black at Python 3.12 without deps.
|
/// Resolve a specific version of Black at Python 3.12 without deps.
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_python_312_no_deps() -> Result<()> {
|
fn compile_python_312_no_deps() -> Result<()> {
|
||||||
|
@ -1426,6 +1485,53 @@ optional-dependencies.bar = [
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_pyproject_toml_all_extras_annotation_line() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"[build-system]
|
||||||
|
requires = ["setuptools", "wheel"]
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
dependencies = ["django==5.0b1"]
|
||||||
|
optional-dependencies.foo = [
|
||||||
|
"anyio==4.0.0",
|
||||||
|
]
|
||||||
|
optional-dependencies.bar = [
|
||||||
|
"httpcore==0.18.0",
|
||||||
|
]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.compile()
|
||||||
|
.arg("--annotation-style=line")
|
||||||
|
.arg("pyproject.toml")
|
||||||
|
.arg("--all-extras"), @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 --annotation-style=line pyproject.toml --all-extras
|
||||||
|
anyio==4.0.0 # via httpcore
|
||||||
|
asgiref==3.7.2 # via django
|
||||||
|
certifi==2023.11.17 # via httpcore
|
||||||
|
django==5.0b1
|
||||||
|
h11==0.14.0 # via httpcore
|
||||||
|
httpcore==0.18.0
|
||||||
|
idna==3.4 # via anyio
|
||||||
|
sniffio==1.3.0 # via anyio, httpcore
|
||||||
|
sqlparse==0.4.4 # via django
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 9 packages in [TIME]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve packages from all optional dependency groups in a `pyproject.toml` file.
|
/// Resolve packages from all optional dependency groups in a `pyproject.toml` file.
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_does_not_allow_both_extra_and_all_extras() -> Result<()> {
|
fn compile_does_not_allow_both_extra_and_all_extras() -> Result<()> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue