python 3.9 and 3.10 compatibility

This commit is contained in:
Gilles Boccon-Gibod
2025-08-30 12:07:08 -07:00
parent 116dc9b319
commit 91ba2f61f1
4 changed files with 33 additions and 10 deletions

View File

@@ -468,7 +468,7 @@ BT_HDP_SINK_SERVICE = UUID.from_16_bits(0x1402,
@dataclasses.dataclass
class ClassOfDevice:
# fmt: off
class MajorServiceClasses(enum.IntFlag):
class MajorServiceClasses(utils.CompatibleIntFlag):
LIMITED_DISCOVERABLE_MODE = (1 << 0)
LE_AUDIO = (1 << 1)
POSITIONING = (1 << 3)
@@ -1647,7 +1647,7 @@ class AdvertisingData:
THREE_D_INFORMATION_DATA = 0x3D
MANUFACTURER_SPECIFIC_DATA = 0xFF
class Flags(enum.IntFlag):
class Flags(utils.CompatibleIntFlag):
LE_LIMITED_DISCOVERABLE_MODE = 1 << 0
LE_GENERAL_DISCOVERABLE_MODE = 1 << 1
BR_EDR_NOT_SUPPORTED = 1 << 2
@@ -2153,7 +2153,7 @@ class LeRole(enum.IntEnum):
# -----------------------------------------------------------------------------
# Security Manager OOB Flag
# -----------------------------------------------------------------------------
class SecurityManagerOutOfBandFlag(enum.IntFlag):
class SecurityManagerOutOfBandFlag(utils.CompatibleIntFlag):
"""
See Supplement to the Bluetooth Core Specification, Part A
1.7 SECURITY MANAGER OUT OF BAND (OOB)

View File

@@ -25,7 +25,7 @@ from __future__ import annotations
import dataclasses
import math
import struct
from typing import Any, ClassVar, Literal, Optional, TypeVar, Union, overload
from typing import Any, ClassVar, Sequence
from typing_extensions import Self
@@ -70,7 +70,7 @@ class ListOfServiceUUIDs(core.DataType):
"""Base class for complete or incomplete lists of UUIDs."""
_uuid_size: ClassVar[int] = 0
uuids: list[core.UUID]
uuids: Sequence[core.UUID]
@classmethod
def from_bytes(cls, data: bytes) -> ListOfServiceUUIDs:
@@ -215,7 +215,7 @@ class Flags(int, core.DataType):
return self.to_bytes(length=bytes_length, byteorder="little")
def value_string(self) -> str:
return core.AdvertisingData.Flags(self).name or ""
return core.AdvertisingData.Flags(self).composite_name
@dataclasses.dataclass
@@ -293,6 +293,13 @@ class FixedSizeBytesDataType(bytes, core.DataType):
def __str__(self) -> str:
return core.DataType.__str__(self)
def __bytes__(self) -> bytes: # pylint: disable=E0308
# Python < 3.11 compatibility (before 3.11, the byte class does not have
# a __bytes__ method).
# Concatenate with an empty string to perform a direct conversion without
# calling bytes() explicity, which may cause an infinite recursion.
return b"" + self
class ClassOfDevice(core.ClassOfDevice, core.DataType):
"""
@@ -416,7 +423,7 @@ class SecurityManagerOutOfBandFlag(int, core.DataType):
return core.DataType.__str__(self)
def value_string(self) -> str:
return core.SecurityManagerOutOfBandFlag(self).name or ""
return core.SecurityManagerOutOfBandFlag(self).composite_name
class SecurityManagerTKValue(FixedSizeBytesDataType):
@@ -751,7 +758,7 @@ class LeSupportedFeatures(int, core.DataType):
return self.to_bytes(length=bytes_length, byteorder="little")
def value_string(self) -> str:
return hci.LeFeatureMask(self).name or ""
return hci.LeFeatureMask(self).composite_name
@dataclasses.dataclass

View File

@@ -1322,7 +1322,7 @@ class LeFeature(SpecableEnum):
MONITORING_ADVERTISERS = 64
FRAME_SPACE_UPDATE = 65
class LeFeatureMask(enum.IntFlag):
class LeFeatureMask(utils.CompatibleIntFlag):
LE_ENCRYPTION = 1 << LeFeature.LE_ENCRYPTION
CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 1 << LeFeature.CONNECTION_PARAMETERS_REQUEST_PROCEDURE
EXTENDED_REJECT_INDICATION = 1 << LeFeature.EXTENDED_REJECT_INDICATION
@@ -1463,7 +1463,7 @@ class LmpFeature(SpecableEnum):
SLOT_AVAILABILITY_MASK = 138
TRAIN_NUDGING = 139
class LmpFeatureMask(enum.IntFlag):
class LmpFeatureMask(utils.CompatibleIntFlag):
# Page 0 (Legacy LMP features)
LMP_3_SLOT_PACKETS = (1 << LmpFeature.LMP_3_SLOT_PACKETS)
LMP_5_SLOT_PACKETS = (1 << LmpFeature.LMP_5_SLOT_PACKETS)

View File

@@ -500,6 +500,22 @@ class OpenIntEnum(enum.IntEnum):
return obj
# -----------------------------------------------------------------------------
class CompatibleIntFlag(enum.IntFlag):
"""
Subclass of `enum.IntFlag` with a `composite_name` property that behaves like the
`name` property of the `enum.IntFlag` implementation for python vesions >= 3.11
"""
@property
def composite_name(self) -> str:
return '|'.join(
name
for flag in self.__class__
if self.value & flag.value and (name := flag.name) is not None
)
# -----------------------------------------------------------------------------
class ByteSerializable(Protocol):
"""