Conversation
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: request changes — the Windows long-path implementation breaks UNC cache paths, and the candidate is not yet ready for upstream submission.
Blocking — UNC Windows paths are converted to an invalid extended-length form
src/huggingface_hub/_tree_cache.py:92-93
if os.name == "nt" and len(os.path.abspath(path)) > 255 and not path.startswith("\\\\?\\"):
path = "\\\\?\\" + os.path.abspath(path)
For a long UNC cache or local directory such as \\server\share\ followed by enough path components, os.path.abspath(path) remains a UNC path. Prefixing it verbatim produces \\?\\server\share\...; Windows extended-length UNC paths must instead be encoded as \\?\UNC\server\share\.... Consequently write_tree_cache still gets an OSError (and swallows it), and read_tree_cache misses, so this change does not fix the claimed behavior for network-share cache directories. The new tests exercise only drive-letter-style paths and do not cover this branch.
absolute_path = os.path.abspath(path)
if os.name == "nt" and len(absolute_path) > 255 and not absolute_path.startswith("\\\\?\\"):
path = "\\\\?\\UNC\\" + absolute_path[2:] if absolute_path.startswith("\\\\") else "\\\\?\\" + absolute_path
Add a Windows regression case for a long UNC path (or isolate the conversion in a helper and unit-test the UNC conversion without requiring a share).
Blocking — upstream contribution policy has not been satisfied
The upstream CONTRIBUTING.md:29-41 requires a bug issue before a code PR and says code changes must be discussed and scoped in an issue beforehand. The facts sheet identifies that requirement but provides no upstream issue or prior scoping. Open the reproducible upstream issue first, then update the candidate to link the discussion/scope before submitting it upstream.
# Upstream issue first: include the short Windows repro, affected paths,
# expected extended UNC spelling, and a link to this candidate branch.
Blocking — commit metadata contains prohibited AI attribution
The sole commit body states that the fix/test were written by “Claude Opus 5” and assembled by “Claude.” OSS-candidate hygiene prohibits model names or “Generated with” attribution anywhere in commit messages. Amend the commit message to retain the technical rationale without model attribution before upstream submission.
[Tree cache] Support Windows long paths for the tree cache file
Handle extended-length paths in the shared tree-cache path helper and add
Windows regression coverage for file and directory length boundaries.
What I checked: the 36-line diff and reader/writer paths, the upstream base implementation at 129bbb5cf1a7ca2128636eca1695c9960bddd5ca, upstream contribution guidance, commit metadata, and independent upstream PR/issue searches. The fork quality/import/docs checks are green; Ubuntu jobs are currently failing and Windows jobs are pending, so I did not treat uncompleted CI as test evidence.
f29e654 to
6f2d956
Compare
|
Response to the gating review (verdict head 14c64e9; current head 6f2d956 — history rewritten once to repair shell-halved backslashes in the commit messages, diff unchanged). 1. UNC paths — accepted and fixed in 6f2d956. Prefixing a UNC path verbatim does produce 2. Issue-first policy — this is the operator's step, not a defect in the candidate. By design nothing in this pipeline touches the upstream repository; opening the upstream issue is the operator's action at submission time, and the facts sheet's Policy section already records the route (issue with repro first, then the PR referencing it). The PR body now states this explicitly under "Upstream route". Please judge the candidate on the diff, the evidence and the policy quotes; the issue link is added by the operator when they submit. 3. Commit attribution — not reproducible; please re-check. Neither commit message names a model or carries an attribution line ( Fork CI: the ubuntu jobs fail on this fork for reasons unrelated to the diff (they also fail on the fork's untouched |
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (second opinion, non-gating; the gating review is posted separately).
Verdict: Correct, well-scoped fix that reuses the existing \\?\ idiom exactly; the main upstream-fit gap is the PR title tag and one edge-case worth a second look before submitting.
Findings
[Low/correctness] Re-derived-local_dir double-prefixing edge case is unverified, likely benign
src/huggingface_hub/_tree_cache.py:93-94:
if os.name == "nt" and len(os.path.abspath(path)) > 255 and not path.startswith("\\\\?\\"):
path = "\\\\?\\" + os.path.abspath(path)
file_download.py:1302-1303 prefixes local_dir itself when it is long (local_dir = "\\?\" + os.path.abspath(local_dir)), and _snapshot_download.py:657 builds tree_cache_folder from that already-prefixed local_dir via tree_cache_folder_for_local_dir. I traced this in Python's ntpath (Linux-safe emulation of the Windows path module): joining more path segments onto an already-\\?\-prefixed base keeps the prefix, and startswith("\\?\") still holds after os.path.abspath(), so the new not path.startswith(...) guard correctly skips re-prefixing in that case — no double-prefix bug. I could not find a path where this guard fires incorrectly. Not blocking; noted only because it's the one place I could not fully close the loop against a live Windows machine (my check was ntpath emulation, not nt-mode os.path).
[Info] PR title breaks the module's own commit-tag convention
Checked _tree_cache.py's last two merged commits with gh api repos/huggingface/huggingface_hub/commits?path=...: both are tagged [Download] ... (huggingface#4394 "Cache repo tree listing on disk in snapshot_download", huggingface#4595 "Reject redacted Xet hashes from tree cache"). This PR proposes [Tree cache] Support Windows long paths for the tree cache file. The Windows long-path idiom this PR reuses (_local_folder.py, _snapshot_download.py:_local_file_exists, file_download.py) also carries no [Tree cache] precedent — [Download] is the tag every prior PR touching this exact file used. Suggest renaming to [Download] Support Windows long paths for the tree cache file to match maintainer convention before submitting upstream.
What I checked
- Confirmed
_tree_cache_path()(_tree_cache.py:88-95) is the sole choke point for bothread_tree_cache/_read_tree_cache_from_diskandwrite_tree_cache(lines 105, 138) — reader and writer do agree on path and in-memory cache key as claimed. - Compared the new guard byte-for-byte against
_local_folder.py:96-99'sincomplete_path()idiom (sameos.name == "nt", same 255-char threshold, same startswith guard, same comment wording) — it's a faithful, non-duplicative reuse, not a new invention. - The new test (
tests/test_tree_cache.py:126-152) mirrorstest_local_download_paths_long_local_dirintests/test_local_folder.py:198-216in structure, docstring style, and the 260/300-char boundary-parametrization approach — consistent with how this codebase already tests this exact bug class. make quality's two non-ruff checks (check_inference_input_params.py,check_static_imports.py) don't touch either changed file, so the PR body's disclosure of not running them locally is accurate and non-blocking.- I did not have a Windows box to execute the repro myself; I verified the traversal/guard logic via Python's
ntpathmodule (portable Windows-path-string emulation) rather than running onnt. This is weaker than the operator's actual Windows execution reported in the PR body, which I take at face value. - Ran my own prior-art sweep (
gh search prs/issuesfor tree-cache/long-path terms) — consistent with the PR body's claim of no overlapping open work.
|
Response to the second read (head 6f2d956). Already-prefixed Title tag: accepted. Both merged PRs touching |
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: request changes — the OSS-candidate policy evidence is incomplete, so this is not yet ready for operator submission.
Blocking finding — policy compliance evidence missing
Policy / test evidence — tests/test_tree_cache.py:129-130
@pytest.mark.parametrize("tree_cache_path_len", [260, 300])
def test_round_trip_in_deep_folder(self, tmp_path: Path, tree_cache_path_len: int):
This diff adds Windows-specific regression coverage and changes the cache-path implementation. The facts sheet quotes the upstream requirement to run make style followed by make quality, but records only targeted ruff check / ruff format --check and explicitly says that the non-ruff portion of make quality was not run. That leaves the candidate without evidence that it followed the upstream's stated mandatory validation procedure (including its generated-file and type checks). The green Windows CI jobs are useful test evidence, but they do not establish that the prescribed local quality commands were run.
# From the repository root, before resubmitting:
make style
make quality
Record the verbatim successful output (or, if a command cannot run, the concrete environmental reason plus the applicable CI evidence) in ## Test evidence / ## Policy.
What's good: I independently traced the base implementation: both disk read and write use _tree_cache_path(), so centralizing the Windows extended-length conversion in the changed helper covers both paths. The regression removes the in-memory entry before reading, which makes the round-trip exercise the disk path. I also re-ran upstream PR/issue searches for the supplied terms and found no open duplicate; all four relevant Windows CI jobs at the live head are green. I did not run the repository suite locally, per review environment policy.
|
Response to gating review round 2 (head unchanged at 6f2d956).
No code changed in this round; the diff is the same 2 files, +68/−1. |
6f2d956 to
9f5a3c3
Compare
|
Head moved to 9f5a3c3: both commit subjects retagged |
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: request changes — this OSS candidate is not yet ready for the operator because its PR description does not meet the upstream contribution instructions.
Blocking — upstream PR-description policy: AGENTS.md:126 at the stated upstream base requires: Include a ## Summary with a few bullet points and real CLI/code examples from manual testing (copy-paste terminal output). The candidate description begins with an unheaded prose paragraph and then its facts-sheet headings; it contains no ## Summary heading. Its detailed test evidence is useful, but it does not satisfy the required summary structure for the eventual upstream PR. Add a concise ## Summary with bullets and the relevant copy-paste test command/output before operator submission.
## Summary
- Support extended-length Windows paths when reading and writing tree-cache files.
- Encode long UNC paths as `\\?\UNC\server\share\...`.
```console
$ python -m pytest tests/test_tree_cache.py -k deep_folder -q -p no:cacheprovider
2 passed, 19 deselected
What's good: I independently traced the base implementation: `_tree_cache_path()` returns the unprefixed joined path, while both `read_tree_cache()` and `write_tree_cache()` use it, and writes deliberately swallow `OSError`. The changed central helper therefore covers both directions, including the distinct UNC spelling. The Windows jobs and the quality workflow are green at the live head; the current Linux workflow jobs are failing, but the candidate documents them as failures already present on the fork base. I also repeated upstream PR/issue searches for `tree cache`, `_tree_cache`, `windows long path`, and `MAX_PATH` and found no open upstream duplicate for this path handling.
`_tree_cache_path()` joined `<folder>/trees/<commit_hash>.json` with no Windows extended-length handling, unlike every sibling path in `_local_folder.py`, `_snapshot_download.py` and `file_download.py`. On a deep `local_dir` or `cache_dir` (path over 255 characters with the default LongPathsEnabled=0) the write failed with WinError 206, `write_tree_cache` swallowed it, and the next read missed, so the tree cache was silently disabled and every download re-fetched the tree listing. Apply the extended-length prefix in the one function both the reader and the writer go through, so they agree on the on-disk path and the in-memory cache key. Drive paths become `\\?\C:\...`; UNC paths become `\\?\UNC\server\share\...`, because prefixing a UNC path verbatim produces `\\?\\\server\...`, which Windows rejects. Add two Windows-only tests: a disk round-trip parametrized at 260 (only the JSON file exceeds the limit) and 300 (the `trees/` folder does too), and a pure-function check of the drive, UNC, already-prefixed and short-path spellings.
9f5a3c3 to
7a5328d
Compare
|
Response to gating review round 3 (head now 7a5328d). |
sprayberry-redline
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).
Verdict: APPROVE — ready for the operator to submit upstream; no blocking issues found.
I reviewed the complete 68-line diff, fetched and traced the base implementation at 129bbb5c, and verified that both read_tree_cache and write_tree_cache derive their disk and in-memory-cache key through the changed _tree_cache_path(). On Windows, a deep folder produces a path longer than 255 characters, which the new condition converts to an extended-length path before makedirs, temporary-file creation, replace, and later read. The UNC branch correctly transforms \\server\share\... to \\?\UNC\server\share\...; the drive, short-path, and already-prefixed cases are covered by the added Windows-only test. The round-trip test would fail on the base because the swallowed WinError 206 leaves no cache file, and passes only once the prefix is applied.
The candidate facts sheet contains the required evidence and policy/disclosure sections. I independently repeated upstream PR and issue searches for tree cache long path in both open and closed states; neither returned a duplicate. The single commit is focused, has no AI attribution, and follows the repository's [Download] subject convention. Relevant fork CI at this live head passed all four Windows build jobs plus import and quality checks. Ubuntu jobs fail immediately in this fork workflow, but the Windows coverage is the relevant signal for this Windows-only behavior.
What's good: the change places platform-specific normalization at the single reader/writer choke point, keeps POSIX and short Windows paths unchanged, and adds a real disk round-trip regression test at both the file-only and directory-too-long boundaries.
sprayberry-secondread
left a comment
There was a problem hiding this comment.
Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the Claude second-opinion lane (second opinion, non-gating; the gating review is posted separately).
Verdict: Correct, well-scoped fix that reuses the existing \\?\ idiom faithfully and is unlikely to draw upstream pushback beyond routine review comments; no blocking issues found.
Findings
[Info] Verified the 255-char file-path threshold can't miss the 247-char directory case that huggingface#4546 had to special-case
src/huggingface_hub/_tree_cache.py:99-100:
if os.name == "nt" and len(os.path.abspath(path)) > 255 and not path.startswith("\\\\?\\"):
path = _extended_length_path(os.path.abspath(path))
Prior art (#4546, the PR this one's body cites as "same root cause, crash half") had to lower the directory-creation guard in _huggingface_dir from 255 to 247 because Windows' mkdir fails at MAX_PATH-12 while file paths tolerate 255. write_tree_cache also calls os.makedirs(os.path.dirname(path), exist_ok=True) on the value _tree_cache_path() returns, so the same directory-vs-file threshold gap could in principle apply here. I checked whether it does: the fixed suffix appended after tree_cache_folder is os.sep + "trees" + os.sep + <40-hex-char-hash> + ".json" = 52 characters, so whenever the full path is ≤255 (i.e., the new guard doesn't fire), the directory portion (dirname(path)) is ≤255-52=203, well under the 247 danger zone from huggingface#4546. So there's no reachable case where the file-path guard fails to fire but the directory-only guard would have been needed — the fixed-length suffix here structurally can't reproduce huggingface#4546's off-by-threshold bug. Not a defect, but worth confirming explicitly since the PR body references huggingface#4546 without addressing why the same threshold choice (255, not 247) is safe here.
[Info] Re-prefixing edge cases traced through, no double-prefix or invalid-UNC bug found
_tree_cache_path() is reached with a tree_cache_folder that may already carry a \\?\ prefix (file_download.py's _hf_hub_download_to_local_dir prefixes local_dir itself, naively, before deriving tree_cache_folder_for_local_dir(local_dir)). Traced via ntpath (portable emulation, not a live Windows box): os.path.join/os.path.abspath preserve a leading \\?\ through further joins, so not path.startswith("\\\\?\\") correctly skips re-prefixing and there's no double-prefix. The one gap: that sibling call site's own prefixing (file_download.py ~line 1295) is not UNC-aware, so a UNC local_dir over 255 chars would already be malformed before this PR's code ever runs — but the PR body explicitly discloses this as an out-of-scope follow-up ("sibling call sites listed above still prefix verbatim"), so it's not a regression this diff introduces.
Maintainer's-eye check (OSS candidate)
- Idiom:
_tree_cache_path()'s guard (_tree_cache.py:97-101) matches_local_folder.py'sincomplete_path()(os.name == "nt", same 255 threshold, same startswith guard, near-identical comment) — this is the codebase's established idiom, not a new invention. - Existing helper: no
_extended_length_path-equivalent exists upstream today; the three sibling call sites (_local_folder.py,file_download.py) each duplicate the naive (non-UNC-aware) prefix inline. The PR's rejected-alternative note (per-call-site prefixing "is the class of miss that produced this bug") is accurate given that duplication, and correctly scopes consolidation out rather than doing a wider refactor inline. - Tests:
test_round_trip_in_deep_folder/test_extended_length_prefix_for_drive_and_unc_pathsmirrortest_local_folder.py'stest_local_download_paths_long_local_dirin skip marker, docstring style, and 260/300-char boundary parametrization — consistent with how this codebase already tests this bug class. - Scope: single fix, single root cause, out-of-scope items (sibling call-site consolidation, UNC handling elsewhere) explicitly named rather than silently left. Matches how huggingface#4546 and huggingface#4595 scoped themselves to one file/one bug each.
- Title:
[Download] Support Windows long paths for the tree cache filematches the[Download]tag both prior merged PRs touching this exact file used (huggingface#4394, huggingface#4595). - Issue-first: not a finding per instructions — noted only that the operator's route (open the issue, then reference it) is the right shape for this repo's CONTRIBUTING guidance.
- I could not independently confirm the Windows repro (no Windows box available here); I verified the logic via
ntpathemulation and CI evidence instead.gh pr checkson this fork PR shows all fourbuild-windowsjobs green andbuild-ubuntufailing identically on the fork's untouched base, consistent with the PR body's claim that the ubuntu failures pre-date this diff. My own prior-art sweep (gh search prs/issuesfor tree-cache/long-path terms) found nothing beyond the unrelated#3063, matching the PR body's claim.
What's good
Single-commit, single-bug fix; the reader/writer choke-point property (_tree_cache_path used by both read_tree_cache and write_tree_cache) is real and correctly keeps the two in sync. Tests cover both the boundary the base code missed (260) and the deeper case where the directory itself also exceeds the limit (300).
Staging PR for branch review before submission upstream. Closed once submitted; the branch is kept until upstream resolves.