Coverage for src / bluetooth_sig / utils / values.py: 100%

20 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-18 11:17 +0000

1"""Value utilities for consumers of parsed Bluetooth SIG data. 

2 

3Provides helpers that avoid leaking implementation details (msgspec, 

4enum ordering) into every consumer codebase. 

5""" 

6 

7from __future__ import annotations 

8 

9import enum 

10from typing import Any 

11 

12import msgspec 

13 

14 

15def is_struct_value(obj: object) -> bool: 

16 """Check whether *obj* is a parsed struct produced by the library. 

17 

18 Use this instead of ``hasattr(obj, '__struct_fields__')`` so consumer 

19 code does not depend on the msgspec implementation detail. 

20 

21 Args: 

22 obj: Any parsed characteristic value. 

23 

24 Returns: 

25 ``True`` if *obj* is a ``msgspec.Struct`` instance. 

26 

27 """ 

28 return isinstance(obj, msgspec.Struct) 

29 

30 

31def to_primitive(value: Any) -> int | float | str | bool: # noqa: ANN401 

32 """Coerce a parsed characteristic value to a plain Python primitive. 

33 

34 Handles the full range of types the library may return 

35 (``bool``, ``IntFlag``, ``IntEnum``, ``Enum``, ``int``, ``float``, 

36 ``str``, ``datetime``, ``timedelta``, msgspec Structs, …). 

37 

38 **Order matters:** 

39 

40 * ``bool`` before ``int`` — ``bool`` is a subclass of ``int``. 

41 * ``IntFlag`` before the ``.name`` branch — bit-field values expose 

42 a ``.name`` attribute but should be stored as a plain ``int``. 

43 * ``IntEnum`` / ``Enum`` → ``.name`` string. 

44 

45 Args: 

46 value: Any value returned by ``BaseCharacteristic.parse_value()`` 

47 or extracted from a struct field. 

48 

49 Returns: 

50 A plain ``int``, ``float``, ``str``, or ``bool``. 

51 

52 """ 

53 if isinstance(value, bool): 

54 return value 

55 if isinstance(value, enum.IntFlag): 

56 return int(value) 

57 if isinstance(value, enum.Enum): 

58 return str(value.name) 

59 if isinstance(value, int): 

60 return int(value) 

61 if isinstance(value, float): 

62 return value 

63 if isinstance(value, str): 

64 return value 

65 return str(value)