Skip to content

Commit fdd436e

Browse files
committed
reduce the use of MMQTTException
use it for protocol/network/system level errors only fixes #201
1 parent 57ed4f0 commit fdd436e

File tree

2 files changed

+42
-29
lines changed

2 files changed

+42
-29
lines changed

adafruit_minimqtt/adafruit_minimqtt.py

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,26 @@
9393

9494

9595
class MMQTTException(Exception):
96-
"""MiniMQTT Exception class."""
96+
"""
97+
MiniMQTT Exception class.
98+
99+
Raised for various mostly protocol or network/system level errors.
100+
In general, the robust way to recover is to call reconnect().
101+
"""
97102

98103
def __init__(self, error, code=None):
99104
super().__init__(error, code)
100105
self.code = code
101106

102107

108+
class MMQTTStateError(MMQTTException):
109+
"""
110+
MiniMQTT invalid state error.
111+
112+
Raised e.g. if a function is called in unexpected state.
113+
"""
114+
115+
103116
class NullLogger:
104117
"""Fake logger class that does not do anything"""
105118

@@ -163,7 +176,7 @@ def __init__( # noqa: PLR0915, PLR0913, Too many statements, Too many arguments
163176
self._use_binary_mode = use_binary_mode
164177

165178
if recv_timeout <= socket_timeout:
166-
raise MMQTTException("recv_timeout must be strictly greater than socket_timeout")
179+
raise ValueError("recv_timeout must be strictly greater than socket_timeout")
167180
self._socket_timeout = socket_timeout
168181
self._recv_timeout = recv_timeout
169182

@@ -181,7 +194,7 @@ def __init__( # noqa: PLR0915, PLR0913, Too many statements, Too many arguments
181194
self._reconnect_timeout = float(0)
182195
self._reconnect_maximum_backoff = 32
183196
if connect_retries <= 0:
184-
raise MMQTTException("connect_retries must be positive")
197+
raise ValueError("connect_retries must be positive")
185198
self._reconnect_attempts_max = connect_retries
186199

187200
self.broker = broker
@@ -190,7 +203,7 @@ def __init__( # noqa: PLR0915, PLR0913, Too many statements, Too many arguments
190203
if (
191204
self._password and len(password.encode("utf-8")) > MQTT_TOPIC_LENGTH_LIMIT
192205
): # [MQTT-3.1.3.5]
193-
raise MMQTTException("Password length is too large.")
206+
raise ValueError("Password length is too large.")
194207

195208
# The connection will be insecure unless is_ssl is set to True.
196209
# If the port is not specified, the security will be set based on the is_ssl parameter.
@@ -286,28 +299,27 @@ def will_set(
286299
"""
287300
self.logger.debug("Setting last will properties")
288301
if self._is_connected:
289-
raise MMQTTException("Last Will should only be called before connect().")
302+
raise MMQTTStateError("Last Will should only be called before connect().")
290303

291304
# check topic/msg/qos kwargs
292305
self._valid_topic(topic)
293306
if "+" in topic or "#" in topic:
294-
raise MMQTTException("Publish topic can not contain wildcards.")
307+
raise ValueError("Publish topic can not contain wildcards.")
295308

296309
if msg is None:
297-
raise MMQTTException("Message can not be None.")
310+
raise ValueError("Message can not be None.")
298311
if isinstance(msg, (int, float)):
299312
msg = str(msg).encode("ascii")
300313
elif isinstance(msg, str):
301314
msg = str(msg).encode("utf-8")
302315
elif isinstance(msg, bytes):
303316
pass
304317
else:
305-
raise MMQTTException("Invalid message data type.")
318+
raise ValueError("Invalid message data type.")
306319
if len(msg) > MQTT_MSG_MAX_SZ:
307-
raise MMQTTException(f"Message size larger than {MQTT_MSG_MAX_SZ} bytes.")
320+
raise ValueError(f"Message size larger than {MQTT_MSG_MAX_SZ} bytes.")
308321

309322
self._valid_qos(qos)
310-
assert 0 <= qos <= 1, "Quality of Service Level 2 is unsupported by this library."
311323

312324
# fixed header. [3.3.1.2], [3.3.1.3]
313325
pub_hdr_fixed = bytearray([MQTT_PUBLISH | retain | qos << 1])
@@ -390,7 +402,7 @@ def username_pw_set(self, username: str, password: Optional[str] = None) -> None
390402
391403
"""
392404
if self._is_connected:
393-
raise MMQTTException("This method must be called before connect().")
405+
raise MMQTTStateError("This method must be called before connect().")
394406
self._username = username
395407
if password is not None:
396408
self._password = password
@@ -670,21 +682,22 @@ def publish( # noqa: PLR0912, Too many branches
670682
self._connected()
671683
self._valid_topic(topic)
672684
if "+" in topic or "#" in topic:
673-
raise MMQTTException("Publish topic can not contain wildcards.")
685+
raise ValueError("Publish topic can not contain wildcards.")
674686
# check msg/qos kwargs
675687
if msg is None:
676-
raise MMQTTException("Message can not be None.")
688+
raise ValueError("Message can not be None.")
677689
if isinstance(msg, (int, float)):
678690
msg = str(msg).encode("ascii")
679691
elif isinstance(msg, str):
680692
msg = str(msg).encode("utf-8")
681693
elif isinstance(msg, bytes):
682694
pass
683695
else:
684-
raise MMQTTException("Invalid message data type.")
696+
raise ValueError("Invalid message data type.")
685697
if len(msg) > MQTT_MSG_MAX_SZ:
686-
raise MMQTTException(f"Message size larger than {MQTT_MSG_MAX_SZ} bytes.")
687-
assert 0 <= qos <= 1, "Quality of Service Level 2 is unsupported by this library."
698+
raise ValueError(f"Message size larger than {MQTT_MSG_MAX_SZ} bytes.")
699+
700+
self._valid_qos(qos)
688701

689702
# fixed header. [3.3.1.2], [3.3.1.3]
690703
pub_hdr_fixed = bytearray([MQTT_PUBLISH | retain | qos << 1])
@@ -849,7 +862,7 @@ def unsubscribe( # noqa: PLR0912, Too many branches
849862
topics.append(t)
850863
for t in topics:
851864
if t not in self._subscribed_topics:
852-
raise MMQTTException("Topic must be subscribed to before attempting unsubscribe.")
865+
raise MMQTTStateError("Topic must be subscribed to before attempting unsubscribe.")
853866
# Assemble packet
854867
self.logger.debug("Sending UNSUBSCRIBE to broker...")
855868
fixed_header = bytearray([MQTT_UNSUB])
@@ -959,7 +972,7 @@ def loop(self, timeout: float = 1.0) -> Optional[list[int]]:
959972
960973
"""
961974
if timeout < self._socket_timeout:
962-
raise MMQTTException(
975+
raise ValueError(
963976
f"loop timeout ({timeout}) must be >= "
964977
+ f"socket timeout ({self._socket_timeout}))"
965978
)
@@ -1153,13 +1166,13 @@ def _valid_topic(topic: str) -> None:
11531166
11541167
"""
11551168
if topic is None:
1156-
raise MMQTTException("Topic may not be NoneType")
1169+
raise ValueError("Topic may not be NoneType")
11571170
# [MQTT-4.7.3-1]
11581171
if not topic:
1159-
raise MMQTTException("Topic may not be empty.")
1172+
raise ValueError("Topic may not be empty.")
11601173
# [MQTT-4.7.3-3]
11611174
if len(topic.encode("utf-8")) > MQTT_TOPIC_LENGTH_LIMIT:
1162-
raise MMQTTException("Topic length is too large.")
1175+
raise ValueError(f"Encoded topic length is larger than {MQTT_TOPIC_LENGTH_LIMIT}")
11631176

11641177
@staticmethod
11651178
def _valid_qos(qos_level: int) -> None:
@@ -1170,16 +1183,16 @@ def _valid_qos(qos_level: int) -> None:
11701183
"""
11711184
if isinstance(qos_level, int):
11721185
if qos_level < 0 or qos_level > 2:
1173-
raise MMQTTException("QoS must be between 1 and 2.")
1186+
raise NotImplementedError("QoS must be between 1 and 2.")
11741187
else:
1175-
raise MMQTTException("QoS must be an integer.")
1188+
raise ValueError("QoS must be an integer.")
11761189

11771190
def _connected(self) -> None:
11781191
"""Returns MQTT client session status as True if connected, raises
1179-
a `MMQTTException` if `False`.
1192+
a `MMQTTStateError exception` if `False`.
11801193
"""
11811194
if not self.is_connected():
1182-
raise MMQTTException("MiniMQTT is not connected")
1195+
raise MMQTTStateError("MiniMQTT is not connected")
11831196

11841197
def is_connected(self) -> bool:
11851198
"""Returns MQTT client session status as True if connected, False

tests/test_loop.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def test_loop_basic(self) -> None:
155155

156156
def test_loop_timeout_vs_socket_timeout(self):
157157
"""
158-
loop() should throw MMQTTException if the timeout argument
158+
loop() should throw ValueError if the timeout argument
159159
is bigger than the socket timeout.
160160
"""
161161
mqtt_client = MQTT.MQTT(
@@ -167,14 +167,14 @@ def test_loop_timeout_vs_socket_timeout(self):
167167
)
168168

169169
mqtt_client.is_connected = lambda: True
170-
with pytest.raises(MQTT.MMQTTException) as context:
170+
with pytest.raises(ValueError) as context:
171171
mqtt_client.loop(timeout=0.5)
172172

173173
assert "loop timeout" in str(context)
174174

175175
def test_loop_is_connected(self):
176176
"""
177-
loop() should throw MMQTTException if not connected
177+
loop() should throw MMQTTStateError if not connected
178178
"""
179179
mqtt_client = MQTT.MQTT(
180180
broker="127.0.0.1",
@@ -183,7 +183,7 @@ def test_loop_is_connected(self):
183183
ssl_context=ssl.create_default_context(),
184184
)
185185

186-
with pytest.raises(MQTT.MMQTTException) as context:
186+
with pytest.raises(MQTT.MMQTTStateError) as context:
187187
mqtt_client.loop(timeout=1)
188188

189189
assert "not connected" in str(context)

0 commit comments

Comments
 (0)