diff --git a/crates/socket-patch-core/src/utils/python_lock.rs b/crates/socket-patch-core/src/utils/python_lock.rs index bc7d01d3e..37d69fb96 100644 --- a/crates/socket-patch-core/src/utils/python_lock.rs +++ b/crates/socket-patch-core/src/utils/python_lock.rs @@ -886,6 +886,37 @@ fn plan_python_lock_rewrite( if pep751 && (package.contains_key("vcs") || package.contains_key("directory")) { return Ok(None); } + // An entry resolved from a direct URL (uv.lock `source = { url }`, a + // pylock `archive = { url }`) is the user's own source choice unless it + // is socket-patch's earlier hosted artifact for this release (#767). + let direct_url = if pep751 { + package + .get("archive") + .and_then(Item::as_table_like) + .and_then(|archive| archive.get("url")) + .and_then(Item::as_str) + } else { + // Both spellings: `source = { url = … }` and uv 0.2's string + // `source = "direct+…"`. + UvSource::of(package).and_then(UvSource::url) + }; + if let Some(url) = direct_url { + let ours = match artifact { + ArtifactSource::Url(location) => { + url == location + || crate::utils::python_script::same_hosted_artifact( + url, location, &name, version, + ) + } + ArtifactSource::Path(_) => false, + }; + if !ours { + return Err(format!( + "the lock resolves {name}=={version} from the direct reference {url}; refusing \ + to overwrite a user-authored source" + )); + } + } let location = artifact.location(); let filename = location .split(['?', '#']) @@ -1220,6 +1251,49 @@ source = { registry = "https://pypi.org/simple" } wheels = [{ url = "https://pypi.org/urllib3-2.0.0-py3-none-any.whl", hash = "sha256:other" }] "#; + /// #767: a lock entry resolved from the user's own direct URL (uv.lock + /// `source = { url }`, pylock `archive = { url }`) is refused in both + /// modes; the hosted rewrite's own earlier artifact is still replaced. + #[test] + fn user_direct_url_entries_are_refused() { + const USER: &str = + "https://files.pythonhosted.org/packages/d9/urllib3-1.26.18-py2.py3-none-any.whl"; + let uv_lock = format!( + "version = 1\nrequires-python = \">=3.9\"\n\n[[package]]\nname = \"urllib3\"\n\ + version = \"1.26.18\"\nsource = {{ url = \"{USER}\" }}\n\ + wheels = [{{ url = \"{USER}\", hash = \"sha256:aa\" }}]\n" + ); + let pylock = format!( + "lock-version = \"1.0\"\ncreated-by = \"uv\"\n\n[[packages]]\nname = \"urllib3\"\n\ + version = \"1.26.18\"\narchive = {{ url = \"{USER}\", hashes = {{ sha256 = \"aa\" }} }}\n" + ); + let legacy = format!( + "version = 1\nrequires-python = \">=3.9\"\n\n[[distribution]]\nname = \"urllib3\"\n\ + version = \"1.26.18\"\nsource = \"direct+{USER}\"\n\ + sdist = {{ url = \"{USER}\", hash = \"sha256:aa\" }}\n" + ); + for lock in [&uv_lock, &pylock, &legacy] { + for artifact in [ + ArtifactSource::Url(URL), + ArtifactSource::Path(".socket/vendor/pypi/u/urllib3-1.26.18-py2.py3-none-any.whl"), + ] { + let err = + rewrite_python_lock(lock, "urllib3", "1.26.18", artifact, SHA256).unwrap_err(); + assert!(err.contains("user-authored source"), "{err}"); + } + // The hosted rewrite's own artifact (a re-scan) is not the user's. + let ours = lock.replace(USER, URL); + assert!(rewrite_python_lock( + &ours, + "urllib3", + "1.26.18", + ArtifactSource::Url(URL), + SHA256 + ) + .is_ok()); + } + } + #[test] fn hosted_native_uses_direct_source_and_keeps_other_versions() { let rewritten = rewrite_python_lock( diff --git a/crates/socket-patch-core/src/utils/python_script.rs b/crates/socket-patch-core/src/utils/python_script.rs index 3374bfc74..cf750924a 100644 --- a/crates/socket-patch-core/src/utils/python_script.rs +++ b/crates/socket-patch-core/src/utils/python_script.rs @@ -4,7 +4,7 @@ use toml_edit::{Array, DocumentMut, InlineTable, Item, Table, Value}; use crate::crawlers::python_crawler::canonicalize_pypi_name; use crate::utils::python_lock::ArtifactSource; -use crate::vendor::common::{pep508_name, pyproject_dependency_specs}; +use crate::vendor::common::{is_pep508_direct_reference, pep508_name, pyproject_dependency_specs}; pub(crate) fn script_metadata(text: &str) -> Result<(Range, String), String> { let mut offset = 0; @@ -65,7 +65,12 @@ fn dependency_name(specifier: &str) -> String { /// package that `current` may replace: a rotated grant token on the same /// artifact path, or (the shared PyPI recognizer) a superseding patch uuid /// for the same name and version on Socket's patch server. -fn same_hosted_artifact(previous: &str, current: &str, name: &str, version: &str) -> bool { +pub(crate) fn same_hosted_artifact( + previous: &str, + current: &str, + name: &str, + version: &str, +) -> bool { if crate::vendor::lock_inventory::pypi::replaceable_hosted_pin(previous, current, name, version) { return true; @@ -230,6 +235,28 @@ fn rewrite_sources( Ok(()) } +/// Refuse when one of `specs` declares `name` (canonical) as a PEP 508 +/// direct reference (`six @ https://…`, `six @ git+…`): that is the user's +/// own source, the `[project]` spelling of a `[tool.uv.sources]` entry, and +/// a source added beside it would silently replace it (hosted) or leave a +/// lock uv rejects (vendored) (#767). +fn refuse_direct_reference<'a>( + file: &str, + name: &str, + specs: impl IntoIterator, +) -> Result<(), String> { + match specs + .into_iter() + .find(|spec| dependency_name(spec) == name && is_pep508_direct_reference(spec)) + { + Some(spec) => Err(format!( + "{file} declares {name} as the PEP 508 direct reference {spec:?}; refusing to \ + overwrite a user-authored source" + )), + None => Ok(()), + } +} + /// The comment the hosted rewrite puts on the line above each /// `override-dependencies` entry it adds. v5 hosted mode keeps no ledger, /// so this comment is the only evidence the upstream restore has that the @@ -299,6 +326,20 @@ pub fn rewrite_project_metadata( .and_then(Item::as_table_like) .and_then(|tool| tool.get("uv")) .and_then(Item::as_table_like); + let legacy_dev = tool_uv + .and_then(|uv| uv.get("dev-dependencies")) + .and_then(Item::as_array) + .into_iter() + .flatten() + .filter_map(Value::as_str); + refuse_direct_reference( + "pyproject.toml", + &name, + pyproject_dependency_specs(&document) + .into_iter() + .map(|(_, spec)| spec) + .chain(legacy_dev), + )?; let direct = pyproject_dependency_specs(&document) .into_iter() .any(|(_, spec)| dependency_name(spec) == name) @@ -337,6 +378,16 @@ pub fn rewrite_script_metadata( .parse() .map_err(|error| format!("invalid script metadata: {error}"))?; let name = canonicalize_pypi_name(name); + refuse_direct_reference( + "the script metadata", + &name, + document + .get("dependencies") + .and_then(Item::as_array) + .into_iter() + .flatten() + .filter_map(Value::as_str), + )?; let direct = contains_dependency(document.get("dependencies"), &name); rewrite_sources( &mut document, @@ -367,6 +418,57 @@ mod tests { assert!(out.contains("# dependencies = [\"a\"]\r\n# ///"), "{out:?}"); } + /// #767: a PEP 508 direct reference to the patched package, wherever + /// the project or script declares it, is the user's own source: the + /// rewrite refuses instead of adding a `[tool.uv.sources]` entry beside it. + #[test] + fn direct_references_to_the_package_are_refused() { + const URL: &str = "https://patch.socket.dev/six-1.16.0-py2.py3-none-any.whl"; + const WHEEL: &str = + "https://files.pythonhosted.org/packages/d9/six-1.16.0-py2.py3-none-any.whl"; + let head = "[project]\nname = \"app\"\nversion = \"0.1.0\"\n"; + for (deps, tail) in [ + (format!("[\"six @ {WHEEL}\", \"idna==3.7\"]"), String::new()), + ( + "[\"Six[x]@git+https://github.com/benjaminp/six@1.16.0\"]".to_string(), + String::new(), + ), + ( + "[\"idna==3.7\"]".to_string(), + format!("\n[project.optional-dependencies]\nx = [\"six @ {WHEEL}\"]\n"), + ), + ( + "[\"idna==3.7\"]".to_string(), + format!("\n[dependency-groups]\ndev = [\"six @ {WHEEL}\"]\n"), + ), + ( + "[\"idna==3.7\"]".to_string(), + format!("\n[tool.uv]\ndev-dependencies = [\"six @ {WHEEL}\"]\n"), + ), + ] { + let text = format!("{head}dependencies = {deps}\n{tail}"); + let err = rewrite_project_metadata(&text, "six", "1.16.0", ArtifactSource::Url(URL)) + .unwrap_err(); + assert!(err.contains("PEP 508 direct reference"), "{err}\n{text}"); + } + let script = + format!("# /// script\n# dependencies = [\"six @ {WHEEL}\"]\n# ///\nimport six\n"); + for artifact in [ + ArtifactSource::Url(URL), + ArtifactSource::Path("w/six-1.16.0-py2.py3-none-any.whl"), + ] { + let err = rewrite_script_metadata(&script, "six", "1.16.0", artifact).unwrap_err(); + assert!(err.contains("PEP 508 direct reference"), "{err}"); + } + // Another package's direct reference is not this one's source. + let other = format!("{head}dependencies = [\"six==1.16.0\", \"idna @ {WHEEL}\"]\n"); + assert!( + rewrite_project_metadata(&other, "six", "1.16.0", ArtifactSource::Url(URL)) + .unwrap() + .is_some() + ); + } + #[test] fn sources_are_paired_without_changing_the_script() { let script = "#!/usr/bin/env python3\n# /// script\n# dependencies = [\"urllib3==1.26.18\"]\n# ///\nprint('preserved')\n"; diff --git a/crates/socket-patch-core/src/vendor/common.rs b/crates/socket-patch-core/src/vendor/common.rs index 485fd1770..e770af59b 100644 --- a/crates/socket-patch-core/src/vendor/common.rs +++ b/crates/socket-patch-core/src/vendor/common.rs @@ -609,6 +609,27 @@ pub(crate) fn pep508_name(spec: &str) -> &str { &s[..end] } +/// Whether a dependency spec is a PEP 508 direct reference +/// (`name[extras] @ `: an `https://` / `file://` archive, a +/// `git+…` checkout, …) rather than a registry requirement. Such a +/// declaration is the user's own source choice, which neither writer may +/// overwrite (#767). +pub(crate) fn is_pep508_direct_reference(spec: &str) -> bool { + let spec = spec.trim_start(); + let name = pep508_name(spec); + if name.is_empty() { + return false; + } + let mut rest = spec[name.len()..].trim_start(); + if rest.starts_with('[') { + let Some(end) = rest.find(']') else { + return false; + }; + rest = rest[end + 1..].trim_start(); + } + rest.starts_with('@') +} + /// The lock's `[[package]]` tables whose `name` canonicalizes (PEP 503) to /// `canon_name` — the poetry/pdm target-guard probe (uv records names /// pre-canonicalized and counts them directly instead). diff --git a/crates/socket-patch-core/src/vendor/pypi_uv.rs b/crates/socket-patch-core/src/vendor/pypi_uv.rs index 51ac4e547..412ab1710 100644 --- a/crates/socket-patch-core/src/vendor/pypi_uv.rs +++ b/crates/socket-patch-core/src/vendor/pypi_uv.rs @@ -385,6 +385,39 @@ pub(super) fn check_target_guards( } } + // A PEP 508 direct reference (`six @ https://…`, `six @ git+…`) in + // `[project]` / `[dependency-groups]` / the legacy `dev-dependencies` is + // the same user-authored source spelled in the requirement itself: a + // `[tool.uv.sources]` path beside it leaves the lock's root requirement + // carrying both `url` and `path`, which `uv sync --locked` rejects + // (#767). + let legacy_dev = p + .pyproject + .get("tool") + .and_then(|t| item_get(t, "uv")) + .and_then(|u| item_get(u, "dev-dependencies")) + .and_then(Item::as_array) + .into_iter() + .flatten() + .filter_map(Value::as_str); + if let Some(spec) = pyproject_dependency_specs(&p.pyproject) + .into_iter() + .map(|(_, spec)| spec) + .chain(legacy_dev) + .find(|spec| { + canonicalize_pypi_name(pep508_name(spec)) == canon_name + && super::common::is_pep508_direct_reference(spec) + }) + { + return Err(( + "pypi_uv_source_already_exists", + format!( + "pyproject.toml declares {canon_name} as the PEP 508 direct reference {spec:?}; \ + refusing to overwrite a user-authored source" + ), + )); + } + // A user override pins this package already; layering ours on top would // change resolution behind the user's back. if let Some(overrides) = p @@ -2786,6 +2819,34 @@ wheels = [ assert_eq!(err.0, "pypi_uv_source_already_exists"); } + /// #767: a PEP 508 direct reference to the target in any declaration + /// table refuses before the wheel is built or anything is written. + #[tokio::test] + async fn guards_refuse_a_direct_reference_declaration() { + const WHEEL: &str = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl"; + for pyproject in [ + DIRECT_REGISTRY_PYPROJECT.replace("\"six==1.16.0\"", &format!("\"six @ {WHEEL}\"")), + DIRECT_REGISTRY_PYPROJECT.replace( + "\"six==1.16.0\"", + "\"six @ git+https://github.com/benjaminp/six@1.16.0\"", + ), + format!( + "{}\n[dependency-groups]\ndev = [\"six @ {WHEEL}\"]\n", + DIRECT_REGISTRY_PYPROJECT.replace("[\"six==1.16.0\"]", "[]") + ), + format!( + "{}\n[project.optional-dependencies]\nx = [\"six @ {WHEEL}\"]\n", + DIRECT_REGISTRY_PYPROJECT.replace("[\"six==1.16.0\"]", "[]") + ), + ] { + let tmp = write_pair(&pyproject, DIRECT_REGISTRY_LOCK).await; + let p = load_uv_project(tmp.path()).await.unwrap(); + let err = check_target_guards(&p, "six", UUID).unwrap_err(); + assert_eq!(err.0, "pypi_uv_source_already_exists", "{pyproject}"); + assert!(err.1.contains("PEP 508 direct reference"), "{}", err.1); + } + } + #[tokio::test] async fn untested_lock_revision_is_a_warning_not_a_refusal() { let tmp = write_pair( diff --git a/crates/socket-patch-core/tests/equivalence/python_lock_rewrite.golden b/crates/socket-patch-core/tests/equivalence/python_lock_rewrite.golden index e0e37c16d..924aa0b70 100644 --- a/crates/socket-patch-core/tests/equivalence/python_lock_rewrite.golden +++ b/crates/socket-patch-core/tests/equivalence/python_lock_rewrite.golden @@ -1,12 +1,12 @@ # One seeded uv / pylock / PEP 723 lock set + overrides + wheel metadata. # 0-4 cd5cd36a37fa8c9b 48a0e144464b8f8a -5-9 f58e23b53c07f972 75999b38c69c4eb8 -10-14 63a0aa035a8a4e90 11acef318dd3f602 +5-9 f58e23b53c07f972 2e6a9ab6c49b5af3 +10-14 63a0aa035a8a4e90 2941d973ad6169bd 15-19 7cf3d76e36752625 590f305643295efc 20-24 c7c648dc10c3c7cd 9fa1deaf96f07fe2 25-29 831189dc733b8849 84549312d5b50708 -30-34 dcb109d643382adf ade5041ad97eb6f3 +30-34 dcb109d643382adf 003703da5df0e30f 35-39 99a313947928f593 040d41e8e286d342 40-44 cf5611d067871c91 bec1d86832e78be6 45-49 04f0e41a9e302f59 703a8fae640714d8 @@ -14,14 +14,14 @@ 55-59 daf9f41b5decbf2c c8555affaeef1b02 60-64 58413f6f2f266d0d e269d6e79181da6a 65-69 cd4f77526796ec70 ffec6efe319265a7 -70-74 3befe3b6747c7c5d 5db4b142bfc674a2 +70-74 3befe3b6747c7c5d ca9f27350d04be89 75-79 f704740390513318 42df63da4590a254 -80-84 6bb2704eadd90bbb 5ca41094d0424e3d -85-89 2b41ca85da12924d 4fa8ca1b59e12b20 +80-84 6bb2704eadd90bbb d31e655a72986955 +85-89 2b41ca85da12924d 5130902a838aec0f 90-94 b4f4d21d57b4e3e6 0f3435d61d7a36c1 -95-99 1fcae5cb34b3557b c47c75663ff48676 +95-99 1fcae5cb34b3557b 4eb4e16e7ffeb917 100-104 22d4b081083ec3cf c96c490301f692ed -105-109 b33352ae7c78a21f bea4bd35a69bdd15 +105-109 b33352ae7c78a21f 819b31e2f7334ee6 110-114 459ff3efdff5f2cc 81712bc6d977c7c0 115-119 291d724de0819b8d f5448022649e1124 120-124 21a80a5bc6b9e421 1eedfb98cce3fef5 @@ -32,48 +32,48 @@ 145-149 e366e05fd9c0ef0b fb53672d3a8f3bd1 150-154 7f552bc377ac439b d1b393713d916f80 155-159 5c987ad06f4c2cf3 853fcb9da6efea74 -160-164 2fd97b5cdeea8344 4a2af11d90d6bbc2 -165-169 8f16946d73613e52 4ee31b57d3a5fb45 +160-164 2fd97b5cdeea8344 602b3155438aa1b5 +165-169 8f16946d73613e52 77dc4a7d5887d3c0 170-174 72c1e1cb26500151 07da3560a0bf3737 -175-179 4333d230209fdfdd 1ce61cb74433885a +175-179 4333d230209fdfdd 5f85b72bba81cfc7 180-184 70af4e6d0a9804eb a50449016f534bed 185-189 6073f94564a728c8 42f8708863320edf 190-194 0976869c3d0f4aeb 03f687f9a1ef3754 -195-199 6e26da934df4169e 33c08ab4ccc5ce89 +195-199 6e26da934df4169e 61889d549f7bf5b1 200-204 fbb5bb450083c437 31aad032ae8b9c21 -205-209 5f65204b08f141e8 b0f2b706aaac0850 -210-214 5325f0b8e4f86617 52244908f002b1ae -215-219 93c1a1fd0318dd14 4dd9af60759027c9 -220-224 4b3c1e25ed139fda ea1624f09c3d14b2 -225-229 01df8d39a686d542 8c7911dafdc13d2f +205-209 5f65204b08f141e8 82746d245db80449 +210-214 5325f0b8e4f86617 3875683118d50fb9 +215-219 93c1a1fd0318dd14 e1110991fbf29b6a +220-224 4b3c1e25ed139fda 6a359c12ce11bf75 +225-229 01df8d39a686d542 57d31b8eda79aafd 230-234 d8671c2aa0925484 0bac935606117b80 -235-239 c2ffbce64c565d4e 69b3aa958f41d110 +235-239 c2ffbce64c565d4e 58b0fd772f4d8d6f 240-244 8840614f5d6ed107 8e780f81f0f963a3 245-249 a3a20836097b17a3 e7d647ebe4acb5c1 -250-254 55507d905e78d536 3db4ef078698537d +250-254 55507d905e78d536 51efab89af60246f 255-259 ddb6e7f50dc7b9a4 74573807d9a5ab22 260-264 9e561d5848a362eb b8ca2b7ed478437b 265-269 d94a03864c991b66 a1e761f976f07a29 270-274 d0100fe28bcc0b9f 420f0773032cf669 275-279 c991021006094a2d 6bbae3ade35f43ac -280-284 9630c4201e5eb600 276f9d4d7d5524b6 -285-289 fb53958c9f671828 5f7f1a56d0410550 +280-284 9630c4201e5eb600 3875dd202ef094ae +285-289 fb53958c9f671828 532e901e273cb325 290-294 c5bf6275b89317cf 7e3341f6a56d0554 295-299 99fb36209f586bca e891c6658cee191a 300-304 5240085dd621df82 458bf378d060d912 305-309 616cfc3aae79162c 9773426cddea96c8 -310-314 a386fe7bc32fc73e 3f5f54be7cb93049 -315-319 5d7084c26ae0f3f2 63616a7462620995 +310-314 a386fe7bc32fc73e ffcb8883f20fd068 +315-319 5d7084c26ae0f3f2 84663f27fddc4978 320-324 e0a4ea3772d1038a 73f4cabd6f62f2b1 -325-329 0cb388111d9a1b86 6c57e6841ecf35c3 +325-329 0cb388111d9a1b86 839131fd61828cf2 330-334 d1c41d95d297d164 35fdd17d28531ad6 335-339 2d6db0a042990332 61f544ab33b2cfd6 -340-344 acfe94db4b59c187 45ece0e56bd210c5 +340-344 acfe94db4b59c187 5569acdc9e22f787 345-349 f443e9c02e4c493c c69bdb6548e13ca0 -350-354 6b8189fa7c7eb359 09371ad1064ca8e6 +350-354 6b8189fa7c7eb359 dfb1eb97be7092d2 355-359 48640c9838b42488 279989c235a1b232 -360-364 86adc1124beaf310 ee7e33f8f8305703 -365-369 c75c9390e1ec4dc0 b29e2441298ccb44 +360-364 86adc1124beaf310 71f8b11ce3847ea3 +365-369 c75c9390e1ec4dc0 29610066db8a37d4 370-374 4b47480d5c6430c8 eb537062c9b097a3 375-379 97cadfe0779bf656 9ab1bbb442319fc5 380-384 bae2ef0777128159 394248e293242674 @@ -83,102 +83,102 @@ 400-404 27e91e5f8d9d0500 06b82bdf156dad07 405-409 d97cfed3b6019a84 4c43f9bf02b1c878 410-414 9fc1135cea15545b be2a91c3665da64a -415-419 ec0440f6f542cdbe 167b4bffef187ba6 -420-424 820d32ca97f38bf1 0ecad2dddb4eea7d +415-419 ec0440f6f542cdbe 78e26f5a0c86f7bd +420-424 820d32ca97f38bf1 ba90066643d2da90 425-429 a187a84aad4ddd82 f5d0b8ab277a2913 430-434 63cab5602f883c2b e5605a0a65a3449f -435-439 6b51186155d6b708 e538bd19340bb105 +435-439 6b51186155d6b708 9f185e6f0051e7a7 440-444 d3c75dad29457c53 30d025363bf2622b -445-449 5b312af32839186f ae824a4613425ce3 -450-454 867d9e5d19789b27 83fb356046a4d10e -455-459 845c435d60d254cd c252519be14e8275 -460-464 fe6ab0fb975606e6 1c2cf9bde750b81e +445-449 5b312af32839186f 8fbf6337f2932e00 +450-454 867d9e5d19789b27 7d1aed6653d5101b +455-459 845c435d60d254cd 0e0499e922caa21f +460-464 fe6ab0fb975606e6 ec3f3ab76d25c8de 465-469 cf94bc0890135d7d fd4b8e2927bebd03 -470-474 5e2dbff33d066336 2d8a71aa7154e62a +470-474 5e2dbff33d066336 29ac24fb4b12017a 475-479 0479e5e66eeb0b35 b85602f8b33a3a40 -480-484 728477d2aff05013 44b10c0e085711e8 -485-489 71a0a6cd3a0f1dc4 209f6097b55a6075 +480-484 728477d2aff05013 2d2937d0ac2fdf23 +485-489 71a0a6cd3a0f1dc4 e4d9c7504d28f0f3 490-494 778ed33daefe5147 f2ef44e740b04752 -495-499 3f3ef0e47bc79f65 ae0ae1306e3ff6b9 -500-504 aa9adf3185bd29fd 5cb08498d8c7701d +495-499 3f3ef0e47bc79f65 f0233be4e062ccdb +500-504 aa9adf3185bd29fd 7c7d2a919fd338f9 505-509 45a1e1c9fbf16b34 8d22ee5d9c4a28c1 510-514 818d16cd8b3bec8a ade0d01f88cdff6f 515-519 c7422c418ed92126 4a93055b7f3ee09a 520-524 a3e57516f4ee56fe 06a47c0e77469fcd 525-529 554002fd18293950 43a791951252a9e9 530-534 70f5bc4e1811144d 2d5fece8c7f51bcc -535-539 3bc9d29e9245a772 202e07f57fcd5ad4 +535-539 3bc9d29e9245a772 772970ae662a3597 540-544 f2d746650f56eb6a a412eafd24000b57 -545-549 3ebc5e64e438a7ed 39ece5aa2758497b +545-549 3ebc5e64e438a7ed 2e5e08f544df97c7 550-554 b9d3351453b65997 68c9a15ecf308f27 555-559 37bcc4d58a6d31ef a265f9a2caf4552c -560-564 e03fbe4bdb0ce235 c2b10fbe263818f5 -565-569 0b1e0cc286d8e2db ea3a72e1357f016a -570-574 0eca795339181b14 4d67dc6131e1e0d7 +560-564 e03fbe4bdb0ce235 f3eba7e85189cf67 +565-569 0b1e0cc286d8e2db 3b8dcc6421736fb2 +570-574 0eca795339181b14 4db62dea573c24d1 575-579 5ca964a495b608c9 dccc25c013660626 -580-584 c526bfcdbdb19c13 2c2c062689744a81 -585-589 c0f55f7b738667c8 cf5507fcd05366b8 -590-594 9a57465f007322f6 89832f8515a2ef5b +580-584 c526bfcdbdb19c13 d560ec858b3b2219 +585-589 c0f55f7b738667c8 f46682e9b36c7702 +590-594 9a57465f007322f6 3c64a5df77f9c4c6 595-599 60d49df8b1a1ed60 e340ad56f3c2b7b2 -600-604 eb8f971fa2c853f1 c041b158a6e15cff -605-609 b9a0d832ab3703a4 33a266c3126b7268 +600-604 eb8f971fa2c853f1 11b66641ca6ae56a +605-609 b9a0d832ab3703a4 f96c87efc5d481bc 610-614 b7cd2826e9ed4bad 1dae82316d44d372 615-619 dd6ea62b42964c5d efea9c54f9b7df69 620-624 f8cafac82b3a2938 056ff72402397371 625-629 71dda2f6b4fcf3ad 35647b7151bbe854 -630-634 c0625fb844d5bd9c b51049d5a507e712 +630-634 c0625fb844d5bd9c c64a526fe5b72142 635-639 89ce072ed93bd8b3 0578500fea7bcf61 640-644 9a060a7b22a5ea63 3487fe4c2cec2c2a 645-649 4f18a0402cf96703 bf14b749f94b2db2 -650-654 799d0d7818b8ecdd 8a9bfc4768db7bb8 +650-654 799d0d7818b8ecdd 614c01474bb1e118 655-659 0f78bdef454a269d 8926ce36aa150d51 660-664 f1cd906360c54cfa e85fffa3833a1371 -665-669 a5564a8ea2270906 148be46a43610317 +665-669 a5564a8ea2270906 ba8d3f1d46b116bc 670-674 c196359f0ba0e6c1 b7bf3747d5fbf62b 675-679 e0011b91b2f33aef 83b29f9e3c6941d3 680-684 54159021ceb1a6a4 38d783f189586236 685-689 8121cfcade405227 420a3a319d2d57be -690-694 841fe56913ede31a 2396629113d82883 +690-694 841fe56913ede31a 1e3044225dd8d63c 695-699 968e63891e83acad 7c71d615d9bf67fb 700-704 b53560cda70aadb8 bbaa6aa02e40ab6a -705-709 83bece44cdd8d964 8ce390034469015e -710-714 0096b81bcf606d08 8f7e9fd300bbcf23 -715-719 afbe3acb8a936112 6c033a3cb4d2f9d3 +705-709 83bece44cdd8d964 4f5ebb508596336a +710-714 0096b81bcf606d08 6439c26f360ca790 +715-719 afbe3acb8a936112 df6e9deb669be132 720-724 e212d53b068c9367 aadfdb29a8bcc99e 725-729 b532d08326d3a1c1 2de2fbb2c43c7f8a -730-734 fb89f0ab21e41184 491db122776600fc -735-739 7f21245b19048f6d 2138cf6efd2f4588 +730-734 fb89f0ab21e41184 8a330cf19706a811 +735-739 7f21245b19048f6d 61a95bc86e9de20b 740-744 f5afc189901e25a7 e2e144556544337d 745-749 0d669a0c40d9ec06 b97784b37ffa6263 750-754 a2da5259aa3dc6bb 012fdbd877db9c91 755-759 d5fed3964610e80b f12a5d7653e27e6f 760-764 eb74f4ed1d526a82 959dc5c757d52c8d -765-769 446fbe655c39474a 36b1b969e02be22c +765-769 446fbe655c39474a 9b16fa067e375a47 770-774 3ed2f6336ae1d8b4 93d42391b807598c -775-779 0c443aac3fea26b0 24950068997cf758 +775-779 0c443aac3fea26b0 d014fd768c4bf4c7 780-784 fff5de443b6d9df3 aea7405cafc4c5b7 -785-789 95fa315efc5c1f38 b468e5b40c9f7e5f -790-794 f7e4a7ca516970fa af6b032b8a30c9e4 +785-789 95fa315efc5c1f38 fb9d734eaf54786c +790-794 f7e4a7ca516970fa 87aa607e96342d45 795-799 c26cb82b9d6af0ae 7fe79ef93103d105 -800-804 a278c8bb8be83819 6388df7540367932 +800-804 a278c8bb8be83819 515b49f0b6c50a17 805-809 69d4f412ccd64cea 75694104c6c260c6 810-814 76e6c0857a866513 a054910d270c4865 815-819 4bf0e13795a7faff 73bb5b3877877d35 -820-824 e661808004ced5e0 b5ddf35a824246bf +820-824 e661808004ced5e0 1c27f0c8126edd04 825-829 d25f4793e042db64 c547d6d2f42aab92 830-834 e3d03e8ef8b1295d ece90eb006469f2c -835-839 c00270de24a49e16 fff84b0888928de4 +835-839 c00270de24a49e16 0356942dabe3c1a1 840-844 865292082469a123 3a61750ab0484c76 845-849 0a2f4f3e161db2bd c5d2f7f0c3a95ec8 850-854 d5b245a2d4762809 7875eeafd9b0852c 855-859 d7a4147ec83ccdf3 77002587e0e6b904 -860-864 e0dfd4ee3b9b9115 0ecfe32c7b48c46a +860-864 e0dfd4ee3b9b9115 fde9fa84bc8f9ec2 865-869 3e913b7411e46c27 4306522989316626 870-874 fdd6c8ad6bcba04e 1882f07d3fed58b1 875-879 daeef0f506171b5e ecf437a4a9aaa27b -880-884 a19a9111ca539727 78f4d9f2f0dd1801 -885-889 4964fd890aa0994b e784bc10d7654d18 -890-894 50069923d0e0ff0c 6abc83e6c4ad3382 +880-884 a19a9111ca539727 47d9a8a193744011 +885-889 4964fd890aa0994b 9b7572f260470607 +890-894 50069923d0e0ff0c d4c627d208bbfe53 895-899 45a6202dc931bd35 ddc86e63ebde42c6 900-904 3e4b61b6dfc3922b 83e3365600063210 905-909 c91a83be7091fe1c 24162906c0e7bfe9 @@ -186,51 +186,51 @@ 915-919 5a056b11f328365b e87365882f0beeba 920-924 34137d01c0b33616 25eb24321c54f780 925-929 2bfe9d227e06cc61 36a2a2804e7199c0 -930-934 44414e0793c1989b 1ad25b23895a8f7d -935-939 2388bd7d5fdcaea3 be8347aa8ab22131 +930-934 44414e0793c1989b 81aeeee46cb523dd +935-939 2388bd7d5fdcaea3 ec1d5d5736d89bb4 940-944 9526b9c0ef958cfb 0bb7947f46f77e87 945-949 c82059b88e2418d3 0b69206a8bdd92aa 950-954 2af969418fd97751 0d8028b27ca53895 955-959 cb5af4f26e818f69 35a711c543af79eb 960-964 703724ea36030a9a fcdcb792020ab11d -965-969 a5f349b716e2c17b 9d66821879c38a70 -970-974 2c51d78e4d21f34a a2def4c7cf5ff7cd -975-979 02fe70fe43798656 7814a06ce9bc2f43 +965-969 a5f349b716e2c17b 0991658fe758303a +970-974 2c51d78e4d21f34a 9daf5dd74a4769b4 +975-979 02fe70fe43798656 f88366d84fbc1a9b 980-984 2dfdeb1d730d437f a0d94833e09775ea -985-989 c0ce894b61174b60 19c66bd535d5d3ee +985-989 c0ce894b61174b60 a20389482ab6ee4b 990-994 a4c96d873de9cde3 eb3b3c6230974260 995-999 3b004642590efb27 e3d1abbe834ad7fc -1000-1004 1e11b2228a87e2cd 12b7aab35f50be29 +1000-1004 1e11b2228a87e2cd 87eb56ba44441baf 1005-1009 863857d4840e1755 355fdf9286f05798 1010-1014 f32eb531a0227019 b9b5a3a04eecda96 -1015-1019 5df98b07a3f6fa22 13becee02a1664a2 -1020-1024 7788148d1188683a 489900f10b3cb72c -1025-1029 2a42ee2c74e792bf 1ea623632db51651 +1015-1019 5df98b07a3f6fa22 ee20a674cbd0f164 +1020-1024 7788148d1188683a 186dad916be4a966 +1025-1029 2a42ee2c74e792bf aca5c17e940b4cb5 1030-1034 8615096392ecaf53 eb809bbae88c2982 1035-1039 c24e3d3e2b5b3a7c eacecb30a4ac9cb7 1040-1044 7940b56d09ea5bc3 2eb630023ef89bd6 -1045-1049 5be1c725b126b43d e2be386d181e5b93 +1045-1049 5be1c725b126b43d 2537be48e3fcd24a 1050-1054 baf53597dd1ee0f7 bc2a9fe2197ec4b7 -1055-1059 89da0c30103093c2 2aa998e0f1f21b96 -1060-1064 088462cc1aad7bca 48a9e7854dbc4dc5 +1055-1059 89da0c30103093c2 42027a2a39ebde27 +1060-1064 088462cc1aad7bca dd6cd0776f80e22f 1065-1069 b3d2b22d55140c4d 5565fb9078c47532 1070-1074 744a72bd25aa8f8a 068bb08e149f4464 -1075-1079 b865bc68064f3bf3 fef6ee44b83a0b99 +1075-1079 b865bc68064f3bf3 629a7afbccb7b938 1080-1084 22a031b03ca02431 6c8443bf709e07a0 1085-1089 7fc382e8a1cfb392 9b87d1e77b5a7538 1090-1094 09636a2b50a36582 319c81aa9068c32e 1095-1099 848d4cb5147266ea 4aee1674aed627ed 1100-1104 717a47c102e234ea 5579c18523a5832c -1105-1109 b86d27ca80e7477d 00301364a11e3c8a +1105-1109 b86d27ca80e7477d 034de03cd652a927 1110-1114 e75533c276ca26b6 d7616af8e73b78de -1115-1119 0967dca7a3b7f0db 9f9f702b1ebf2b52 +1115-1119 0967dca7a3b7f0db d8db41208638249f 1120-1124 2b3d27a962eeecaa 6bbca72310c14bb7 1125-1129 4c0f197f16fdc203 01b4ba3d9dfd2cb1 -1130-1134 b8c883083e2354b4 e45c7b0c69d9f2b3 +1130-1134 b8c883083e2354b4 5f2e017c0be56cfc 1135-1139 052826a8ca213062 7cf591ec1d8fbd99 1140-1144 643000c4068a73dc 3e49d60c36f682fd 1145-1149 a20dbdc962bf496d 480ad4ea25e080c5 -1150-1154 a8ff0a76fb490212 64986e06854ec675 +1150-1154 a8ff0a76fb490212 86381bb67dd20a29 1155-1159 265ef1e7e4d619b0 39f15128482bd634 1160-1164 fd031848e1246ce5 be60639cbdf765b0 1165-1169 46392bbe676ae180 4576a4072bbea30b @@ -238,32 +238,32 @@ 1175-1179 42ba5427c32b6a50 f74c503679d95c63 1180-1184 ec53e605cb4007b0 c99cfb38d4eb1f79 1185-1189 a9bca1c3f2f28d14 380cee83a22a9e81 -1190-1194 99c3c937889a535d 832e7d93f711a7a4 +1190-1194 99c3c937889a535d 9b9afb080dcb6d25 1195-1199 516dc4d7731053d5 b805922b219f6337 1200-1204 615780e5c51bd99d 3bad989b2e4a0c9d -1205-1209 2ba50ff0fb6738c8 f6fcc74b42dd88e7 -1210-1214 945fc79f04d19aaa 00119cb67fa3ecd6 -1215-1219 0f1fd24ee8266b2f e7b94f1e48147a90 -1220-1224 d00cf5cc9e90ba28 231dd0874c6755f5 -1225-1229 d297953e40966981 df2a03c51d6c3fdc -1230-1234 2b75e34b10cb22be ed005080d9665ba7 -1235-1239 299fb21e399aef5d 9efbbdd5f61c814e +1205-1209 2ba50ff0fb6738c8 927f591180c674c1 +1210-1214 945fc79f04d19aaa f637ef58a7981b2f +1215-1219 0f1fd24ee8266b2f abc160e18a574734 +1220-1224 d00cf5cc9e90ba28 f0ef15e8c3556e5f +1225-1229 d297953e40966981 b3b321c730c2a926 +1230-1234 2b75e34b10cb22be 6845360ccbcfbce3 +1235-1239 299fb21e399aef5d 74cf8eb7a9e159b2 1240-1244 c0ae0ef2eacb1d20 3edd06583588d801 1245-1249 8cece7e0363ae620 512797b949f264fd -1250-1254 124798b1ca2185da b109b99694d7f831 +1250-1254 124798b1ca2185da c5f06eb60c4ef186 1255-1259 32bb20f47d09e0fd f88d92dd1bcada2c 1260-1264 a90cc7dbee7863ee a4f79bdb0ab09720 -1265-1269 9601afdc614c3378 c0724f621dccf436 -1270-1274 e4027cab6820ab9c 6ce85e6ae2c08b17 +1265-1269 9601afdc614c3378 bfe9f968dbed5e18 +1270-1274 e4027cab6820ab9c f7909eaf9d4be339 1275-1279 52f35f77c626c7a9 3abae8154491db24 -1280-1284 54db489225a8498f 9ced5547bd54c1a9 -1285-1289 503688b417683963 aef1aee8bc4ac267 -1290-1294 0ec750caf91a885a b76dabc7bb1ae36b +1280-1284 54db489225a8498f a23b0bca17624b1c +1285-1289 503688b417683963 78e995256686d747 +1290-1294 0ec750caf91a885a f1e7e9c4de111992 1295-1299 57b68256c467948e 48c3046250deef23 -1300-1304 f69a72cf286243a6 7a99eefa76f2470d +1300-1304 f69a72cf286243a6 bb86e358f7072cf6 1305-1309 5427028ca0307488 b63f19108a0245c6 -1310-1314 687bd0b688c5245a b59934dfd01b315c -1315-1319 a3c063f7319cc243 d45a129e78008a8e +1310-1314 687bd0b688c5245a b114a72652c69bf7 +1315-1319 a3c063f7319cc243 e4de2fccfcf488e4 1320-1324 f71d4ea9b4e443ea 48b0bff72e8cc16c 1325-1329 36b1c933b4769bef d3794598cba32e40 1330-1334 12fec017822c9408 5e641f7a680d56c8 @@ -272,9 +272,9 @@ 1345-1349 2227034cb8224e92 6e9c0ec28caf5ebb 1350-1354 d1c3cc3f3053abef 628e65bcd7739e72 1355-1359 837b255beca431d9 ea6849feb7fcc751 -1360-1364 5a0320d1b8d0c441 41b409e99d3d6407 +1360-1364 5a0320d1b8d0c441 b02b6307ffd735ab 1365-1369 bc3c4bdab54f5895 8e01f8a9337c54eb -1370-1374 5b91bc73f002c848 871b91e8470daec6 +1370-1374 5b91bc73f002c848 752aba121679327c 1375-1379 b942d2d527e313bb 659eee69e96502c0 1380-1384 ba26e589d1e33e42 c16656410a682f46 1385-1389 814d598e06f65107 da581797f0d135d1 @@ -282,21 +282,21 @@ 1395-1399 b26420e6d29fdb12 4e4a263e2423589f 1400-1404 f9fe3b626f1686b5 cef2ca1c367efd3c 1405-1409 4e57a3753006f235 bce4cb677d18c7a8 -1410-1414 115e306882ded7e8 71313b54a3fa6c86 +1410-1414 115e306882ded7e8 10af2c55531cb3e3 1415-1419 434caf3c3910b014 bf4169615aefe9c5 1420-1424 05902fd403f63f45 0944208a7b10e1f2 -1425-1429 8ab91ea0295bfb52 a24c343175848041 +1425-1429 8ab91ea0295bfb52 80fefec043525894 1430-1434 3484e596f0cec9e4 4888e0644305f3e7 1435-1439 13c16ded50903e22 c4ffe4dd5943efca 1440-1444 31e0740b70544201 2c7a5fe6c18d9400 -1445-1449 8be55a7cd8438be9 67b24598732fa3fe -1450-1454 e9d1c354908dc7d5 029a1152a0c94435 +1445-1449 8be55a7cd8438be9 247bbaaa4f55ab76 +1450-1454 e9d1c354908dc7d5 858c296e8455d9e7 1455-1459 c8229a6a88eec0ac 80d9c92c73325cb7 1460-1464 ce73f815cae74496 bb6784e45e8f2626 1465-1469 1695babf780735fd cd1492eabdb7ef4a 1470-1474 7285c5cca58b9f85 9ebcb3c0e2ee91ad 1475-1479 826e8660179fe16a 66fd8f473ce0d8ea -1480-1484 8ecf1216c69ed810 69f317816faa2b4b -1485-1489 8a3905febae9ece0 f893373add948ea0 -1490-1494 2f1c19a2427058be 0a940a145b4b89ca -1495-1499 952ea603e646e84a 077b55523590479c +1480-1484 8ecf1216c69ed810 e1a7dfcb53524ed9 +1485-1489 8a3905febae9ece0 352251fd114c8e13 +1490-1494 2f1c19a2427058be ccd75a4f323eb2a2 +1495-1499 952ea603e646e84a 0a46e856da36a94c diff --git a/docs/testing/uv-compatibility.md b/docs/testing/uv-compatibility.md index 36f33b95c..599a6d10e 100644 --- a/docs/testing/uv-compatibility.md +++ b/docs/testing/uv-compatibility.md @@ -152,6 +152,16 @@ frozen, locked, and ordinary installation outcomes separately where supported. - A script lock requires its paired script and a valid PEP 723 metadata block. Missing metadata or an incompatible existing source is reported before either file is rewritten. +- A PEP 508 direct reference to the patched package (`six @ https://…whl`, + `six @ git+…`) in `[project]` dependencies, an extra, a `[dependency-groups]` + group, the legacy `[tool.uv] dev-dependencies` or a PEP 723 script, or a + lock entry resolved from a direct URL (uv.lock `source = { url }`, pylock + `archive = { url }`), is the user's own source. Both modes refuse it before + any file is written: vendored with `pypi_uv_source_already_exists`, hosted + with `redirect_uv_project_unsupported` / `redirect_uv_script_unsupported` / + `redirect_uv_lock_unsupported` (#767). Before, vendored left a lock that + `uv sync --locked` rejects, and hosted replaced the user's URL with one its + own rollback then refused to restore. - Native projects and scripts resolving multiple versions of the same package are refused when a global uv source would replace another version. Supporting those cases requires marker-specific source mappings. Standalone PEP 751