From 1066a039b151b4b3bf5d992d652c9d1a8fa9ca03 Mon Sep 17 00:00:00 2001 From: Pau Figueras Date: Thu, 30 Apr 2026 14:18:44 +0200 Subject: [PATCH 1/2] move names definition to class-level ResonCode documentation states: > See ReasonCode.names for a list of possible numeric values > along with their names and the packets to which they apply. Signed-off-by: Pau Figueras --- src/paho/mqtt/reasoncodes.py | 143 ++++++++++++++++------------------- 1 file changed, 66 insertions(+), 77 deletions(-) diff --git a/src/paho/mqtt/reasoncodes.py b/src/paho/mqtt/reasoncodes.py index 243ac96f..f3bf8944 100644 --- a/src/paho/mqtt/reasoncodes.py +++ b/src/paho/mqtt/reasoncodes.py @@ -30,7 +30,72 @@ class ReasonCode: """ - def __init__(self, packetType: int, aName: str ="Success", identifier: int =-1): + names = { + 0: { + "Success": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.PUBREL, PacketTypes.PUBCOMP, PacketTypes.UNSUBACK, PacketTypes.AUTH], + "Normal disconnection": [PacketTypes.DISCONNECT], + "Granted QoS 0": [PacketTypes.SUBACK], + }, + 1: {"Granted QoS 1": [PacketTypes.SUBACK]}, + 2: {"Granted QoS 2": [PacketTypes.SUBACK]}, + 4: {"Disconnect with will message": [PacketTypes.DISCONNECT]}, + 16: {"No matching subscribers": [PacketTypes.PUBACK, PacketTypes.PUBREC]}, + 17: {"No subscription found": [PacketTypes.UNSUBACK]}, + 24: {"Continue authentication": [PacketTypes.AUTH]}, + 25: {"Re-authenticate": [PacketTypes.AUTH]}, + 128: { + "Unspecified error": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], + }, + 129: {"Malformed packet": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 130: {"Protocol error": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 131: { + "Implementation specific error": [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, + ], + }, + 132: {"Unsupported protocol version": [PacketTypes.CONNACK]}, + 133: {"Client identifier not valid": [PacketTypes.CONNACK]}, + 134: {"Bad user name or password": [PacketTypes.CONNACK]}, + 135: { + "Not authorized": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], + }, + 136: {"Server unavailable": [PacketTypes.CONNACK]}, + 137: {"Server busy": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 138: {"Banned": [PacketTypes.CONNACK]}, + 139: {"Server shutting down": [PacketTypes.DISCONNECT]}, + 140: {"Bad authentication method": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 141: {"Keep alive timeout": [PacketTypes.DISCONNECT]}, + 142: {"Session taken over": [PacketTypes.DISCONNECT]}, + 143: {"Topic filter invalid": [PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, + 144: {"Topic name invalid": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + 145: {"Packet identifier in use": [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, + 146: {"Packet identifier not found": [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, + 147: {"Receive maximum exceeded": [PacketTypes.DISCONNECT]}, + 148: {"Topic alias invalid": [PacketTypes.DISCONNECT]}, + 149: {"Packet too large": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 150: {"Message rate too high": [PacketTypes.DISCONNECT]}, + 151: { + "Quota exceeded": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.DISCONNECT], + }, + 152: {"Administrative action": [PacketTypes.DISCONNECT]}, + 153: {"Payload format invalid": [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + 154: {"Retain not supported": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 155: {"QoS not supported": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 156: {"Use another server": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 157: {"Server moved": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 158: {"Shared subscription not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + 159: {"Connection rate exceeded": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 160: {"Maximum connect time": [PacketTypes.DISCONNECT]}, + 161: {"Subscription identifiers not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + 162: {"Wildcard subscription not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + } + + def __init__(self, packetType: int, aName: str = "Success", identifier: int = -1): """ packetType: the type of the packet, such as PacketTypes.CONNECT that this reason code will be used with. Some reason codes have different @@ -44,82 +109,6 @@ def __init__(self, packetType: int, aName: str ="Success", identifier: int =-1): """ self.packetType = packetType - self.names = { - 0: {"Success": [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.PUBREL, PacketTypes.PUBCOMP, - PacketTypes.UNSUBACK, PacketTypes.AUTH], - "Normal disconnection": [PacketTypes.DISCONNECT], - "Granted QoS 0": [PacketTypes.SUBACK]}, - 1: {"Granted QoS 1": [PacketTypes.SUBACK]}, - 2: {"Granted QoS 2": [PacketTypes.SUBACK]}, - 4: {"Disconnect with will message": [PacketTypes.DISCONNECT]}, - 16: {"No matching subscribers": - [PacketTypes.PUBACK, PacketTypes.PUBREC]}, - 17: {"No subscription found": [PacketTypes.UNSUBACK]}, - 24: {"Continue authentication": [PacketTypes.AUTH]}, - 25: {"Re-authenticate": [PacketTypes.AUTH]}, - 128: {"Unspecified error": [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, - PacketTypes.DISCONNECT], }, - 129: {"Malformed packet": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 130: {"Protocol error": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 131: {"Implementation specific error": [PacketTypes.CONNACK, - PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, - PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], }, - 132: {"Unsupported protocol version": [PacketTypes.CONNACK]}, - 133: {"Client identifier not valid": [PacketTypes.CONNACK]}, - 134: {"Bad user name or password": [PacketTypes.CONNACK]}, - 135: {"Not authorized": [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, - PacketTypes.DISCONNECT], }, - 136: {"Server unavailable": [PacketTypes.CONNACK]}, - 137: {"Server busy": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 138: {"Banned": [PacketTypes.CONNACK]}, - 139: {"Server shutting down": [PacketTypes.DISCONNECT]}, - 140: {"Bad authentication method": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 141: {"Keep alive timeout": [PacketTypes.DISCONNECT]}, - 142: {"Session taken over": [PacketTypes.DISCONNECT]}, - 143: {"Topic filter invalid": - [PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, - 144: {"Topic name invalid": - [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, - 145: {"Packet identifier in use": - [PacketTypes.PUBACK, PacketTypes.PUBREC, - PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, - 146: {"Packet identifier not found": - [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, - 147: {"Receive maximum exceeded": [PacketTypes.DISCONNECT]}, - 148: {"Topic alias invalid": [PacketTypes.DISCONNECT]}, - 149: {"Packet too large": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 150: {"Message rate too high": [PacketTypes.DISCONNECT]}, - 151: {"Quota exceeded": [PacketTypes.CONNACK, PacketTypes.PUBACK, - PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.DISCONNECT], }, - 152: {"Administrative action": [PacketTypes.DISCONNECT]}, - 153: {"Payload format invalid": - [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, - 154: {"Retain not supported": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 155: {"QoS not supported": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 156: {"Use another server": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 157: {"Server moved": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 158: {"Shared subscription not supported": - [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, - 159: {"Connection rate exceeded": - [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 160: {"Maximum connect time": - [PacketTypes.DISCONNECT]}, - 161: {"Subscription identifiers not supported": - [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, - 162: {"Wildcard subscription not supported": - [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, - } if identifier == -1: if packetType == PacketTypes.DISCONNECT and aName == "Success": aName = "Normal disconnection" From 7d615d4e9563ecb2633ac9ea99b952bd8f72bcb0 Mon Sep 17 00:00:00 2001 From: Pau Figueras Date: Mon, 15 Jun 2026 10:21:45 +0200 Subject: [PATCH 2/2] refactor: class attribute and instance attribute Make the class attribute immutable using a `MappingProxyType`, as suggested, and mask it a mutable instance class. --- src/paho/mqtt/reasoncodes.py | 146 +++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/src/paho/mqtt/reasoncodes.py b/src/paho/mqtt/reasoncodes.py index f3bf8944..f4aae792 100644 --- a/src/paho/mqtt/reasoncodes.py +++ b/src/paho/mqtt/reasoncodes.py @@ -16,6 +16,7 @@ import functools import warnings +from types import MappingProxyType from typing import Any from .packettypes import PacketTypes @@ -30,70 +31,80 @@ class ReasonCode: """ - names = { - 0: { - "Success": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.PUBREL, PacketTypes.PUBCOMP, PacketTypes.UNSUBACK, PacketTypes.AUTH], - "Normal disconnection": [PacketTypes.DISCONNECT], - "Granted QoS 0": [PacketTypes.SUBACK], - }, - 1: {"Granted QoS 1": [PacketTypes.SUBACK]}, - 2: {"Granted QoS 2": [PacketTypes.SUBACK]}, - 4: {"Disconnect with will message": [PacketTypes.DISCONNECT]}, - 16: {"No matching subscribers": [PacketTypes.PUBACK, PacketTypes.PUBREC]}, - 17: {"No subscription found": [PacketTypes.UNSUBACK]}, - 24: {"Continue authentication": [PacketTypes.AUTH]}, - 25: {"Re-authenticate": [PacketTypes.AUTH]}, - 128: { - "Unspecified error": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], - }, - 129: {"Malformed packet": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 130: {"Protocol error": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 131: { - "Implementation specific error": [ - PacketTypes.CONNACK, - PacketTypes.PUBACK, - PacketTypes.PUBREC, - PacketTypes.SUBACK, - PacketTypes.UNSUBACK, - PacketTypes.DISCONNECT, - ], - }, - 132: {"Unsupported protocol version": [PacketTypes.CONNACK]}, - 133: {"Client identifier not valid": [PacketTypes.CONNACK]}, - 134: {"Bad user name or password": [PacketTypes.CONNACK]}, - 135: { - "Not authorized": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], - }, - 136: {"Server unavailable": [PacketTypes.CONNACK]}, - 137: {"Server busy": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 138: {"Banned": [PacketTypes.CONNACK]}, - 139: {"Server shutting down": [PacketTypes.DISCONNECT]}, - 140: {"Bad authentication method": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 141: {"Keep alive timeout": [PacketTypes.DISCONNECT]}, - 142: {"Session taken over": [PacketTypes.DISCONNECT]}, - 143: {"Topic filter invalid": [PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, - 144: {"Topic name invalid": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, - 145: {"Packet identifier in use": [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, - 146: {"Packet identifier not found": [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, - 147: {"Receive maximum exceeded": [PacketTypes.DISCONNECT]}, - 148: {"Topic alias invalid": [PacketTypes.DISCONNECT]}, - 149: {"Packet too large": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 150: {"Message rate too high": [PacketTypes.DISCONNECT]}, - 151: { - "Quota exceeded": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.DISCONNECT], - }, - 152: {"Administrative action": [PacketTypes.DISCONNECT]}, - 153: {"Payload format invalid": [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, - 154: {"Retain not supported": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 155: {"QoS not supported": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 156: {"Use another server": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 157: {"Server moved": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 158: {"Shared subscription not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, - 159: {"Connection rate exceeded": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, - 160: {"Maximum connect time": [PacketTypes.DISCONNECT]}, - 161: {"Subscription identifiers not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, - 162: {"Wildcard subscription not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, - } + names = MappingProxyType( + { + 0: { + "Success": [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.PUBREL, + PacketTypes.PUBCOMP, + PacketTypes.UNSUBACK, + PacketTypes.AUTH, + ], + "Normal disconnection": [PacketTypes.DISCONNECT], + "Granted QoS 0": [PacketTypes.SUBACK], + }, + 1: {"Granted QoS 1": [PacketTypes.SUBACK]}, + 2: {"Granted QoS 2": [PacketTypes.SUBACK]}, + 4: {"Disconnect with will message": [PacketTypes.DISCONNECT]}, + 16: {"No matching subscribers": [PacketTypes.PUBACK, PacketTypes.PUBREC]}, + 17: {"No subscription found": [PacketTypes.UNSUBACK]}, + 24: {"Continue authentication": [PacketTypes.AUTH]}, + 25: {"Re-authenticate": [PacketTypes.AUTH]}, + 128: { + "Unspecified error": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], + }, + 129: {"Malformed packet": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 130: {"Protocol error": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 131: { + "Implementation specific error": [ + PacketTypes.CONNACK, + PacketTypes.PUBACK, + PacketTypes.PUBREC, + PacketTypes.SUBACK, + PacketTypes.UNSUBACK, + PacketTypes.DISCONNECT, + ], + }, + 132: {"Unsupported protocol version": [PacketTypes.CONNACK]}, + 133: {"Client identifier not valid": [PacketTypes.CONNACK]}, + 134: {"Bad user name or password": [PacketTypes.CONNACK]}, + 135: { + "Not authorized": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], + }, + 136: {"Server unavailable": [PacketTypes.CONNACK]}, + 137: {"Server busy": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 138: {"Banned": [PacketTypes.CONNACK]}, + 139: {"Server shutting down": [PacketTypes.DISCONNECT]}, + 140: {"Bad authentication method": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 141: {"Keep alive timeout": [PacketTypes.DISCONNECT]}, + 142: {"Session taken over": [PacketTypes.DISCONNECT]}, + 143: {"Topic filter invalid": [PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, + 144: {"Topic name invalid": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + 145: {"Packet identifier in use": [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, + 146: {"Packet identifier not found": [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, + 147: {"Receive maximum exceeded": [PacketTypes.DISCONNECT]}, + 148: {"Topic alias invalid": [PacketTypes.DISCONNECT]}, + 149: {"Packet too large": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 150: {"Message rate too high": [PacketTypes.DISCONNECT]}, + 151: { + "Quota exceeded": [PacketTypes.CONNACK, PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.DISCONNECT], + }, + 152: {"Administrative action": [PacketTypes.DISCONNECT]}, + 153: {"Payload format invalid": [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, + 154: {"Retain not supported": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 155: {"QoS not supported": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 156: {"Use another server": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 157: {"Server moved": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 158: {"Shared subscription not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + 159: {"Connection rate exceeded": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, + 160: {"Maximum connect time": [PacketTypes.DISCONNECT]}, + 161: {"Subscription identifiers not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + 162: {"Wildcard subscription not supported": [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, + } + ) def __init__(self, packetType: int, aName: str = "Success", identifier: int = -1): """ @@ -108,6 +119,7 @@ def __init__(self, packetType: int, aName: str = "Success", identifier: int = -1 """ + self.names = dict(type(self).names) self.packetType = packetType if identifier == -1: if packetType == PacketTypes.DISCONNECT and aName == "Success": @@ -156,8 +168,7 @@ def unpack(self, buffer): return 1 def getName(self): - """Returns the reason code name corresponding to the numeric value which is set. - """ + """Returns the reason code name corresponding to the numeric value which is set.""" return self.__getName__(self.packetType, self.value) def __eq__(self, other): @@ -205,7 +216,8 @@ def __instancecheck__(self, other: Any) -> bool: class ReasonCodes(ReasonCode, metaclass=_CompatibilityIsInstance): def __init__(self, *args, **kwargs): - warnings.warn("ReasonCodes is deprecated, use ReasonCode (singular) instead", + warnings.warn( + "ReasonCodes is deprecated, use ReasonCode (singular) instead", category=DeprecationWarning, stacklevel=2, )