From 56cec235af465a8bcdbd32598a5f6bd75a153164 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Fri, 22 Dec 2023 07:50:11 -0800 Subject: [PATCH 01/11] Switch to using ConnectionManager --- .gitignore | 6 +- .pre-commit-config.yaml | 2 +- adafruit_minimqtt/adafruit_minimqtt.py | 147 ++++--------------------- conftest.py | 17 +++ requirements.txt | 1 + tox.ini | 37 ++++++- 6 files changed, 79 insertions(+), 131 deletions(-) create mode 100644 conftest.py diff --git a/.gitignore b/.gitignore index 342b4999..a06dc67a 100644 --- a/.gitignore +++ b/.gitignore @@ -47,5 +47,9 @@ _build .vscode *~ -# tox local cache +# tox-specific files .tox +build + +# coverage-specific files +.coverage diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69d..e2c88316 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,4 +39,4 @@ repos: types: [python] files: "^tests/" args: - - --disable=missing-docstring,consider-using-f-string,duplicate-code + - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code diff --git a/adafruit_minimqtt/adafruit_minimqtt.py b/adafruit_minimqtt/adafruit_minimqtt.py index 41e01605..4eeb6864 100644 --- a/adafruit_minimqtt/adafruit_minimqtt.py +++ b/adafruit_minimqtt/adafruit_minimqtt.py @@ -26,12 +26,21 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases +* Adafruit's Connection Manager library: + https://github.com/adafruit/Adafruit_CircuitPython_ConnectionManager + """ import errno import struct import time from random import randint +from adafruit_connectionmanager import ( + get_connection_manager, + SocketGetOSError, + SocketConnectMemoryError, +) + try: from typing import List, Optional, Tuple, Type, Union except ImportError: @@ -78,60 +87,12 @@ _default_sock = None # pylint: disable=invalid-name _fake_context = None # pylint: disable=invalid-name +TemporaryError = (SocketGetOSError, SocketConnectMemoryError) + class MMQTTException(Exception): """MiniMQTT Exception class.""" - # pylint: disable=unnecessary-pass - # pass - - -class TemporaryError(Exception): - """Temporary error class used for handling reconnects.""" - - -# Legacy ESP32SPI Socket API -def set_socket(sock, iface=None) -> None: - """Legacy API for setting the socket and network interface. - - :param sock: socket object. - :param iface: internet interface object - - """ - global _default_sock # pylint: disable=invalid-name, global-statement - global _fake_context # pylint: disable=invalid-name, global-statement - _default_sock = sock - if iface: - _default_sock.set_interface(iface) - _fake_context = _FakeSSLContext(iface) - - -class _FakeSSLSocket: - def __init__(self, socket, tls_mode) -> None: - self._socket = socket - self._mode = tls_mode - self.settimeout = socket.settimeout - self.send = socket.send - self.recv = socket.recv - self.close = socket.close - - def connect(self, address): - """connect wrapper to add non-standard mode parameter""" - try: - return self._socket.connect(address, self._mode) - except RuntimeError as error: - raise OSError(errno.ENOMEM) from error - - -class _FakeSSLContext: - def __init__(self, iface) -> None: - self._iface = iface - - def wrap_socket(self, socket, server_hostname=None) -> _FakeSSLSocket: - """Return the same socket""" - # pylint: disable=unused-argument - return _FakeSSLSocket(socket, self._iface.TLS_MODE) - class NullLogger: """Fake logger class that does not do anything""" @@ -139,7 +100,6 @@ class NullLogger: # pylint: disable=unused-argument def nothing(self, msg: str, *args) -> None: """no action""" - pass def __init__(self) -> None: for log_level in ["debug", "info", "warning", "error", "critical"]: @@ -194,6 +154,7 @@ def __init__( user_data=None, use_imprecise_time: Optional[bool] = None, ) -> None: + self._connection_manager = get_connection_manager(socket_pool) self._socket_pool = socket_pool self._ssl_context = ssl_context self._sock = None @@ -300,77 +261,6 @@ def get_monotonic_time(self) -> float: return time.monotonic() - # pylint: disable=too-many-branches - def _get_connect_socket(self, host: str, port: int, *, timeout: int = 1): - """Obtains a new socket and connects to a broker. - - :param str host: Desired broker hostname - :param int port: Desired broker port - :param int timeout: Desired socket timeout, in seconds - """ - # For reconnections - check if we're using a socket already and close it - if self._sock: - self._sock.close() - self._sock = None - - # Legacy API - use the interface's socket instead of a passed socket pool - if self._socket_pool is None: - self._socket_pool = _default_sock - - # Legacy API - fake the ssl context - if self._ssl_context is None: - self._ssl_context = _fake_context - - if not isinstance(port, int): - raise RuntimeError("Port must be an integer") - - if self._is_ssl and not self._ssl_context: - raise RuntimeError( - "ssl_context must be set before using adafruit_mqtt for secure MQTT." - ) - - if self._is_ssl: - self.logger.info(f"Establishing a SECURE SSL connection to {host}:{port}") - else: - self.logger.info(f"Establishing an INSECURE connection to {host}:{port}") - - addr_info = self._socket_pool.getaddrinfo( - host, port, 0, self._socket_pool.SOCK_STREAM - )[0] - - try: - sock = self._socket_pool.socket(addr_info[0], addr_info[1]) - except OSError as exc: - # Do not consider this for back-off. - self.logger.warning( - f"Failed to create socket for host {addr_info[0]} and port {addr_info[1]}" - ) - raise TemporaryError from exc - - connect_host = addr_info[-1][0] - if self._is_ssl: - sock = self._ssl_context.wrap_socket(sock, server_hostname=host) - connect_host = host - sock.settimeout(timeout) - - last_exception = None - try: - sock.connect((connect_host, port)) - except MemoryError as exc: - sock.close() - self.logger.warning(f"Failed to allocate memory for connect: {exc}") - # Do not consider this for back-off. - raise TemporaryError from exc - except OSError as exc: - sock.close() - last_exception = exc - - if last_exception: - raise last_exception - - self._backwards_compatible_sock = not hasattr(sock, "recv_into") - return sock - def __enter__(self): return self @@ -593,8 +483,15 @@ def _connect( time.sleep(self._reconnect_timeout) # Get a new socket - self._sock = self._get_connect_socket( - self.broker, self.port, timeout=self._socket_timeout + self._sock = self._connection_manager.get_socket( + self.broker, + self.port, + "mqtt:", + timeout=self._socket_timeout, + is_ssl=self._is_ssl, + ssl_context=self._ssl_context, + max_retries=1, # setting to 1 since we want to handle backoff internally + exception_passthrough=True, ) # Fixed Header @@ -689,7 +586,7 @@ def disconnect(self) -> None: except RuntimeError as e: self.logger.warning(f"Unable to send DISCONNECT packet: {e}") self.logger.debug("Closing socket") - self._sock.close() + self._connection_manager.free_socket(self._sock) self._is_connected = False self._subscribed_topics = [] if self.on_disconnect is not None: diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..a7aef478 --- /dev/null +++ b/conftest.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2023 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +""" PyTest Setup """ + +import pytest +import adafruit_connectionmanager + + +@pytest.fixture(autouse=True) +def reset_connection_manager(monkeypatch): + """Reset the ConnectionManager, since it's a singlton and will hold data""" + monkeypatch.setattr( + "adafruit_minimqtt.adafruit_minimqtt.get_connection_manager", + adafruit_connectionmanager.ConnectionManager, + ) diff --git a/requirements.txt b/requirements.txt index 7a984a47..77565050 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Unlicense Adafruit-Blinka +Adafruit-Circuitpython-ConnectionManager@git+https://github.com/justmobilize/Adafruit_CircuitPython_ConnectionManager diff --git a/tox.ini b/tox.ini index 6a9584b3..d707d5b7 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,38 @@ # SPDX-License-Identifier: MIT [tox] -envlist = py39 +envlist = py311 [testenv] -changedir = {toxinidir}/tests -deps = pytest==6.2.5 -commands = pytest -v +description = run tests +deps = + pytest==7.4.3 + pytest-subtests==0.11.0 +commands = pytest + +[testenv:coverage] +description = run coverage +deps = + pytest==7.4.3 + pytest-cov==4.1.0 + pytest-subtests==0.11.0 +package = editable +commands = + coverage run --source=. --omit=tests/* --branch {posargs} -m pytest + coverage report + coverage html + +[testenv:lint] +description = run linters +deps = + pre-commit==3.6.0 +skip_install = true +commands = pre-commit run {posargs} + +[testenv:docs] +description = build docs +deps = + -r requirements.txt + -r docs/requirements.txt +skip_install = true +commands = sphinx-build -W -b html docs/ _build/ From 024e83d05b0b68cc2c7acf1ff8d43389ccc657a4 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Fri, 22 Dec 2023 10:43:41 -0800 Subject: [PATCH 02/11] Switch from adafruit_connectionmanager.py -> adafruit_connection_manager.py --- adafruit_minimqtt/adafruit_minimqtt.py | 2 +- conftest.py | 4 ++-- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/adafruit_minimqtt/adafruit_minimqtt.py b/adafruit_minimqtt/adafruit_minimqtt.py index 4eeb6864..5e0986d2 100644 --- a/adafruit_minimqtt/adafruit_minimqtt.py +++ b/adafruit_minimqtt/adafruit_minimqtt.py @@ -35,7 +35,7 @@ import time from random import randint -from adafruit_connectionmanager import ( +from adafruit_connection_manager import ( get_connection_manager, SocketGetOSError, SocketConnectMemoryError, diff --git a/conftest.py b/conftest.py index a7aef478..a4b8d631 100644 --- a/conftest.py +++ b/conftest.py @@ -5,7 +5,7 @@ """ PyTest Setup """ import pytest -import adafruit_connectionmanager +import adafruit_connection_manager @pytest.fixture(autouse=True) @@ -13,5 +13,5 @@ def reset_connection_manager(monkeypatch): """Reset the ConnectionManager, since it's a singlton and will hold data""" monkeypatch.setattr( "adafruit_minimqtt.adafruit_minimqtt.get_connection_manager", - adafruit_connectionmanager.ConnectionManager, + adafruit_connection_manager.ConnectionManager, ) diff --git a/requirements.txt b/requirements.txt index 77565050..d83a6788 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ # SPDX-License-Identifier: Unlicense Adafruit-Blinka -Adafruit-Circuitpython-ConnectionManager@git+https://github.com/justmobilize/Adafruit_CircuitPython_ConnectionManager +Adafruit-Circuitpython-ConnectionManager@git+https://github.com/justmobilize/Adafruit_CircuitPython_ConnectionManager@connection-manager From fe125108c31687d0517cb8389c1673ae9b0f69a6 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Wed, 27 Dec 2023 07:57:27 -0800 Subject: [PATCH 03/11] Update tox docs command --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d707d5b7..6dc4619e 100644 --- a/tox.ini +++ b/tox.ini @@ -37,4 +37,4 @@ deps = -r requirements.txt -r docs/requirements.txt skip_install = true -commands = sphinx-build -W -b html docs/ _build/ +commands = sphinx-build -E -W -b html docs/. _build/html From 497ea40c08c6141c3f4acd922c631a56da216f39 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Sat, 3 Feb 2024 14:16:20 -0800 Subject: [PATCH 04/11] Specify proto on get_socket --- adafruit_minimqtt/adafruit_minimqtt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_minimqtt/adafruit_minimqtt.py b/adafruit_minimqtt/adafruit_minimqtt.py index 6562635c..aeef9920 100644 --- a/adafruit_minimqtt/adafruit_minimqtt.py +++ b/adafruit_minimqtt/adafruit_minimqtt.py @@ -486,7 +486,7 @@ def _connect( self._sock = self._connection_manager.get_socket( self.broker, self.port, - "mqtt:", + proto="mqtt:", timeout=self._socket_timeout, is_ssl=self._is_ssl, ssl_context=self._ssl_context, From d905b5d30af9a8ba37022cf7942822ceb0d069a6 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Wed, 14 Feb 2024 09:06:02 -0800 Subject: [PATCH 05/11] Simplify socket connect retry logic --- adafruit_minimqtt/adafruit_minimqtt.py | 14 +++----------- tests/test_port_ssl.py | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/adafruit_minimqtt/adafruit_minimqtt.py b/adafruit_minimqtt/adafruit_minimqtt.py index 2a355cfe..9483b092 100644 --- a/adafruit_minimqtt/adafruit_minimqtt.py +++ b/adafruit_minimqtt/adafruit_minimqtt.py @@ -35,11 +35,7 @@ import time from random import randint -from adafruit_connection_manager import ( - get_connection_manager, - SocketGetOSError, - SocketConnectMemoryError, -) +from adafruit_connection_manager import get_connection_manager try: from typing import List, Optional, Tuple, Type, Union @@ -87,8 +83,6 @@ _default_sock = None # pylint: disable=invalid-name _fake_context = None # pylint: disable=invalid-name -TemporaryError = (SocketGetOSError, SocketConnectMemoryError) - class MMQTTException(Exception): """MiniMQTT Exception class.""" @@ -430,8 +424,8 @@ def connect( ) self._reset_reconnect_backoff() return ret - except TemporaryError as e: - self.logger.warning(f"temporary error when connecting: {e}") + except RuntimeError as e: + self.logger.warning(f"Socket error when connecting: {e}") backoff = False except MMQTTException as e: last_exception = e @@ -486,8 +480,6 @@ def _connect( timeout=self._socket_timeout, is_ssl=self._is_ssl, ssl_context=self._ssl_context, - max_retries=1, # setting to 1 since we want to handle backoff internally - exception_passthrough=True, ) fixed_header = bytearray([0x10]) diff --git a/tests/test_port_ssl.py b/tests/test_port_ssl.py index 6263dbfc..1c6d5e3f 100644 --- a/tests/test_port_ssl.py +++ b/tests/test_port_ssl.py @@ -116,7 +116,7 @@ def test_tls_without_ssl_context(self) -> None: connect_retries=1, ) - with self.assertRaises(RuntimeError) as context: + with self.assertRaises(AttributeError) as context: mqtt_client.connect() self.assertTrue("ssl_context must be set" in str(context)) From 769e723485f44b825cb71660d8c69c567e04e16c Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Wed, 21 Feb 2024 13:27:31 -0800 Subject: [PATCH 06/11] Change to pypi and other tweaks --- .pre-commit-config.yaml | 6 +++--- adafruit_minimqtt/adafruit_minimqtt.py | 1 + requirements.txt | 2 +- tox.ini | 5 +++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e2c88316..09cc1f1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/python/black - rev: 23.3.0 + rev: 24.2.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool @@ -32,11 +32,11 @@ repos: types: [python] files: "^examples/" args: - - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code + - --disable=consider-using-f-string,duplicate-code,missing-docstring,invalid-name - id: pylint name: pylint (test code) description: Run pylint rules on "tests/*.py" files types: [python] files: "^tests/" args: - - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code + - --disable=consider-using-f-string,duplicate-code,missing-docstring,invalid-name,protected-access diff --git a/adafruit_minimqtt/adafruit_minimqtt.py b/adafruit_minimqtt/adafruit_minimqtt.py index 9483b092..64e56581 100644 --- a/adafruit_minimqtt/adafruit_minimqtt.py +++ b/adafruit_minimqtt/adafruit_minimqtt.py @@ -481,6 +481,7 @@ def _connect( is_ssl=self._is_ssl, ssl_context=self._ssl_context, ) + self._backwards_compatible_sock = not hasattr(self._sock, "recv_into") fixed_header = bytearray([0x10]) diff --git a/requirements.txt b/requirements.txt index d83a6788..25052887 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ # SPDX-License-Identifier: Unlicense Adafruit-Blinka -Adafruit-Circuitpython-ConnectionManager@git+https://github.com/justmobilize/Adafruit_CircuitPython_ConnectionManager@connection-manager +Adafruit-Circuitpython-ConnectionManager diff --git a/tox.ini b/tox.ini index 6dc4619e..693cfb88 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,5 @@ # SPDX-FileCopyrightText: 2023 VladimĂ­r Kotal +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries # # SPDX-License-Identifier: MIT @@ -9,7 +10,7 @@ envlist = py311 description = run tests deps = pytest==7.4.3 - pytest-subtests==0.11.0 +pytest-subtests==0.11.0 commands = pytest [testenv:coverage] @@ -17,7 +18,7 @@ description = run coverage deps = pytest==7.4.3 pytest-cov==4.1.0 - pytest-subtests==0.11.0 +pytest-subtests==0.11.0 package = editable commands = coverage run --source=. --omit=tests/* --branch {posargs} -m pytest From 7659f215bf7bfdea70a6e4cb44e976025f3c448a Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Tue, 27 Feb 2024 03:20:29 -0800 Subject: [PATCH 07/11] Standardize tests --- tests/{backoff_test.py => test_backoff.py} | 12 ++-- tests/test_loop.py | 16 ++--- tests/test_port_ssl.py | 80 ++++++++++------------ tox.ini | 2 - 4 files changed, 48 insertions(+), 62 deletions(-) rename tests/{backoff_test.py => test_backoff.py} (86%) diff --git a/tests/backoff_test.py b/tests/test_backoff.py similarity index 86% rename from tests/backoff_test.py rename to tests/test_backoff.py index ed66dd5f..573b2050 100644 --- a/tests/backoff_test.py +++ b/tests/test_backoff.py @@ -4,16 +4,16 @@ """exponential back-off tests""" +import pytest import socket import ssl import time -from unittest import TestCase, main from unittest.mock import call, patch import adafruit_minimqtt.adafruit_minimqtt as MQTT -class ExpBackOff(TestCase): +class TestExpBackOff: """basic exponential back-off test""" connect_times = [] @@ -42,9 +42,9 @@ def test_failing_connect(self) -> None: connect_retries=connect_retries, ) print("connecting") - with self.assertRaises(MQTT.MMQTTException) as context: + with pytest.raises(MQTT.MMQTTException) as context: mqtt_client.connect() - self.assertTrue("Repeated connect failures" in str(context.exception)) + assert "Repeated connect failures" in str(context) mock_method.assert_called() calls = [call((host, port)) for _ in range(0, connect_retries)] @@ -53,7 +53,3 @@ def test_failing_connect(self) -> None: print(f"connect() call times: {self.connect_times}") for i in range(1, connect_retries): assert self.connect_times[i] >= 2**i - - -if __name__ == "__main__": - main() diff --git a/tests/test_loop.py b/tests/test_loop.py index 173fe526..9253b560 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -4,13 +4,13 @@ """loop() tests""" +import pytest import random import socket import ssl import time import errno -from unittest import TestCase, main from unittest.mock import patch from unittest import mock @@ -101,7 +101,7 @@ def recv_into(self, retbuf, bufsize): raise exc -class Loop(TestCase): +class TestLoop: """basic loop() test""" connect_times = [] @@ -173,10 +173,10 @@ def test_loop_timeout_vs_socket_timeout(self): ) mqtt_client.is_connected = lambda: True - with self.assertRaises(MQTT.MMQTTException) as context: + with pytest.raises(MQTT.MMQTTException) as context: mqtt_client.loop(timeout=0.5) - assert "loop timeout" in str(context.exception) + assert "loop timeout" in str(context) def test_loop_is_connected(self): """ @@ -189,10 +189,10 @@ def test_loop_is_connected(self): ssl_context=ssl.create_default_context(), ) - with self.assertRaises(MQTT.MMQTTException) as context: + with pytest.raises(MQTT.MMQTTException) as context: mqtt_client.loop(timeout=1) - assert "not connected" in str(context.exception) + assert "not connected" in str(context) # pylint: disable=no-self-use def test_loop_ping_timeout(self): @@ -258,7 +258,3 @@ def test_loop_ping_vs_msgs_sent(self): # This means no other messages than the PUBLISH messages generated by the code above. assert len(mocket.sent) == i * (2 + 2 + len(topic) + len(message)) - - -if __name__ == "__main__": - main() diff --git a/tests/test_port_ssl.py b/tests/test_port_ssl.py index 1c6d5e3f..4204bb60 100644 --- a/tests/test_port_ssl.py +++ b/tests/test_port_ssl.py @@ -4,15 +4,15 @@ """tests that verify the connect behavior w.r.t. port number and TLS""" +import pytest import socket import ssl -from unittest import TestCase, main from unittest.mock import Mock, call, patch import adafruit_minimqtt.adafruit_minimqtt as MQTT -class PortSslSetup(TestCase): +class TestPortSslSetup: """This class contains tests that verify how host/port and TLS is set for connect(). These tests assume that there is no MQTT broker running on the hosts/ports they connect to. """ @@ -35,7 +35,7 @@ def test_default_port(self) -> None: ssl_mock = Mock() ssl_context.wrap_socket = ssl_mock - with self.assertRaises(MQTT.MMQTTException): + with pytest.raises(MQTT.MMQTTException): mqtt_client.connect() ssl_mock.assert_not_called() @@ -58,51 +58,51 @@ def test_connect_override(self): connect_retries=1, ) - with self.assertRaises(MQTT.MMQTTException): + with pytest.raises(MQTT.MMQTTException): expected_host = "127.0.0.2" expected_port = 1884 - self.assertNotEqual(expected_port, port, "port override should differ") - self.assertNotEqual(expected_host, host, "host override should differ") + assert expected_port != port # port override should differ + assert expected_host != host # host override should differ mqtt_client.connect(host=expected_host, port=expected_port) connect_mock.assert_called() # Assuming the repeated calls will have the same arguments. connect_mock.assert_has_calls([call((expected_host, expected_port))]) - def test_tls_port(self) -> None: + @pytest.mark.parametrize("port", (None, 8883)) + def test_tls_port(self, port) -> None: """verify that when is_ssl=True is set, the default port is 8883 and the socket is TLS wrapped. Also test that the TLS port can be overridden.""" host = "127.0.0.1" - for port in [None, 8884]: - if port is None: - expected_port = 8883 - else: - expected_port = port - with self.subTest(): - ssl_mock = Mock() - mqtt_client = MQTT.MQTT( - broker=host, - port=port, - socket_pool=socket, - is_ssl=True, - ssl_context=ssl_mock, - connect_retries=1, - ) - - socket_mock = Mock() - connect_mock = Mock(side_effect=OSError) - socket_mock.connect = connect_mock - ssl_mock.wrap_socket = Mock(return_value=socket_mock) - - with self.assertRaises(MQTT.MMQTTException): - mqtt_client.connect() - - ssl_mock.wrap_socket.assert_called() - - connect_mock.assert_called() - # Assuming the repeated calls will have the same arguments. - connect_mock.assert_has_calls([call((host, expected_port))]) + if port is None: + expected_port = 8883 + else: + expected_port = port + + ssl_mock = Mock() + mqtt_client = MQTT.MQTT( + broker=host, + port=port, + socket_pool=socket, + is_ssl=True, + ssl_context=ssl_mock, + connect_retries=1, + ) + + socket_mock = Mock() + connect_mock = Mock(side_effect=OSError) + socket_mock.connect = connect_mock + ssl_mock.wrap_socket = Mock(return_value=socket_mock) + + with pytest.raises(MQTT.MMQTTException): + mqtt_client.connect() + + ssl_mock.wrap_socket.assert_called() + + connect_mock.assert_called() + # Assuming the repeated calls will have the same arguments. + connect_mock.assert_has_calls([call((host, expected_port))]) def test_tls_without_ssl_context(self) -> None: """verify that when is_ssl=True is set, the code will check that ssl_context is not None""" @@ -116,10 +116,6 @@ def test_tls_without_ssl_context(self) -> None: connect_retries=1, ) - with self.assertRaises(AttributeError) as context: + with pytest.raises(AttributeError) as context: mqtt_client.connect() - self.assertTrue("ssl_context must be set" in str(context)) - - -if __name__ == "__main__": - main() + assert "ssl_context must be set" in str(context) diff --git a/tox.ini b/tox.ini index 693cfb88..d0cddcf3 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,6 @@ envlist = py311 description = run tests deps = pytest==7.4.3 -pytest-subtests==0.11.0 commands = pytest [testenv:coverage] @@ -18,7 +17,6 @@ description = run coverage deps = pytest==7.4.3 pytest-cov==4.1.0 -pytest-subtests==0.11.0 package = editable commands = coverage run --source=. --omit=tests/* --branch {posargs} -m pytest From f1222a2c2d716f4da5ddccfedb65706c3e578852 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Tue, 27 Feb 2024 04:03:40 -0800 Subject: [PATCH 08/11] Remove set_socket --- .../cellular/minimqtt_adafruitio_cellular.py | 31 +++++++------ .../cellular/minimqtt_simpletest_cellular.py | 29 +++++++------ .../cpython/minimqtt_adafruitio_cpython.py | 19 ++++---- .../cpython/minimqtt_simpletest_cpython.py | 23 +++++----- .../esp32spi/minimqtt_adafruitio_esp32spi.py | 8 ++-- .../esp32spi/minimqtt_certificate_esp32spi.py | 12 ++++-- .../minimqtt_pub_sub_blocking_esp32spi.py | 12 ++++-- ...b_sub_blocking_topic_callbacks_esp32spi.py | 13 ++++-- .../minimqtt_pub_sub_nonblocking_esp32spi.py | 12 ++++-- .../minimqtt_pub_sub_pyportal_esp32spi.py | 29 +++++++------ .../esp32spi/minimqtt_simpletest_esp32spi.py | 43 ++++++++----------- examples/ethernet/minimqtt_adafruitio_eth.py | 29 +++++++------ examples/ethernet/minimqtt_simpletest_eth.py | 26 +++++------ examples/minimqtt_simpletest.py | 8 ++-- .../minimqtt_adafruitio_native_networking.py | 5 +-- ...mqtt_pub_sub_blocking_native_networking.py | 5 +-- ...cking_topic_callbacks_native_networking.py | 5 +-- 17 files changed, 166 insertions(+), 143 deletions(-) diff --git a/examples/cellular/minimqtt_adafruitio_cellular.py b/examples/cellular/minimqtt_adafruitio_cellular.py index 5f1c4d11..88fa317b 100755 --- a/examples/cellular/minimqtt_adafruitio_cellular.py +++ b/examples/cellular/minimqtt_adafruitio_cellular.py @@ -1,22 +1,24 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import time import board import busio import digitalio +import adafruit_connection_manager from adafruit_fona.adafruit_fona import FONA import adafruit_fona.adafruit_fona_network as network -import adafruit_fona.adafruit_fona_socket as socket +import adafruit_fona.adafruit_fona_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT -# Get Adafruit IO details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("GPRS secrets are kept in secrets.py, please add them there!") - raise +# Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys +# with your GPRS credentials. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. + +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") ### Cellular ### @@ -29,10 +31,10 @@ ### Feeds ### # Setup a feed named 'photocell' for publishing to a feed -photocell_feed = secrets["aio_username"] + "/feeds/photocell" +photocell_feed = aio_username + "/feeds/photocell" # Setup a feed named 'onoff' for subscribing to changes -onoff_feed = secrets["aio_username"] + "/feeds/onoff" +onoff_feed = aio_username + "/feeds/onoff" ### Code ### @@ -60,7 +62,7 @@ def message(client, topic, message): # Initialize cellular data network network = network.CELLULAR( - fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"]) + fona, (os.getenv("apn"), os.getenv("apn_username"), os.getenv("apn_password")) ) while not network.is_attached: @@ -74,16 +76,17 @@ def message(client, topic, message): time.sleep(0.5) print("Network Connected!") -# Initialize MQTT interface with the cellular interface -MQTT.set_socket(socket, fona) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, fona) # Set up a MiniMQTT Client # NOTE: We'll need to connect insecurely for ethernet configurations. mqtt_client = MQTT.MQTT( broker="io.adafruit.com", - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, is_ssl=False, + socket_pool=pool, + ssl_context=ssl_context, ) # Setup the callback methods above diff --git a/examples/cellular/minimqtt_simpletest_cellular.py b/examples/cellular/minimqtt_simpletest_cellular.py index 57b11676..09e7350d 100644 --- a/examples/cellular/minimqtt_simpletest_cellular.py +++ b/examples/cellular/minimqtt_simpletest_cellular.py @@ -1,24 +1,24 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import time import board import busio import digitalio +import adafruit_connection_manager from adafruit_fona.adafruit_fona import FONA import adafruit_fona.adafruit_fona_network as network -import adafruit_fona.adafruit_fona_socket as socket +import adafruit_fona.adafruit_fona_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT -### Cellular ### +# Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys +# with your GPRS credentials. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. -# Get cellular details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("Cellular secrets are kept in secrets.py, please add them there!") - raise +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") # Create a serial connection for the FONA connection uart = busio.UART(board.TX, board.RX) @@ -71,7 +71,7 @@ def publish(client, userdata, topic, pid): # Initialize cellular data network network = network.CELLULAR( - fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"]) + fona, (os.getenv("apn"), os.getenv("apn_username"), os.getenv("apn_password")) ) while not network.is_attached: @@ -85,15 +85,16 @@ def publish(client, userdata, topic, pid): time.sleep(0.5) print("Network Connected!") -# Initialize MQTT interface with the cellular interface -MQTT.set_socket(socket, fona) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, fona) # Set up a MiniMQTT Client client = MQTT.MQTT( - broker=secrets["broker"], - username=secrets["user"], - password=secrets["pass"], + broker=os.getenv("broker"), + username=os.getenv("username"), + password=os.getenv("password"), is_ssl=False, + socket_pool=pool, + ssl_context=ssl_context, ) # Connect callback handlers to client diff --git a/examples/cpython/minimqtt_adafruitio_cpython.py b/examples/cpython/minimqtt_adafruitio_cpython.py index 7ebcf0fe..1440ebad 100644 --- a/examples/cpython/minimqtt_adafruitio_cpython.py +++ b/examples/cpython/minimqtt_adafruitio_cpython.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import socket import ssl import time @@ -9,19 +10,19 @@ ### Secrets File Setup ### -try: - from secrets import secrets -except ImportError: - print("Connection secrets are kept in secrets.py, please add them there!") - raise +# Add settings.toml to your filesystem. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. + +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") ### Feeds ### # Setup a feed named 'photocell' for publishing to a feed -photocell_feed = secrets["aio_username"] + "/feeds/photocell" +photocell_feed = aio_username + "/feeds/photocell" # Setup a feed named 'onoff' for subscribing to changes -onoff_feed = secrets["aio_username"] + "/feeds/onoff" +onoff_feed = aio_username + "/feeds/onoff" ### Code ### @@ -50,8 +51,8 @@ def message(client, topic, message): # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( broker="io.adafruit.com", - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, socket_pool=socket, is_ssl=True, ssl_context=ssl.create_default_context(), diff --git a/examples/cpython/minimqtt_simpletest_cpython.py b/examples/cpython/minimqtt_simpletest_cpython.py index 551fa0b7..2a6e3a97 100644 --- a/examples/cpython/minimqtt_simpletest_cpython.py +++ b/examples/cpython/minimqtt_simpletest_cpython.py @@ -1,19 +1,16 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import ssl import socket import adafruit_minimqtt.adafruit_minimqtt as MQTT -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +# Add settings.toml to your filesystems. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. + +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") ### Topic Setup ### @@ -23,7 +20,7 @@ # Adafruit IO-style Topic # Use this topic if you'd like to connect to io.adafruit.com -# mqtt_topic = secrets["aio_username"] + "/feeds/temperature" +# mqtt_topic = aio_username + "/feeds/temperature" ### Code ### @@ -64,9 +61,9 @@ def message(client, topic, message): # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( - broker=secrets["broker"], - username=secrets["aio_username"], - password=secrets["aio_key"], + broker=os.getenv("broker"), + username=aio_username, + password=aio_key, socket_pool=socket, ssl_context=ssl.create_default_context(), ) diff --git a/examples/esp32spi/minimqtt_adafruitio_esp32spi.py b/examples/esp32spi/minimqtt_adafruitio_esp32spi.py index 68bf3dd6..88a8c10c 100644 --- a/examples/esp32spi/minimqtt_adafruitio_esp32spi.py +++ b/examples/esp32spi/minimqtt_adafruitio_esp32spi.py @@ -7,8 +7,9 @@ import busio from digitalio import DigitalInOut import neopixel +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -82,14 +83,15 @@ def message(client, topic, message): esp.connect_AP(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( broker="io.adafruit.com", username=aio_username, password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Setup the callback methods above diff --git a/examples/esp32spi/minimqtt_certificate_esp32spi.py b/examples/esp32spi/minimqtt_certificate_esp32spi.py index f3e2d791..6039667f 100644 --- a/examples/esp32spi/minimqtt_certificate_esp32spi.py +++ b/examples/esp32spi/minimqtt_certificate_esp32spi.py @@ -5,9 +5,10 @@ import busio from digitalio import DigitalInOut import neopixel +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -110,12 +111,15 @@ def publish(client, userdata, topic, pid): wifi.connect() print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client client = MQTT.MQTT( - broker=secrets["broker"], username=secrets["user"], password=secrets["pass"] + broker=secrets["broker"], + username=secrets["user"], + password=secrets["pass"], + socket_pool=pool, + ssl_context=ssl_context, ) # Connect callback handlers to client diff --git a/examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py b/examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py index a4e508c9..414dbf02 100644 --- a/examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py +++ b/examples/esp32spi/minimqtt_pub_sub_blocking_esp32spi.py @@ -7,8 +7,9 @@ import busio from digitalio import DigitalInOut import neopixel +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -82,12 +83,15 @@ def message(client, topic, message): esp.connect_AP(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( - broker="io.adafruit.com", username=aio_username, password=aio_key + broker="io.adafruit.com", + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Setup the callback methods above diff --git a/examples/esp32spi/minimqtt_pub_sub_blocking_topic_callbacks_esp32spi.py b/examples/esp32spi/minimqtt_pub_sub_blocking_topic_callbacks_esp32spi.py index 5094e4b4..0b9313e3 100644 --- a/examples/esp32spi/minimqtt_pub_sub_blocking_topic_callbacks_esp32spi.py +++ b/examples/esp32spi/minimqtt_pub_sub_blocking_topic_callbacks_esp32spi.py @@ -1,14 +1,16 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import time import board import busio from digitalio import DigitalInOut import neopixel +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi from adafruit_esp32spi import adafruit_esp32spi_wifimanager -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -91,10 +93,15 @@ def on_message(client, topic, message): wifi.connect() print("Connected!") -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client -client = MQTT.MQTT(broker=secrets["broker"], port=secrets["broker_port"]) +client = MQTT.MQTT( + broker=os.getenv("broker"), + port=os.getenv("broker_port"), + socket_pool=pool, + ssl_context=ssl_context, +) # Setup the callback methods above client.on_connect = connected diff --git a/examples/esp32spi/minimqtt_pub_sub_nonblocking_esp32spi.py b/examples/esp32spi/minimqtt_pub_sub_nonblocking_esp32spi.py index fee8f285..cc937512 100644 --- a/examples/esp32spi/minimqtt_pub_sub_nonblocking_esp32spi.py +++ b/examples/esp32spi/minimqtt_pub_sub_nonblocking_esp32spi.py @@ -7,8 +7,9 @@ import busio from digitalio import DigitalInOut import neopixel +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -81,12 +82,15 @@ def message(client, topic, message): esp.connect_AP(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) print("Connected!") -# Initialize MQTT interface with the esp interface -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( - broker="io.adafruit.com", username=aio_username, password=aio_key + broker="io.adafruit.com", + username=aio_username, + password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Setup the callback methods above diff --git a/examples/esp32spi/minimqtt_pub_sub_pyportal_esp32spi.py b/examples/esp32spi/minimqtt_pub_sub_pyportal_esp32spi.py index b98d92b5..9c53c41f 100644 --- a/examples/esp32spi/minimqtt_pub_sub_pyportal_esp32spi.py +++ b/examples/esp32spi/minimqtt_pub_sub_pyportal_esp32spi.py @@ -1,22 +1,22 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import time -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_connection_manager +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_pyportal import adafruit_minimqtt.adafruit_minimqtt as MQTT pyportal = adafruit_pyportal.PyPortal() -### WiFi ### +# Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys +# with your WiFi credentials. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. -# Get wifi details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") # ------------- MQTT Topic Setup ------------- # mqtt_topic = "test/topic" @@ -51,16 +51,19 @@ def message(client, topic, message): pyportal.network.connect() print("Connected!") -# Initialize MQTT interface with the esp interface # pylint: disable=protected-access -MQTT.set_socket(socket, pyportal.network._wifi.esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context( + pool, pyportal.network._wifi.esp +) # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( - broker=secrets["broker"], - username=secrets["user"], - password=secrets["pass"], + broker=os.getenv("broker"), + username=os.getenv("username"), + password=os.getenv("password"), is_ssl=False, + socket_pool=pool, + ssl_context=ssl_context, ) # Setup the callback methods above diff --git a/examples/esp32spi/minimqtt_simpletest_esp32spi.py b/examples/esp32spi/minimqtt_simpletest_esp32spi.py index 96a71474..a4f27c5a 100644 --- a/examples/esp32spi/minimqtt_simpletest_esp32spi.py +++ b/examples/esp32spi/minimqtt_simpletest_esp32spi.py @@ -1,27 +1,21 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT + +import os import board import busio from digitalio import DigitalInOut +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT -# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and -# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other -# source control. -# pylint: disable=no-name-in-module,wrong-import-order -try: - from secrets import secrets -except ImportError: - print("WiFi secrets are kept in secrets.py, please add them there!") - raise - -# Set your Adafruit IO Username and Key in secrets.py -# (visit io.adafruit.com if you need to create an account, -# or if you need your Adafruit IO key.) -aio_username = secrets["aio_username"] -aio_key = secrets["aio_key"] +# Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys +# with your WiFi credentials. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. + +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) @@ -39,7 +33,7 @@ print("Connecting to AP...") while not esp.is_connected: try: - esp.connect_AP(secrets["ssid"], secrets["password"]) + esp.connect_AP(os.getenv("ssid"), os.getenv("password")) except RuntimeError as e: print("could not connect to AP, retrying: ", e) continue @@ -53,7 +47,7 @@ # Adafruit IO-style Topic # Use this topic if you'd like to connect to io.adafruit.com -# mqtt_topic = secrets["aio_username"] + '/feeds/temperature' +# mqtt_topic = aio_username + '/feeds/temperature' ### Code ### @@ -92,15 +86,16 @@ def message(client, topic, message): print("New message on topic {0}: {1}".format(topic, message)) -socket.set_interface(esp) -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( - broker=secrets["broker"], - port=secrets["port"], - username=secrets["username"], - password=secrets["password"], + broker=os.getenv("broker"), + port=os.getenv("port"), + username=os.getenv("username"), + password=os.getenv("password"), + socket_pool=pool, + ssl_context=ssl_context, ) # Connect callback handlers to mqtt_client diff --git a/examples/ethernet/minimqtt_adafruitio_eth.py b/examples/ethernet/minimqtt_adafruitio_eth.py index 5c114cee..5b19faa0 100755 --- a/examples/ethernet/minimqtt_adafruitio_eth.py +++ b/examples/ethernet/minimqtt_adafruitio_eth.py @@ -1,22 +1,22 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import time import board import busio from digitalio import DigitalInOut - +import adafruit_connection_manager from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K -import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket +import adafruit_wiznet5k.adafruit_wiznet5k_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT -# Get Adafruit IO details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("Adafruit IO secrets are kept in secrets.py, please add them there!") - raise +# Add settings.toml to your filesystem. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. + +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") cs = DigitalInOut(board.D10) spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -27,10 +27,10 @@ ### Feeds ### # Setup a feed named 'photocell' for publishing to a feed -photocell_feed = secrets["aio_username"] + "/feeds/photocell" +photocell_feed = aio_username + "/feeds/photocell" # Setup a feed named 'onoff' for subscribing to changes -onoff_feed = secrets["aio_username"] + "/feeds/onoff" +onoff_feed = aio_username + "/feeds/onoff" ### Code ### @@ -56,16 +56,17 @@ def message(client, topic, message): print("New message on topic {0}: {1}".format(topic, message)) -# Initialize MQTT interface with the ethernet interface -MQTT.set_socket(socket, eth) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, eth) # Set up a MiniMQTT Client # NOTE: We'll need to connect insecurely for ethernet configurations. mqtt_client = MQTT.MQTT( broker="io.adafruit.com", - username=secrets["aio_username"], - password=secrets["aio_key"], + username=aio_username, + password=aio_key, is_ssl=False, + socket_pool=pool, + ssl_context=ssl_context, ) # Setup the callback methods above diff --git a/examples/ethernet/minimqtt_simpletest_eth.py b/examples/ethernet/minimqtt_simpletest_eth.py index 16846d5c..850cf5eb 100644 --- a/examples/ethernet/minimqtt_simpletest_eth.py +++ b/examples/ethernet/minimqtt_simpletest_eth.py @@ -1,20 +1,21 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT +import os import board import busio from digitalio import DigitalInOut +import adafruit_connection_manager from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K -import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket +import adafruit_wiznet5k.adafruit_wiznet5k_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT -# Get MQTT details and more from a secrets.py file -try: - from secrets import secrets -except ImportError: - print("MQTT secrets are kept in secrets.py, please add them there!") - raise +# Add settings.toml to your filesystem. Add your Adafruit IO username and key as well. +# DO NOT share that file or commit it into Git or other source control. + +aio_username = os.getenv("aio_username") +aio_key = os.getenv("aio_key") cs = DigitalInOut(board.D10) spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -64,16 +65,17 @@ def publish(client, userdata, topic, pid): print("Published to {0} with PID {1}".format(topic, pid)) -# Initialize MQTT interface with the ethernet interface -MQTT.set_socket(socket, eth) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, eth) # Set up a MiniMQTT Client # NOTE: We'll need to connect insecurely for ethernet configurations. client = MQTT.MQTT( - broker=secrets["broker"], - username=secrets["user"], - password=secrets["pass"], + broker=os.getenv("broker"), + username=os.getenv("username"), + password=os.getenv("password"), is_ssl=False, + socket_pool=pool, + ssl_context=ssl_context, ) # Connect callback handlers to client diff --git a/examples/minimqtt_simpletest.py b/examples/minimqtt_simpletest.py index f9ab2771..a5757866 100644 --- a/examples/minimqtt_simpletest.py +++ b/examples/minimqtt_simpletest.py @@ -5,8 +5,9 @@ import board import busio from digitalio import DigitalInOut +import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi -import adafruit_esp32spi.adafruit_esp32spi_socket as socket +import adafruit_esp32spi.adafruit_esp32spi_socket as pool import adafruit_minimqtt.adafruit_minimqtt as MQTT # Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys @@ -88,14 +89,15 @@ def message(client, topic, message): print("New message on topic {0}: {1}".format(topic, message)) -socket.set_interface(esp) -MQTT.set_socket(socket, esp) +ssl_context = adafruit_connection_manager.create_fake_ssl_context(pool, esp) # Set up a MiniMQTT Client mqtt_client = MQTT.MQTT( broker="io.adafruit.com", username=aio_username, password=aio_key, + socket_pool=pool, + ssl_context=ssl_context, ) # Connect callback handlers to mqtt_client diff --git a/examples/native_networking/minimqtt_adafruitio_native_networking.py b/examples/native_networking/minimqtt_adafruitio_native_networking.py index 512b83e6..a828d25f 100644 --- a/examples/native_networking/minimqtt_adafruitio_native_networking.py +++ b/examples/native_networking/minimqtt_adafruitio_native_networking.py @@ -60,12 +60,11 @@ def message(client, topic, message): ssl_context = ssl.create_default_context() # If you need to use certificate/key pair authentication (e.g. X.509), you can load them in the -# ssl context by uncommenting the lines below and adding the following keys to the "secrets" -# dictionary in your secrets.py file: +# ssl context by uncommenting the lines below and adding the following keys to your settings.toml: # "device_cert_path" - Path to the Device Certificate # "device_key_path" - Path to the RSA Private Key # ssl_context.load_cert_chain( -# certfile=secrets["device_cert_path"], keyfile=secrets["device_key_path"] +# certfile=os.getenv("device_cert_path"), keyfile=os.getenv("device_key_path") # ) # Set up a MiniMQTT Client diff --git a/examples/native_networking/minimqtt_pub_sub_blocking_native_networking.py b/examples/native_networking/minimqtt_pub_sub_blocking_native_networking.py index 0ba76dfb..36084458 100644 --- a/examples/native_networking/minimqtt_pub_sub_blocking_native_networking.py +++ b/examples/native_networking/minimqtt_pub_sub_blocking_native_networking.py @@ -60,12 +60,11 @@ def message(client, topic, message): ssl_context = ssl.create_default_context() # If you need to use certificate/key pair authentication (e.g. X.509), you can load them in the -# ssl context by uncommenting the lines below and adding the following keys to the "secrets" -# dictionary in your secrets.py file: +# ssl context by uncommenting the lines below and adding the following keys to your settings.toml: # "device_cert_path" - Path to the Device Certificate # "device_key_path" - Path to the RSA Private Key # ssl_context.load_cert_chain( -# certfile=secrets["device_cert_path"], keyfile=secrets["device_key_path"] +# certfile=os.getenv("device_cert_path"), keyfile=os.getenv("device_key_path") # ) # Set up a MiniMQTT Client diff --git a/examples/native_networking/minimqtt_pub_sub_blocking_topic_callbacks_native_networking.py b/examples/native_networking/minimqtt_pub_sub_blocking_topic_callbacks_native_networking.py index 82b9ce18..9e50bb08 100644 --- a/examples/native_networking/minimqtt_pub_sub_blocking_topic_callbacks_native_networking.py +++ b/examples/native_networking/minimqtt_pub_sub_blocking_topic_callbacks_native_networking.py @@ -67,12 +67,11 @@ def on_message(client, topic, message): ssl_context = ssl.create_default_context() # If you need to use certificate/key pair authentication (e.g. X.509), you can load them in the -# ssl context by uncommenting the lines below and adding the following keys to the "secrets" -# dictionary in your secrets.py file: +# ssl context by uncommenting the lines below and adding the following keys to your settings.toml: # "device_cert_path" - Path to the Device Certificate # "device_key_path" - Path to the RSA Private Key # ssl_context.load_cert_chain( -# certfile=secrets["device_cert_path"], keyfile=secrets["device_key_path"] +# certfile=os.getenv("device_cert_path"), keyfile=os.getenv("device_key_path") # ) # Set up a MiniMQTT Client From 1f0c0c71b83c3513f9d956c6f0ea10cfc5a95bda Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Tue, 27 Feb 2024 04:38:16 -0800 Subject: [PATCH 09/11] Fix linting --- tests/test_backoff.py | 3 ++- tests/test_loop.py | 4 +++- tests/test_port_ssl.py | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_backoff.py b/tests/test_backoff.py index 573b2050..e26d07a4 100644 --- a/tests/test_backoff.py +++ b/tests/test_backoff.py @@ -4,12 +4,13 @@ """exponential back-off tests""" -import pytest + import socket import ssl import time from unittest.mock import call, patch +import pytest import adafruit_minimqtt.adafruit_minimqtt as MQTT diff --git a/tests/test_loop.py b/tests/test_loop.py index 9253b560..4d79b65b 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -4,7 +4,6 @@ """loop() tests""" -import pytest import random import socket import ssl @@ -14,6 +13,7 @@ from unittest.mock import patch from unittest import mock +import pytest import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -158,6 +158,7 @@ def test_loop_basic(self) -> None: assert ret_code == expected_rc expected_rc += 1 + # pylint: disable=no-self-use # pylint: disable=invalid-name def test_loop_timeout_vs_socket_timeout(self): """ @@ -178,6 +179,7 @@ def test_loop_timeout_vs_socket_timeout(self): assert "loop timeout" in str(context) + # pylint: disable=no-self-use def test_loop_is_connected(self): """ loop() should throw MMQTTException if not connected diff --git a/tests/test_port_ssl.py b/tests/test_port_ssl.py index 4204bb60..9ac154da 100644 --- a/tests/test_port_ssl.py +++ b/tests/test_port_ssl.py @@ -4,11 +4,11 @@ """tests that verify the connect behavior w.r.t. port number and TLS""" -import pytest import socket import ssl from unittest.mock import Mock, call, patch +import pytest import adafruit_minimqtt.adafruit_minimqtt as MQTT @@ -17,6 +17,7 @@ class TestPortSslSetup: These tests assume that there is no MQTT broker running on the hosts/ports they connect to. """ + # pylint: disable=no-self-use def test_default_port(self) -> None: """verify default port value and that TLS is not used""" host = "127.0.0.1" @@ -44,6 +45,7 @@ def test_default_port(self) -> None: # Assuming the repeated calls will have the same arguments. connect_mock.assert_has_calls([call((host, expected_port))]) + # pylint: disable=no-self-use def test_connect_override(self): """Test that connect() can override host and port.""" host = "127.0.0.1" @@ -69,6 +71,7 @@ def test_connect_override(self): # Assuming the repeated calls will have the same arguments. connect_mock.assert_has_calls([call((expected_host, expected_port))]) + # pylint: disable=no-self-use @pytest.mark.parametrize("port", (None, 8883)) def test_tls_port(self, port) -> None: """verify that when is_ssl=True is set, the default port is 8883 @@ -104,6 +107,7 @@ def test_tls_port(self, port) -> None: # Assuming the repeated calls will have the same arguments. connect_mock.assert_has_calls([call((host, expected_port))]) + # pylint: disable=no-self-use def test_tls_without_ssl_context(self) -> None: """verify that when is_ssl=True is set, the code will check that ssl_context is not None""" host = "127.0.0.1" From 508c281746f49717d3b7198e24755833703f02e8 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Thu, 29 Feb 2024 06:36:22 -0800 Subject: [PATCH 10/11] Fix README requirements --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 2ca1660e..f5fcd464 100644 --- a/README.rst +++ b/README.rst @@ -24,6 +24,7 @@ Dependencies This driver depends on: * `Adafruit CircuitPython `_ +* `Adafruit CircuitPython ConnectionManager `_ Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading From 8a1b617e2d44ccab83203eea0ae019381cf9f8c5 Mon Sep 17 00:00:00 2001 From: Justin Myers Date: Thu, 29 Feb 2024 11:21:21 -0800 Subject: [PATCH 11/11] Move conftest --- conftest.py => tests/conftest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename conftest.py => tests/conftest.py (100%) diff --git a/conftest.py b/tests/conftest.py similarity index 100% rename from conftest.py rename to tests/conftest.py