nwp500-python 5.0.1__tar.gz → 5.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/CHANGELOG.rst +14 -0
- {nwp500_python-5.0.1/src/nwp500_python.egg-info → nwp500_python-5.0.2}/PKG-INFO +1 -1
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_connection.py +72 -6
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_subscriptions.py +28 -2
- {nwp500_python-5.0.1 → nwp500_python-5.0.2/src/nwp500_python.egg-info}/PKG-INFO +1 -1
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.coveragerc +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.github/copilot-instructions.md +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.github/workflows/ci.yml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.github/workflows/release.yml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.gitignore +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.pre-commit-config.yaml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/.readthedocs.yml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/AUTHORS.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/CONTRIBUTING.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/LICENSE.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/Makefile +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/README.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/RELEASE.md +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/Makefile +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/_static/.gitignore +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/authors.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/changelog.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/conf.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/configuration.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/development/contributing.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/development/history.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/guides/auto_recovery.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/guides/command_queue.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/guides/energy_monitoring.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/guides/event_system.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/guides/reservations.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/guides/time_of_use.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/index.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/installation.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/license.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/openapi.yaml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/protocol/device_features.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/protocol/device_status.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/protocol/error_codes.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/protocol/firmware_tracking.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/protocol/mqtt_protocol.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/protocol/rest_api.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/api_client.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/auth_client.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/cli.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/constants.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/events.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/exceptions.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/models.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/python_api/mqtt_client.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/quickstart.rst +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/docs/requirements.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/.ruff.toml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/README.md +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/anti_legionella_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/api_client_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/auth_constructor_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/authenticate.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/auto_recovery_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/combined_callbacks.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/command_queue_demo.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/device_feature_callback.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/device_status_callback.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/device_status_callback_debug.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/energy_usage_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/event_emitter_demo.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/exception_handling_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/improved_auth_pattern.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/mask.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/mqtt_client_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/periodic_device_info.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/periodic_requests.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/power_control_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/reconnection_demo.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/reservation_schedule_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/set_dhw_temperature_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/set_mode_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/simple_auto_recovery.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/simple_periodic_info.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/simple_periodic_status.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/test_api_client.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/test_mqtt_connection.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/test_mqtt_messaging.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/test_periodic_minimal.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/token_restoration_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/tou_openei_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/examples/tou_schedule_example.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/pyproject.toml +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/scripts/README.md +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/scripts/bump_version.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/scripts/format.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/scripts/lint.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/scripts/setup-dev.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/scripts/validate_version.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/setup.cfg +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/setup.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/__init__.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/api_client.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/auth.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/cli/__init__.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/cli/__main__.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/cli/commands.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/cli/monitoring.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/cli/output_formatters.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/cli/token_storage.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/config.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/constants.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/encoding.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/events.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/exceptions.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/models.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_client.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_command_queue.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_device_control.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_periodic.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_reconnection.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/mqtt_utils.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/py.typed +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500/utils.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500_python.egg-info/SOURCES.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500_python.egg-info/entry_points.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500_python.egg-info/not-zip-safe +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500_python.egg-info/requires.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/src/nwp500_python.egg-info/top_level.txt +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/conftest.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/test_api_helpers.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/test_auth.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/test_command_queue.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/test_events.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/test_exceptions.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tests/test_utils.py +0 -0
- {nwp500_python-5.0.1 → nwp500_python-5.0.2}/tox.ini +0 -0
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 5.0.2 (2025-10-31)
|
|
6
|
+
==========================
|
|
7
|
+
|
|
8
|
+
Fixed
|
|
9
|
+
-----
|
|
10
|
+
|
|
11
|
+
- **MQTT Future Cancellation**: Fixed InvalidStateError exceptions during disconnect
|
|
12
|
+
|
|
13
|
+
- Added asyncio.shield() to protect concurrent.futures.Future objects from cancellation
|
|
14
|
+
- Applied consistent cancellation handling across all MQTT operations (connect, disconnect, subscribe, unsubscribe, publish)
|
|
15
|
+
- AWS CRT callbacks can now complete independently without raising InvalidStateError
|
|
16
|
+
- Added debug logging when operations are cancelled for better diagnostics
|
|
17
|
+
- Ensures clean shutdown without spurious exception messages
|
|
18
|
+
|
|
5
19
|
Version 5.0.1 (2025-10-27)
|
|
6
20
|
==========================
|
|
7
21
|
|
|
@@ -140,9 +140,23 @@ class MqttConnection:
|
|
|
140
140
|
_logger.info("Establishing MQTT connection...")
|
|
141
141
|
|
|
142
142
|
# Convert concurrent.futures.Future to asyncio.Future and await
|
|
143
|
+
# Use shield to prevent cancellation from propagating to
|
|
144
|
+
# underlying future
|
|
143
145
|
if self._connection is not None:
|
|
144
146
|
connect_future = self._connection.connect()
|
|
145
|
-
|
|
147
|
+
try:
|
|
148
|
+
connect_result = await asyncio.shield(
|
|
149
|
+
asyncio.wrap_future(connect_future)
|
|
150
|
+
)
|
|
151
|
+
except asyncio.CancelledError:
|
|
152
|
+
# Shield was cancelled - the underlying connect will
|
|
153
|
+
# complete independently, preventing InvalidStateError
|
|
154
|
+
# in AWS CRT callbacks
|
|
155
|
+
_logger.debug(
|
|
156
|
+
"Connect operation was cancelled but will complete "
|
|
157
|
+
"in background"
|
|
158
|
+
)
|
|
159
|
+
raise
|
|
146
160
|
else:
|
|
147
161
|
raise MqttConnectionError("Connection not initialized")
|
|
148
162
|
|
|
@@ -196,8 +210,20 @@ class MqttConnection:
|
|
|
196
210
|
|
|
197
211
|
try:
|
|
198
212
|
# Convert concurrent.futures.Future to asyncio.Future and await
|
|
213
|
+
# Use shield to prevent cancellation from propagating to
|
|
214
|
+
# underlying future
|
|
199
215
|
disconnect_future = self._connection.disconnect()
|
|
200
|
-
|
|
216
|
+
try:
|
|
217
|
+
await asyncio.shield(asyncio.wrap_future(disconnect_future))
|
|
218
|
+
except asyncio.CancelledError:
|
|
219
|
+
# Shield was cancelled - the underlying disconnect will
|
|
220
|
+
# complete independently, preventing InvalidStateError
|
|
221
|
+
# in AWS CRT callbacks
|
|
222
|
+
_logger.debug(
|
|
223
|
+
"Disconnect operation was cancelled but will complete "
|
|
224
|
+
"in background"
|
|
225
|
+
)
|
|
226
|
+
raise
|
|
201
227
|
|
|
202
228
|
self._connected = False
|
|
203
229
|
self._connection = None
|
|
@@ -232,10 +258,22 @@ class MqttConnection:
|
|
|
232
258
|
_logger.debug(f"Subscribing to topic: {topic}")
|
|
233
259
|
|
|
234
260
|
# Convert concurrent.futures.Future to asyncio.Future and await
|
|
261
|
+
# Use shield to prevent cancellation from propagating to
|
|
262
|
+
# underlying future
|
|
235
263
|
subscribe_future, packet_id = self._connection.subscribe(
|
|
236
264
|
topic=topic, qos=qos, callback=callback
|
|
237
265
|
)
|
|
238
|
-
|
|
266
|
+
try:
|
|
267
|
+
await asyncio.shield(asyncio.wrap_future(subscribe_future))
|
|
268
|
+
except asyncio.CancelledError:
|
|
269
|
+
# Shield was cancelled - the underlying subscribe will
|
|
270
|
+
# complete independently, preventing InvalidStateError
|
|
271
|
+
# in AWS CRT callbacks
|
|
272
|
+
_logger.debug(
|
|
273
|
+
f"Subscribe to '{topic}' was cancelled but will complete "
|
|
274
|
+
"in background"
|
|
275
|
+
)
|
|
276
|
+
raise
|
|
239
277
|
|
|
240
278
|
_logger.info(f"Subscribed to '{topic}' with packet_id {packet_id}")
|
|
241
279
|
return (subscribe_future, packet_id)
|
|
@@ -259,10 +297,22 @@ class MqttConnection:
|
|
|
259
297
|
_logger.debug(f"Unsubscribing from topic: {topic}")
|
|
260
298
|
|
|
261
299
|
# Convert concurrent.futures.Future to asyncio.Future and await
|
|
300
|
+
# Use shield to prevent cancellation from propagating to
|
|
301
|
+
# underlying future
|
|
262
302
|
unsubscribe_future, packet_id = self._connection.unsubscribe(
|
|
263
303
|
topic=topic
|
|
264
304
|
)
|
|
265
|
-
|
|
305
|
+
try:
|
|
306
|
+
await asyncio.shield(asyncio.wrap_future(unsubscribe_future))
|
|
307
|
+
except asyncio.CancelledError:
|
|
308
|
+
# Shield was cancelled - the underlying unsubscribe will
|
|
309
|
+
# complete independently, preventing InvalidStateError
|
|
310
|
+
# in AWS CRT callbacks
|
|
311
|
+
_logger.debug(
|
|
312
|
+
f"Unsubscribe from '{topic}' was cancelled but will "
|
|
313
|
+
"complete in background"
|
|
314
|
+
)
|
|
315
|
+
raise
|
|
266
316
|
|
|
267
317
|
_logger.info(f"Unsubscribed from '{topic}' with packet_id {packet_id}")
|
|
268
318
|
return int(packet_id)
|
|
@@ -286,6 +336,7 @@ class MqttConnection:
|
|
|
286
336
|
|
|
287
337
|
Raises:
|
|
288
338
|
RuntimeError: If not connected
|
|
339
|
+
asyncio.CancelledError: If operation cancelled during disconnect
|
|
289
340
|
"""
|
|
290
341
|
if not self._connected or not self._connection:
|
|
291
342
|
raise MqttNotConnectedError("Not connected to MQTT broker")
|
|
@@ -303,11 +354,26 @@ class MqttConnection:
|
|
|
303
354
|
# Try to JSON encode other types
|
|
304
355
|
payload_bytes = json.dumps(payload).encode("utf-8")
|
|
305
356
|
|
|
306
|
-
#
|
|
357
|
+
# Publish and get the concurrent.futures.Future
|
|
307
358
|
publish_future, packet_id = self._connection.publish(
|
|
308
359
|
topic=topic, payload=payload_bytes, qos=qos
|
|
309
360
|
)
|
|
310
|
-
|
|
361
|
+
|
|
362
|
+
# Shield the operation to prevent cancellation from propagating to
|
|
363
|
+
# the underlying concurrent.futures.Future. This avoids
|
|
364
|
+
# InvalidStateError when AWS CRT tries to set exception on a
|
|
365
|
+
# cancelled future.
|
|
366
|
+
try:
|
|
367
|
+
await asyncio.shield(asyncio.wrap_future(publish_future))
|
|
368
|
+
except asyncio.CancelledError:
|
|
369
|
+
# Shield was cancelled - the underlying publish will complete
|
|
370
|
+
# independently, preventing InvalidStateError in AWS CRT
|
|
371
|
+
# callbacks
|
|
372
|
+
_logger.debug(
|
|
373
|
+
f"Publish to '{topic}' was cancelled but will complete "
|
|
374
|
+
"in background"
|
|
375
|
+
)
|
|
376
|
+
raise
|
|
311
377
|
|
|
312
378
|
_logger.debug(f"Published to '{topic}' with packet_id {packet_id}")
|
|
313
379
|
return int(packet_id)
|
|
@@ -214,10 +214,24 @@ class MqttSubscriptionManager:
|
|
|
214
214
|
|
|
215
215
|
try:
|
|
216
216
|
# Convert concurrent.futures.Future to asyncio.Future and await
|
|
217
|
+
# Use shield to prevent cancellation from propagating to
|
|
218
|
+
# underlying future
|
|
217
219
|
subscribe_future, packet_id = self._connection.subscribe(
|
|
218
220
|
topic=topic, qos=qos, callback=self._on_message_received
|
|
219
221
|
)
|
|
220
|
-
|
|
222
|
+
try:
|
|
223
|
+
subscribe_result = await asyncio.shield(
|
|
224
|
+
asyncio.wrap_future(subscribe_future)
|
|
225
|
+
)
|
|
226
|
+
except asyncio.CancelledError:
|
|
227
|
+
# Shield was cancelled - the underlying subscribe will
|
|
228
|
+
# complete independently, preventing InvalidStateError
|
|
229
|
+
# in AWS CRT callbacks
|
|
230
|
+
_logger.debug(
|
|
231
|
+
f"Subscribe to '{redact_topic(topic)}' was cancelled "
|
|
232
|
+
"but will complete in background"
|
|
233
|
+
)
|
|
234
|
+
raise
|
|
221
235
|
|
|
222
236
|
_logger.info(
|
|
223
237
|
f"Subscription succeeded (topic redacted) with QoS "
|
|
@@ -259,8 +273,20 @@ class MqttSubscriptionManager:
|
|
|
259
273
|
|
|
260
274
|
try:
|
|
261
275
|
# Convert concurrent.futures.Future to asyncio.Future and await
|
|
276
|
+
# Use shield to prevent cancellation from propagating to
|
|
277
|
+
# underlying future
|
|
262
278
|
unsubscribe_future, packet_id = self._connection.unsubscribe(topic)
|
|
263
|
-
|
|
279
|
+
try:
|
|
280
|
+
await asyncio.shield(asyncio.wrap_future(unsubscribe_future))
|
|
281
|
+
except asyncio.CancelledError:
|
|
282
|
+
# Shield was cancelled - the underlying unsubscribe will
|
|
283
|
+
# complete independently, preventing InvalidStateError
|
|
284
|
+
# in AWS CRT callbacks
|
|
285
|
+
_logger.debug(
|
|
286
|
+
f"Unsubscribe from '{redact_topic(topic)}' was "
|
|
287
|
+
"cancelled but will complete in background"
|
|
288
|
+
)
|
|
289
|
+
raise
|
|
264
290
|
|
|
265
291
|
# Remove from tracking
|
|
266
292
|
self._subscriptions.pop(topic, None)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|