From 85713ef59a4454c10360454bb4465013666a0e04 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Wed, 24 Apr 2024 08:00:49 -0700 Subject: [PATCH 1/6] Use new SocketPool for ESP32SPI and WIZNET5K --- adafruit_connection_manager.py | 16 +++++++++++++--- tests/conftest.py | 33 +++++++++++++++++++++++++++------ tests/get_radio_test.py | 4 ++-- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/adafruit_connection_manager.py b/adafruit_connection_manager.py index cc70f3f..353f778 100644 --- a/adafruit_connection_manager.py +++ b/adafruit_connection_manager.py @@ -56,6 +56,10 @@ def __init__(self, socket: CircuitPythonSocketType, tls_mode: int) -> None: self.recv = socket.recv self.close = socket.close self.recv_into = socket.recv_into + if hasattr(socket, "_interface"): + self._interface = socket._interface + if hasattr(socket, "_socket_pool"): + self._socket_pool = socket._socket_pool def connect(self, address: Tuple[str, int]) -> None: """Connect wrapper to add non-standard mode parameter""" @@ -93,7 +97,10 @@ def create_fake_ssl_context( * `Adafruit AirLift FeatherWing – ESP32 WiFi Co-Processor `_ """ - socket_pool.set_interface(iface) + if hasattr(socket_pool, "set_interface"): + # this is to manually support legacy hardware like the fona + socket_pool.set_interface(iface) + return _FakeSSLContext(iface) @@ -121,12 +128,15 @@ def get_radio_socketpool(radio): ssl_context = ssl.create_default_context() elif class_name == "ESP_SPIcontrol": - import adafruit_esp32spi.adafruit_esp32spi_socket as pool # pylint: disable=import-outside-toplevel + import adafruit_esp32spi.adafruit_esp32spi_socketpool as socketpool # pylint: disable=import-outside-toplevel + pool = socketpool.SocketPool(radio) ssl_context = create_fake_ssl_context(pool, radio) elif class_name == "WIZNET5K": - import adafruit_wiznet5k.adafruit_wiznet5k_socket as pool # pylint: disable=import-outside-toplevel + import adafruit_wiznet5k.adafruit_wiznet5k_socketpool as socketpool # pylint: disable=import-outside-toplevel + + pool = socketpool.SocketPool(radio) # Note: SSL/TLS connections are not supported by the Wiznet5k library at this time ssl_context = create_fake_ssl_context(pool, radio) diff --git a/tests/conftest.py b/tests/conftest.py index 2d9bb0a..06457cb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,18 +14,39 @@ def set_interface(iface): """Helper to set the global internet interface""" +class SocketPool: + name = None + + def __init__(self, *args, **kwargs): + pass + + @property + def __name__(self): + return self.name + + +class ESP32SPI_SocketPool(SocketPool): # pylint: disable=too-few-public-methods + name = "adafruit_esp32spi_socketpool" + + +class WIZNET5K_SocketPool(SocketPool): # pylint: disable=too-few-public-methods + name = "adafruit_wiznet5k_socketpool" + + socketpool_module = type(sys)("socketpool") socketpool_module.SocketPool = mocket.MocketPool sys.modules["socketpool"] = socketpool_module esp32spi_module = type(sys)("adafruit_esp32spi") -esp32spi_socket_module = type(sys)("adafruit_esp32spi_socket") -esp32spi_socket_module.set_interface = set_interface +esp32spi_socket_module = type(sys)("adafruit_esp32spi_socketpool") +esp32spi_socket_module.SocketPool = ESP32SPI_SocketPool sys.modules["adafruit_esp32spi"] = esp32spi_module -sys.modules["adafruit_esp32spi.adafruit_esp32spi_socket"] = esp32spi_socket_module +sys.modules["adafruit_esp32spi.adafruit_esp32spi_socketpool"] = esp32spi_socket_module wiznet5k_module = type(sys)("adafruit_wiznet5k") -wiznet5k_socket_module = type(sys)("adafruit_wiznet5k_socket") -wiznet5k_socket_module.set_interface = set_interface +wiznet5k_socketpool_module = type(sys)("adafruit_wiznet5k_socketpool") +wiznet5k_socketpool_module.SocketPool = WIZNET5K_SocketPool sys.modules["adafruit_wiznet5k"] = wiznet5k_module -sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socket"] = wiznet5k_socket_module +sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socketpool"] = ( + wiznet5k_socketpool_module +) diff --git a/tests/get_radio_test.py b/tests/get_radio_test.py index ea80f7e..cbd49e8 100644 --- a/tests/get_radio_test.py +++ b/tests/get_radio_test.py @@ -21,13 +21,13 @@ def test_get_radio_socketpool_wifi(): def test_get_radio_socketpool_esp32spi(): radio = mocket.MockRadio.ESP_SPIcontrol() socket_pool = adafruit_connection_manager.get_radio_socketpool(radio) - assert socket_pool.__name__ == "adafruit_esp32spi_socket" + assert socket_pool.__name__ == "adafruit_esp32spi_socketpool" def test_get_radio_socketpool_wiznet5k(): radio = mocket.MockRadio.WIZNET5K() socket_pool = adafruit_connection_manager.get_radio_socketpool(radio) - assert socket_pool.__name__ == "adafruit_wiznet5k_socket" + assert socket_pool.__name__ == "adafruit_wiznet5k_socketpool" def test_get_radio_socketpool_unsupported(): From 8482b9fbdab469da550618b921adf06be8916417 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Wed, 24 Apr 2024 21:26:49 -0700 Subject: [PATCH 2/6] Merge fixes --- tests/conftest.py | 18 ++++++++++-------- tests/get_radio_test.py | 8 ++++---- tests/get_socket_test.py | 4 ++-- tests/ssl_context_test.py | 4 ++-- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 185ef3b..bfea88d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -44,29 +44,32 @@ def circuitpython_socketpool_module(): @pytest.fixture -def adafruit_esp32spi_socket_module(): +def adafruit_esp32spi_socketpool_module(): esp32spi_module = type(sys)("adafruit_esp32spi") esp32spi_socket_module = type(sys)("adafruit_esp32spi_socketpool") esp32spi_socket_module.SocketPool = ESP32SPI_SocketPool sys.modules["adafruit_esp32spi"] = esp32spi_module - sys.modules["adafruit_esp32spi.adafruit_esp32spi_socketpool"] = esp32spi_socket_module + sys.modules["adafruit_esp32spi.adafruit_esp32spi_socketpool"] = ( + esp32spi_socket_module + ) yield del sys.modules["adafruit_esp32spi"] - del sys.modules["adafruit_esp32spi.adafruit_esp32spi_socket"] + del sys.modules["adafruit_esp32spi.adafruit_esp32spi_socketpool"] @pytest.fixture -def adafruit_wiznet5k_socket_module(): +def adafruit_wiznet5k_socketpool_module(): wiznet5k_module = type(sys)("adafruit_wiznet5k") wiznet5k_socketpool_module = type(sys)("adafruit_wiznet5k_socketpool") wiznet5k_socketpool_module.SocketPool = WIZNET5K_SocketPool - wiznet5k_socket_module.SOCK_STREAM = 0x21 + wiznet5k_socketpool_module.SOCK_STREAM = 0x21 sys.modules["adafruit_wiznet5k"] = wiznet5k_module sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socketpool"] = ( - wiznet5k_socketpool_module + wiznet5k_socketpool_module + ) yield del sys.modules["adafruit_wiznet5k"] - del sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socket"] + del sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socketpool"] @pytest.fixture(autouse=True) @@ -79,4 +82,3 @@ def reset_connection_manager(monkeypatch): "adafruit_connection_manager._global_ssl_contexts", {}, ) -) diff --git a/tests/get_radio_test.py b/tests/get_radio_test.py index 05ea5f9..c73c97d 100644 --- a/tests/get_radio_test.py +++ b/tests/get_radio_test.py @@ -22,7 +22,7 @@ def test_get_radio_socketpool_wifi( # pylint: disable=unused-argument def test_get_radio_socketpool_esp32spi( # pylint: disable=unused-argument - adafruit_esp32spi_socket_module, + adafruit_esp32spi_socketpool_module, ): radio = mocket.MockRadio.ESP_SPIcontrol() socket_pool = adafruit_connection_manager.get_radio_socketpool(radio) @@ -30,7 +30,7 @@ def test_get_radio_socketpool_esp32spi( # pylint: disable=unused-argument def test_get_radio_socketpool_wiznet5k( # pylint: disable=unused-argument - adafruit_wiznet5k_socket_module, + adafruit_wiznet5k_socketpool_module, ): radio = mocket.MockRadio.WIZNET5K() with mock.patch("sys.implementation", return_value=[9, 0, 0]): @@ -63,7 +63,7 @@ def test_get_radio_ssl_context_wifi( # pylint: disable=unused-argument def test_get_radio_ssl_context_esp32spi( # pylint: disable=unused-argument - adafruit_esp32spi_socket_module, + adafruit_esp32spi_socketpool_module, ): radio = mocket.MockRadio.ESP_SPIcontrol() ssl_contexts = adafruit_connection_manager.get_radio_ssl_context(radio) @@ -71,7 +71,7 @@ def test_get_radio_ssl_context_esp32spi( # pylint: disable=unused-argument def test_get_radio_ssl_context_wiznet5k( # pylint: disable=unused-argument - adafruit_wiznet5k_socket_module, + adafruit_wiznet5k_socketpool_module, ): radio = mocket.MockRadio.WIZNET5K() with mock.patch("sys.implementation", return_value=[9, 0, 0]): diff --git a/tests/get_socket_test.py b/tests/get_socket_test.py index ea252cc..2937dd7 100644 --- a/tests/get_socket_test.py +++ b/tests/get_socket_test.py @@ -218,7 +218,7 @@ def test_get_socket_runtime_error_ties_again_only_once(): def test_fake_ssl_context_connect( # pylint: disable=unused-argument - adafruit_esp32spi_socket_module, + adafruit_esp32spi_socketpool_module, ): mock_pool = mocket.MocketPool() mock_socket_1 = mocket.Mocket() @@ -237,7 +237,7 @@ def test_fake_ssl_context_connect( # pylint: disable=unused-argument def test_fake_ssl_context_connect_error( # pylint: disable=unused-argument - adafruit_esp32spi_socket_module, + adafruit_esp32spi_socketpool_module, ): mock_pool = mocket.MocketPool() mock_socket_1 = mocket.Mocket() diff --git a/tests/ssl_context_test.py b/tests/ssl_context_test.py index abc857f..9f4a8a1 100644 --- a/tests/ssl_context_test.py +++ b/tests/ssl_context_test.py @@ -13,7 +13,7 @@ def test_connect_esp32spi_https( # pylint: disable=unused-argument - adafruit_esp32spi_socket_module, + adafruit_esp32spi_socketpool_module, ): mock_pool = mocket.MocketPool() mock_socket_1 = mocket.Mocket() @@ -46,7 +46,7 @@ def test_connect_wifi_https( # pylint: disable=unused-argument def test_connect_wiznet5k_https_not_supported( # pylint: disable=unused-argument - adafruit_wiznet5k_socket_module, + adafruit_wiznet5k_socketpool_module, ): mock_pool = mocket.MocketPool() radio = mocket.MockRadio.WIZNET5K() From b0d5e027c63965ff321cc7464a9d0642057b8cb5 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Thu, 25 Apr 2024 17:40:48 -0700 Subject: [PATCH 3/6] Merge fixes --- adafruit_connection_manager.py | 1 - tests/conftest.py | 27 +++++++++++++++------------ tests/get_connection_manager_test.py | 2 +- tests/ssl_context_test.py | 2 +- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/adafruit_connection_manager.py b/adafruit_connection_manager.py index 0c6e0d0..f1cadba 100644 --- a/adafruit_connection_manager.py +++ b/adafruit_connection_manager.py @@ -150,7 +150,6 @@ def get_radio_socketpool(radio): import ssl # pylint: disable=import-outside-toplevel ssl_context = ssl.create_default_context() - pool.set_interface(radio) except ImportError: # if SSL not on board, default to fake_ssl_context pass diff --git a/tests/conftest.py b/tests/conftest.py index f6fc130..f105d92 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,11 +10,6 @@ import pytest -# pylint: disable=unused-argument -def set_interface(iface): - """Helper to set the global internet interface""" - - class SocketPool: name = None @@ -32,6 +27,14 @@ class ESP32SPI_SocketPool(SocketPool): # pylint: disable=too-few-public-methods class WIZNET5K_SocketPool(SocketPool): # pylint: disable=too-few-public-methods name = "adafruit_wiznet5k_socketpool" + SOCK_STREAM = 0x21 + + +class WIZNET5K_With_SSL_SocketPool( + SocketPool +): # pylint: disable=too-few-public-methods + name = "adafruit_wiznet5k_socketpool" + SOCK_STREAM = 0x1 @pytest.fixture @@ -62,7 +65,6 @@ def adafruit_wiznet5k_socketpool_module(): wiznet5k_module = type(sys)("adafruit_wiznet5k") wiznet5k_socketpool_module = type(sys)("adafruit_wiznet5k_socketpool") wiznet5k_socketpool_module.SocketPool = WIZNET5K_SocketPool - wiznet5k_socketpool_module.SOCK_STREAM = 0x21 sys.modules["adafruit_wiznet5k"] = wiznet5k_module sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socketpool"] = ( wiznet5k_socketpool_module @@ -73,16 +75,17 @@ def adafruit_wiznet5k_socketpool_module(): @pytest.fixture -def adafruit_wiznet5k_with_ssl_socket_module(): +def adafruit_wiznet5k_with_ssl_socketpool_module(): wiznet5k_module = type(sys)("adafruit_wiznet5k") - wiznet5k_socket_module = type(sys)("adafruit_wiznet5k_socket") - wiznet5k_socket_module.set_interface = set_interface - wiznet5k_socket_module.SOCK_STREAM = 1 + wiznet5k_socketpool_module = type(sys)("adafruit_wiznet5k_socketpool") + wiznet5k_socketpool_module.SocketPool = WIZNET5K_With_SSL_SocketPool sys.modules["adafruit_wiznet5k"] = wiznet5k_module - sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socket"] = wiznet5k_socket_module + sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socketpool"] = ( + wiznet5k_socketpool_module + ) yield del sys.modules["adafruit_wiznet5k"] - del sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socket"] + del sys.modules["adafruit_wiznet5k.adafruit_wiznet5k_socketpool"] @pytest.fixture(autouse=True) diff --git a/tests/get_connection_manager_test.py b/tests/get_connection_manager_test.py index 324d032..c5f7817 100644 --- a/tests/get_connection_manager_test.py +++ b/tests/get_connection_manager_test.py @@ -19,7 +19,7 @@ def test_get_connection_manager(): def test_different_connection_manager_different_pool( # pylint: disable=unused-argument - circuitpython_socketpool_module, adafruit_esp32spi_socket_module + circuitpython_socketpool_module, adafruit_esp32spi_socketpool_module ): radio_wifi = mocket.MockRadio.Radio() radio_esp = mocket.MockRadio.ESP_SPIcontrol() diff --git a/tests/ssl_context_test.py b/tests/ssl_context_test.py index 90c0adf..2f2e370 100644 --- a/tests/ssl_context_test.py +++ b/tests/ssl_context_test.py @@ -66,7 +66,7 @@ def test_connect_wiznet5k_https_not_supported( # pylint: disable=unused-argumen def test_connect_wiznet5k_https_supported( # pylint: disable=unused-argument - adafruit_wiznet5k_with_ssl_socket_module, + adafruit_wiznet5k_with_ssl_socketpool_module, ): radio = mocket.MockRadio.WIZNET5K() with mock.patch("sys.implementation", (None, WIZNET5K_SSL_SUPPORT_VERSION)): From be9b9ddb2cea6614d8db3c3cb3279b3dc325bbd6 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Fri, 26 Apr 2024 07:04:02 -0700 Subject: [PATCH 4/6] Update hash keys --- adafruit_connection_manager.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/adafruit_connection_manager.py b/adafruit_connection_manager.py index f1cadba..ecad474 100644 --- a/adafruit_connection_manager.py +++ b/adafruit_connection_manager.py @@ -110,6 +110,14 @@ def create_fake_ssl_context( _global_ssl_contexts = {} +def _get_radio_hash_key(radio): + class_name = radio.__class__.__name__ + # trying to use wifi.radio as a key results in: + # TypeError: unsupported type for __hash__: 'Radio' + # So just use the class name in this case + return class_name if class_name == "Radio" else radio + + def get_radio_socketpool(radio): """Helper to get a socket pool for common boards @@ -119,8 +127,9 @@ def get_radio_socketpool(radio): * Using the ESP32 WiFi Co-Processor (like the Adafruit AirLift) * Using a WIZ5500 (Like the Adafruit Ethernet FeatherWing) """ - class_name = radio.__class__.__name__ - if class_name not in _global_socketpool: + key = _get_radio_hash_key(radio) + if key not in _global_socketpool: + class_name = radio.__class__.__name__ if class_name == "Radio": import ssl # pylint: disable=import-outside-toplevel @@ -160,10 +169,10 @@ def get_radio_socketpool(radio): else: raise AttributeError(f"Unsupported radio class: {class_name}") - _global_socketpool[class_name] = pool - _global_ssl_contexts[class_name] = ssl_context + _global_socketpool[key] = pool + _global_ssl_contexts[key] = ssl_context - return _global_socketpool[class_name] + return _global_socketpool[key] def get_radio_ssl_context(radio): @@ -175,9 +184,8 @@ def get_radio_ssl_context(radio): * Using the ESP32 WiFi Co-Processor (like the Adafruit AirLift) * Using a WIZ5500 (Like the Adafruit Ethernet FeatherWing) """ - class_name = radio.__class__.__name__ get_radio_socketpool(radio) - return _global_ssl_contexts[class_name] + return _global_ssl_contexts[_get_radio_hash_key(radio)] # main class From 778b78f77f120566631ee4eb6f37ecf5f50d9b5e Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Fri, 26 Apr 2024 08:03:14 -0700 Subject: [PATCH 5/6] Better hashing --- adafruit_connection_manager.py | 9 ++++----- tests/get_radio_test.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/adafruit_connection_manager.py b/adafruit_connection_manager.py index ecad474..b58d042 100644 --- a/adafruit_connection_manager.py +++ b/adafruit_connection_manager.py @@ -111,11 +111,10 @@ def create_fake_ssl_context( def _get_radio_hash_key(radio): - class_name = radio.__class__.__name__ - # trying to use wifi.radio as a key results in: - # TypeError: unsupported type for __hash__: 'Radio' - # So just use the class name in this case - return class_name if class_name == "Radio" else radio + try: + return hash(radio) + except TypeError: + return radio.__class__.__name__ def get_radio_socketpool(radio): diff --git a/tests/get_radio_test.py b/tests/get_radio_test.py index c73c97d..c4ffde6 100644 --- a/tests/get_radio_test.py +++ b/tests/get_radio_test.py @@ -13,6 +13,18 @@ import adafruit_connection_manager +def test__get_radio_hash_key(): + radio = mocket.MockRadio.Radio() + assert adafruit_connection_manager._get_radio_hash_key(radio) == hash(radio) + + +def test__get_radio_hash_key_not_hashable(): + radio = mocket.MockRadio.Radio() + + with mock.patch("builtins.hash", side_effect=TypeError()): + assert adafruit_connection_manager._get_radio_hash_key(radio) == "Radio" + + def test_get_radio_socketpool_wifi( # pylint: disable=unused-argument circuitpython_socketpool_module, ): From b14ed9920a651abb02e4ea38bda75556b1b8b8b9 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Tue, 30 Apr 2024 06:19:49 -0700 Subject: [PATCH 6/6] Code review updates --- adafruit_connection_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/adafruit_connection_manager.py b/adafruit_connection_manager.py index 4b5eb1d..5b8a10c 100644 --- a/adafruit_connection_manager.py +++ b/adafruit_connection_manager.py @@ -58,10 +58,10 @@ def __init__(self, socket: CircuitPythonSocketType, tls_mode: int) -> None: self.recv = socket.recv self.close = socket.close self.recv_into = socket.recv_into - if hasattr(socket, "_interface"): - self._interface = socket._interface - if hasattr(socket, "_socket_pool"): - self._socket_pool = socket._socket_pool + # For sockets that come from software socketpools (like the esp32api), they track + # the interface and socket pool. We need to make sure the clones do as well + self._interface = getattr(socket, "_interface", None) + self._socket_pool = getattr(socket, "_socket_pool", None) def connect(self, address: Tuple[str, int]) -> None: """Connect wrapper to add non-standard mode parameter"""