From c262b1b1c1997495542fb876c8a68f5312852ec7 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 11 Apr 2026 13:32:15 +0200 Subject: [PATCH 1/2] fix: correct exception handling in get_external_ip fallback chain The fallback chain in get_external_ip() catches ExternalIPNotFound but none of the code inside the try blocks raises that exception. The actual exceptions (requests.RequestException, AssertionError, OSError, json.JSONDecodeError, etc.) propagate unhandled, preventing fallthrough to the next IP provider. Changes: - Replace all `except ExternalIPNotFound` with `except Exception` to properly catch real exceptions and allow fallthrough - Replace deprecated `os.popen()` calls with `subprocess.run()` - Replace bare `assert` validation with direct `ip_to_int()` calls which raise on invalid input regardless of -O flag Fixes #3309 --- bittensor/utils/networking.py | 48 +++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/bittensor/utils/networking.py b/bittensor/utils/networking.py index 4558907a73..bd93629142 100644 --- a/bittensor/utils/networking.py +++ b/bittensor/utils/networking.py @@ -1,6 +1,7 @@ """Utils for handling local network with ip and ports.""" import os +import subprocess from typing import Optional from urllib import request as urllib_request @@ -66,55 +67,58 @@ def get_external_ip() -> str: # --- Try AWS try: external_ip = requests.get("https://checkip.amazonaws.com").text.strip() - assert isinstance(ip_to_int(external_ip), int) + ip_to_int(external_ip) return str(external_ip) - except ExternalIPNotFound: + except Exception: pass - # --- Try ipconfig. + # --- Try ifconfig.me try: - process = os.popen("curl -s ifconfig.me") - external_ip = process.readline() - process.close() - assert isinstance(ip_to_int(external_ip), int) + result = subprocess.run( + ["curl", "-s", "ifconfig.me"], capture_output=True, text=True, timeout=10 + ) + external_ip = result.stdout.strip() + ip_to_int(external_ip) return str(external_ip) - except ExternalIPNotFound: + except Exception: pass # --- Try ipinfo. try: - process = os.popen("curl -s https://ipinfo.io") - external_ip = json.loads(process.read())["ip"] - process.close() - assert isinstance(ip_to_int(external_ip), int) + result = subprocess.run( + ["curl", "-s", "https://ipinfo.io"], capture_output=True, text=True, timeout=10 + ) + external_ip = json.loads(result.stdout)["ip"] + ip_to_int(external_ip) return str(external_ip) - except ExternalIPNotFound: + except Exception: pass # --- Try myip.dnsomatic try: - process = os.popen("curl -s myip.dnsomatic.com") - external_ip = process.readline() - process.close() - assert isinstance(ip_to_int(external_ip), int) + result = subprocess.run( + ["curl", "-s", "myip.dnsomatic.com"], capture_output=True, text=True, timeout=10 + ) + external_ip = result.stdout.strip() + ip_to_int(external_ip) return str(external_ip) - except ExternalIPNotFound: + except Exception: pass # --- Try urllib ipv6 try: external_ip = urllib_request.urlopen("https://ident.me").read().decode("utf8") - assert isinstance(ip_to_int(external_ip), int) + ip_to_int(external_ip) return str(external_ip) - except ExternalIPNotFound: + except Exception: pass # --- Try Wikipedia try: external_ip = requests.get("https://www.wikipedia.org").headers["X-Client-IP"] - assert isinstance(ip_to_int(external_ip), int) + ip_to_int(external_ip) return str(external_ip) - except ExternalIPNotFound: + except Exception: pass raise ExternalIPNotFound From cf54e7f44cce6cbdf64c773237d1895d5ae778b3 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 11 Apr 2026 21:45:34 +0200 Subject: [PATCH 2/2] fix(tests): update test mock from os.popen to subprocess.run The implementation in bittensor/utils/networking.py was updated in this PR to replace os.popen("curl ...") with subprocess.run(...), but the test test_get_external_ip_os_request_urllib_broken still mocked os.popen which no longer intercepts any calls. Update the test to mock subprocess.run and bittensor.utils.networking.urllib_request.urlopen so all fallback paths are properly blocked and the expected ExternalIPNotFound exception is raised. Co-Authored-By: Claude Sonnet 4.6 --- tests/unit_tests/utils/test_networking.py | 31 ++++++++--------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/tests/unit_tests/utils/test_networking.py b/tests/unit_tests/utils/test_networking.py index a3f2c54ac6..8d5c95a5a7 100644 --- a/tests/unit_tests/utils/test_networking.py +++ b/tests/unit_tests/utils/test_networking.py @@ -134,31 +134,22 @@ def mock_call(): def test_get_external_ip_os_request_urllib_broken(): - """Test getting the external IP address when os.popen and requests.get/urllib.request are broken.""" + """Test getting the external IP address when subprocess.run, requests.get, and urllib.request are broken.""" - class FakeReadline: - def readline(self): - return 1 - - def mock_call(): - return FakeReadline() + def mock_subprocess_run(*args, **kwargs): + raise OSError("subprocess.run mocked to fail") class FakeResponse: - def text(self): - return 1 + text = "not-an-ip" - def mock_call_two(): - return FakeResponse() + def mock_requests_get(*args, **kwargs): + raise requests.exceptions.ConnectionError("requests.get mocked to fail") - class FakeRequest: - def urlopen(self): - return 1 - - with mock.patch.object(os, "popen", new=mock_call): - with mock.patch.object(requests, "get", new=mock_call_two): - urllib.request = MagicMock(return_value=FakeRequest()) - with pytest.raises(Exception): - assert utils.networking.get_external_ip() + with mock.patch("subprocess.run", new=mock_subprocess_run): + with mock.patch.object(requests, "get", new=mock_requests_get): + with mock.patch("bittensor.utils.networking.urllib_request.urlopen", side_effect=OSError("urlopen mocked to fail")): + with pytest.raises(Exception): + assert utils.networking.get_external_ip() # Test formatting WebSocket endpoint URL