Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 1 addition & 12 deletions crates/uv-normalize/src/group_name.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::{Display, Formatter};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::LazyLock;

Expand Down Expand Up @@ -98,17 +98,6 @@ pub struct PipGroupName {
pub name: GroupName,
}

impl PipGroupName {
/// Gets the path to use, applying the default if it's missing
pub fn path(&self) -> &Path {
if let Some(path) = &self.path {
path
} else {
Path::new("pyproject.toml")
}
}
}

impl FromStr for PipGroupName {
type Err = InvalidPipGroupError;

Expand Down
36 changes: 30 additions & 6 deletions crates/uv-requirements/src/specification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use uv_distribution_types::{
UnresolvedRequirementSpecification,
};
use uv_fs::{CWD, Simplified};
use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_normalize::{ExtraName, PackageName, PipGroupName};
use uv_requirements_txt::{RequirementsTxt, RequirementsTxtRequirement};
use uv_warnings::warn_user;
use uv_workspace::pyproject::PyProjectToml;
Expand Down Expand Up @@ -215,7 +215,7 @@ impl RequirementsSpecification {
requirements: &[RequirementsSource],
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
groups: BTreeMap<PathBuf, Vec<GroupName>>,
groups: Option<&GroupsSpecification>,
client_builder: &BaseClientBuilder<'_>,
) -> Result<Self> {
let mut spec = Self::default();
Expand Down Expand Up @@ -272,7 +272,7 @@ impl RequirementsSpecification {
"Cannot specify constraints with a `pylock.toml` file"
));
}
if !groups.is_empty() {
if groups.is_some_and(|groups| !groups.groups.is_empty()) {
return Err(anyhow::anyhow!(
"Cannot specify groups with a `pylock.toml` file"
));
Expand All @@ -287,9 +287,24 @@ impl RequirementsSpecification {
}

// pip `--group` flags specify their own sources, which we need to process here
if !groups.is_empty() {
if let Some(groups) = groups {
// First, we collect all groups by their path.
let mut groups_by_path = BTreeMap::new();
for group in &groups.groups {
// If there's no path provided, expect a pyproject.toml in the project-dir
// (Which is typically the current working directory, matching pip's behaviour)
let pyproject_path = group
.path
.clone()
.unwrap_or_else(|| groups.root.join("pyproject.toml"));
groups_by_path
.entry(pyproject_path)
.or_insert_with(Vec::new)
.push(group.name.clone());
}

let mut group_specs = BTreeMap::new();
for (path, groups) in groups {
for (path, groups) in groups_by_path {
let group_spec = DependencyGroups::from_args(
false,
false,
Expand Down Expand Up @@ -426,7 +441,7 @@ impl RequirementsSpecification {
requirements: &[RequirementsSource],
client_builder: &BaseClientBuilder<'_>,
) -> Result<Self> {
Self::from_sources(requirements, &[], &[], BTreeMap::default(), client_builder).await
Self::from_sources(requirements, &[], &[], None, client_builder).await
}

/// Initialize a [`RequirementsSpecification`] from a list of [`Requirement`].
Expand Down Expand Up @@ -485,3 +500,12 @@ impl RequirementsSpecification {
self.requirements.is_empty() && self.source_trees.is_empty() && self.overrides.is_empty()
}
}

#[derive(Debug, Default, Clone)]
pub struct GroupsSpecification {
/// The path to the project root, relative to which the default `pyproject.toml` file is
/// located.
pub root: PathBuf,
/// The enabled groups.
pub groups: Vec<PipGroupName>,
}
13 changes: 7 additions & 6 deletions crates/uv/src/commands/pip/compile.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeSet;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::str::FromStr;

use anyhow::{Result, anyhow};
Expand All @@ -26,15 +26,16 @@ use uv_distribution_types::{
use uv_fs::{CWD, Simplified};
use uv_git::ResolvedRepositoryReference;
use uv_install_wheel::LinkMode;
use uv_normalize::{GroupName, PackageName};
use uv_normalize::PackageName;
use uv_pypi_types::{Conflicts, SupportedEnvironments};
use uv_python::{
EnvironmentPreference, PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest,
PythonVersion, VersionRequest,
};
use uv_requirements::upgrade::{LockedRequirements, read_pylock_toml_requirements};
use uv_requirements::{
RequirementsSource, RequirementsSpecification, is_pylock_toml, upgrade::read_requirements_txt,
GroupsSpecification, RequirementsSource, RequirementsSpecification, is_pylock_toml,
upgrade::read_requirements_txt,
};
use uv_resolver::{
AnnotationStyle, DependencyMode, DisplayResolutionGraph, ExcludeNewer, FlatIndex, ForkStrategy,
Expand Down Expand Up @@ -64,7 +65,7 @@ pub(crate) async fn pip_compile(
build_constraints_from_workspace: Vec<Requirement>,
environments: SupportedEnvironments,
extras: ExtrasSpecification,
groups: BTreeMap<PathBuf, Vec<GroupName>>,
groups: GroupsSpecification,
output_file: Option<&Path>,
format: Option<ExportFormat>,
resolution_mode: ResolutionMode,
Expand Down Expand Up @@ -207,7 +208,7 @@ pub(crate) async fn pip_compile(
requirements,
constraints,
overrides,
groups,
Some(&groups),
&client_builder,
)
.await?;
Expand Down
10 changes: 4 additions & 6 deletions crates/uv/src/commands/pip/install.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeSet;
use std::fmt::Write;
use std::path::PathBuf;

use anyhow::Context;
use itertools::Itertools;
Expand All @@ -23,14 +22,13 @@ use uv_distribution_types::{
use uv_fs::Simplified;
use uv_install_wheel::LinkMode;
use uv_installer::{SatisfiesResult, SitePackages};
use uv_normalize::GroupName;
use uv_pep508::PackageName;
use uv_pypi_types::Conflicts;
use uv_python::{
EnvironmentPreference, Prefix, PythonEnvironment, PythonInstallation, PythonPreference,
PythonRequest, PythonVersion, Target,
};
use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_requirements::{GroupsSpecification, RequirementsSource, RequirementsSpecification};
use uv_resolver::{
DependencyMode, ExcludeNewer, FlatIndex, OptionsBuilder, PrereleaseMode, PylockToml,
PythonRequirement, ResolutionMode, ResolverEnvironment,
Expand Down Expand Up @@ -59,7 +57,7 @@ pub(crate) async fn pip_install(
overrides_from_workspace: Vec<Requirement>,
build_constraints_from_workspace: Vec<Requirement>,
extras: &ExtrasSpecification,
groups: BTreeMap<PathBuf, Vec<GroupName>>,
groups: &GroupsSpecification,
resolution_mode: ResolutionMode,
prerelease_mode: PrereleaseMode,
dependency_mode: DependencyMode,
Expand Down Expand Up @@ -128,7 +126,7 @@ pub(crate) async fn pip_install(
constraints,
overrides,
extras,
groups,
Some(groups),
&client_builder,
)
.await?;
Expand Down
20 changes: 8 additions & 12 deletions crates/uv/src/commands/pip/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ use uv_distribution_types::{
use uv_fs::Simplified;
use uv_install_wheel::LinkMode;
use uv_installer::{Plan, Planner, Preparer, SitePackages};
use uv_normalize::{GroupName, PackageName};
use uv_normalize::PackageName;
use uv_pep508::{MarkerEnvironment, RequirementOrigin};
use uv_platform_tags::Tags;
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
use uv_python::{PythonEnvironment, PythonInstallation};
use uv_requirements::{
LookaheadResolver, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification,
SourceTreeResolver,
GroupsSpecification, LookaheadResolver, NamedRequirementsResolver, RequirementsSource,
RequirementsSpecification, SourceTreeResolver,
};
use uv_resolver::{
DependencyMode, Exclusions, FlatIndex, InMemoryIndex, Manifest, Options, Preference,
Expand All @@ -55,7 +55,7 @@ pub(crate) async fn read_requirements(
constraints: &[RequirementsSource],
overrides: &[RequirementsSource],
extras: &ExtrasSpecification,
groups: BTreeMap<PathBuf, Vec<GroupName>>,
groups: Option<&GroupsSpecification>,
client_builder: &BaseClientBuilder<'_>,
) -> Result<RequirementsSpecification, Error> {
// If the user requests `extras` but does not provide a valid source (e.g., a `pyproject.toml`),
Expand Down Expand Up @@ -91,15 +91,11 @@ pub(crate) async fn read_constraints(
constraints: &[RequirementsSource],
client_builder: &BaseClientBuilder<'_>,
) -> Result<Vec<NameRequirementSpecification>, Error> {
Ok(RequirementsSpecification::from_sources(
&[],
constraints,
&[],
BTreeMap::default(),
client_builder,
Ok(
RequirementsSpecification::from_sources(&[], constraints, &[], None, client_builder)
.await?
.constraints,
)
.await?
.constraints)
}

/// Resolve a set of requirements, similar to running `pip compile`.
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/src/commands/pip/sync.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeSet;
use std::fmt::Write;

use anyhow::{Context, Result};
Expand Down Expand Up @@ -92,7 +92,7 @@ pub(crate) async fn pip_sync(
// Initialize a few defaults.
let overrides = &[];
let extras = ExtrasSpecification::default();
let groups = BTreeMap::default();
let groups = None;
let upgrade = Upgrade::default();
let resolution_mode = ResolutionMode::default();
let prerelease_mode = PrereleaseMode::default();
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ pub(crate) async fn add(
&requirements,
&constraints,
&[],
BTreeMap::default(),
None,
&client_builder,
)
.await?;
Expand Down
3 changes: 1 addition & 2 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::BTreeMap;
use std::fmt::Write;
use std::str::FromStr;

Expand Down Expand Up @@ -261,7 +260,7 @@ pub(crate) async fn install(
with,
constraints,
overrides,
BTreeMap::default(),
None,
&client_builder,
)
.await?;
Expand Down
3 changes: 1 addition & 2 deletions crates/uv/src/commands/tool/run.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::BTreeMap;
use std::fmt::Display;
use std::fmt::Write;
use std::path::Path;
Expand Down Expand Up @@ -871,7 +870,7 @@ async fn get_or_create_environment(
with,
constraints,
overrides,
BTreeMap::default(),
None,
&client_builder,
)
.await?;
Expand Down
41 changes: 10 additions & 31 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::ffi::OsString;
use std::fmt::Write;
use std::io::stdout;
Expand Down Expand Up @@ -36,7 +35,7 @@ use uv_pep440::release_specifiers_to_ranges;
use uv_pep508::VersionOrUrl;
use uv_pypi_types::{ParsedDirectoryUrl, ParsedUrl};
use uv_python::PythonRequest;
use uv_requirements::RequirementsSource;
use uv_requirements::{GroupsSpecification, RequirementsSource};
use uv_requirements_txt::RequirementsTxtRequirement;
use uv_scripts::{Pep723Error, Pep723Item, Pep723ItemRef, Pep723Metadata, Pep723Script};
use uv_settings::{Combine, EnvironmentOptions, FilesystemOptions, Options};
Expand Down Expand Up @@ -472,20 +471,10 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.into_iter()
.map(RequirementsSource::from_constraints_txt)
.collect::<Result<Vec<_>, _>>()?;

let mut groups = BTreeMap::new();
for group in args.settings.groups {
// If there's no path provided, expect a pyproject.toml in the project-dir
// (Which is typically the current working directory, matching pip's behaviour)
let pyproject_path = group
.path
.clone()
.unwrap_or_else(|| project_dir.join("pyproject.toml"));
groups
.entry(pyproject_path)
.or_insert_with(Vec::new)
.push(group.name.clone());
}
let groups = GroupsSpecification {
root: project_dir.to_path_buf(),
groups: args.settings.groups,
};

commands::pip_compile(
&requirements,
Expand Down Expand Up @@ -657,20 +646,10 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.into_iter()
.map(RequirementsSource::from_overrides_txt)
.collect::<Result<Vec<_>, _>>()?;

let mut groups = BTreeMap::new();
for group in args.settings.groups {
// If there's no path provided, expect a pyproject.toml in the project-dir
// (Which is typically the current working directory, matching pip's behaviour)
let pyproject_path = group
.path
.clone()
.unwrap_or_else(|| project_dir.join("pyproject.toml"));
groups
.entry(pyproject_path)
.or_insert_with(Vec::new)
.push(group.name.clone());
}
let groups = GroupsSpecification {
root: project_dir.to_path_buf(),
groups: args.settings.groups,
};

// Special-case: any source trees specified on the command-line are automatically
// reinstalled. This matches user expectations: `uv pip install .` should always
Expand Down Expand Up @@ -730,7 +709,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.overrides_from_workspace,
args.build_constraints_from_workspace,
&args.settings.extras,
groups,
&groups,
args.settings.resolution,
args.settings.prerelease,
args.settings.dependency_mode,
Expand Down
Loading