mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-14 12:29:04 +00:00
Use portable paths when serializing sources (#7504)
## Summary Closes https://github.com/astral-sh/uv/issues/7493.
This commit is contained in:
parent
1379b530f6
commit
e36cc99b0d
8 changed files with 70 additions and 36 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4863,6 +4863,7 @@ dependencies = [
|
|||
"fs2",
|
||||
"junction",
|
||||
"path-slash",
|
||||
"schemars",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
|
|
|
@ -88,20 +88,20 @@ impl LoweredRequirement {
|
|||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||
return Err(LoweringError::ConflictingUrls);
|
||||
}
|
||||
git_source(&git, subdirectory, rev, tag, branch)?
|
||||
git_source(&git, subdirectory.map(PathBuf::from), rev, tag, branch)?
|
||||
}
|
||||
Source::Url { url, subdirectory } => {
|
||||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||
return Err(LoweringError::ConflictingUrls);
|
||||
}
|
||||
url_source(url, subdirectory)?
|
||||
url_source(url, subdirectory.map(PathBuf::from))?
|
||||
}
|
||||
Source::Path { path, editable } => {
|
||||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||
return Err(LoweringError::ConflictingUrls);
|
||||
}
|
||||
path_source(
|
||||
path,
|
||||
PathBuf::from(path),
|
||||
origin,
|
||||
project_dir,
|
||||
workspace.install_path(),
|
||||
|
@ -203,19 +203,25 @@ impl LoweredRequirement {
|
|||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||
return Err(LoweringError::ConflictingUrls);
|
||||
}
|
||||
git_source(&git, subdirectory, rev, tag, branch)?
|
||||
git_source(&git, subdirectory.map(PathBuf::from), rev, tag, branch)?
|
||||
}
|
||||
Source::Url { url, subdirectory } => {
|
||||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||
return Err(LoweringError::ConflictingUrls);
|
||||
}
|
||||
url_source(url, subdirectory)?
|
||||
url_source(url, subdirectory.map(PathBuf::from))?
|
||||
}
|
||||
Source::Path { path, editable } => {
|
||||
if matches!(requirement.version_or_url, Some(VersionOrUrl::Url(_))) {
|
||||
return Err(LoweringError::ConflictingUrls);
|
||||
}
|
||||
path_source(path, Origin::Project, dir, dir, editable.unwrap_or(false))?
|
||||
path_source(
|
||||
PathBuf::from(path),
|
||||
Origin::Project,
|
||||
dir,
|
||||
dir,
|
||||
editable.unwrap_or(false),
|
||||
)?
|
||||
}
|
||||
Source::Registry { index } => registry_source(&requirement, index)?,
|
||||
Source::Workspace { .. } => {
|
||||
|
|
|
@ -22,6 +22,7 @@ encoding_rs_io = { workspace = true }
|
|||
fs-err = { workspace = true }
|
||||
fs2 = { workspace = true }
|
||||
path-slash = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
tokio = { workspace = true, optional = true}
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
@ -301,6 +301,17 @@ pub struct PortablePath<'a>(&'a Path);
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PortablePathBuf(PathBuf);
|
||||
|
||||
#[cfg(feature = "schemars")]
|
||||
impl schemars::JsonSchema for PortablePathBuf {
|
||||
fn schema_name() -> String {
|
||||
PathBuf::schema_name()
|
||||
}
|
||||
|
||||
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||
PathBuf::json_schema(_gen)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for PortablePath<'_> {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.0
|
||||
|
|
|
@ -16,7 +16,7 @@ workspace = true
|
|||
pep440_rs = { workspace = true }
|
||||
pep508_rs = { workspace = true }
|
||||
pypi-types = { workspace = true }
|
||||
uv-fs = { workspace = true, features = ["tokio"] }
|
||||
uv-fs = { workspace = true, features = ["tokio", "schemars"] }
|
||||
uv-git = { workspace = true }
|
||||
uv-macros = { workspace = true }
|
||||
uv-normalize = { workspace = true }
|
||||
|
|
|
@ -17,7 +17,7 @@ use url::Url;
|
|||
|
||||
use pep440_rs::{Version, VersionSpecifiers};
|
||||
use pypi_types::{RequirementSource, SupportedEnvironments, VerbatimParsedUrl};
|
||||
use uv_fs::relative_to;
|
||||
use uv_fs::{relative_to, PortablePathBuf};
|
||||
use uv_git::GitReference;
|
||||
use uv_macros::OptionsMetadata;
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
|
@ -413,7 +413,7 @@ pub enum Source {
|
|||
/// The repository URL (without the `git+` prefix).
|
||||
git: Url,
|
||||
/// The path to the directory with the `pyproject.toml`, if it's not in the archive root.
|
||||
subdirectory: Option<PathBuf>,
|
||||
subdirectory: Option<PortablePathBuf>,
|
||||
// Only one of the three may be used; we'll validate this later and emit a custom error.
|
||||
rev: Option<String>,
|
||||
tag: Option<String>,
|
||||
|
@ -430,13 +430,13 @@ pub enum Source {
|
|||
url: Url,
|
||||
/// For source distributions, the path to the directory with the `pyproject.toml`, if it's
|
||||
/// not in the archive root.
|
||||
subdirectory: Option<PathBuf>,
|
||||
subdirectory: Option<PortablePathBuf>,
|
||||
},
|
||||
/// The path to a dependency, either a wheel (a `.whl` file), source distribution (a `.zip` or
|
||||
/// `.tar.gz` file), or source tree (i.e., a directory containing a `pyproject.toml` or
|
||||
/// `setup.py` file in the root).
|
||||
Path {
|
||||
path: PathBuf,
|
||||
path: PortablePathBuf,
|
||||
/// `false` by default.
|
||||
editable: Option<bool>,
|
||||
},
|
||||
|
@ -454,12 +454,12 @@ pub enum Source {
|
|||
/// A catch-all variant used to emit precise error messages when deserializing.
|
||||
CatchAll {
|
||||
git: String,
|
||||
subdirectory: Option<PathBuf>,
|
||||
subdirectory: Option<PortablePathBuf>,
|
||||
rev: Option<String>,
|
||||
tag: Option<String>,
|
||||
branch: Option<String>,
|
||||
url: String,
|
||||
path: PathBuf,
|
||||
path: PortablePathBuf,
|
||||
index: String,
|
||||
workspace: bool,
|
||||
},
|
||||
|
@ -534,15 +534,17 @@ impl Source {
|
|||
RequirementSource::Path { install_path, .. }
|
||||
| RequirementSource::Directory { install_path, .. } => Source::Path {
|
||||
editable,
|
||||
path: relative_to(&install_path, root)
|
||||
.or_else(|_| std::path::absolute(&install_path))
|
||||
.map_err(SourceError::Absolute)?,
|
||||
path: PortablePathBuf::from(
|
||||
relative_to(&install_path, root)
|
||||
.or_else(|_| std::path::absolute(&install_path))
|
||||
.map_err(SourceError::Absolute)?,
|
||||
),
|
||||
},
|
||||
RequirementSource::Url {
|
||||
subdirectory, url, ..
|
||||
} => Source::Url {
|
||||
url: url.to_url(),
|
||||
subdirectory,
|
||||
subdirectory: subdirectory.map(PortablePathBuf::from),
|
||||
},
|
||||
RequirementSource::Git {
|
||||
repository,
|
||||
|
@ -566,7 +568,7 @@ impl Source {
|
|||
tag,
|
||||
branch,
|
||||
git: repository,
|
||||
subdirectory,
|
||||
subdirectory: subdirectory.map(PortablePathBuf::from),
|
||||
}
|
||||
} else {
|
||||
Source::Git {
|
||||
|
@ -574,7 +576,7 @@ impl Source {
|
|||
tag,
|
||||
branch,
|
||||
git: repository,
|
||||
subdirectory,
|
||||
subdirectory: subdirectory.map(PortablePathBuf::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use assert_cmd::assert::OutputAssertExt;
|
|||
use assert_fs::prelude::*;
|
||||
use indoc::indoc;
|
||||
use insta::assert_snapshot;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::common::{decode_token, packse_index_url};
|
||||
use common::{uv_snapshot, TestContext};
|
||||
|
@ -2019,7 +2020,7 @@ fn add_path() -> Result<()> {
|
|||
build-backend = "setuptools.build_meta"
|
||||
"#})?;
|
||||
|
||||
let child = workspace.child("child");
|
||||
let child = workspace.child("packages").child("child");
|
||||
child.child("pyproject.toml").write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "child"
|
||||
|
@ -2032,7 +2033,7 @@ fn add_path() -> Result<()> {
|
|||
build-backend = "setuptools.build_meta"
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.add().arg("./child").current_dir(workspace.path()), @r###"
|
||||
uv_snapshot!(context.filters(), context.add().arg(Path::new("packages").join("child")).current_dir(workspace.path()), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
@ -2043,7 +2044,7 @@ fn add_path() -> Result<()> {
|
|||
Resolved 2 packages in [TIME]
|
||||
Prepared 2 packages in [TIME]
|
||||
Installed 2 packages in [TIME]
|
||||
+ child==0.1.0 (from file://[TEMP_DIR]/workspace/child)
|
||||
+ child==0.1.0 (from file://[TEMP_DIR]/workspace/packages/child)
|
||||
+ parent==0.1.0 (from file://[TEMP_DIR]/workspace)
|
||||
"###);
|
||||
|
||||
|
@ -2067,7 +2068,7 @@ fn add_path() -> Result<()> {
|
|||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.uv.sources]
|
||||
child = { path = "child" }
|
||||
child = { path = "packages/child" }
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
@ -2089,7 +2090,7 @@ fn add_path() -> Result<()> {
|
|||
[[package]]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
source = { directory = "child" }
|
||||
source = { directory = "packages/child" }
|
||||
|
||||
[[package]]
|
||||
name = "parent"
|
||||
|
@ -2100,7 +2101,7 @@ fn add_path() -> Result<()> {
|
|||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "child", directory = "child" }]
|
||||
requires-dist = [{ name = "child", directory = "packages/child" }]
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
|
34
uv.schema.json
generated
34
uv.schema.json
generated
|
@ -1230,9 +1230,13 @@
|
|||
},
|
||||
"subdirectory": {
|
||||
"description": "The path to the directory with the `pyproject.toml`, if it's not in the archive root.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/String"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": {
|
||||
|
@ -1253,9 +1257,13 @@
|
|||
"properties": {
|
||||
"subdirectory": {
|
||||
"description": "For source distributions, the path to the directory with the `pyproject.toml`, if it's not in the archive root.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/String"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
|
@ -1280,7 +1288,7 @@
|
|||
]
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/String"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -1336,7 +1344,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/String"
|
||||
},
|
||||
"rev": {
|
||||
"type": [
|
||||
|
@ -1345,9 +1353,13 @@
|
|||
]
|
||||
},
|
||||
"subdirectory": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/String"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tag": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue