From e2f07818cc261f5e81aba44210138d3d64ea6b74 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Fri, 19 Sep 2025 14:23:19 -0600 Subject: [PATCH] Fix race conditions in RPC and web API multicommand methods Multiple multicommand methods were double-awaiting tasks - first via asyncio.gather() with return_exceptions=True, then calling .result() on the same tasks. This caused ConnectionResetError and other exceptions when connections were lost. Changed to properly use the results from gather() instead of calling .result() on completed tasks, preventing exceptions from being raised after they were already caught. Fixed in: - pyasic/rpc/base.py:144 - RPC _send_split_multicommand - pyasic/web/espminer.py:79 - ESPMiner multicommand - pyasic/web/auradine.py:149 - Auradine multicommand --- pyasic/rpc/base.py | 11 +++++------ pyasic/web/auradine.py | 11 +++++------ pyasic/web/espminer.py | 11 +++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/pyasic/rpc/base.py b/pyasic/rpc/base.py index c8adc7a4..060e2061 100644 --- a/pyasic/rpc/base.py +++ b/pyasic/rpc/base.py @@ -136,17 +136,16 @@ class BaseMinerRPCAPI: self.send_command(cmd, allow_warning=allow_warning) ) - await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True) + results = await asyncio.gather( + *[tasks[cmd] for cmd in tasks], return_exceptions=True + ) data = {} - for cmd in tasks: - try: - result = tasks[cmd].result() + for cmd, result in zip(tasks.keys(), results): + if not isinstance(result, (APIError, Exception)): if result is None or result == {}: result = {} data[cmd] = [result] - except APIError: - pass return data diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index ea1f1c7e..faa73cf7 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -141,17 +141,16 @@ class AuradineWebAPI(BaseWebAPI): self.send_command(cmd, allow_warning=allow_warning) ) - await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True) + results = await asyncio.gather( + *[tasks[cmd] for cmd in tasks], return_exceptions=True + ) data = {"multicommand": True} - for cmd in tasks: - try: - result = tasks[cmd].result() + for cmd, result in zip(tasks.keys(), results): + if not isinstance(result, (APIError, Exception)): if result is None or result == {}: result = {} data[cmd] = result - except APIError: - pass return data diff --git a/pyasic/web/espminer.py b/pyasic/web/espminer.py index 8ce1b5de..bb20d3a3 100644 --- a/pyasic/web/espminer.py +++ b/pyasic/web/espminer.py @@ -71,17 +71,16 @@ class ESPMinerWebAPI(BaseWebAPI): self.send_command(cmd, allow_warning=allow_warning) ) - await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True) + results = await asyncio.gather( + *[tasks[cmd] for cmd in tasks], return_exceptions=True + ) data = {"multicommand": True} - for cmd in tasks: - try: - result = tasks[cmd].result() + for cmd, result in zip(tasks.keys(), results): + if not isinstance(result, (APIError, Exception)): if result is None or result == {}: result = {} data[cmd] = result - except APIError: - pass return data