Coverage for src/bluetooth_sig/registry/utils.py: 83%

42 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-30 00:10 +0000

1"""Common utilities for registry modules.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6from typing import Any, cast 

7 

8import msgspec 

9 

10from bluetooth_sig.types.uuid import BluetoothUUID 

11 

12 

13def load_yaml_uuids(file_path: Path) -> list[dict[str, Any]]: 

14 """Load UUID entries from a YAML file. 

15 

16 Args: 

17 file_path: Path to the YAML file 

18 

19 Returns: 

20 List of UUID entry dictionaries 

21 """ 

22 if not file_path.exists(): 

23 return [] 

24 

25 with file_path.open("r", encoding="utf-8") as file_handle: 

26 data = msgspec.yaml.decode(file_handle.read()) 

27 

28 if not isinstance(data, dict): 

29 return [] 

30 

31 data_dict = cast(dict[str, Any], data) 

32 uuid_entries = data_dict.get("uuids") 

33 if not isinstance(uuid_entries, list): 

34 return [] 

35 

36 typed_entries: list[dict[str, Any]] = [] 

37 for entry in uuid_entries: 

38 if isinstance(entry, dict): 

39 typed_entries.append(cast(dict[str, Any], entry)) 

40 

41 return typed_entries 

42 

43 

44def normalize_uuid_string(uuid: str | int) -> str: 

45 """Normalize a UUID string or int to uppercase hex without 0x prefix. 

46 

47 Args: 

48 uuid: UUID as string (with or without 0x) or int 

49 

50 Returns: 

51 Normalized UUID string 

52 """ 

53 if isinstance(uuid, str): 

54 uuid = uuid.replace("0x", "").replace("0X", "") 

55 else: 

56 uuid = hex(uuid)[2:].upper() 

57 return uuid 

58 

59 

60def find_bluetooth_sig_path() -> Path | None: 

61 """Find the Bluetooth SIG assigned_numbers directory. 

62 

63 Returns: 

64 Path to the uuids directory, or None if not found 

65 """ 

66 # Try development location first (git submodule) 

67 project_root = Path(__file__).parent.parent.parent.parent 

68 base_path = project_root / "bluetooth_sig" / "assigned_numbers" / "uuids" 

69 

70 if base_path.exists(): 

71 return base_path 

72 

73 # Try installed package location 

74 pkg_root = Path(__file__).parent.parent 

75 base_path = pkg_root / "bluetooth_sig" / "assigned_numbers" / "uuids" 

76 

77 return base_path if base_path.exists() else None 

78 

79 

80def parse_bluetooth_uuid(uuid: str | int | BluetoothUUID) -> BluetoothUUID: 

81 """Parse various UUID formats into a BluetoothUUID. 

82 

83 Args: 

84 uuid: UUID as string (with or without 0x), int, or BluetoothUUID 

85 

86 Returns: 

87 BluetoothUUID instance 

88 

89 Raises: 

90 ValueError: If UUID format is invalid 

91 """ 

92 if isinstance(uuid, BluetoothUUID): 

93 return uuid 

94 if isinstance(uuid, int): 

95 uuid_str = hex(uuid)[2:].upper() 

96 return BluetoothUUID(uuid_str) 

97 uuid_str = str(uuid).replace("0x", "").replace("0X", "") 

98 return BluetoothUUID(uuid_str)