Skip to content

Commit b111e6f

Browse files
authored
Enhance internet connection check logic
Refactor internet connection check to support proxy testing and improve error handling.
1 parent 708c259 commit b111e6f

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)