Skip FileDownloader monkey-patch when native retry support exists#467
Conversation
Detect if platformio-core's FileDownloader already has a RETRY class attribute (introduced in the upstream retry enhancement). When present, the native implementation handles retries with urllib3.Retry for initial connections and _stream_with_retry for mid-download failures, making the monkey-patch redundant. This avoids double-retry scenarios (up to 5x5 = 25 attempts) and exception type mismatches (native code raises IOError, not PackageException) when running against an enhanced platformio-core. Amp-Thread-ID: https://ampcode.com/threads/T-019d8365-f1fd-713f-80df-fede9167d179 Co-authored-by: Amp <amp@ampcode.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai analyze retry download function together with PR Jason2866/platformio-core#34 |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 9691 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 11 🏁 Script executed: Length of output: 53602
Combined Analysis:
|
| Layer | Mechanism | Max attempts |
|---|---|---|
urllib3 HTTPAdapter |
Status codes [413,429,500,502,503,504] | 5 |
_stream_with_retry |
Mid-stream RequestException |
5 |
Monkey-patch __init__ |
PackageException on init |
5 |
The guard collapses this to just the two native layers (urllib3 connection-level + stream-level), which is the intended design. ✅
Exception Type Mismatch — Avoided
| Path | Exception raised |
|---|---|
| Monkey-patch | catches PackageException |
Native _stream_with_retry after max retries |
raises IOError |
Native __init__ on bad status |
raises PackageException |
If both were active, PackageException from init would be caught by the monkey-patch for a retry, but IOError from _stream_with_retry would not — leading to unexpected propagation. The guard prevents this inconsistency. ✅
⚠️ Status Code Coverage Gap (404)
This is the most notable behavioral difference to be aware of:
- Monkey-patch: retries on any
PackageException, including those triggered by a 404 response in__init__. This was intentional — GitHub sometimes returns transient 404s. - Native urllib3 retry:
status_forcelist = [413, 429, 500, 502, 503, 504]— 404 is not included.
When the guard skips the monkey-patch, a transient GitHub 404 will not be retried at the urllib3 level. The __init__ will raise PackageException, which nothing now catches for a retry.
Given your previous clarification that packages are verified before download is attempted (so real 404s don't happen in practice), this gap may be acceptable. But if GitHub's transient 404s are still a concern, PR #34 could add 404 to status_forcelist:
RETRY = Retry(
total=5,
backoff_factor=1,
status_forcelist=[404, 413, 429, 500, 502, 503, 504], # add 404
raise_on_status=False,
)Backoff Schedule Comparison
| Attempt | Monkey-patch (2^(n+1)) |
Native stream (1 × 2^(n-1)) |
|---|---|---|
| 1 | 2s | 1s |
| 2 | 4s | 2s |
| 3 | 8s | 4s |
| 4 | 16s | 8s |
| 5 | 32s | 16s |
Native retry is slightly more aggressive (shorter waits). Both are reasonable.
Improvements in Native Retry (PR #34)
- Resumable downloads: Uses
Range: bytes=N-andIf-Rangeheaders to resume interrupted streams rather than re-downloading from byte 0 — a significant improvement. ✅ - Progress tracking: The
_STREAM_RESETsentinel correctly resets progress bars when a restart is forced. ✅ - Separation of concerns: urllib3 handles transport-level retries;
_stream_with_retryhandles mid-stream failures. Clean architecture. ✅
Summary
The two PRs are well-designed to work together. The hasattr(FileDownloader, 'RETRY') guard in PR #467 is the correct and minimal mechanism to enable a smooth transition. The only item worth double-checking is whether the 404 exclusion from status_forcelist in PR #34 could re-introduce the transient GitHub 404 issue that the original monkey-patch (PR #463) was meant to solve.
🧠 Learnings used
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 0
File: :0-0
Timestamp: 2026-04-12T20:24:06.280Z
Learning: In the pioarduino/platform-espressif32 project, retrying on all HTTP error status codes (including 404) during package downloads is intentional. GitHub sometimes returns incorrect/misleading status codes (including 404) as transient errors. Packages are verified to be available before download is attempted, so real 404 (not found) errors do not occur in practice. The broad `PackageException` catch in the retry logic (e.g., in `patch_file_downloader()`) should not be narrowed to specific status codes like 502/503 only.
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 268
File: builder/penv_setup.py:87-96
Timestamp: 2025-08-26T14:52:29.044Z
Learning: For the pioarduino/platform-espressif32 project, Jason2866 prefers to trust system executables when falling back to PATH-based command resolution (like "uv"), rather than implementing additional validation checks for security concerns.
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 0
File: :0-0
Timestamp: 2025-09-23T12:35:35.508Z
Learning: For the pioarduino/platform-espressif32 project, Jason2866 has explicitly instructed to NEVER create issues or PRs outside of his repositories (pioarduino/*). All issue and PR creation must be strictly limited to his own repositories only, never in upstream or other repositories.
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 0
File: :0-0
Timestamp: 2025-09-23T12:35:35.508Z
Learning: For the pioarduino/platform-espressif32 project, Jason2866 has explicitly instructed to NEVER create issues or PRs outside of his repositories (pioarduino/*). All issue and PR creation must be strictly limited to his own repositories only, never in upstream or other repositories.
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 281
File: builder/frameworks/component_manager.py:1047-1054
Timestamp: 2025-09-04T15:27:18.112Z
Learning: In the pioarduino/platform-espressif32 project, Jason2866 prefers not to add guard checks for missing arduino_libs_mcu in backup operations, preferring to let the operation proceed rather than silently skip when the directory might be missing.
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 296
File: builder/main.py:49-50
Timestamp: 2025-09-22T14:47:08.855Z
Learning: For the pioarduino/platform-espressif32 project, Jason2866 prefers that critical setup operations like platform.setup_python_env should crash immediately if they fail rather than having fallback mechanisms, as this ensures problems are detected early and clearly rather than being masked.
Learnt from: Jason2866
Repo: pioarduino/platform-espressif32 PR: 371
File: builder/spiffsgen.py:620-629
Timestamp: 2025-12-27T16:33:38.710Z
Learning: In the pioarduino/platform-espressif32 project, when extracting SPIFFS files in builder/spiffsgen.py, Jason2866 prefers to check if rel_path is empty after lstrip('/') and skip such files with a warning message, rather than attempting to handle empty parent directories in os.makedirs calls. This treats empty paths as invalid/corrupted data.
Summary
Detects if
platformio-core'sFileDownloaderalready has native retry support by checking for theRETRYclass attribute. When present, the monkey-patch from #463 is skipped to avoid:IOErroron network failures, notPackageException, so the monkey-patch'sexcept PackageExceptionwouldn't catch them anywayHow it works
RETRYattribute absent → monkey-patch applies as beforeRETRYattribute present → monkey-patch skipped, native retry handles everythingChecklist
Summary by CodeRabbit