mirror of https://github.com/astral-sh/uv
Add `uv export` support for PEP 751 (#12955)
## Summary This PR adds `uv export` support for [PEP 751](https://peps.python.org/pep-0751). We don't yet expose a way to consume the generated lockfile, but it's a first step. The logic to go from `uv.lock` to "flat set of packages to include, with markers telling us when to include them" is all shared with the `requirements.txt` export (and extracted in https://github.com/astral-sh/uv/pull/12956). So most of the code is just converting from our internal types to the PEP 751 schema.
This commit is contained in:
parent
9484e3663c
commit
d8cea2fd49
|
|
@ -3763,7 +3763,7 @@ pub struct TreeArgs {
|
|||
pub struct ExportArgs {
|
||||
/// The format to which `uv.lock` should be exported.
|
||||
///
|
||||
/// At present, only `requirements-txt` is supported.
|
||||
/// Supports both `requirements.txt` and `pylock.toml` (PEP 751) output formats.
|
||||
#[arg(long, value_enum, default_value_t = ExportFormat::default())]
|
||||
pub format: ExportFormat,
|
||||
|
||||
|
|
|
|||
|
|
@ -11,4 +11,8 @@ pub enum ExportFormat {
|
|||
clap(name = "requirements.txt", alias = "requirements-txt")
|
||||
)]
|
||||
RequirementsTxt,
|
||||
/// Export in `pylock.toml` format.
|
||||
#[serde(rename = "pylock.toml", alias = "pylock-toml")]
|
||||
#[cfg_attr(feature = "clap", clap(name = "pylock.toml", alias = "pylock-toml"))]
|
||||
PylockToml,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,11 +185,15 @@ impl Default for Yanked {
|
|||
/// A dictionary mapping a hash name to a hex encoded digest of the file.
|
||||
///
|
||||
/// PEP 691 says multiple hashes can be included and the interpretation is left to the client.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default, Deserialize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default, Deserialize, Serialize)]
|
||||
pub struct Hashes {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub md5: Option<SmallString>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub sha256: Option<SmallString>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub sha384: Option<SmallString>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub sha512: Option<SmallString>,
|
||||
}
|
||||
|
||||
|
|
@ -490,6 +494,21 @@ impl From<Hashes> for HashDigests {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<HashDigests> for Hashes {
|
||||
fn from(value: HashDigests) -> Self {
|
||||
let mut hashes = Hashes::default();
|
||||
for digest in value {
|
||||
match digest.algorithm() {
|
||||
HashAlgorithm::Md5 => hashes.md5 = Some(digest.digest),
|
||||
HashAlgorithm::Sha256 => hashes.sha256 = Some(digest.digest),
|
||||
HashAlgorithm::Sha384 => hashes.sha384 = Some(digest.digest),
|
||||
HashAlgorithm::Sha512 => hashes.sha512 = Some(digest.digest),
|
||||
}
|
||||
}
|
||||
hashes
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HashDigest> for HashDigests {
|
||||
fn from(value: HashDigest) -> Self {
|
||||
Self(Box::new([value]))
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ pub use exclusions::Exclusions;
|
|||
pub use flat_index::{FlatDistributions, FlatIndex};
|
||||
pub use fork_strategy::ForkStrategy;
|
||||
pub use lock::{
|
||||
Installable, Lock, LockError, LockVersion, Package, PackageMap, RequirementsTxtExport,
|
||||
ResolverManifest, SatisfiesResult, TreeDisplay, VERSION,
|
||||
Installable, Lock, LockError, LockVersion, Package, PackageMap, PylockToml,
|
||||
RequirementsTxtExport, ResolverManifest, SatisfiesResult, TreeDisplay, VERSION,
|
||||
};
|
||||
pub use manifest::Manifest;
|
||||
pub use options::{Flexibility, Options, OptionsBuilder};
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ use uv_pep508::MarkerTree;
|
|||
use uv_pypi_types::ConflictItem;
|
||||
|
||||
use crate::graph_ops::{marker_reachability, Reachable};
|
||||
pub use crate::lock::export::pylock_toml::PylockToml;
|
||||
pub use crate::lock::export::requirements_txt::RequirementsTxtExport;
|
||||
use crate::universal_marker::resolve_conflicts;
|
||||
use crate::{Installable, Package};
|
||||
|
||||
mod pylock_toml;
|
||||
mod requirements_txt;
|
||||
|
||||
/// A flat requirement, with its associated marker.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,604 @@
|
|||
use jiff::tz::TimeZone;
|
||||
use jiff::Timestamp;
|
||||
use toml_edit::{value, Array, ArrayOfTables, Item, Table};
|
||||
use url::Url;
|
||||
|
||||
use uv_configuration::{DependencyGroupsWithDefaults, ExtrasSpecification, InstallOptions};
|
||||
use uv_distribution_types::{IndexUrl, RegistryBuiltWheel, RemoteSource, SourceDist};
|
||||
use uv_fs::{relative_to, PortablePathBuf};
|
||||
use uv_git_types::GitOid;
|
||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||
use uv_pep440::Version;
|
||||
use uv_pep508::MarkerTree;
|
||||
use uv_pypi_types::{Hashes, VcsKind};
|
||||
use uv_small_str::SmallString;
|
||||
|
||||
use crate::lock::export::ExportableRequirements;
|
||||
use crate::lock::{each_element_on_its_line_array, LockErrorKind, Source};
|
||||
use crate::{Installable, LockError, RequiresPython};
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct PylockToml {
|
||||
lock_version: Version,
|
||||
created_by: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
requires_python: Option<RequiresPython>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
extras: Vec<ExtraName>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
dependency_groups: Vec<GroupName>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
default_groups: Vec<GroupName>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
packages: Vec<PylockTomlPackage>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
attestation_identities: Vec<PylockTomlAttestationIdentity>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlPackage {
|
||||
name: PackageName,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
version: Option<Version>,
|
||||
#[serde(
|
||||
skip_serializing_if = "uv_pep508::marker::ser::is_empty",
|
||||
serialize_with = "uv_pep508::marker::ser::serialize",
|
||||
default
|
||||
)]
|
||||
marker: MarkerTree,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
requires_python: Option<RequiresPython>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
dependencies: Vec<PylockTomlDependency>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
index: Option<Url>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
vcs: Option<PylockTomlVcs>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
directory: Option<PylockTomlDirectory>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
archive: Option<PylockTomlArchive>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sdist: Option<PylockTomlSdist>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
wheels: Option<Vec<PylockTomlWheel>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlDependency;
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlDirectory {
|
||||
path: PortablePathBuf,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
editable: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
subdirectory: Option<PortablePathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlVcs {
|
||||
r#type: VcsKind,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
url: Option<Url>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PortablePathBuf>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
requested_revision: Option<String>,
|
||||
commit_id: GitOid,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
subdirectory: Option<PortablePathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlArchive {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
url: Option<Url>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PortablePathBuf>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
size: Option<u64>,
|
||||
#[serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
serialize_with = "timestamp_to_toml_datetime"
|
||||
)]
|
||||
upload_time: Option<Timestamp>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
subdirectory: Option<PortablePathBuf>,
|
||||
hashes: Hashes,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlSdist {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
name: Option<SmallString>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
url: Option<Url>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PortablePathBuf>,
|
||||
#[serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
serialize_with = "timestamp_to_toml_datetime"
|
||||
)]
|
||||
upload_time: Option<Timestamp>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
size: Option<u64>,
|
||||
hashes: Hashes,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlWheel {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
name: Option<SmallString>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
url: Option<Url>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PortablePathBuf>,
|
||||
#[serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
serialize_with = "timestamp_to_toml_datetime"
|
||||
)]
|
||||
upload_time: Option<Timestamp>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
size: Option<u64>,
|
||||
hashes: Hashes,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PylockTomlAttestationIdentity {
|
||||
kind: String,
|
||||
}
|
||||
|
||||
impl<'lock> PylockToml {
|
||||
/// Construct a [`PylockToml`] from a uv lockfile.
|
||||
pub fn from_lock(
|
||||
target: &impl Installable<'lock>,
|
||||
prune: &[PackageName],
|
||||
extras: &ExtrasSpecification,
|
||||
dev: &DependencyGroupsWithDefaults,
|
||||
annotate: bool,
|
||||
install_options: &'lock InstallOptions,
|
||||
) -> Result<Self, LockError> {
|
||||
// Extract the packages from the lock file.
|
||||
let ExportableRequirements(mut nodes) = ExportableRequirements::from_lock(
|
||||
target,
|
||||
prune,
|
||||
extras,
|
||||
dev,
|
||||
annotate,
|
||||
install_options,
|
||||
);
|
||||
|
||||
// Sort the nodes, such that unnamed URLs (editables) appear at the top.
|
||||
nodes.sort_unstable_by_key(|node| &node.package.id);
|
||||
|
||||
// The lock version is always `1.0` at time of writing.
|
||||
let lock_version = Version::new([1, 0]);
|
||||
|
||||
// The created by field is always `uv` at time of writing.
|
||||
let created_by = "uv".to_string();
|
||||
|
||||
// Use the `requires-python` from the target lockfile.
|
||||
let requires_python = target.lock().requires_python.clone();
|
||||
|
||||
// We don't support locking for multiple extras at time of writing.
|
||||
let extras = vec![];
|
||||
|
||||
// We don't support locking for multiple dependency groups at time of writing.
|
||||
let dependency_groups = vec![];
|
||||
|
||||
// We don't support locking for multiple dependency groups at time of writing.
|
||||
let default_groups = vec![];
|
||||
|
||||
// We don't support attestation identities at time of writing.
|
||||
let attestation_identities = vec![];
|
||||
|
||||
// Convert each node to a `pylock.toml`-style package.
|
||||
let mut packages = Vec::with_capacity(nodes.len());
|
||||
for node in nodes {
|
||||
let package = node.package;
|
||||
|
||||
// Extract the `packages.wheels` field.
|
||||
//
|
||||
// This field only includes wheels from a registry. Wheels included via direct URL or
|
||||
// direct path instead map to the `packages.archive` field.
|
||||
let wheels = match &package.id.source {
|
||||
Source::Registry(source) => {
|
||||
let wheels = package
|
||||
.wheels
|
||||
.iter()
|
||||
.map(|wheel| wheel.to_registry_dist(source, target.install_path()))
|
||||
.collect::<Result<Vec<RegistryBuiltWheel>, LockError>>()?;
|
||||
Some(
|
||||
wheels
|
||||
.into_iter()
|
||||
.map(|wheel| {
|
||||
let url =
|
||||
wheel.file.url.to_url().map_err(LockErrorKind::InvalidUrl)?;
|
||||
Ok(PylockTomlWheel {
|
||||
// Optional "when the last component of path/ url would be the same value".
|
||||
name: if url
|
||||
.filename()
|
||||
.is_ok_and(|filename| filename == *wheel.file.filename)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(wheel.file.filename.clone())
|
||||
},
|
||||
upload_time: wheel
|
||||
.file
|
||||
.upload_time_utc_ms
|
||||
.map(Timestamp::from_millisecond)
|
||||
.transpose()
|
||||
.map_err(LockErrorKind::InvalidTimestamp)?,
|
||||
url: Some(url),
|
||||
path: None,
|
||||
size: wheel.file.size,
|
||||
hashes: Hashes::from(wheel.file.hashes),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, LockError>>()?,
|
||||
)
|
||||
}
|
||||
Source::Path(..) => None,
|
||||
Source::Git(..) => None,
|
||||
Source::Direct(..) => None,
|
||||
Source::Directory(..) => None,
|
||||
Source::Editable(..) => None,
|
||||
Source::Virtual(..) => {
|
||||
// Omit virtual packages entirely; they shouldn't be installed.
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Extract the source distribution from the lockfile entry.
|
||||
let sdist = package.to_source_dist(target.install_path())?;
|
||||
|
||||
// Extract some common fields from the source distribution.
|
||||
let size = package
|
||||
.sdist
|
||||
.as_ref()
|
||||
.and_then(super::super::SourceDist::size);
|
||||
let hash = package.sdist.as_ref().and_then(|sdist| sdist.hash());
|
||||
|
||||
// Extract the `packages.directory` field.
|
||||
let directory = match &sdist {
|
||||
Some(SourceDist::Directory(sdist)) => Some(PylockTomlDirectory {
|
||||
path: PortablePathBuf::from(
|
||||
relative_to(&sdist.install_path, target.install_path())
|
||||
.unwrap_or_else(|_| sdist.install_path.to_path_buf())
|
||||
.into_boxed_path(),
|
||||
),
|
||||
editable: Some(sdist.editable),
|
||||
subdirectory: None,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Extract the `packages.vcs` field.
|
||||
let vcs = match &sdist {
|
||||
Some(SourceDist::Git(sdist)) => Some(PylockTomlVcs {
|
||||
r#type: VcsKind::Git,
|
||||
url: Some(sdist.git.repository().clone()),
|
||||
path: None,
|
||||
requested_revision: sdist.git.reference().as_str().map(ToString::to_string),
|
||||
commit_id: sdist.git.precise().unwrap_or_else(|| {
|
||||
panic!("Git distribution is missing a precise hash: {sdist}")
|
||||
}),
|
||||
subdirectory: sdist.subdirectory.clone().map(PortablePathBuf::from),
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Extract the `packages.archive` field, which can either be a direct URL or a local
|
||||
// path, pointing to either a source distribution or a wheel.
|
||||
let archive = match &sdist {
|
||||
Some(SourceDist::DirectUrl(sdist)) => Some(PylockTomlArchive {
|
||||
url: Some(sdist.url.to_url()),
|
||||
path: None,
|
||||
size,
|
||||
upload_time: None,
|
||||
subdirectory: sdist.subdirectory.clone().map(PortablePathBuf::from),
|
||||
hashes: hash.cloned().map(Hashes::from).unwrap_or_default(),
|
||||
}),
|
||||
Some(SourceDist::Path(sdist)) => Some(PylockTomlArchive {
|
||||
url: None,
|
||||
path: Some(PortablePathBuf::from(
|
||||
relative_to(&sdist.install_path, target.install_path())
|
||||
.unwrap_or_else(|_| sdist.install_path.to_path_buf())
|
||||
.into_boxed_path(),
|
||||
)),
|
||||
size,
|
||||
upload_time: None,
|
||||
subdirectory: None,
|
||||
hashes: hash.cloned().map(Hashes::from).unwrap_or_default(),
|
||||
}),
|
||||
_ => match &package.id.source {
|
||||
Source::Registry(..) => None,
|
||||
Source::Path(source) => package.wheels.first().map(|wheel| PylockTomlArchive {
|
||||
url: None,
|
||||
path: Some(PortablePathBuf::from(
|
||||
relative_to(source, target.install_path())
|
||||
.unwrap_or_else(|_| source.to_path_buf())
|
||||
.into_boxed_path(),
|
||||
)),
|
||||
size: wheel.size,
|
||||
upload_time: None,
|
||||
subdirectory: None,
|
||||
hashes: wheel.hash.clone().map(Hashes::from).unwrap_or_default(),
|
||||
}),
|
||||
Source::Git(..) => None,
|
||||
Source::Direct(source, ..) => {
|
||||
if let Some(wheel) = package.wheels.first() {
|
||||
Some(PylockTomlArchive {
|
||||
url: Some(source.to_url()?),
|
||||
path: None,
|
||||
size: wheel.size,
|
||||
upload_time: None,
|
||||
subdirectory: None,
|
||||
hashes: wheel.hash.clone().map(Hashes::from).unwrap_or_default(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Source::Directory(..) => None,
|
||||
Source::Editable(..) => None,
|
||||
Source::Virtual(..) => None,
|
||||
},
|
||||
};
|
||||
|
||||
// Extract the `packages.sdist` field.
|
||||
let sdist = match &sdist {
|
||||
Some(SourceDist::Registry(sdist)) => {
|
||||
let url = sdist.file.url.to_url().map_err(LockErrorKind::InvalidUrl)?;
|
||||
Some(PylockTomlSdist {
|
||||
// Optional "when the last component of path/ url would be the same value".
|
||||
name: if url
|
||||
.filename()
|
||||
.is_ok_and(|filename| filename == *sdist.file.filename)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(sdist.file.filename.clone())
|
||||
},
|
||||
upload_time: sdist
|
||||
.file
|
||||
.upload_time_utc_ms
|
||||
.map(Timestamp::from_millisecond)
|
||||
.transpose()
|
||||
.map_err(LockErrorKind::InvalidTimestamp)?,
|
||||
url: Some(url),
|
||||
path: None,
|
||||
size,
|
||||
hashes: hash.cloned().map(Hashes::from).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Extract the `packages.index` field.
|
||||
let index = package
|
||||
.index(target.install_path())?
|
||||
.map(IndexUrl::into_url);
|
||||
|
||||
let package = PylockTomlPackage {
|
||||
name: package.id.name.clone(),
|
||||
version: package.id.version.clone(),
|
||||
marker: node.marker,
|
||||
requires_python: None,
|
||||
dependencies: vec![],
|
||||
index,
|
||||
vcs,
|
||||
directory,
|
||||
archive,
|
||||
sdist,
|
||||
wheels,
|
||||
};
|
||||
|
||||
packages.push(package);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
lock_version,
|
||||
created_by,
|
||||
requires_python: Some(requires_python),
|
||||
extras,
|
||||
dependency_groups,
|
||||
default_groups,
|
||||
packages,
|
||||
attestation_identities,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the TOML representation of this lockfile.
|
||||
pub fn to_toml(&self) -> Result<String, toml_edit::ser::Error> {
|
||||
// We construct a TOML document manually instead of going through Serde to enable
|
||||
// the use of inline tables.
|
||||
let mut doc = toml_edit::DocumentMut::new();
|
||||
|
||||
doc.insert("lock-version", value(self.lock_version.to_string()));
|
||||
doc.insert("created-by", value(self.created_by.to_string()));
|
||||
if let Some(ref requires_python) = self.requires_python {
|
||||
doc.insert("requires-python", value(requires_python.to_string()));
|
||||
}
|
||||
if !self.extras.is_empty() {
|
||||
doc.insert(
|
||||
"extras",
|
||||
value(each_element_on_its_line_array(
|
||||
self.extras.iter().map(ToString::to_string),
|
||||
)),
|
||||
);
|
||||
}
|
||||
if !self.dependency_groups.is_empty() {
|
||||
doc.insert(
|
||||
"dependency-groups",
|
||||
value(each_element_on_its_line_array(
|
||||
self.dependency_groups.iter().map(ToString::to_string),
|
||||
)),
|
||||
);
|
||||
}
|
||||
if !self.default_groups.is_empty() {
|
||||
doc.insert(
|
||||
"default-groups",
|
||||
value(each_element_on_its_line_array(
|
||||
self.default_groups.iter().map(ToString::to_string),
|
||||
)),
|
||||
);
|
||||
}
|
||||
if !self.attestation_identities.is_empty() {
|
||||
let attestation_identities = self
|
||||
.attestation_identities
|
||||
.iter()
|
||||
.map(|attestation_identity| {
|
||||
serde::Serialize::serialize(
|
||||
&attestation_identity,
|
||||
toml_edit::ser::ValueSerializer::new(),
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let attestation_identities = match attestation_identities.as_slice() {
|
||||
[] => Array::new(),
|
||||
[attestation_identity] => Array::from_iter([attestation_identity]),
|
||||
attestation_identities => {
|
||||
each_element_on_its_line_array(attestation_identities.iter())
|
||||
}
|
||||
};
|
||||
doc.insert("attestation-identities", value(attestation_identities));
|
||||
}
|
||||
if !self.packages.is_empty() {
|
||||
let mut packages = ArrayOfTables::new();
|
||||
for dist in &self.packages {
|
||||
packages.push(dist.to_toml()?);
|
||||
}
|
||||
doc.insert("packages", Item::ArrayOfTables(packages));
|
||||
}
|
||||
|
||||
Ok(doc.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl PylockTomlPackage {
|
||||
fn to_toml(&self) -> Result<Table, toml_edit::ser::Error> {
|
||||
let mut table = Table::new();
|
||||
table.insert("name", value(self.name.to_string()));
|
||||
if let Some(ref version) = self.version {
|
||||
table.insert("version", value(version.to_string()));
|
||||
}
|
||||
if let Some(marker) = self.marker.try_to_string() {
|
||||
table.insert("marker", value(marker));
|
||||
}
|
||||
if let Some(ref requires_python) = self.requires_python {
|
||||
table.insert("requires-python", value(requires_python.to_string()));
|
||||
}
|
||||
if !self.dependencies.is_empty() {
|
||||
let dependencies = self
|
||||
.dependencies
|
||||
.iter()
|
||||
.map(|dependency| {
|
||||
serde::Serialize::serialize(&dependency, toml_edit::ser::ValueSerializer::new())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let dependencies = match dependencies.as_slice() {
|
||||
[] => Array::new(),
|
||||
[dependency] => Array::from_iter([dependency]),
|
||||
dependencies => each_element_on_its_line_array(dependencies.iter()),
|
||||
};
|
||||
table.insert("dependencies", value(dependencies));
|
||||
}
|
||||
if let Some(ref index) = self.index {
|
||||
table.insert("index", value(index.to_string()));
|
||||
}
|
||||
if let Some(ref vcs) = self.vcs {
|
||||
table.insert(
|
||||
"vcs",
|
||||
value(serde::Serialize::serialize(
|
||||
&vcs,
|
||||
toml_edit::ser::ValueSerializer::new(),
|
||||
)?),
|
||||
);
|
||||
}
|
||||
if let Some(ref directory) = self.directory {
|
||||
table.insert(
|
||||
"directory",
|
||||
value(serde::Serialize::serialize(
|
||||
&directory,
|
||||
toml_edit::ser::ValueSerializer::new(),
|
||||
)?),
|
||||
);
|
||||
}
|
||||
if let Some(ref archive) = self.archive {
|
||||
table.insert(
|
||||
"archive",
|
||||
value(serde::Serialize::serialize(
|
||||
&archive,
|
||||
toml_edit::ser::ValueSerializer::new(),
|
||||
)?),
|
||||
);
|
||||
}
|
||||
if let Some(ref sdist) = self.sdist {
|
||||
table.insert(
|
||||
"sdist",
|
||||
value(serde::Serialize::serialize(
|
||||
&sdist,
|
||||
toml_edit::ser::ValueSerializer::new(),
|
||||
)?),
|
||||
);
|
||||
}
|
||||
if let Some(wheels) = self.wheels.as_ref().filter(|wheels| !wheels.is_empty()) {
|
||||
let wheels = wheels
|
||||
.iter()
|
||||
.map(|wheel| {
|
||||
serde::Serialize::serialize(wheel, toml_edit::ser::ValueSerializer::new())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let wheels = match wheels.as_slice() {
|
||||
[] => Array::new(),
|
||||
[wheel] => Array::from_iter([wheel]),
|
||||
wheels => each_element_on_its_line_array(wheels.iter()),
|
||||
};
|
||||
table.insert("wheels", value(wheels));
|
||||
}
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a Jiff timestamp to a TOML datetime.
|
||||
#[allow(clippy::ref_option)]
|
||||
fn timestamp_to_toml_datetime<S>(
|
||||
timestamp: &Option<Timestamp>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let Some(timestamp) = timestamp else {
|
||||
return serializer.serialize_none();
|
||||
};
|
||||
let timestamp = timestamp.to_zoned(TimeZone::UTC);
|
||||
let timestamp = toml_edit::Datetime {
|
||||
date: Some(toml_edit::Date {
|
||||
year: u16::try_from(timestamp.year()).map_err(serde::ser::Error::custom)?,
|
||||
month: u8::try_from(timestamp.month()).map_err(serde::ser::Error::custom)?,
|
||||
day: u8::try_from(timestamp.day()).map_err(serde::ser::Error::custom)?,
|
||||
}),
|
||||
time: Some(toml_edit::Time {
|
||||
hour: u8::try_from(timestamp.hour()).map_err(serde::ser::Error::custom)?,
|
||||
minute: u8::try_from(timestamp.minute()).map_err(serde::ser::Error::custom)?,
|
||||
second: u8::try_from(timestamp.second()).map_err(serde::ser::Error::custom)?,
|
||||
nanosecond: u32::try_from(timestamp.nanosecond()).map_err(serde::ser::Error::custom)?,
|
||||
}),
|
||||
offset: Some(toml_edit::Offset::Z),
|
||||
};
|
||||
serializer.serialize_some(×tamp)
|
||||
}
|
||||
|
|
@ -42,13 +42,15 @@ use uv_platform_tags::{
|
|||
AbiTag, IncompatibleTag, LanguageTag, PlatformTag, TagCompatibility, TagPriority, Tags,
|
||||
};
|
||||
use uv_pypi_types::{
|
||||
ConflictPackage, Conflicts, HashDigest, HashDigests, ParsedArchiveUrl, ParsedGitUrl,
|
||||
ConflictPackage, Conflicts, HashAlgorithm, HashDigest, HashDigests, Hashes, ParsedArchiveUrl,
|
||||
ParsedGitUrl,
|
||||
};
|
||||
use uv_small_str::SmallString;
|
||||
use uv_types::{BuildContext, HashStrategy};
|
||||
use uv_workspace::WorkspaceMember;
|
||||
|
||||
use crate::fork_strategy::ForkStrategy;
|
||||
pub use crate::lock::export::PylockToml;
|
||||
pub use crate::lock::export::RequirementsTxtExport;
|
||||
pub use crate::lock::installable::Installable;
|
||||
pub use crate::lock::map::PackageMap;
|
||||
|
|
@ -3685,7 +3687,7 @@ impl SourceDist {
|
|||
}
|
||||
}
|
||||
|
||||
fn hash(&self) -> Option<&Hash> {
|
||||
pub(crate) fn hash(&self) -> Option<&Hash> {
|
||||
match &self {
|
||||
SourceDist::Metadata { metadata } => metadata.hash.as_ref(),
|
||||
SourceDist::Url { metadata, .. } => metadata.hash.as_ref(),
|
||||
|
|
@ -3693,7 +3695,7 @@ impl SourceDist {
|
|||
}
|
||||
}
|
||||
|
||||
fn size(&self) -> Option<u64> {
|
||||
pub(crate) fn size(&self) -> Option<u64> {
|
||||
match &self {
|
||||
SourceDist::Metadata { metadata } => metadata.size,
|
||||
SourceDist::Url { metadata, .. } => metadata.size,
|
||||
|
|
@ -4181,7 +4183,7 @@ impl Wheel {
|
|||
}
|
||||
}
|
||||
|
||||
fn to_registry_dist(
|
||||
pub(crate) fn to_registry_dist(
|
||||
&self,
|
||||
source: &RegistrySource,
|
||||
root: &Path,
|
||||
|
|
@ -4565,6 +4567,37 @@ impl<'de> serde::Deserialize<'de> for Hash {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Hash> for Hashes {
|
||||
fn from(value: Hash) -> Self {
|
||||
match value.0.algorithm {
|
||||
HashAlgorithm::Md5 => Hashes {
|
||||
md5: Some(value.0.digest),
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
HashAlgorithm::Sha256 => Hashes {
|
||||
md5: None,
|
||||
sha256: Some(value.0.digest),
|
||||
sha384: None,
|
||||
sha512: None,
|
||||
},
|
||||
HashAlgorithm::Sha384 => Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: Some(value.0.digest),
|
||||
sha512: None,
|
||||
},
|
||||
HashAlgorithm::Sha512 => Hashes {
|
||||
md5: None,
|
||||
sha256: None,
|
||||
sha384: None,
|
||||
sha512: Some(value.0.digest),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a [`FileLocation`] into a normalized [`UrlString`].
|
||||
fn normalize_file_location(location: &FileLocation) -> Result<UrlString, ToUrlError> {
|
||||
match location {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use uv_configuration::{
|
|||
};
|
||||
use uv_normalize::{DefaultGroups, PackageName};
|
||||
use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
|
||||
use uv_resolver::RequirementsTxtExport;
|
||||
use uv_resolver::{PylockToml, RequirementsTxtExport};
|
||||
use uv_scripts::{Pep723ItemRef, Pep723Script};
|
||||
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache};
|
||||
|
||||
|
|
@ -276,6 +276,26 @@ pub(crate) async fn export(
|
|||
}
|
||||
write!(writer, "{export}")?;
|
||||
}
|
||||
ExportFormat::PylockToml => {
|
||||
let export = PylockToml::from_lock(
|
||||
&target,
|
||||
&prune,
|
||||
&extras,
|
||||
&dev,
|
||||
include_annotations,
|
||||
&install_options,
|
||||
)?;
|
||||
|
||||
if include_header {
|
||||
writeln!(
|
||||
writer,
|
||||
"{}",
|
||||
"# This file was autogenerated by uv via the following command:".green()
|
||||
)?;
|
||||
writeln!(writer, "{}", format!("# {}", cmd()).green())?;
|
||||
}
|
||||
write!(writer, "{}", export.to_toml()?)?;
|
||||
}
|
||||
}
|
||||
|
||||
writer.commit().await?;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use insta::assert_snapshot;
|
|||
use std::process::Stdio;
|
||||
|
||||
#[test]
|
||||
fn dependency() -> Result<()> {
|
||||
fn requirements_txt_dependency() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -57,7 +57,7 @@ fn dependency() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn export_no_header() -> Result<()> {
|
||||
fn requirements_txt_export_no_header() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -103,7 +103,7 @@ fn export_no_header() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn dependency_extra() -> Result<()> {
|
||||
fn requirements_txt_dependency_extra() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -186,7 +186,7 @@ fn dependency_extra() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn project_extra() -> Result<()> {
|
||||
fn requirements_txt_project_extra() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -338,7 +338,7 @@ fn project_extra() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn prune() -> Result<()> {
|
||||
fn requirements_txt_prune() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -403,7 +403,7 @@ fn prune() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn dependency_marker() -> Result<()> {
|
||||
fn requirements_txt_dependency_marker() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -455,7 +455,7 @@ fn dependency_marker() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn dependency_multiple_markers() -> Result<()> {
|
||||
fn requirements_txt_dependency_multiple_markers() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -538,7 +538,7 @@ fn dependency_multiple_markers() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn dependency_conflicting_markers() -> Result<()> {
|
||||
fn requirements_txt_dependency_conflicting_markers() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -782,7 +782,7 @@ fn dependency_conflicting_markers() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn non_root() -> Result<()> {
|
||||
fn requirements_txt_non_root() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -843,7 +843,7 @@ fn non_root() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn all() -> Result<()> {
|
||||
fn allrequirements_txt_() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -918,7 +918,7 @@ fn all() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn frozen() -> Result<()> {
|
||||
fn requirements_txt_frozen() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1006,7 +1006,7 @@ fn frozen() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn create_missing_dir() -> Result<()> {
|
||||
fn requirements_txt_create_missing_dir() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1084,7 +1084,7 @@ fn create_missing_dir() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn non_project() -> Result<()> {
|
||||
fn requirements_txt_non_project() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1139,7 +1139,7 @@ fn non_project() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn non_project_marker() -> Result<()> {
|
||||
fn requirements_txt_non_project_marker() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1194,7 +1194,7 @@ fn non_project_marker() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn non_project_workspace() -> Result<()> {
|
||||
fn requirements_txt_non_project_workspace() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1272,7 +1272,7 @@ fn non_project_workspace() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn non_project_fork() -> Result<()> {
|
||||
fn requirements_txt_non_project_fork() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1498,7 +1498,7 @@ fn non_project_fork() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn relative_path() -> Result<()> {
|
||||
fn requirements_txt_relative_path() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let dependency = context.temp_dir.child("dependency");
|
||||
|
|
@ -1586,7 +1586,7 @@ fn relative_path() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn dev() -> Result<()> {
|
||||
fn devrequirements_txt_() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1678,7 +1678,7 @@ fn dev() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn no_hashes() -> Result<()> {
|
||||
fn requirements_txt_no_hashes() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1720,7 +1720,7 @@ fn no_hashes() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn output_file() -> Result<()> {
|
||||
fn requirements_txt_output_file() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1787,7 +1787,7 @@ fn output_file() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn no_emit() -> Result<()> {
|
||||
fn requirements_txt_no_emit() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -1973,7 +1973,7 @@ fn no_emit() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn no_editable() -> Result<()> {
|
||||
fn requirements_txt_no_editable() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -2048,7 +2048,7 @@ fn no_editable() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn export_group() -> Result<()> {
|
||||
fn requirements_txt_export_group() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -2225,7 +2225,7 @@ fn export_group() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn script() -> Result<()> {
|
||||
fn requirements_txt_script() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let script = context.temp_dir.child("script.py");
|
||||
|
|
@ -2488,7 +2488,7 @@ fn script() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn conflicts() -> Result<()> {
|
||||
fn requirements_txt_conflicts() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -2590,7 +2590,7 @@ fn conflicts() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn simple_conflict_markers() -> Result<()> {
|
||||
fn requirements_txt_simple_conflict_markers() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -2675,7 +2675,7 @@ fn simple_conflict_markers() -> Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn complex_conflict_markers() -> Result<()> {
|
||||
fn requirements_txt_complex_conflict_markers() -> Result<()> {
|
||||
let context = TestContext::new("3.12").with_exclude_newer("2025-01-30T00:00:00Z");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -3050,7 +3050,7 @@ fn complex_conflict_markers() -> Result<()> {
|
|||
|
||||
/// Export requirements in the presence of a cycle.
|
||||
#[test]
|
||||
fn cyclic_dependencies() -> Result<()> {
|
||||
fn requirements_txt_cyclic_dependencies() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -3136,7 +3136,7 @@ fn cyclic_dependencies() -> Result<()> {
|
|||
|
||||
/// Export requirements in the presence of a cycle, with conflicts enabled.
|
||||
#[test]
|
||||
fn cyclic_dependencies_conflict() -> Result<()> {
|
||||
fn requirements_txt_cyclic_dependencies_conflict() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
|
|
@ -3233,3 +3233,693 @@ fn cyclic_dependencies_conflict() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_dependency() -> 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("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/fefbf7e78ab3b805dec67d698dc18dd505af7a18a8dd08868c9b4fa736b5/anyio-3.7.0.tar.gz", upload-time = 2023-05-27T11:12:46Z, size = 142737, hashes = { sha256 = "275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/68/fe/7ce1926952c8a403b35029e194555558514b365ad77d75125f521a2bec62/anyio-3.7.0-py3-none-any.whl", upload-time = 2023-05-27T11:12:44Z, size = 80873, hashes = { sha256 = "eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 4 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_export_no_header() -> 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("--format").arg("pylock.toml").arg("--no-header"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/fefbf7e78ab3b805dec67d698dc18dd505af7a18a8dd08868c9b4fa736b5/anyio-3.7.0.tar.gz", upload-time = 2023-05-27T11:12:46Z, size = 142737, hashes = { sha256 = "275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/68/fe/7ce1926952c8a403b35029e194555558514b365ad77d75125f521a2bec62/anyio-3.7.0-py3-none-any.whl", upload-time = 2023-05-27T11:12:44Z, size = 80873, hashes = { sha256 = "eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 4 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_dependency_extra() -> 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 = ["flask[dotenv]"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "blinker"
|
||||
version = "1.7.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/13/6df5fc090ff4e5d246baf1f45fe9e5623aa8565757dfa5bd243f6a545f9e/blinker-1.7.0.tar.gz", upload-time = 2023-11-01T22:06:01Z, size = 28134, hashes = { sha256 = "e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl", upload-time = 2023-11-01T22:06:00Z, size = 13068, hashes = { sha256 = "c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", upload-time = 2023-08-17T17:29:11Z, size = 336121, hashes = { sha256 = "ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", upload-time = 2023-08-17T17:29:10Z, size = 97941, hashes = { sha256 = "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
marker = "sys_platform == 'win32'"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", upload-time = 2022-10-25T02:36:22Z, size = 27697, hashes = { sha256 = "08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", upload-time = 2022-10-25T02:36:20Z, size = 25335, hashes = { sha256 = "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "flask"
|
||||
version = "3.0.2"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3f/e0/a89e8120faea1edbfca1a9b171cff7f2bf62ec860bbafcb2c2387c0317be/flask-3.0.2.tar.gz", upload-time = 2024-02-03T21:11:44Z, size = 675248, hashes = { sha256 = "822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/93/a6/aa98bfe0eb9b8b15d36cdfd03c8ca86a03968a87f27ce224fb4f766acb23/flask-3.0.2-py3-none-any.whl", upload-time = 2024-02-03T21:11:42Z, size = 101300, hashes = { sha256 = "3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "itsdangerous"
|
||||
version = "2.1.2"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", upload-time = 2022-03-24T15:12:15Z, size = 56143, hashes = { sha256 = "5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", upload-time = 2022-03-24T15:12:13Z, size = 15749, hashes = { sha256 = "2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "jinja2"
|
||||
version = "3.1.3"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b2/5e/3a21abf3cd467d7876045335e681d276ac32492febe6d98ad89562d1a7e1/Jinja2-3.1.3.tar.gz", upload-time = 2024-01-10T23:12:21Z, size = 268261, hashes = { sha256 = "ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" } }
|
||||
wheels = [{ name = "jinja2-3.1.3-py3-none-any.whl", url = "https://files.pythonhosted.org/packages/30/6d/6de6be2d02603ab56e72997708809e8a5b0fbfee080735109b40a3564843/Jinja2-3.1.3-py3-none-any.whl", upload-time = 2024-01-10T23:12:19Z, size = 133236, hashes = { sha256 = "7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.5"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", upload-time = 2024-02-02T16:31:22Z, size = 19384, hashes = { sha256 = "d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b" } }
|
||||
wheels = [
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", upload-time = 2024-02-02T16:30:33Z, size = 18215, hashes = { sha256 = "8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", upload-time = 2024-02-02T16:30:34Z, size = 14069, hashes = { sha256 = "3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-02-02T16:30:35Z, size = 29452, hashes = { sha256 = "ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-02-02T16:30:36Z, size = 28462, hashes = { sha256 = "f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2024-02-02T16:30:37Z, size = 27869, hashes = { sha256 = "ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", upload-time = 2024-02-02T16:30:39Z, size = 33906, hashes = { sha256 = "d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", upload-time = 2024-02-02T16:30:40Z, size = 32296, hashes = { sha256 = "bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", upload-time = 2024-02-02T16:30:42Z, size = 33038, hashes = { sha256 = "58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-win32.whl", url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", upload-time = 2024-02-02T16:30:43Z, size = 16572, hashes = { sha256 = "8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad" } },
|
||||
{ name = "markupsafe-2.1.5-cp312-cp312-win_amd64.whl", url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", upload-time = 2024-02-02T16:30:44Z, size = 17127, hashes = { sha256 = "823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb" } },
|
||||
]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", upload-time = 2024-01-23T06:33:00Z, size = 39115, hashes = { sha256 = "e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", upload-time = 2024-01-23T06:32:58Z, size = 19863, hashes = { sha256 = "f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "werkzeug"
|
||||
version = "3.0.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0d/cc/ff1904eb5eb4b455e442834dabf9427331ac0fa02853bf83db817a7dd53d/werkzeug-3.0.1.tar.gz", upload-time = 2023-10-24T20:57:50Z, size = 801436, hashes = { sha256 = "507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c3/fc/254c3e9b5feb89ff5b9076a23218dafbc99c96ac5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl", upload-time = 2023-10-24T20:57:47Z, size = 226669, hashes = { sha256 = "90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 10 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_project_extra() -> 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 = ["typing-extensions"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
async = ["anyio==3.7.0"]
|
||||
pytest = ["iniconfig"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", upload-time = 2024-02-25T22:12:49Z, size = 77558, hashes = { sha256 = "b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", upload-time = 2024-02-25T22:12:47Z, size = 33926, hashes = { sha256 = "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
"#);
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml").arg("--extra").arg("pytest").arg("--extra").arg("async").arg("--no-extra").arg("pytest"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml --extra pytest --extra async --no-extra pytest
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/fefbf7e78ab3b805dec67d698dc18dd505af7a18a8dd08868c9b4fa736b5/anyio-3.7.0.tar.gz", upload-time = 2023-05-27T11:12:46Z, size = 142737, hashes = { sha256 = "275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/68/fe/7ce1926952c8a403b35029e194555558514b365ad77d75125f521a2bec62/anyio-3.7.0-py3-none-any.whl", upload-time = 2023-05-27T11:12:44Z, size = 80873, hashes = { sha256 = "eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", upload-time = 2024-02-25T22:12:49Z, size = 77558, hashes = { sha256 = "b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", upload-time = 2024-02-25T22:12:47Z, size = 33926, hashes = { sha256 = "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
"#);
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml").arg("--extra").arg("pytest"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml --extra pytest
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", upload-time = 2023-01-07T11:08:11Z, size = 4646, hashes = { sha256 = "2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", upload-time = 2023-01-07T11:08:09Z, size = 5892, hashes = { sha256 = "b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", upload-time = 2024-02-25T22:12:49Z, size = 77558, hashes = { sha256 = "b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", upload-time = 2024-02-25T22:12:47Z, size = 33926, hashes = { sha256 = "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
"#);
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml").arg("--all-extras"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml --all-extras
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/fefbf7e78ab3b805dec67d698dc18dd505af7a18a8dd08868c9b4fa736b5/anyio-3.7.0.tar.gz", upload-time = 2023-05-27T11:12:46Z, size = 142737, hashes = { sha256 = "275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/68/fe/7ce1926952c8a403b35029e194555558514b365ad77d75125f521a2bec62/anyio-3.7.0-py3-none-any.whl", upload-time = 2023-05-27T11:12:44Z, size = 80873, hashes = { sha256 = "eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", upload-time = 2023-01-07T11:08:11Z, size = 4646, hashes = { sha256 = "2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", upload-time = 2023-01-07T11:08:09Z, size = 5892, hashes = { sha256 = "b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", upload-time = 2024-02-25T22:12:49Z, size = 77558, hashes = { sha256 = "b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", upload-time = 2024-02-25T22:12:47Z, size = 33926, hashes = { sha256 = "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
"#);
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml").arg("--all-extras").arg("--no-extra").arg("pytest"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml --all-extras --no-extra pytest
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "3.7.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/b3/fefbf7e78ab3b805dec67d698dc18dd505af7a18a8dd08868c9b4fa736b5/anyio-3.7.0.tar.gz", upload-time = 2023-05-27T11:12:46Z, size = 142737, hashes = { sha256 = "275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/68/fe/7ce1926952c8a403b35029e194555558514b365ad77d75125f521a2bec62/anyio-3.7.0-py3-none-any.whl", upload-time = 2023-05-27T11:12:44Z, size = 80873, hashes = { sha256 = "eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
directory = { path = ".", editable = true }
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", upload-time = 2024-02-25T22:12:49Z, size = 77558, hashes = { sha256 = "b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", upload-time = 2024-02-25T22:12:47Z, size = 33926, hashes = { sha256 = "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_git_dependency() -> 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 = ["uv-public-pypackage"]
|
||||
|
||||
[tool.uv.sources]
|
||||
uv-public-pypackage = { git = "git+https://github.com/astral-test/uv-public-pypackage" }
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "uv-public-pypackage"
|
||||
version = "0.1.0"
|
||||
vcs = { type = "git", url = "https://github.com/astral-test/uv-public-pypackage", commit-id = "b270df1a2fb5d012294e9aaf05e7e0bab1e6a389" }
|
||||
|
||||
----- stderr -----
|
||||
Resolved 2 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_wheel_url() -> 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 @ https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "4.3.0"
|
||||
archive = { url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", hashes = { sha256 = "048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8" } }
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 4 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_sdist_url() -> 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 @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "4.3.0"
|
||||
archive = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hashes = { sha256 = "f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6" } }
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 4 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep_751_sdist_url_subdirectory() -> 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 = ["root"]
|
||||
|
||||
[tool.uv.sources]
|
||||
root = { url = "https://github.com/user-attachments/files/18216295/subdirectory-test.tar.gz", subdirectory = "packages/root" }
|
||||
"#,
|
||||
)?;
|
||||
|
||||
context.lock().assert().success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.export().arg("--format").arg("pylock.toml"), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export --cache-dir [CACHE_DIR] --format pylock.toml
|
||||
lock-version = "1.0"
|
||||
created-by = "uv"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[packages]]
|
||||
name = "anyio"
|
||||
version = "4.3.0"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", upload-time = 2024-02-19T08:36:28Z, size = 159642, hashes = { sha256 = "f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl", upload-time = 2024-02-19T08:36:26Z, size = 85584, hashes = { sha256 = "048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", upload-time = 2023-11-25T15:40:54Z, size = 175426, hashes = { sha256 = "9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", upload-time = 2023-11-25T15:40:52Z, size = 61567, hashes = { sha256 = "c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" } }]
|
||||
|
||||
[[packages]]
|
||||
name = "root"
|
||||
version = "0.0.1"
|
||||
archive = { url = "https://github.com/user-attachments/files/18216295/subdirectory-test.tar.gz#subdirectory=packages/root", subdirectory = "packages/root", hashes = { sha256 = "24b55efee28d08ad3cdc58903e359e820601baa6a4a4b3424311541ebcfb09d3" } }
|
||||
|
||||
[[packages]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
index = "https://pypi.org/simple"
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } }
|
||||
wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }]
|
||||
|
||||
----- stderr -----
|
||||
Resolved 5 packages in [TIME]
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2330,13 +2330,15 @@ uv export [OPTIONS]
|
|||
</ul>
|
||||
</dd><dt id="uv-export--format"><a href="#uv-export--format"><code>--format</code></a> <i>format</i></dt><dd><p>The format to which <code>uv.lock</code> should be exported.</p>
|
||||
|
||||
<p>At present, only <code>requirements-txt</code> is supported.</p>
|
||||
<p>Supports both <code>requirements.txt</code> and <code>pylock.toml</code> (PEP 751) output formats.</p>
|
||||
|
||||
<p>[default: requirements.txt]</p>
|
||||
<p>Possible values:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>requirements.txt</code>: Export in <code>requirements.txt</code> format</li>
|
||||
|
||||
<li><code>pylock.toml</code>: Export in <code>pylock.toml</code> format</li>
|
||||
</ul>
|
||||
</dd><dt id="uv-export--frozen"><a href="#uv-export--frozen"><code>--frozen</code></a></dt><dd><p>Do not update the <code>uv.lock</code> before exporting.</p>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue