From aed7db21b3771ad97d685d5fd34644341f42879f Mon Sep 17 00:00:00 2001 From: Matthew Wolter Date: Sun, 12 Apr 2026 04:52:07 -0700 Subject: [PATCH] =?UTF-8?q?G7:=20M03+M05+M07=20=E2=80=94=20cleanup:=20Type?= =?UTF-8?q?Error=20guard,=20dead=20code=20removal,=20None=20normalization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why: Three minor cleanup fixes. M03 adds an else branch in set_flood_scope so unsupported scope types raise TypeError instead of UnboundLocalError. M05 removes the dead `out_path_len >> 6` shift in update_contact (high bits always zero due to reader masking) and initializes path_hash_mode=0 explicitly. M07 normalizes three `return None` paths in get_contacts to return Event(EventType.ERROR, ...) so callers can rely on the return type always being Event. Refs: Forensics report findings M03, M05, M07 --- src/meshcore/commands/contact.py | 18 ++++++++++++------ src/meshcore/commands/messaging.py | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/meshcore/commands/contact.py b/src/meshcore/commands/contact.py index ac723ea..436a066 100644 --- a/src/meshcore/commands/contact.py +++ b/src/meshcore/commands/contact.py @@ -43,13 +43,17 @@ class ContactCommands(CommandHandlerBase): logger.debug("Timeout while getting contacts") for future in pending: # cancel all futures future.cancel() - return None + return Event(EventType.ERROR, {"reason": "timeout waiting for contacts"}) for future in done: event = await future - if event is None or event.type != EventType.NEXT_CONTACT: - for future in pending: - future.cancel() + if event is None: + for f in pending: + f.cancel() + return Event(EventType.ERROR, {"reason": "no event received during contacts retrieval"}) + if event.type != EventType.NEXT_CONTACT: + for f in pending: + f.cancel() return event futures = [] @@ -64,7 +68,7 @@ class ContactCommands(CommandHandlerBase): except asyncio.TimeoutError: logger.debug(f"Timeout receiving contacts") - return None + return Event(EventType.ERROR, {"reason": "asyncio timeout receiving contacts"}) except Exception as e: logger.debug(f"Command error: {e}") return Event(EventType.ERROR, {"error": str(e)}) @@ -116,7 +120,9 @@ class ContactCommands(CommandHandlerBase): path_hash_mode = int(path.split(":")[1]) path = path.split(":")[0].replace(":","") else: # use device one by default - path_hash_mode = contact["out_path_len"] >> 6 # would fallback to previous val + # out_path_len is pre-masked (& 0x3F) in reader.py, so high bits are always 0; + # the actual path_hash_mode is fetched from the device query below. + path_hash_mode = 0 res = await self.send_device_query() if not res is None and res.type != EventType.ERROR: if "path_hash_mode" in res.payload: diff --git a/src/meshcore/commands/messaging.py b/src/meshcore/commands/messaging.py index b266ae0..0c15479 100644 --- a/src/meshcore/commands/messaging.py +++ b/src/meshcore/commands/messaging.py @@ -313,6 +313,8 @@ class MessagingCommands(CommandHandlerBase): elif isinstance (scope, bytes): # scope has been sent directly as byte logger.debug(f"Directly setting scope to {scope}") scope_key = scope + else: + raise TypeError(f"set_flood_scope: unsupported scope type {type(scope).__name__}") logger.debug(f"Setting scope to {scope_key.hex()}")