uuid: add separator to to_hex_str + type hints

This commit is contained in:
uael
2023-04-20 17:33:28 +00:00
parent 29bd693bab
commit fdee5ecf70
3 changed files with 43 additions and 36 deletions

View File

@@ -152,7 +152,12 @@ class UUID:
BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1] # little-endian BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1] # little-endian
UUIDS: List[UUID] = [] # Registry of all instances created UUIDS: List[UUID] = [] # Registry of all instances created
def __init__(self, uuid_str_or_int, name=None): uuid_bytes: bytes
name: Optional[str]
def __init__(
self, uuid_str_or_int: Union[str, int], name: Optional[str] = None
) -> None:
if isinstance(uuid_str_or_int, int): if isinstance(uuid_str_or_int, int):
self.uuid_bytes = struct.pack('<H', uuid_str_or_int) self.uuid_bytes = struct.pack('<H', uuid_str_or_int)
else: else:
@@ -172,7 +177,7 @@ class UUID:
self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str))) self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str)))
self.name = name self.name = name
def register(self): def register(self) -> UUID:
# Register this object in the class registry, and update the entry's name if # Register this object in the class registry, and update the entry's name if
# it wasn't set already # it wasn't set already
for uuid in self.UUIDS: for uuid in self.UUIDS:
@@ -196,22 +201,22 @@ class UUID:
raise ValueError('only 2, 4 and 16 bytes are allowed') raise ValueError('only 2, 4 and 16 bytes are allowed')
@classmethod @classmethod
def from_16_bits(cls, uuid_16, name=None): def from_16_bits(cls, uuid_16: int, name: Optional[str] = None) -> UUID:
return cls.from_bytes(struct.pack('<H', uuid_16), name) return cls.from_bytes(struct.pack('<H', uuid_16), name)
@classmethod @classmethod
def from_32_bits(cls, uuid_32, name=None): def from_32_bits(cls, uuid_32: int, name: Optional[str] = None) -> UUID:
return cls.from_bytes(struct.pack('<I', uuid_32), name) return cls.from_bytes(struct.pack('<I', uuid_32), name)
@classmethod @classmethod
def parse_uuid(cls, uuid_as_bytes, offset): def parse_uuid(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]:
return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:]) return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:])
@classmethod @classmethod
def parse_uuid_2(cls, uuid_as_bytes, offset): def parse_uuid_2(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]:
return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2]) return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2])
def to_bytes(self, force_128=False): def to_bytes(self, force_128: bool = False) -> bytes:
''' '''
Serialize UUID in little-endian byte-order Serialize UUID in little-endian byte-order
''' '''
@@ -227,7 +232,7 @@ class UUID:
else: else:
assert False, "unreachable" assert False, "unreachable"
def to_pdu_bytes(self): def to_pdu_bytes(self) -> bytes:
''' '''
Convert to bytes for use in an ATT PDU. Convert to bytes for use in an ATT PDU.
According to Vol 3, Part F - 3.2.1 Attribute Type: According to Vol 3, Part F - 3.2.1 Attribute Type:
@@ -236,11 +241,11 @@ class UUID:
''' '''
return self.to_bytes(force_128=(len(self.uuid_bytes) == 4)) return self.to_bytes(force_128=(len(self.uuid_bytes) == 4))
def to_hex_str(self) -> str: def to_hex_str(self, separator: str = '') -> str:
if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4: if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4:
return bytes(reversed(self.uuid_bytes)).hex().upper() return bytes(reversed(self.uuid_bytes)).hex().upper()
return ''.join( return separator.join(
[ [
bytes(reversed(self.uuid_bytes[12:16])).hex(), bytes(reversed(self.uuid_bytes[12:16])).hex(),
bytes(reversed(self.uuid_bytes[10:12])).hex(), bytes(reversed(self.uuid_bytes[10:12])).hex(),
@@ -250,10 +255,10 @@ class UUID:
] ]
).upper() ).upper()
def __bytes__(self): def __bytes__(self) -> bytes:
return self.to_bytes() return self.to_bytes()
def __eq__(self, other): def __eq__(self, other: object) -> bool:
if isinstance(other, UUID): if isinstance(other, UUID):
return self.to_bytes(force_128=True) == other.to_bytes(force_128=True) return self.to_bytes(force_128=True) == other.to_bytes(force_128=True)
@@ -262,35 +267,19 @@ class UUID:
return False return False
def __hash__(self): def __hash__(self) -> int:
return hash(self.uuid_bytes) return hash(self.uuid_bytes)
def __str__(self): def __str__(self) -> str:
result = self.to_hex_str(separator='-')
if len(self.uuid_bytes) == 2: if len(self.uuid_bytes) == 2:
uuid = struct.unpack('<H', self.uuid_bytes)[0] result = 'UUID-16:' + result
result = f'UUID-16:{uuid:04X}'
elif len(self.uuid_bytes) == 4: elif len(self.uuid_bytes) == 4:
uuid = struct.unpack('<I', self.uuid_bytes)[0] result = 'UUID-32:' + result
result = f'UUID-32:{uuid:08X}'
else:
result = '-'.join(
[
bytes(reversed(self.uuid_bytes[12:16])).hex(),
bytes(reversed(self.uuid_bytes[10:12])).hex(),
bytes(reversed(self.uuid_bytes[8:10])).hex(),
bytes(reversed(self.uuid_bytes[6:8])).hex(),
bytes(reversed(self.uuid_bytes[0:6])).hex(),
]
).upper()
if self.name is not None: if self.name is not None:
return result + f' ({self.name})' result += f' ({self.name})'
return result return result
def __repr__(self):
return str(self)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Common UUID constants # Common UUID constants

View File

@@ -247,7 +247,7 @@ class TemplateService(Service):
to expose their UUID as a class property to expose their UUID as a class property
''' '''
UUID = None UUID: Optional[UUID] = None
def __init__(self, characteristics, primary=True): def __init__(self, characteristics, primary=True):
super().__init__(self.UUID, characteristics, primary) super().__init__(self.UUID, characteristics, primary)

View File

@@ -15,7 +15,7 @@
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Imports # Imports
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
from bumble.core import AdvertisingData, get_dict_key_by_value from bumble.core import AdvertisingData, UUID, get_dict_key_by_value
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def test_ad_data(): def test_ad_data():
@@ -49,6 +49,24 @@ def test_get_dict_key_by_value():
assert get_dict_key_by_value(dictionary, 3) is None assert get_dict_key_by_value(dictionary, 3) is None
# -----------------------------------------------------------------------------
def test_uuid_to_hex_str() -> None:
assert UUID("b5ea").to_hex_str() == "B5EA"
assert UUID("df5ce654").to_hex_str() == "DF5CE654"
assert (
UUID("df5ce654-e059-11ed-b5ea-0242ac120002").to_hex_str()
== "DF5CE654E05911EDB5EA0242AC120002"
)
assert UUID("b5ea").to_hex_str('-') == "B5EA"
assert UUID("df5ce654").to_hex_str('-') == "DF5CE654"
assert (
UUID("df5ce654-e059-11ed-b5ea-0242ac120002").to_hex_str('-')
== "DF5CE654-E059-11ED-B5EA-0242AC120002"
)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
if __name__ == '__main__': if __name__ == '__main__':
test_ad_data() test_ad_data()
test_get_dict_key_by_value()
test_uuid_to_hex_str()