Skip to content

Commit 564f5e0

Browse files
authored
Enhance internet connection check and error handling
Refactor internet connection check to include proxy support and improve error handling. Update error messages to warnings for better clarity.
1 parent 655c22b commit 564f5e0

File tree

1 file changed

+68
-39
lines changed

1 file changed

+68
-39
lines changed

builder/penv_setup.py

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import subprocess
2222
import sys
2323
from pathlib import Path
24+
from urllib.parse import urlparse
2425

2526
from platformio.package.version import pepver_to_semver
2627
from platformio.compat import IS_WINDOWS
@@ -59,16 +60,44 @@
5960
}
6061

6162

62-
def has_internet_connection(host="1.1.1.1", port=53, timeout=2):
63+
def has_internet_connection(timeout=5):
6364
"""
64-
Checks if an internet connection is available (default: Cloudflare DNS server).
65-
Returns True if a connection is possible, otherwise False.
65+
Checks practical internet reachability for dependency installation.
66+
1) If HTTPS/HTTP proxy environment variable is set, test TCP connectivity to the proxy endpoint.
67+
2) Otherwise, test direct TCP connectivity to common HTTPS endpoints (port 443).
68+
69+
Args:
70+
timeout (int): Timeout duration in seconds for the connection test.
71+
72+
Returns:
73+
True if at least one path appears reachable; otherwise False.
6674
"""
67-
try:
68-
with socket.create_connection((host, port), timeout=timeout):
75+
# 1) Test TCP connectivity to the proxy endpoint.
76+
proxy = os.getenv("HTTPS_PROXY") or os.getenv("https_proxy") or os.getenv("HTTP_PROXY") or os.getenv("http_proxy")
77+
if proxy:
78+
try:
79+
u = urlparse(proxy if "://" in proxy else f"http://{proxy}")
80+
host = u.hostname
81+
port = u.port or (443 if u.scheme == "https" else 80)
82+
if host and port:
83+
socket.create_connection((host, port), timeout=timeout).close()
84+
return True
85+
except Exception:
86+
# If proxy connection fails, fall back to direct connection test
87+
pass
88+
89+
# 2) Test direct TCP connectivity to common HTTPS endpoints (port 443).
90+
https_hosts = ("pypi.org", "files.pythonhosted.org", "github.com")
91+
for host in https_hosts:
92+
try:
93+
socket.create_connection((host, 443), timeout=timeout).close()
6994
return True
70-
except OSError:
71-
return False
95+
except Exception:
96+
continue
97+
98+
# Direct DNS:53 connection is abolished due to many false positives on enterprise networks
99+
# (add it at the end if necessary)
100+
return False
72101

73102

74103
def get_executable_path(penv_dir, executable_name):
@@ -286,18 +315,18 @@ def _get_installed_uv_packages():
286315
for p in packages:
287316
result[p["name"].lower()] = pepver_to_semver(p["version"])
288317
else:
289-
print(f"Error: uv pip list failed with exit code {result_obj.returncode}")
318+
print(f"Warning: uv pip list failed with exit code {result_obj.returncode}")
290319
if result_obj.stderr:
291320
print(f"Error output: {result_obj.stderr.strip()}")
292321

293322
except subprocess.TimeoutExpired:
294-
print("Error: uv pip list command timed out")
323+
print("Warning: uv pip list command timed out")
295324
except (json.JSONDecodeError, KeyError) as e:
296-
print(f"Error: Could not parse package list: {e}")
325+
print(f"Warning: Could not parse package list: {e}")
297326
except FileNotFoundError:
298-
print("Error: uv command not found")
327+
print("Warning: uv command not found")
299328
except Exception as e:
300-
print(f"Error! Couldn't extract the list of installed Python packages: {e}")
329+
print(f"Warning! Couldn't extract the list of installed Python packages: {e}")
301330

302331
return result
303332

@@ -306,39 +335,39 @@ def _get_installed_uv_packages():
306335

307336
if packages_to_install:
308337
packages_list = []
309-
package_map = {}
310338
for p in packages_to_install:
311339
spec = python_deps[p]
312340
if spec.startswith(('http://', 'https://', 'git+', 'file://')):
313341
packages_list.append(spec)
314-
package_map[spec] = p
315342
else:
316-
full_spec = f"{p}{spec}"
317-
packages_list.append(full_spec)
318-
package_map[full_spec] = p
343+
packages_list.append(f"{p}{spec}")
319344

320-
for package_spec in packages_list:
321-
cmd = [
322-
penv_uv_executable, "pip", "install",
323-
f"--python={python_exe}",
324-
"--quiet", "--upgrade",
325-
package_spec
326-
]
327-
try:
328-
subprocess.check_call(
329-
cmd,
330-
stdout=subprocess.DEVNULL,
331-
stderr=subprocess.STDOUT,
332-
timeout=300
333-
)
334-
except subprocess.CalledProcessError as e:
335-
print(f"Error: Installing package '{package_map.get(package_spec, package_spec)}' failed (exit code {e.returncode}).")
336-
except subprocess.TimeoutExpired:
337-
print(f"Error: Installing package '{package_map.get(package_spec, package_spec)}' timed out.")
338-
except FileNotFoundError:
339-
print("Error: uv command not found")
340-
except Exception as e:
341-
print(f"Error: Installing package '{package_map.get(package_spec, package_spec)}': {e}.")
345+
cmd = [
346+
penv_uv_executable, "pip", "install",
347+
f"--python={python_exe}",
348+
"--quiet", "--upgrade"
349+
] + packages_list
350+
351+
try:
352+
subprocess.check_call(
353+
cmd,
354+
stdout=subprocess.DEVNULL,
355+
stderr=subprocess.STDOUT,
356+
timeout=300
357+
)
358+
359+
except subprocess.CalledProcessError as e:
360+
print(f"Error: Failed to install Python dependencies (exit code: {e.returncode})")
361+
return False
362+
except subprocess.TimeoutExpired:
363+
print("Error: Python dependencies installation timed out")
364+
return False
365+
except FileNotFoundError:
366+
print("Error: uv command not found")
367+
return False
368+
except Exception as e:
369+
print(f"Error installing Python dependencies: {e}")
370+
return False
342371

343372
return True
344373

0 commit comments

Comments
 (0)