Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 62 additions & 8 deletions platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import shutil
import struct
import subprocess
import time
from pathlib import Path
from typing import Optional, Dict, List, Any, Union

Expand Down Expand Up @@ -139,6 +140,47 @@ def is_internet_available():
"""
return has_internet_connection()


def patch_file_downloader():
"""Monkey-patch PlatformIO's FileDownloader to retry on transient HTTP errors."""
from platformio.package.download import FileDownloader
from platformio.package.exception import PackageException

if getattr(FileDownloader.__init__, "_patched", False):
return

original_init = FileDownloader.__init__

def patched_init(self, *args, **kwargs):
max_retries = 5
for attempt in range(max_retries):
try:
original_init(self, *args, **kwargs)
return
except PackageException as e:
if attempt < max_retries - 1:
delay = 2 ** (attempt + 1)
logger.warning(
"Package download failed: %s. Retrying in %ds... (attempt %d/%d)",
e, delay, attempt + 1, max_retries,
)
try:
if hasattr(self, "_http_response") and self._http_response is not None:
self._http_response.close()
if hasattr(self, "_http_session"):
self._http_session.close()
except (AttributeError, OSError) as cleanup_err:
logger.debug("Retry cleanup failed: %s", cleanup_err)
time.sleep(delay)
else:
raise

patched_init._patched = True
FileDownloader.__init__ = patched_init


patch_file_downloader()

def safe_file_operation(operation_func):
"""Decorator for safe filesystem operations with error handling."""
def wrapper(*args, **kwargs):
Expand Down Expand Up @@ -571,14 +613,26 @@ def _configure_arduino_framework(self, frameworks: List[str], mcu: str) -> None:
self.packages["framework-arduinoespressif32"]["optional"] = False
self.packages["framework-arduinoespressif32-libs"]["optional"] = False
if is_internet_available():
try:
response = requests.get(ARDUINO_ESP32_PACKAGE_URL, timeout=30)
response.raise_for_status()
packjdata = response.json()
dyn_lib_url = packjdata['packages'][0]['tools'][0]['systems'][0]['url']
self.packages["framework-arduinoespressif32-libs"]["version"] = dyn_lib_url
except (requests.RequestException, KeyError, IndexError) as e:
logger.error(f"Failed to fetch Arduino framework library URL: {e}")
max_retries = 5
for attempt in range(max_retries):
try:
response = requests.get(ARDUINO_ESP32_PACKAGE_URL, timeout=30)
response.raise_for_status()
packjdata = response.json()
dyn_lib_url = packjdata['packages'][0]['tools'][0]['systems'][0]['url']
self.packages["framework-arduinoespressif32-libs"]["version"] = dyn_lib_url
break
except (requests.RequestException, ValueError, KeyError, IndexError) as e:
if attempt < max_retries - 1:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
delay = 2 ** (attempt + 1)
logger.warning(
"Failed to fetch Arduino framework library URL: %s. "
"Retrying in %ds... (attempt %d/%d)",
e, delay, attempt + 1, max_retries,
)
time.sleep(delay)
else:
logger.error(f"Failed to fetch Arduino framework library URL: {e}")
if mcu == "esp32c2":
self.packages["framework-arduino-c2-skeleton-lib"]["optional"] = False
if mcu == "esp32c61":
Expand Down