From cde90c49ce07cf639c06cde0b1eb5e8cd6404656 Mon Sep 17 00:00:00 2001 From: Mikola Lysenko Date: Fri, 9 Oct 2026 12:44:23 -0400 Subject: [PATCH 1/3] Start v5 blocker fix (uv-direct-refs) Empty commit to open the draft PR. Co-Authored-By: Claude Opus 5.5 (1M context) From a2b1a8bd4b096f294c3db80539a8f24c82016e6a Mon Sep 17 00:00:00 2001 From: Mikola Lysenko Date: Fri, 9 Oct 2026 13:56:18 -0400 Subject: [PATCH 2/3] Refuse uv patching over PEP 508 direct refs A uv project that declares the patched package as a direct reference (`six @ https://...whl`, `six @ git+...`) in dependencies, an extra, a dependency group, the legacy dev-dependencies or a PEP 723 script was rewritten anyway. Vendored mode left a lock with both `url` and `path` on the root requirement, so `uv sync --locked` failed while the scan said success and vex attested. Hosted mode silently replaced the user's URL, and rollback then refused to restore it. Both modes now refuse such a declaration before writing anything, the same way they refuse a user `[tool.uv.sources]` entry: vendored with `pypi_uv_source_already_exists`, hosted with `redirect_uv_project_unsupported` / `redirect_uv_script_unsupported`. The shared lock planner also refuses an entry the lock resolves from a user direct URL (uv.lock `source = { url }`, pylock `archive = { url }`) unless it is socket-patch's own earlier hosted artifact, which covers lock-only and pylock-only checkouts. Fixes #767 Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/utils/python_lock.rs | 71 ++++++ .../src/utils/python_script.rs | 106 ++++++++- crates/socket-patch-core/src/vendor/common.rs | 21 ++ .../socket-patch-core/src/vendor/pypi_uv.rs | 61 +++++ .../equivalence/python_lock_rewrite.golden | 216 +++++++++--------- docs/testing/uv-compatibility.md | 10 + 6 files changed, 375 insertions(+), 110 deletions(-) diff --git a/crates/socket-patch-core/src/utils/python_lock.rs b/crates/socket-patch-core/src/utils/python_lock.rs index bc7d01d3e..ab720db49 100644 --- a/crates/socket-patch-core/src/utils/python_lock.rs +++ b/crates/socket-patch-core/src/utils/python_lock.rs @@ -886,6 +886,39 @@ 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 { + original_source + .as_ref() + .and_then(source_identity) + .filter(|(kind, _)| *kind == "url") + .map(|(_, url)| 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 +1253,44 @@ 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" + ); + for lock in [&uv_lock, &pylock] { + 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 79b044afc..0fda5d4f7 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; @@ -227,6 +232,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(()), + } +} + fn contains_dependency(item: Option<&Item>, name: &str) -> bool { item.and_then(Item::as_array).is_some_and(|dependencies| { dependencies @@ -258,6 +285,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) @@ -296,6 +337,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, @@ -326,6 +377,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 87a429ea7..9fd6a141d 100644 --- a/crates/socket-patch-core/src/vendor/common.rs +++ b/crates/socket-patch-core/src/vendor/common.rs @@ -607,6 +607,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 1bae31c55..e86fcf640 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 @@ -2792,6 +2825,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 dcb427b11..4729c5ca4 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 7df91de609aadf9a -10-14 63a0aa035a8a4e90 ce91f93cd49ca4f5 +5-9 f58e23b53c07f972 2e6a9ab6c49b5af3 +10-14 63a0aa035a8a4e90 53b61cb67847beec 15-19 7cf3d76e36752625 590f305643295efc 20-24 c7c648dc10c3c7cd 9fa1deaf96f07fe2 25-29 831189dc733b8849 3ab9faa9395cf06f -30-34 dcb109d643382adf ade5041ad97eb6f3 +30-34 dcb109d643382adf 003703da5df0e30f 35-39 99a313947928f593 040d41e8e286d342 40-44 cf5611d067871c91 b9005c5ab4e2d992 45-49 04f0e41a9e302f59 703a8fae640714d8 @@ -14,14 +14,14 @@ 55-59 daf9f41b5decbf2c 34103170b8df8696 60-64 58413f6f2f266d0d e269d6e79181da6a 65-69 cd4f77526796ec70 ffec6efe319265a7 -70-74 3befe3b6747c7c5d b1e8a696d2d54b16 +70-74 3befe3b6747c7c5d 77a264ded592ba91 75-79 f704740390513318 42df63da4590a254 -80-84 6bb2704eadd90bbb 5ca41094d0424e3d -85-89 2b41ca85da12924d 65b152efd83c3bd6 +80-84 6bb2704eadd90bbb d31e655a72986955 +85-89 2b41ca85da12924d bacc932ce5593790 90-94 b4f4d21d57b4e3e6 0f3435d61d7a36c1 -95-99 1fcae5cb34b3557b c47c75663ff48676 +95-99 1fcae5cb34b3557b 4eb4e16e7ffeb917 100-104 22d4b081083ec3cf 630cb1f82af66ee9 -105-109 b33352ae7c78a21f e20dfee24af86bff +105-109 b33352ae7c78a21f 81349be26593bacb 110-114 459ff3efdff5f2cc c6e5134cb2c1fd85 115-119 291d724de0819b8d 8466395f3165a0ef 120-124 21a80a5bc6b9e421 41fc3cf934fc6087 @@ -32,48 +32,48 @@ 145-149 e366e05fd9c0ef0b f16c3c8253ab5ddc 150-154 7f552bc377ac439b 121853d116c28aa3 155-159 5c987ad06f4c2cf3 853fcb9da6efea74 -160-164 2fd97b5cdeea8344 34d616cba9ff2f92 -165-169 8f16946d73613e52 4ee31b57d3a5fb45 +160-164 2fd97b5cdeea8344 b4eb21da8c542e23 +165-169 8f16946d73613e52 0ced9526c8e4e0ef 170-174 72c1e1cb26500151 4d8622bd269f21c5 -175-179 4333d230209fdfdd fe0ef3c12d1a0cb2 +175-179 4333d230209fdfdd 25a1693501b7f92b 180-184 70af4e6d0a9804eb 849fc5b3c32d6b64 185-189 6073f94564a728c8 42f8708863320edf 190-194 0976869c3d0f4aeb 03f687f9a1ef3754 -195-199 6e26da934df4169e a2787877f35e9517 +195-199 6e26da934df4169e 320a13e082f60b8d 200-204 fbb5bb450083c437 31aad032ae8b9c21 -205-209 5f65204b08f141e8 b0f2b706aaac0850 -210-214 5325f0b8e4f86617 78fbabe1106e949b -215-219 93c1a1fd0318dd14 4dd9af60759027c9 -220-224 4b3c1e25ed139fda ea1624f09c3d14b2 -225-229 01df8d39a686d542 6351804ffd423e35 +205-209 5f65204b08f141e8 82746d245db80449 +210-214 5325f0b8e4f86617 30d8f71d63e071ad +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 a0c928a84a61285a -250-254 55507d905e78d536 d0f0d350180f3eee +250-254 55507d905e78d536 d3cfd0e2dede0eb5 255-259 ddb6e7f50dc7b9a4 d1e8dd678ec4364f 260-264 9e561d5848a362eb c28304e6d6901fde 265-269 d94a03864c991b66 aa18c672fc3c2eaf 270-274 d0100fe28bcc0b9f 420f0773032cf669 275-279 c991021006094a2d bb0f59f239f75d75 -280-284 9630c4201e5eb600 bfc9c9e993e9a71a -285-289 fb53958c9f671828 5f7f1a56d0410550 +280-284 9630c4201e5eb600 c11c31f3f7b9a0ee +285-289 fb53958c9f671828 532e901e273cb325 290-294 c5bf6275b89317cf 2b87546cfa85399e 295-299 99fb36209f586bca e891c6658cee191a 300-304 5240085dd621df82 458bf378d060d912 305-309 616cfc3aae79162c 9773426cddea96c8 -310-314 a386fe7bc32fc73e aebfc7302ea42601 -315-319 5d7084c26ae0f3f2 2a770b1138e80898 +310-314 a386fe7bc32fc73e e7ca00a4075b89b4 +315-319 5d7084c26ae0f3f2 43b9a51e3ad1a48f 320-324 e0a4ea3772d1038a 7c2702afd192fef0 -325-329 0cb388111d9a1b86 6c57e6841ecf35c3 +325-329 0cb388111d9a1b86 839131fd61828cf2 330-334 d1c41d95d297d164 35fdd17d28531ad6 335-339 2d6db0a042990332 61f544ab33b2cfd6 340-344 acfe94db4b59c187 164a716794d54313 345-349 f443e9c02e4c493c 1836f9a125c48b47 -350-354 6b8189fa7c7eb359 75535c0ded362ece +350-354 6b8189fa7c7eb359 4a5d842eaf619ff6 355-359 48640c9838b42488 135f6304ae30656f -360-364 86adc1124beaf310 ee7e33f8f8305703 -365-369 c75c9390e1ec4dc0 159ef5b3c823d104 +360-364 86adc1124beaf310 71f8b11ce3847ea3 +365-369 c75c9390e1ec4dc0 13b02b2fb58afe8d 370-374 4b47480d5c6430c8 eb537062c9b097a3 375-379 97cadfe0779bf656 0345c0b1783881a7 380-384 bae2ef0777128159 394248e293242674 @@ -83,102 +83,102 @@ 400-404 27e91e5f8d9d0500 06b82bdf156dad07 405-409 d97cfed3b6019a84 4c43f9bf02b1c878 410-414 9fc1135cea15545b 82c68ccb78463e3d -415-419 ec0440f6f542cdbe 167b4bffef187ba6 -420-424 820d32ca97f38bf1 d23974a3a1068894 +415-419 ec0440f6f542cdbe 78e26f5a0c86f7bd +420-424 820d32ca97f38bf1 7d7628705cedf832 425-429 a187a84aad4ddd82 f5d0b8ab277a2913 430-434 63cab5602f883c2b e5605a0a65a3449f -435-439 6b51186155d6b708 29d92ee2415d97a6 +435-439 6b51186155d6b708 9f185e6f0051e7a7 440-444 d3c75dad29457c53 4565a668f3b1af38 445-449 5b312af32839186f ae824a4613425ce3 -450-454 867d9e5d19789b27 509f4c37ddd7e5c0 -455-459 845c435d60d254cd ed317c05f75fc6d8 -460-464 fe6ab0fb975606e6 1c2cf9bde750b81e +450-454 867d9e5d19789b27 2fcddeeba16f2fea +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 1f45e83958ffd0fc -485-489 71a0a6cd3a0f1dc4 209f6097b55a6075 +480-484 728477d2aff05013 65087a03276eaa63 +485-489 71a0a6cd3a0f1dc4 e4d9c7504d28f0f3 490-494 778ed33daefe5147 f2ef44e740b04752 -495-499 3f3ef0e47bc79f65 14e10c33f2200fd2 -500-504 aa9adf3185bd29fd 5cb08498d8c7701d +495-499 3f3ef0e47bc79f65 3c070278b8ad430a +500-504 aa9adf3185bd29fd 7c7d2a919fd338f9 505-509 45a1e1c9fbf16b34 8d22ee5d9c4a28c1 510-514 818d16cd8b3bec8a ade0d01f88cdff6f 515-519 c7422c418ed92126 4a93055b7f3ee09a 520-524 a3e57516f4ee56fe 5cd6cc5bb8431e8a 525-529 554002fd18293950 43a791951252a9e9 530-534 70f5bc4e1811144d 2d5fece8c7f51bcc -535-539 3bc9d29e9245a772 202e07f57fcd5ad4 +535-539 3bc9d29e9245a772 772970ae662a3597 540-544 f2d746650f56eb6a a412eafd24000b57 -545-549 3ebc5e64e438a7ed 67e8d3c608595c3b +545-549 3ebc5e64e438a7ed 097d3288d933dfe3 550-554 b9d3351453b65997 b218188b3377d409 555-559 37bcc4d58a6d31ef 46055bd9e4a3bb42 -560-564 e03fbe4bdb0ce235 2484d12a07617823 -565-569 0b1e0cc286d8e2db 29a97162543d8b50 -570-574 0eca795339181b14 9129c77c05f88fc1 +560-564 e03fbe4bdb0ce235 e6fa3cc2aca2c4ce +565-569 0b1e0cc286d8e2db 8096f7fda9cced12 +570-574 0eca795339181b14 de36ee668881978f 575-579 5ca964a495b608c9 dccc25c013660626 -580-584 c526bfcdbdb19c13 2c2c062689744a81 -585-589 c0f55f7b738667c8 cf5507fcd05366b8 -590-594 9a57465f007322f6 fe36de2dc975e97d +580-584 c526bfcdbdb19c13 d560ec858b3b2219 +585-589 c0f55f7b738667c8 f46682e9b36c7702 +590-594 9a57465f007322f6 4930b9c4cef45a4f 595-599 60d49df8b1a1ed60 d2a9ec7caa9fafd6 -600-604 eb8f971fa2c853f1 4c047a0a600ee68c -605-609 b9a0d832ab3703a4 33a266c3126b7268 +600-604 eb8f971fa2c853f1 1fef72b29c245268 +605-609 b9a0d832ab3703a4 f96c87efc5d481bc 610-614 b7cd2826e9ed4bad 1dae82316d44d372 615-619 dd6ea62b42964c5d efea9c54f9b7df69 620-624 f8cafac82b3a2938 cc34678cbba8cbc4 625-629 71dda2f6b4fcf3ad 35647b7151bbe854 -630-634 c0625fb844d5bd9c b51049d5a507e712 +630-634 c0625fb844d5bd9c c64a526fe5b72142 635-639 89ce072ed93bd8b3 97c29483a27d20fb 640-644 9a060a7b22a5ea63 3487fe4c2cec2c2a 645-649 4f18a0402cf96703 3a62197bc682135c -650-654 799d0d7818b8ecdd 8a9bfc4768db7bb8 +650-654 799d0d7818b8ecdd 614c01474bb1e118 655-659 0f78bdef454a269d 8926ce36aa150d51 660-664 f1cd906360c54cfa e85fffa3833a1371 -665-669 a5564a8ea2270906 ca29a80390668675 +665-669 a5564a8ea2270906 6abc4ea83f0dae7c 670-674 c196359f0ba0e6c1 b7bf3747d5fbf62b 675-679 e0011b91b2f33aef 83b29f9e3c6941d3 680-684 54159021ceb1a6a4 aee86cac7c126b10 685-689 8121cfcade405227 420a3a319d2d57be -690-694 841fe56913ede31a 9a4bf80f37fc9789 +690-694 841fe56913ede31a c449ef9f44bdf818 695-699 968e63891e83acad fa37072b6e4e7450 700-704 b53560cda70aadb8 bbaa6aa02e40ab6a -705-709 83bece44cdd8d964 8ce390034469015e -710-714 0096b81bcf606d08 a05f22cc7d6e9e1c -715-719 afbe3acb8a936112 6c033a3cb4d2f9d3 +705-709 83bece44cdd8d964 4f5ebb508596336a +710-714 0096b81bcf606d08 de5faa8e93490a83 +715-719 afbe3acb8a936112 df6e9deb669be132 720-724 e212d53b068c9367 aadfdb29a8bcc99e 725-729 b532d08326d3a1c1 091c5a374fc1377c -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 ea15a0ee1b498b25 755-759 d5fed3964610e80b f12a5d7653e27e6f 760-764 eb74f4ed1d526a82 959dc5c757d52c8d -765-769 446fbe655c39474a 049f159ef1f568d2 +765-769 446fbe655c39474a 737c8395530a7200 770-774 3ed2f6336ae1d8b4 0166b4a53d4e4cef -775-779 0c443aac3fea26b0 24950068997cf758 +775-779 0c443aac3fea26b0 d014fd768c4bf4c7 780-784 fff5de443b6d9df3 aea7405cafc4c5b7 -785-789 95fa315efc5c1f38 d09cb03d8bcaa1c0 -790-794 f7e4a7ca516970fa af6b032b8a30c9e4 +785-789 95fa315efc5c1f38 10948b3a571ec04c +790-794 f7e4a7ca516970fa 87aa607e96342d45 795-799 c26cb82b9d6af0ae 7fe79ef93103d105 -800-804 a278c8bb8be83819 6388df7540367932 +800-804 a278c8bb8be83819 515b49f0b6c50a17 805-809 69d4f412ccd64cea 37cd7bcf3db57250 810-814 76e6c0857a866513 e8dce9cbdaf9c660 815-819 4bf0e13795a7faff 73bb5b3877877d35 -820-824 e661808004ced5e0 b5ddf35a824246bf +820-824 e661808004ced5e0 1c27f0c8126edd04 825-829 d25f4793e042db64 d0a1e91738e2ec76 830-834 e3d03e8ef8b1295d ece90eb006469f2c -835-839 c00270de24a49e16 cf44816b0061e2ae +835-839 c00270de24a49e16 3644b775077aa18e 840-844 865292082469a123 114514f32ba03093 845-849 0a2f4f3e161db2bd c5d2f7f0c3a95ec8 850-854 d5b245a2d4762809 732ca28e3d4297b0 855-859 d7a4147ec83ccdf3 6aa69904a771b1be -860-864 e0dfd4ee3b9b9115 bbfe2b44c90238ee +860-864 e0dfd4ee3b9b9115 60c1c1d300dbd822 865-869 3e913b7411e46c27 4306522989316626 870-874 fdd6c8ad6bcba04e bad87cd34faa9fff 875-879 daeef0f506171b5e 68cb56df14f44059 880-884 a19a9111ca539727 78f4d9f2f0dd1801 -885-889 4964fd890aa0994b 3c93b27e52621833 -890-894 50069923d0e0ff0c a3c5804a9da388fc +885-889 4964fd890aa0994b 4b2aa7abc93b010d +890-894 50069923d0e0ff0c 3854fd510329a0ac 895-899 45a6202dc931bd35 ddc86e63ebde42c6 900-904 3e4b61b6dfc3922b 83e3365600063210 905-909 c91a83be7091fe1c 457ee81515713cc2 @@ -186,36 +186,36 @@ 915-919 5a056b11f328365b e87365882f0beeba 920-924 34137d01c0b33616 25eb24321c54f780 925-929 2bfe9d227e06cc61 36a2a2804e7199c0 -930-934 44414e0793c1989b c88f218576317d18 -935-939 2388bd7d5fdcaea3 599467bdcd4949b8 +930-934 44414e0793c1989b 3829da420506e5e7 +935-939 2388bd7d5fdcaea3 003b94b7e470da89 940-944 9526b9c0ef958cfb 0bb7947f46f77e87 945-949 c82059b88e2418d3 0b69206a8bdd92aa 950-954 2af969418fd97751 0d8028b27ca53895 955-959 cb5af4f26e818f69 35a711c543af79eb 960-964 703724ea36030a9a fcdcb792020ab11d -965-969 a5f349b716e2c17b accc4d3b52b55f17 -970-974 2c51d78e4d21f34a d97e1ecca67f167c -975-979 02fe70fe43798656 1382eae2605e3b7d +965-969 a5f349b716e2c17b 993953275ec9ab49 +970-974 2c51d78e4d21f34a a6f87d6ffa8bb295 +975-979 02fe70fe43798656 c77aafa40b45fc9e 980-984 2dfdeb1d730d437f a0d94833e09775ea -985-989 c0ce894b61174b60 732049e217f02ccb +985-989 c0ce894b61174b60 75d2cc978a612c35 990-994 a4c96d873de9cde3 eb3b3c6230974260 995-999 3b004642590efb27 d7ebad60ceec36e0 -1000-1004 1e11b2228a87e2cd 0727318c8714df42 +1000-1004 1e11b2228a87e2cd efa03fc6ad36ff3e 1005-1009 863857d4840e1755 355fdf9286f05798 1010-1014 f32eb531a0227019 b9b5a3a04eecda96 -1015-1019 5df98b07a3f6fa22 de4ce5e33e0d1833 -1020-1024 7788148d1188683a 489900f10b3cb72c -1025-1029 2a42ee2c74e792bf 434d0d92660eb334 +1015-1019 5df98b07a3f6fa22 990ce0ec6afae641 +1020-1024 7788148d1188683a 186dad916be4a966 +1025-1029 2a42ee2c74e792bf 1639c18c832937c2 1030-1034 8615096392ecaf53 e781914100066bf6 1035-1039 c24e3d3e2b5b3a7c eacecb30a4ac9cb7 1040-1044 7940b56d09ea5bc3 2eb630023ef89bd6 -1045-1049 5be1c725b126b43d 36356c5c4b00e9bd +1045-1049 5be1c725b126b43d f406c748bdfe5a00 1050-1054 baf53597dd1ee0f7 bc2a9fe2197ec4b7 -1055-1059 89da0c30103093c2 9cc3743396a581ac -1060-1064 088462cc1aad7bca 48a9e7854dbc4dc5 +1055-1059 89da0c30103093c2 1bd25cf44242e32b +1060-1064 088462cc1aad7bca dd6cd0776f80e22f 1065-1069 b3d2b22d55140c4d 5565fb9078c47532 1070-1074 744a72bd25aa8f8a 068bb08e149f4464 -1075-1079 b865bc68064f3bf3 fef6ee44b83a0b99 +1075-1079 b865bc68064f3bf3 629a7afbccb7b938 1080-1084 22a031b03ca02431 1e2593e8af06cfbd 1085-1089 7fc382e8a1cfb392 9b87d1e77b5a7538 1090-1094 09636a2b50a36582 319c81aa9068c32e @@ -223,14 +223,14 @@ 1100-1104 717a47c102e234ea 5579c18523a5832c 1105-1109 b86d27ca80e7477d 51c62ff51c8af2e4 1110-1114 e75533c276ca26b6 d7616af8e73b78de -1115-1119 0967dca7a3b7f0db e6760b2ab436b96a +1115-1119 0967dca7a3b7f0db 2d237a01d92f0df9 1120-1124 2b3d27a962eeecaa 6bbca72310c14bb7 1125-1129 4c0f197f16fdc203 ae282d0c8193cd22 -1130-1134 b8c883083e2354b4 6b874f3cfe5a8fc9 +1130-1134 b8c883083e2354b4 5f2e017c0be56cfc 1135-1139 052826a8ca213062 6bda0887d04230ba 1140-1144 643000c4068a73dc 3fb8ade336a5c7d6 1145-1149 a20dbdc962bf496d 480ad4ea25e080c5 -1150-1154 a8ff0a76fb490212 64986e06854ec675 +1150-1154 a8ff0a76fb490212 86381bb67dd20a29 1155-1159 265ef1e7e4d619b0 e1c407246e3db283 1160-1164 fd031848e1246ce5 be60639cbdf765b0 1165-1169 46392bbe676ae180 85e1f076d383b5fb @@ -238,32 +238,32 @@ 1175-1179 42ba5427c32b6a50 f74c503679d95c63 1180-1184 ec53e605cb4007b0 9c2343af621629d9 1185-1189 a9bca1c3f2f28d14 730d26cf221de4a6 -1190-1194 99c3c937889a535d 6c0326f584d4cc47 +1190-1194 99c3c937889a535d 504cc2b9fc92f628 1195-1199 516dc4d7731053d5 b805922b219f6337 1200-1204 615780e5c51bd99d 1283e8e570bda37e -1205-1209 2ba50ff0fb6738c8 9da0a95622af4322 -1210-1214 945fc79f04d19aaa 00119cb67fa3ecd6 -1215-1219 0f1fd24ee8266b2f e7b94f1e48147a90 +1205-1209 2ba50ff0fb6738c8 50c5b20c28d21f21 +1210-1214 945fc79f04d19aaa f637ef58a7981b2f +1215-1219 0f1fd24ee8266b2f abc160e18a574734 1220-1224 d00cf5cc9e90ba28 231dd0874c6755f5 1225-1229 d297953e40966981 df2a03c51d6c3fdc -1230-1234 2b75e34b10cb22be 670c47a3cce7618c -1235-1239 299fb21e399aef5d 821efaa24f2b62b9 +1230-1234 2b75e34b10cb22be 8ef04dc863917d10 +1235-1239 299fb21e399aef5d 406cd336db7358e3 1240-1244 c0ae0ef2eacb1d20 3edd06583588d801 1245-1249 8cece7e0363ae620 512797b949f264fd -1250-1254 124798b1ca2185da 57a14aa19e34660c +1250-1254 124798b1ca2185da 0e70f57677d864df 1255-1259 32bb20f47d09e0fd f88d92dd1bcada2c 1260-1264 a90cc7dbee7863ee a4f79bdb0ab09720 -1265-1269 9601afdc614c3378 4611ad1016b5130b -1270-1274 e4027cab6820ab9c 6ce85e6ae2c08b17 +1265-1269 9601afdc614c3378 8982c48f5779dcfd +1270-1274 e4027cab6820ab9c f7909eaf9d4be339 1275-1279 52f35f77c626c7a9 7d0b26ff90d6b6e2 -1280-1284 54db489225a8498f 9ced5547bd54c1a9 -1285-1289 503688b417683963 5b7faaa1a0713bfd -1290-1294 0ec750caf91a885a b95d052756067610 +1280-1284 54db489225a8498f a23b0bca17624b1c +1285-1289 503688b417683963 4b027581095b5205 +1290-1294 0ec750caf91a885a 66ba1f715f6ef252 1295-1299 57b68256c467948e 48c3046250deef23 -1300-1304 f69a72cf286243a6 7a99eefa76f2470d +1300-1304 f69a72cf286243a6 bb86e358f7072cf6 1305-1309 5427028ca0307488 b63f19108a0245c6 -1310-1314 687bd0b688c5245a 928623220e0baeef -1315-1319 a3c063f7319cc243 d45a129e78008a8e +1310-1314 687bd0b688c5245a eb0201ceee18064d +1315-1319 a3c063f7319cc243 e4de2fccfcf488e4 1320-1324 f71d4ea9b4e443ea 48b0bff72e8cc16c 1325-1329 36b1c933b4769bef 66255eb274356b46 1330-1334 12fec017822c9408 bb5cb44c0018f1f2 @@ -272,9 +272,9 @@ 1345-1349 2227034cb8224e92 4d638b363a59f14a 1350-1354 d1c3cc3f3053abef 6517564f58e88568 1355-1359 837b255beca431d9 ea6849feb7fcc751 -1360-1364 5a0320d1b8d0c441 41b409e99d3d6407 +1360-1364 5a0320d1b8d0c441 b02b6307ffd735ab 1365-1369 bc3c4bdab54f5895 8e01f8a9337c54eb -1370-1374 5b91bc73f002c848 5ce79828e38e3251 +1370-1374 5b91bc73f002c848 8440b39fc904730c 1375-1379 b942d2d527e313bb f8bc89701ecdb22f 1380-1384 ba26e589d1e33e42 c16656410a682f46 1385-1389 814d598e06f65107 4e0785c55962a338 @@ -282,21 +282,21 @@ 1395-1399 b26420e6d29fdb12 4e4a263e2423589f 1400-1404 f9fe3b626f1686b5 7e7fb731f6c82704 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 c3274156d1ccc915 1440-1444 31e0740b70544201 2c7a5fe6c18d9400 -1445-1449 8be55a7cd8438be9 67b24598732fa3fe -1450-1454 e9d1c354908dc7d5 029a1152a0c94435 +1445-1449 8be55a7cd8438be9 247bbaaa4f55ab76 +1450-1454 e9d1c354908dc7d5 858c296e8455d9e7 1455-1459 c8229a6a88eec0ac 8768bc139f54bb69 1460-1464 ce73f815cae74496 de3ace466142c7e6 1465-1469 1695babf780735fd cd1492eabdb7ef4a 1470-1474 7285c5cca58b9f85 9ebcb3c0e2ee91ad 1475-1479 826e8660179fe16a 83cc3b42bc69dce7 -1480-1484 8ecf1216c69ed810 69f317816faa2b4b +1480-1484 8ecf1216c69ed810 e1a7dfcb53524ed9 1485-1489 8a3905febae9ece0 03ac60bf7554ad65 -1490-1494 2f1c19a2427058be 0a940a145b4b89ca -1495-1499 952ea603e646e84a 077b55523590479c +1490-1494 2f1c19a2427058be ccd75a4f323eb2a2 +1495-1499 952ea603e646e84a 0a46e856da36a94c diff --git a/docs/testing/uv-compatibility.md b/docs/testing/uv-compatibility.md index 5448481c0..5f13d9a39 100644 --- a/docs/testing/uv-compatibility.md +++ b/docs/testing/uv-compatibility.md @@ -149,6 +149,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 From af973ed634328a43f8277d357fcc22351d1220f8 Mon Sep 17 00:00:00 2001 From: Mikola Lysenko Date: Fri, 9 Oct 2026 15:09:26 -0400 Subject: [PATCH 3/3] Refuse uv 0.2 direct+ lock sources too The direct-URL refusal read only the table spelling of a uv.lock source (`source = { url = ... }`). uv 0.2 `[[distribution]]` locks spell the same source as the string `source = "direct+"`, which was still overwritten. Read both through `UvSource::url`. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/utils/python_lock.rs | 15 ++++++++------ .../equivalence/python_lock_rewrite.golden | 20 +++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/crates/socket-patch-core/src/utils/python_lock.rs b/crates/socket-patch-core/src/utils/python_lock.rs index ab720db49..37d69fb96 100644 --- a/crates/socket-patch-core/src/utils/python_lock.rs +++ b/crates/socket-patch-core/src/utils/python_lock.rs @@ -896,11 +896,9 @@ fn plan_python_lock_rewrite( .and_then(|archive| archive.get("url")) .and_then(Item::as_str) } else { - original_source - .as_ref() - .and_then(source_identity) - .filter(|(kind, _)| *kind == "url") - .map(|(_, url)| url) + // 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 { @@ -1269,7 +1267,12 @@ wheels = [{ url = "https://pypi.org/urllib3-2.0.0-py3-none-any.whl", hash = "sha "lock-version = \"1.0\"\ncreated-by = \"uv\"\n\n[[packages]]\nname = \"urllib3\"\n\ version = \"1.26.18\"\narchive = {{ url = \"{USER}\", hashes = {{ sha256 = \"aa\" }} }}\n" ); - for lock in [&uv_lock, &pylock] { + 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"), 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 4729c5ca4..c396e7e28 100644 --- a/crates/socket-patch-core/tests/equivalence/python_lock_rewrite.golden +++ b/crates/socket-patch-core/tests/equivalence/python_lock_rewrite.golden @@ -33,7 +33,7 @@ 150-154 7f552bc377ac439b 121853d116c28aa3 155-159 5c987ad06f4c2cf3 853fcb9da6efea74 160-164 2fd97b5cdeea8344 b4eb21da8c542e23 -165-169 8f16946d73613e52 0ced9526c8e4e0ef +165-169 8f16946d73613e52 77dc4a7d5887d3c0 170-174 72c1e1cb26500151 4d8622bd269f21c5 175-179 4333d230209fdfdd 25a1693501b7f92b 180-184 70af4e6d0a9804eb 849fc5b3c32d6b64 @@ -68,7 +68,7 @@ 325-329 0cb388111d9a1b86 839131fd61828cf2 330-334 d1c41d95d297d164 35fdd17d28531ad6 335-339 2d6db0a042990332 61f544ab33b2cfd6 -340-344 acfe94db4b59c187 164a716794d54313 +340-344 acfe94db4b59c187 15c96cca4ea7a506 345-349 f443e9c02e4c493c 1836f9a125c48b47 350-354 6b8189fa7c7eb359 4a5d842eaf619ff6 355-359 48640c9838b42488 135f6304ae30656f @@ -89,14 +89,14 @@ 430-434 63cab5602f883c2b e5605a0a65a3449f 435-439 6b51186155d6b708 9f185e6f0051e7a7 440-444 d3c75dad29457c53 4565a668f3b1af38 -445-449 5b312af32839186f ae824a4613425ce3 +445-449 5b312af32839186f 8fbf6337f2932e00 450-454 867d9e5d19789b27 2fcddeeba16f2fea 455-459 845c435d60d254cd 0e0499e922caa21f 460-464 fe6ab0fb975606e6 ec3f3ab76d25c8de 465-469 cf94bc0890135d7d fd4b8e2927bebd03 470-474 5e2dbff33d066336 29ac24fb4b12017a 475-479 0479e5e66eeb0b35 b85602f8b33a3a40 -480-484 728477d2aff05013 65087a03276eaa63 +480-484 728477d2aff05013 0ba960e5bd4cb457 485-489 71a0a6cd3a0f1dc4 e4d9c7504d28f0f3 490-494 778ed33daefe5147 f2ef44e740b04752 495-499 3f3ef0e47bc79f65 3c070278b8ad430a @@ -172,11 +172,11 @@ 845-849 0a2f4f3e161db2bd c5d2f7f0c3a95ec8 850-854 d5b245a2d4762809 732ca28e3d4297b0 855-859 d7a4147ec83ccdf3 6aa69904a771b1be -860-864 e0dfd4ee3b9b9115 60c1c1d300dbd822 +860-864 e0dfd4ee3b9b9115 9fb5041180054109 865-869 3e913b7411e46c27 4306522989316626 870-874 fdd6c8ad6bcba04e bad87cd34faa9fff 875-879 daeef0f506171b5e 68cb56df14f44059 -880-884 a19a9111ca539727 78f4d9f2f0dd1801 +880-884 a19a9111ca539727 47d9a8a193744011 885-889 4964fd890aa0994b 4b2aa7abc93b010d 890-894 50069923d0e0ff0c 3854fd510329a0ac 895-899 45a6202dc931bd35 ddc86e63ebde42c6 @@ -221,7 +221,7 @@ 1090-1094 09636a2b50a36582 319c81aa9068c32e 1095-1099 848d4cb5147266ea abb36a2405d57084 1100-1104 717a47c102e234ea 5579c18523a5832c -1105-1109 b86d27ca80e7477d 51c62ff51c8af2e4 +1105-1109 b86d27ca80e7477d 705910ca96d5ce2b 1110-1114 e75533c276ca26b6 d7616af8e73b78de 1115-1119 0967dca7a3b7f0db 2d237a01d92f0df9 1120-1124 2b3d27a962eeecaa 6bbca72310c14bb7 @@ -244,8 +244,8 @@ 1205-1209 2ba50ff0fb6738c8 50c5b20c28d21f21 1210-1214 945fc79f04d19aaa f637ef58a7981b2f 1215-1219 0f1fd24ee8266b2f abc160e18a574734 -1220-1224 d00cf5cc9e90ba28 231dd0874c6755f5 -1225-1229 d297953e40966981 df2a03c51d6c3fdc +1220-1224 d00cf5cc9e90ba28 f0ef15e8c3556e5f +1225-1229 d297953e40966981 b3b321c730c2a926 1230-1234 2b75e34b10cb22be 8ef04dc863917d10 1235-1239 299fb21e399aef5d 406cd336db7358e3 1240-1244 c0ae0ef2eacb1d20 3edd06583588d801 @@ -297,6 +297,6 @@ 1470-1474 7285c5cca58b9f85 9ebcb3c0e2ee91ad 1475-1479 826e8660179fe16a 83cc3b42bc69dce7 1480-1484 8ecf1216c69ed810 e1a7dfcb53524ed9 -1485-1489 8a3905febae9ece0 03ac60bf7554ad65 +1485-1489 8a3905febae9ece0 18aa32c8bccb0395 1490-1494 2f1c19a2427058be ccd75a4f323eb2a2 1495-1499 952ea603e646e84a 0a46e856da36a94c