mirror of https://github.com/astral-sh/uv
Support `extras` and `dependency_groups` markers on `uv pip install` and `uv pip sync` (#14755)
## Summary We don't yet support writing these, but we can at least read them (which, e.g., allows you to install PDM-exported `pylock.toml` files with uv, since PDM _always_ writes a default group). Closes #14740.
This commit is contained in:
parent
ab48dfd0cb
commit
b81cce9152
|
|
@ -1202,6 +1202,14 @@ pub struct PipCompileArgs {
|
|||
#[arg(long, overrides_with("all_extras"), hide = true)]
|
||||
pub no_all_extras: bool,
|
||||
|
||||
/// Install the specified dependency group from a `pyproject.toml`.
|
||||
///
|
||||
/// If no path is provided, the `pyproject.toml` in the working directory is used.
|
||||
///
|
||||
/// May be provided multiple times.
|
||||
#[arg(long, group = "sources")]
|
||||
pub group: Vec<PipGroupName>,
|
||||
|
||||
#[command(flatten)]
|
||||
pub resolver: ResolverArgs,
|
||||
|
||||
|
|
@ -1216,14 +1224,6 @@ pub struct PipCompileArgs {
|
|||
#[arg(long, overrides_with("no_deps"), hide = true)]
|
||||
pub deps: bool,
|
||||
|
||||
/// Install the specified dependency group from a `pyproject.toml`.
|
||||
///
|
||||
/// If no path is provided, the `pyproject.toml` in the working directory is used.
|
||||
///
|
||||
/// May be provided multiple times.
|
||||
#[arg(long, group = "sources")]
|
||||
pub group: Vec<PipGroupName>,
|
||||
|
||||
/// Write the compiled requirements to the given `requirements.txt` or `pylock.toml` file.
|
||||
///
|
||||
/// If the file already exists, the existing versions will be preferred when resolving
|
||||
|
|
@ -1518,6 +1518,30 @@ pub struct PipSyncArgs {
|
|||
#[arg(long, short, alias = "build-constraint", env = EnvVars::UV_BUILD_CONSTRAINT, value_delimiter = ' ', value_parser = parse_maybe_file_path)]
|
||||
pub build_constraints: Vec<Maybe<PathBuf>>,
|
||||
|
||||
/// Include optional dependencies from the specified extra name; may be provided more than once.
|
||||
///
|
||||
/// Only applies to `pylock.toml`, `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
|
||||
#[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)]
|
||||
pub extra: Option<Vec<ExtraName>>,
|
||||
|
||||
/// Include all optional dependencies.
|
||||
///
|
||||
/// Only applies to `pylock.toml`, `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
|
||||
#[arg(long, conflicts_with = "extra", overrides_with = "no_all_extras")]
|
||||
pub all_extras: bool,
|
||||
|
||||
#[arg(long, overrides_with("all_extras"), hide = true)]
|
||||
pub no_all_extras: bool,
|
||||
|
||||
/// Install the specified dependency group from a `pylock.toml` or `pyproject.toml`.
|
||||
///
|
||||
/// If no path is provided, the `pylock.toml` or `pyproject.toml` in the working directory is
|
||||
/// used.
|
||||
///
|
||||
/// May be provided multiple times.
|
||||
#[arg(long, group = "sources")]
|
||||
pub group: Vec<PipGroupName>,
|
||||
|
||||
#[command(flatten)]
|
||||
pub installer: InstallerArgs,
|
||||
|
||||
|
|
@ -1798,19 +1822,28 @@ pub struct PipInstallArgs {
|
|||
|
||||
/// Include optional dependencies from the specified extra name; may be provided more than once.
|
||||
///
|
||||
/// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
|
||||
/// Only applies to `pylock.toml`, `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
|
||||
#[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)]
|
||||
pub extra: Option<Vec<ExtraName>>,
|
||||
|
||||
/// Include all optional dependencies.
|
||||
///
|
||||
/// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
|
||||
/// Only applies to `pylock.toml`, `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
|
||||
#[arg(long, conflicts_with = "extra", overrides_with = "no_all_extras")]
|
||||
pub all_extras: bool,
|
||||
|
||||
#[arg(long, overrides_with("all_extras"), hide = true)]
|
||||
pub no_all_extras: bool,
|
||||
|
||||
/// Install the specified dependency group from a `pylock.toml` or `pyproject.toml`.
|
||||
///
|
||||
/// If no path is provided, the `pylock.toml` or `pyproject.toml` in the working directory is
|
||||
/// used.
|
||||
///
|
||||
/// May be provided multiple times.
|
||||
#[arg(long, group = "sources")]
|
||||
pub group: Vec<PipGroupName>,
|
||||
|
||||
#[command(flatten)]
|
||||
pub installer: ResolverInstallerArgs,
|
||||
|
||||
|
|
@ -1825,14 +1858,6 @@ pub struct PipInstallArgs {
|
|||
#[arg(long, overrides_with("no_deps"), hide = true)]
|
||||
pub deps: bool,
|
||||
|
||||
/// Install the specified dependency group from a `pyproject.toml`.
|
||||
///
|
||||
/// If no path is provided, the `pyproject.toml` in the working directory is used.
|
||||
///
|
||||
/// May be provided multiple times.
|
||||
#[arg(long, group = "sources")]
|
||||
pub group: Vec<PipGroupName>,
|
||||
|
||||
/// Require a matching hash for each requirement.
|
||||
///
|
||||
/// By default, uv will verify any available hashes in the requirements file, but will not
|
||||
|
|
|
|||
|
|
@ -186,6 +186,18 @@ impl DependencyGroupsInner {
|
|||
self.include.names().chain(&self.exclude)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all groups that are included in the specification,
|
||||
/// assuming `all_names` is an iterator over all groups.
|
||||
pub fn group_names<'a, Names>(
|
||||
&'a self,
|
||||
all_names: Names,
|
||||
) -> impl Iterator<Item = &'a GroupName> + 'a
|
||||
where
|
||||
Names: Iterator<Item = &'a GroupName> + 'a,
|
||||
{
|
||||
all_names.filter(move |name| self.contains(name))
|
||||
}
|
||||
|
||||
/// Iterate over all groups the user explicitly asked for on the CLI
|
||||
pub fn explicit_names(&self) -> impl Iterator<Item = &GroupName> {
|
||||
let DependencyGroupsHistory {
|
||||
|
|
|
|||
|
|
@ -754,6 +754,51 @@ impl Display for MarkerExpression {
|
|||
}
|
||||
}
|
||||
|
||||
/// The extra and dependency group names to use when evaluating a marker tree.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum ExtrasEnvironment<'a> {
|
||||
/// E.g., `extra == '...'`
|
||||
Extras(&'a [ExtraName]),
|
||||
/// E.g., `'...' in extras` or `'...' in dependency_groups`
|
||||
Pep751(&'a [ExtraName], &'a [GroupName]),
|
||||
}
|
||||
|
||||
impl<'a> ExtrasEnvironment<'a> {
|
||||
/// Creates a new [`ExtrasEnvironment`] for the given `extra` names.
|
||||
fn from_extras(extras: &'a [ExtraName]) -> Self {
|
||||
Self::Extras(extras)
|
||||
}
|
||||
|
||||
/// Creates a new [`ExtrasEnvironment`] for the given PEP 751 `extras` and `dependency_groups`.
|
||||
fn from_pep751(extras: &'a [ExtraName], dependency_groups: &'a [GroupName]) -> Self {
|
||||
Self::Pep751(extras, dependency_groups)
|
||||
}
|
||||
|
||||
/// Returns the `extra` names in this environment.
|
||||
fn extra(&self) -> &[ExtraName] {
|
||||
match self {
|
||||
ExtrasEnvironment::Extras(extra) => extra,
|
||||
ExtrasEnvironment::Pep751(..) => &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `extras` names in this environment, as in a PEP 751 lockfile.
|
||||
fn extras(&self) -> &[ExtraName] {
|
||||
match self {
|
||||
ExtrasEnvironment::Extras(..) => &[],
|
||||
ExtrasEnvironment::Pep751(extras, ..) => extras,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `dependency_group` group names in this environment, as in a PEP 751 lockfile.
|
||||
fn dependency_groups(&self) -> &[GroupName] {
|
||||
match self {
|
||||
ExtrasEnvironment::Extras(..) => &[],
|
||||
ExtrasEnvironment::Pep751(.., groups) => groups,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents one or more nested marker expressions with and/or/parentheses.
|
||||
///
|
||||
/// Marker trees are canonical, meaning any two functionally equivalent markers
|
||||
|
|
@ -1001,7 +1046,27 @@ impl MarkerTree {
|
|||
|
||||
/// Does this marker apply in the given environment?
|
||||
pub fn evaluate(self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
self.evaluate_reporter_impl(env, extras, &mut TracingReporter)
|
||||
self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_extras(extras),
|
||||
&mut TracingReporter,
|
||||
)
|
||||
}
|
||||
|
||||
/// Evaluate a marker in the context of a PEP 751 lockfile, which exposes several additional
|
||||
/// markers (`extras` and `dependency_groups`) that are not available in any other context,
|
||||
/// per the spec.
|
||||
pub fn evaluate_pep751(
|
||||
self,
|
||||
env: &MarkerEnvironment,
|
||||
extras: &[ExtraName],
|
||||
groups: &[GroupName],
|
||||
) -> bool {
|
||||
self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_pep751(extras, groups),
|
||||
&mut TracingReporter,
|
||||
)
|
||||
}
|
||||
|
||||
/// Evaluates this marker tree against an optional environment and a
|
||||
|
|
@ -1018,7 +1083,11 @@ impl MarkerTree {
|
|||
) -> bool {
|
||||
match env {
|
||||
None => self.evaluate_extras(extras),
|
||||
Some(env) => self.evaluate_reporter_impl(env, extras, &mut TracingReporter),
|
||||
Some(env) => self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_extras(extras),
|
||||
&mut TracingReporter,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1030,13 +1099,13 @@ impl MarkerTree {
|
|||
extras: &[ExtraName],
|
||||
reporter: &mut impl Reporter,
|
||||
) -> bool {
|
||||
self.evaluate_reporter_impl(env, extras, reporter)
|
||||
self.evaluate_reporter_impl(env, ExtrasEnvironment::from_extras(extras), reporter)
|
||||
}
|
||||
|
||||
fn evaluate_reporter_impl(
|
||||
self,
|
||||
env: &MarkerEnvironment,
|
||||
extras: &[ExtraName],
|
||||
extras: ExtrasEnvironment,
|
||||
reporter: &mut impl Reporter,
|
||||
) -> bool {
|
||||
match self.kind() {
|
||||
|
|
@ -1088,12 +1157,18 @@ impl MarkerTree {
|
|||
}
|
||||
MarkerTreeKind::Extra(marker) => {
|
||||
return marker
|
||||
.edge(extras.contains(marker.name().extra()))
|
||||
.edge(extras.extra().contains(marker.name().extra()))
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
}
|
||||
// TODO(charlie): Add support for evaluating container extras in PEP 751 lockfiles.
|
||||
MarkerTreeKind::Extras(..) | MarkerTreeKind::DependencyGroups(..) => {
|
||||
return false;
|
||||
MarkerTreeKind::Extras(marker) => {
|
||||
return marker
|
||||
.edge(extras.extras().contains(marker.name().extra()))
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
}
|
||||
MarkerTreeKind::DependencyGroups(marker) => {
|
||||
return marker
|
||||
.edge(extras.dependency_groups().contains(marker.name().group()))
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -273,13 +273,13 @@ impl RequirementsSource {
|
|||
pub fn allows_extras(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::PyprojectToml(_) | Self::SetupPy(_) | Self::SetupCfg(_)
|
||||
Self::PylockToml(_) | Self::PyprojectToml(_) | Self::SetupPy(_) | Self::SetupCfg(_)
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if the source allows groups to be specified.
|
||||
pub fn allows_groups(&self) -> bool {
|
||||
matches!(self, Self::PyprojectToml(_))
|
||||
matches!(self, Self::PylockToml(_) | Self::PyprojectToml(_))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -250,10 +250,13 @@ impl RequirementsSpecification {
|
|||
|
||||
// If we have a `pylock.toml`, don't allow additional requirements, constraints, or
|
||||
// overrides.
|
||||
if requirements
|
||||
.iter()
|
||||
.any(|source| matches!(source, RequirementsSource::PylockToml(..)))
|
||||
{
|
||||
if let Some(pylock_toml) = requirements.iter().find_map(|source| {
|
||||
if let RequirementsSource::PylockToml(path) = source {
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
if requirements
|
||||
.iter()
|
||||
.any(|source| !matches!(source, RequirementsSource::PylockToml(..)))
|
||||
|
|
@ -272,22 +275,38 @@ impl RequirementsSpecification {
|
|||
"Cannot specify constraints with a `pylock.toml` file"
|
||||
));
|
||||
}
|
||||
if groups.is_some_and(|groups| !groups.groups.is_empty()) {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Cannot specify groups with a `pylock.toml` file"
|
||||
));
|
||||
|
||||
// If we have a `pylock.toml`, disallow specifying paths for groups; instead, require
|
||||
// that all groups refer to the `pylock.toml` file.
|
||||
if let Some(groups) = groups {
|
||||
let mut names = Vec::new();
|
||||
for group in &groups.groups {
|
||||
if group.path.is_some() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Cannot specify paths for groups with a `pylock.toml` file; all groups must refer to the `pylock.toml` file"
|
||||
));
|
||||
}
|
||||
names.push(group.name.clone());
|
||||
}
|
||||
|
||||
if !names.is_empty() {
|
||||
spec.groups.insert(
|
||||
pylock_toml.clone(),
|
||||
DependencyGroups::from_args(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
false,
|
||||
names,
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve sources into specifications so we know their `source_tree`.
|
||||
let mut requirement_sources = Vec::new();
|
||||
for source in requirements {
|
||||
let source = Self::from_source(source, client_builder).await?;
|
||||
requirement_sources.push(source);
|
||||
}
|
||||
|
||||
// pip `--group` flags specify their own sources, which we need to process here
|
||||
if let Some(groups) = groups {
|
||||
} else if let Some(groups) = groups {
|
||||
// pip `--group` flags specify their own sources, which we need to process here.
|
||||
// First, we collect all groups by their path.
|
||||
let mut groups_by_path = BTreeMap::new();
|
||||
for group in &groups.groups {
|
||||
|
|
@ -320,6 +339,13 @@ impl RequirementsSpecification {
|
|||
spec.groups = group_specs;
|
||||
}
|
||||
|
||||
// Resolve sources into specifications so we know their `source_tree`.
|
||||
let mut requirement_sources = Vec::new();
|
||||
for source in requirements {
|
||||
let source = Self::from_source(source, client_builder).await?;
|
||||
requirement_sources.push(source);
|
||||
}
|
||||
|
||||
// Read all requirements, and keep track of all requirements _and_ constraints.
|
||||
// A `requirements.txt` can contain a `-c constraints.txt` directive within it, so reading
|
||||
// a requirements file can also add constraints.
|
||||
|
|
|
|||
|
|
@ -188,11 +188,11 @@ pub struct PylockToml {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
requires_python: Option<RequiresPython>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
extras: Vec<ExtraName>,
|
||||
pub extras: Vec<ExtraName>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
dependency_groups: Vec<GroupName>,
|
||||
pub dependency_groups: Vec<GroupName>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
default_groups: Vec<GroupName>,
|
||||
pub default_groups: Vec<GroupName>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
pub packages: Vec<PylockTomlPackage>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
|
|
@ -966,9 +966,12 @@ impl<'lock> PylockToml {
|
|||
self,
|
||||
install_path: &Path,
|
||||
markers: &MarkerEnvironment,
|
||||
extras: &[ExtraName],
|
||||
groups: &[GroupName],
|
||||
tags: &Tags,
|
||||
build_options: &BuildOptions,
|
||||
) -> Result<Resolution, PylockTomlError> {
|
||||
// Convert the extras and dependency groups specifications to a concrete environment.
|
||||
let mut graph =
|
||||
petgraph::graph::DiGraph::with_capacity(self.packages.len(), self.packages.len());
|
||||
|
||||
|
|
@ -977,7 +980,7 @@ impl<'lock> PylockToml {
|
|||
|
||||
for package in self.packages {
|
||||
// Omit packages that aren't relevant to the current environment.
|
||||
if !package.marker.evaluate(markers, &[]) {
|
||||
if !package.marker.evaluate_pep751(markers, extras, groups) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use uv_distribution_types::{
|
|||
use uv_fs::Simplified;
|
||||
use uv_install_wheel::LinkMode;
|
||||
use uv_installer::{SatisfiesResult, SitePackages};
|
||||
use uv_normalize::{DefaultExtras, DefaultGroups};
|
||||
use uv_pep508::PackageName;
|
||||
use uv_pypi_types::Conflicts;
|
||||
use uv_python::{
|
||||
|
|
@ -439,11 +440,35 @@ pub(crate) async fn pip_install(
|
|||
let install_path = std::path::absolute(&pylock)?;
|
||||
let install_path = install_path.parent().unwrap();
|
||||
let content = fs_err::tokio::read_to_string(&pylock).await?;
|
||||
let lock = toml::from_str::<PylockToml>(&content)
|
||||
.with_context(|| format!("Not a valid pylock.toml file: {}", pylock.user_display()))?;
|
||||
let lock = toml::from_str::<PylockToml>(&content).with_context(|| {
|
||||
format!("Not a valid `pylock.toml` file: {}", pylock.user_display())
|
||||
})?;
|
||||
|
||||
let resolution =
|
||||
lock.to_resolution(install_path, marker_env.markers(), &tags, &build_options)?;
|
||||
// Convert the extras and groups specifications into a concrete form.
|
||||
let extras = extras.with_defaults(DefaultExtras::default());
|
||||
let extras = extras
|
||||
.extra_names(lock.extras.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let groups = groups
|
||||
.get(&pylock)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.with_defaults(DefaultGroups::List(lock.default_groups.clone()));
|
||||
let groups = groups
|
||||
.group_names(lock.dependency_groups.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let resolution = lock.to_resolution(
|
||||
install_path,
|
||||
marker_env.markers(),
|
||||
&extras,
|
||||
&groups,
|
||||
&tags,
|
||||
&build_options,
|
||||
)?;
|
||||
let hasher = HashStrategy::from_resolution(&resolution, HashCheckingMode::Verify)?;
|
||||
|
||||
(resolution, hasher)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ pub(crate) async fn read_requirements(
|
|||
"Use `package[extra]` syntax instead."
|
||||
};
|
||||
return Err(anyhow!(
|
||||
"Requesting extras requires a `pyproject.toml`, `setup.cfg`, or `setup.py` file. {hint}"
|
||||
"Requesting extras requires a `pylock.toml`, `pyproject.toml`, `setup.cfg`, or `setup.py` file. {hint}"
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@ use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, Origin, R
|
|||
use uv_fs::Simplified;
|
||||
use uv_install_wheel::LinkMode;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_normalize::{DefaultExtras, DefaultGroups};
|
||||
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,
|
||||
|
|
@ -48,6 +49,8 @@ pub(crate) async fn pip_sync(
|
|||
requirements: &[RequirementsSource],
|
||||
constraints: &[RequirementsSource],
|
||||
build_constraints: &[RequirementsSource],
|
||||
extras: &ExtrasSpecification,
|
||||
groups: &GroupsSpecification,
|
||||
reinstall: Reinstall,
|
||||
link_mode: LinkMode,
|
||||
compile: bool,
|
||||
|
|
@ -91,8 +94,6 @@ pub(crate) async fn pip_sync(
|
|||
|
||||
// Initialize a few defaults.
|
||||
let overrides = &[];
|
||||
let extras = ExtrasSpecification::default();
|
||||
let groups = None;
|
||||
let upgrade = Upgrade::default();
|
||||
let resolution_mode = ResolutionMode::default();
|
||||
let prerelease_mode = PrereleaseMode::default();
|
||||
|
|
@ -118,8 +119,8 @@ pub(crate) async fn pip_sync(
|
|||
requirements,
|
||||
constraints,
|
||||
overrides,
|
||||
&extras,
|
||||
groups,
|
||||
extras,
|
||||
Some(groups),
|
||||
&client_builder,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -377,11 +378,35 @@ pub(crate) async fn pip_sync(
|
|||
let install_path = std::path::absolute(&pylock)?;
|
||||
let install_path = install_path.parent().unwrap();
|
||||
let content = fs_err::tokio::read_to_string(&pylock).await?;
|
||||
let lock = toml::from_str::<PylockToml>(&content)
|
||||
.with_context(|| format!("Not a valid pylock.toml file: {}", pylock.user_display()))?;
|
||||
let lock = toml::from_str::<PylockToml>(&content).with_context(|| {
|
||||
format!("Not a valid `pylock.toml` file: {}", pylock.user_display())
|
||||
})?;
|
||||
|
||||
let resolution =
|
||||
lock.to_resolution(install_path, marker_env.markers(), &tags, &build_options)?;
|
||||
// Convert the extras and groups specifications into a concrete form.
|
||||
let extras = extras.with_defaults(DefaultExtras::default());
|
||||
let extras = extras
|
||||
.extra_names(lock.extras.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let groups = groups
|
||||
.get(&pylock)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.with_defaults(DefaultGroups::List(lock.default_groups.clone()));
|
||||
let groups = groups
|
||||
.group_names(lock.dependency_groups.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let resolution = lock.to_resolution(
|
||||
install_path,
|
||||
marker_env.markers(),
|
||||
&extras,
|
||||
&groups,
|
||||
&tags,
|
||||
&build_options,
|
||||
)?;
|
||||
let hasher = HashStrategy::from_resolution(&resolution, HashCheckingMode::Verify)?;
|
||||
|
||||
(resolution, hasher)
|
||||
|
|
@ -406,7 +431,7 @@ pub(crate) async fn pip_sync(
|
|||
source_trees,
|
||||
project,
|
||||
BTreeSet::default(),
|
||||
&extras,
|
||||
extras,
|
||||
&groups,
|
||||
preferences,
|
||||
site_packages.clone(),
|
||||
|
|
|
|||
|
|
@ -566,11 +566,17 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
.into_iter()
|
||||
.map(RequirementsSource::from_constraints_txt)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let groups = GroupsSpecification {
|
||||
root: project_dir.to_path_buf(),
|
||||
groups: args.settings.groups,
|
||||
};
|
||||
|
||||
commands::pip_sync(
|
||||
&requirements,
|
||||
&constraints,
|
||||
&build_constraints,
|
||||
&args.settings.extras,
|
||||
&groups,
|
||||
args.settings.reinstall,
|
||||
args.settings.link_mode,
|
||||
args.settings.compile_bytecode,
|
||||
|
|
|
|||
|
|
@ -2058,6 +2058,10 @@ impl PipSyncSettings {
|
|||
src_file,
|
||||
constraints,
|
||||
build_constraints,
|
||||
extra,
|
||||
all_extras,
|
||||
no_all_extras,
|
||||
group,
|
||||
installer,
|
||||
refresh,
|
||||
require_hashes,
|
||||
|
|
@ -2122,6 +2126,9 @@ impl PipSyncSettings {
|
|||
python_version,
|
||||
python_platform,
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
extra,
|
||||
all_extras: flag(all_extras, no_all_extras, "all-extras"),
|
||||
group: Some(group),
|
||||
torch_backend,
|
||||
..PipOptions::from(installer)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1298,27 +1298,27 @@ fn install_extras() -> Result<()> {
|
|||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--all-extras")
|
||||
.arg("-e")
|
||||
.arg(context.workspace_root.join("scripts/packages/poetry_editable")), @r###"
|
||||
.arg(context.workspace_root.join("scripts/packages/poetry_editable")), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Requesting extras requires a `pyproject.toml`, `setup.cfg`, or `setup.py` file. Use `<dir>[extra]` syntax or `-r <file>` instead.
|
||||
"###
|
||||
error: Requesting extras requires a `pylock.toml`, `pyproject.toml`, `setup.cfg`, or `setup.py` file. Use `<dir>[extra]` syntax or `-r <file>` instead.
|
||||
"
|
||||
);
|
||||
|
||||
// Request extras for a source tree
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--all-extras")
|
||||
.arg(context.workspace_root.join("scripts/packages/poetry_editable")), @r###"
|
||||
.arg(context.workspace_root.join("scripts/packages/poetry_editable")), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Requesting extras requires a `pyproject.toml`, `setup.cfg`, or `setup.py` file. Use `package[extra]` syntax instead.
|
||||
"###
|
||||
error: Requesting extras requires a `pylock.toml`, `pyproject.toml`, `setup.cfg`, or `setup.py` file. Use `package[extra]` syntax instead.
|
||||
"
|
||||
);
|
||||
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
|
|
@ -1327,14 +1327,14 @@ fn install_extras() -> Result<()> {
|
|||
// Request extras for a requirements file
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--all-extras")
|
||||
.arg("-r").arg("requirements.txt"), @r###"
|
||||
.arg("-r").arg("requirements.txt"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Requesting extras requires a `pyproject.toml`, `setup.cfg`, or `setup.py` file. Use `package[extra]` syntax instead.
|
||||
"###
|
||||
error: Requesting extras requires a `pylock.toml`, `pyproject.toml`, `setup.cfg`, or `setup.py` file. Use `package[extra]` syntax instead.
|
||||
"
|
||||
);
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -11392,6 +11392,205 @@ fn pep_751_multiple_sources() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_groups() -> Result<()> {
|
||||
let context = TestContext::new("3.13");
|
||||
|
||||
let pylock_toml = context.temp_dir.child("pylock.toml");
|
||||
pylock_toml.write_str(
|
||||
r#"
|
||||
lock-version = "1.0"
|
||||
requires-python = "==3.13.*"
|
||||
environments = [
|
||||
"python_version == \"3.13\"",
|
||||
]
|
||||
extras = ["async", "dev"]
|
||||
dependency-groups = ["default", "test"]
|
||||
default-groups = ["default"]
|
||||
created-by = "pdm"
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "4.9.0"
|
||||
requires-python = ">=3.9"
|
||||
sdist = {name = "anyio-4.9.0.tar.gz", url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hashes = {sha256 = "673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}}
|
||||
wheels = [
|
||||
{name = "anyio-4.9.0-py3-none-any.whl",url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl",hashes = {sha256 = "9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}},
|
||||
]
|
||||
marker = "\"async\" in extras"
|
||||
|
||||
[packages.tool.pdm]
|
||||
dependencies = [
|
||||
"exceptiongroup>=1.0.2; python_version < \"3.11\"",
|
||||
"idna>=2.8",
|
||||
"sniffio>=1.1",
|
||||
"typing-extensions>=4.5; python_version < \"3.13\"",
|
||||
]
|
||||
|
||||
[[packages]]
|
||||
name = "blinker"
|
||||
version = "1.9.0"
|
||||
requires-python = ">=3.9"
|
||||
sdist = {name = "blinker-1.9.0.tar.gz", url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hashes = {sha256 = "b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}}
|
||||
wheels = [
|
||||
{name = "blinker-1.9.0-py3-none-any.whl",url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl",hashes = {sha256 = "ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}},
|
||||
]
|
||||
marker = "\"dev\" in extras"
|
||||
|
||||
[packages.tool.pdm]
|
||||
dependencies = []
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
requires-python = ">=3.6"
|
||||
sdist = {name = "idna-3.10.tar.gz", url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hashes = {sha256 = "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}}
|
||||
wheels = [
|
||||
{name = "idna-3.10-py3-none-any.whl",url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl",hashes = {sha256 = "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}},
|
||||
]
|
||||
marker = "\"async\" in extras"
|
||||
|
||||
[packages.tool.pdm]
|
||||
dependencies = []
|
||||
|
||||
[[packages]]
|
||||
name = "iniconfig"
|
||||
version = "2.1.0"
|
||||
requires-python = ">=3.8"
|
||||
sdist = {name = "iniconfig-2.1.0.tar.gz", url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hashes = {sha256 = "3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}}
|
||||
wheels = [
|
||||
{name = "iniconfig-2.1.0-py3-none-any.whl",url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl",hashes = {sha256 = "9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}},
|
||||
]
|
||||
marker = "\"default\" in dependency_groups"
|
||||
|
||||
[packages.tool.pdm]
|
||||
dependencies = []
|
||||
|
||||
[[packages]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
requires-python = ">=3.8"
|
||||
sdist = {name = "pygments-2.19.2.tar.gz", url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hashes = {sha256 = "636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}}
|
||||
wheels = [
|
||||
{name = "pygments-2.19.2-py3-none-any.whl",url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl",hashes = {sha256 = "86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}},
|
||||
]
|
||||
marker = "\"test\" in dependency_groups"
|
||||
|
||||
[packages.tool.pdm]
|
||||
dependencies = []
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
requires-python = ">=3.7"
|
||||
sdist = {name = "sniffio-1.3.1.tar.gz", url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hashes = {sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}}
|
||||
wheels = [
|
||||
{name = "sniffio-1.3.1-py3-none-any.whl",url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl",hashes = {sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}},
|
||||
]
|
||||
marker = "\"async\" in extras"
|
||||
|
||||
[packages.tool.pdm]
|
||||
dependencies = []
|
||||
|
||||
[tool.pdm]
|
||||
hashes = {sha256 = "51795362d337720c28bd6c3a26eb33751f2b69590261f599ffb4172ee2c441c6"}
|
||||
|
||||
[[tool.pdm.targets]]
|
||||
requires_python = "==3.13.*"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// By default, only `iniconfig` should be installed, since it's in the default group.
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--preview")
|
||||
.arg("-r")
|
||||
.arg("pylock.toml"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ iniconfig==2.1.0
|
||||
"
|
||||
);
|
||||
|
||||
// With `--extra async`, `anyio` should be installed.
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--preview")
|
||||
.arg("-r")
|
||||
.arg("pylock.toml")
|
||||
.arg("--extra")
|
||||
.arg("async"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Prepared 3 packages in [TIME]
|
||||
Installed 3 packages in [TIME]
|
||||
+ anyio==4.9.0
|
||||
+ idna==3.10
|
||||
+ sniffio==1.3.1
|
||||
"
|
||||
);
|
||||
|
||||
// With `--group test`, `pygments` should be installed.
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--preview")
|
||||
.arg("-r")
|
||||
.arg("pylock.toml")
|
||||
.arg("--group")
|
||||
.arg("test"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ pygments==2.19.2
|
||||
"
|
||||
);
|
||||
|
||||
// With `--all-extras`, `blinker` should be installed.
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--preview")
|
||||
.arg("-r")
|
||||
.arg("pylock.toml")
|
||||
.arg("--all-extras"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Prepared 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
+ blinker==1.9.0
|
||||
"
|
||||
);
|
||||
|
||||
// `--group pylock.toml:test` should be rejeceted.
|
||||
uv_snapshot!(context.filters(), context.pip_install()
|
||||
.arg("--preview")
|
||||
.arg("-r")
|
||||
.arg("pylock.toml")
|
||||
.arg("--group")
|
||||
.arg("pylock.toml:test"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: invalid value 'pylock.toml:test' for '--group <GROUP>': The `--group` path is required to end in 'pyproject.toml' for compatibility with pip; got: pylock.toml
|
||||
|
||||
For more information, try '--help'.
|
||||
"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that uv doesn't hang if an index returns a distribution for the wrong package.
|
||||
#[tokio::test]
|
||||
async fn bogus_redirect() -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -3637,7 +3637,9 @@ uv pip sync [OPTIONS] <SRC_FILE>...
|
|||
|
||||
<h3 class="cli-reference">Options</h3>
|
||||
|
||||
<dl class="cli-reference"><dt id="uv-pip-sync--allow-empty-requirements"><a href="#uv-pip-sync--allow-empty-requirements"><code>--allow-empty-requirements</code></a></dt><dd><p>Allow sync of empty requirements, which will clear the environment of all packages</p>
|
||||
<dl class="cli-reference"><dt id="uv-pip-sync--all-extras"><a href="#uv-pip-sync--all-extras"><code>--all-extras</code></a></dt><dd><p>Include all optional dependencies.</p>
|
||||
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
</dd><dt id="uv-pip-sync--allow-empty-requirements"><a href="#uv-pip-sync--allow-empty-requirements"><code>--allow-empty-requirements</code></a></dt><dd><p>Allow sync of empty requirements, which will clear the environment of all packages</p>
|
||||
</dd><dt id="uv-pip-sync--allow-insecure-host"><a href="#uv-pip-sync--allow-insecure-host"><code>--allow-insecure-host</code></a>, <code>--trusted-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
|
||||
<p>Can be provided multiple times.</p>
|
||||
<p>Expects to receive either a hostname (e.g., <code>localhost</code>), a host-port pair (e.g., <code>localhost:8080</code>), or a URL (e.g., <code>https://localhost</code>).</p>
|
||||
|
|
@ -3675,13 +3677,18 @@ uv pip sync [OPTIONS] <SRC_FILE>...
|
|||
</dd><dt id="uv-pip-sync--dry-run"><a href="#uv-pip-sync--dry-run"><code>--dry-run</code></a></dt><dd><p>Perform a dry run, i.e., don't actually install anything but resolve the dependencies and print the resulting plan</p>
|
||||
</dd><dt id="uv-pip-sync--exclude-newer"><a href="#uv-pip-sync--exclude-newer"><code>--exclude-newer</code></a> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
|
||||
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and local dates in the same format (e.g., <code>2006-12-02</code>) in your system's configured time zone.</p>
|
||||
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p></dd><dt id="uv-pip-sync--extra-index-url"><a href="#uv-pip-sync--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
|
||||
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p></dd><dt id="uv-pip-sync--extra"><a href="#uv-pip-sync--extra"><code>--extra</code></a> <i>extra</i></dt><dd><p>Include optional dependencies from the specified extra name; may be provided more than once.</p>
|
||||
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
</dd><dt id="uv-pip-sync--extra-index-url"><a href="#uv-pip-sync--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
|
||||
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
|
||||
<p>All indexes provided via this flag take priority over the index specified by <code>--index-url</code> (which defaults to PyPI). When multiple <code>--extra-index-url</code> flags are provided, earlier values take priority.</p>
|
||||
<p>May also be set with the <code>UV_EXTRA_INDEX_URL</code> environment variable.</p></dd><dt id="uv-pip-sync--find-links"><a href="#uv-pip-sync--find-links"><code>--find-links</code></a>, <code>-f</code> <i>find-links</i></dt><dd><p>Locations to search for candidate distributions, in addition to those found in the registry indexes.</p>
|
||||
<p>If a path, the target must be a directory that contains packages as wheel files (<code>.whl</code>) or source distributions (e.g., <code>.tar.gz</code> or <code>.zip</code>) at the top level.</p>
|
||||
<p>If a URL, the page must contain a flat list of links to package files adhering to the formats described above.</p>
|
||||
<p>May also be set with the <code>UV_FIND_LINKS</code> environment variable.</p></dd><dt id="uv-pip-sync--help"><a href="#uv-pip-sync--help"><code>--help</code></a>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
|
||||
<p>May also be set with the <code>UV_FIND_LINKS</code> environment variable.</p></dd><dt id="uv-pip-sync--group"><a href="#uv-pip-sync--group"><code>--group</code></a> <i>group</i></dt><dd><p>Install the specified dependency group from a <code>pylock.toml</code> or <code>pyproject.toml</code>.</p>
|
||||
<p>If no path is provided, the <code>pylock.toml</code> or <code>pyproject.toml</code> in the working directory is used.</p>
|
||||
<p>May be provided multiple times.</p>
|
||||
</dd><dt id="uv-pip-sync--help"><a href="#uv-pip-sync--help"><code>--help</code></a>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
|
||||
</dd><dt id="uv-pip-sync--index"><a href="#uv-pip-sync--index"><code>--index</code></a> <i>index</i></dt><dd><p>The URLs to use when resolving dependencies, in addition to the default index.</p>
|
||||
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
|
||||
<p>All indexes provided via this flag take priority over the index specified by <code>--default-index</code> (which defaults to PyPI). When multiple <code>--index</code> flags are provided, earlier values take priority.</p>
|
||||
|
|
@ -3888,7 +3895,7 @@ uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDIT
|
|||
<h3 class="cli-reference">Options</h3>
|
||||
|
||||
<dl class="cli-reference"><dt id="uv-pip-install--all-extras"><a href="#uv-pip-install--all-extras"><code>--all-extras</code></a></dt><dd><p>Include all optional dependencies.</p>
|
||||
<p>Only applies to <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
</dd><dt id="uv-pip-install--allow-insecure-host"><a href="#uv-pip-install--allow-insecure-host"><code>--allow-insecure-host</code></a>, <code>--trusted-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
|
||||
<p>Can be provided multiple times.</p>
|
||||
<p>Expects to receive either a hostname (e.g., <code>localhost</code>), a host-port pair (e.g., <code>localhost:8080</code>), or a URL (e.g., <code>https://localhost</code>).</p>
|
||||
|
|
@ -3930,7 +3937,7 @@ uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDIT
|
|||
</dd><dt id="uv-pip-install--exclude-newer"><a href="#uv-pip-install--exclude-newer"><code>--exclude-newer</code></a> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>
|
||||
<p>Accepts both RFC 3339 timestamps (e.g., <code>2006-12-02T02:07:43Z</code>) and local dates in the same format (e.g., <code>2006-12-02</code>) in your system's configured time zone.</p>
|
||||
<p>May also be set with the <code>UV_EXCLUDE_NEWER</code> environment variable.</p></dd><dt id="uv-pip-install--extra"><a href="#uv-pip-install--extra"><code>--extra</code></a> <i>extra</i></dt><dd><p>Include optional dependencies from the specified extra name; may be provided more than once.</p>
|
||||
<p>Only applies to <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
<p>Only applies to <code>pylock.toml</code>, <code>pyproject.toml</code>, <code>setup.py</code>, and <code>setup.cfg</code> sources.</p>
|
||||
</dd><dt id="uv-pip-install--extra-index-url"><a href="#uv-pip-install--extra-index-url"><code>--extra-index-url</code></a> <i>extra-index-url</i></dt><dd><p>(Deprecated: use <code>--index</code> instead) Extra URLs of package indexes to use, in addition to <code>--index-url</code>.</p>
|
||||
<p>Accepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.</p>
|
||||
<p>All indexes provided via this flag take priority over the index specified by <code>--index-url</code> (which defaults to PyPI). When multiple <code>--extra-index-url</code> flags are provided, earlier values take priority.</p>
|
||||
|
|
@ -3944,8 +3951,8 @@ uv pip install [OPTIONS] <PACKAGE|--requirements <REQUIREMENTS>|--editable <EDIT
|
|||
<ul>
|
||||
<li><code>fewest</code>: Optimize for selecting the fewest number of versions for each package. Older versions may be preferred if they are compatible with a wider range of supported Python versions or platforms</li>
|
||||
<li><code>requires-python</code>: Optimize for selecting latest supported version of each package, for each supported Python version</li>
|
||||
</ul></dd><dt id="uv-pip-install--group"><a href="#uv-pip-install--group"><code>--group</code></a> <i>group</i></dt><dd><p>Install the specified dependency group from a <code>pyproject.toml</code>.</p>
|
||||
<p>If no path is provided, the <code>pyproject.toml</code> in the working directory is used.</p>
|
||||
</ul></dd><dt id="uv-pip-install--group"><a href="#uv-pip-install--group"><code>--group</code></a> <i>group</i></dt><dd><p>Install the specified dependency group from a <code>pylock.toml</code> or <code>pyproject.toml</code>.</p>
|
||||
<p>If no path is provided, the <code>pylock.toml</code> or <code>pyproject.toml</code> in the working directory is used.</p>
|
||||
<p>May be provided multiple times.</p>
|
||||
</dd><dt id="uv-pip-install--help"><a href="#uv-pip-install--help"><code>--help</code></a>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>
|
||||
</dd><dt id="uv-pip-install--index"><a href="#uv-pip-install--index"><code>--index</code></a> <i>index</i></dt><dd><p>The URLs to use when resolving dependencies, in addition to the default index.</p>
|
||||
|
|
|
|||
Loading…
Reference in New Issue