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
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
1"""Common utilities for registry modules."""
3from __future__ import annotations
5from pathlib import Path
6from typing import Any, cast
8import msgspec
10from bluetooth_sig.types.uuid import BluetoothUUID
13def load_yaml_uuids(file_path: Path) -> list[dict[str, Any]]:
14 """Load UUID entries from a YAML file.
16 Args:
17 file_path: Path to the YAML file
19 Returns:
20 List of UUID entry dictionaries
21 """
22 if not file_path.exists():
23 return []
25 with file_path.open("r", encoding="utf-8") as file_handle:
26 data = msgspec.yaml.decode(file_handle.read())
28 if not isinstance(data, dict):
29 return []
31 data_dict = cast(dict[str, Any], data)
32 uuid_entries = data_dict.get("uuids")
33 if not isinstance(uuid_entries, list):
34 return []
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))
41 return typed_entries
44def normalize_uuid_string(uuid: str | int) -> str:
45 """Normalize a UUID string or int to uppercase hex without 0x prefix.
47 Args:
48 uuid: UUID as string (with or without 0x) or int
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
60def find_bluetooth_sig_path() -> Path | None:
61 """Find the Bluetooth SIG assigned_numbers directory.
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"
70 if base_path.exists():
71 return base_path
73 # Try installed package location
74 pkg_root = Path(__file__).parent.parent
75 base_path = pkg_root / "bluetooth_sig" / "assigned_numbers" / "uuids"
77 return base_path if base_path.exists() else None
80def parse_bluetooth_uuid(uuid: str | int | BluetoothUUID) -> BluetoothUUID:
81 """Parse various UUID formats into a BluetoothUUID.
83 Args:
84 uuid: UUID as string (with or without 0x), int, or BluetoothUUID
86 Returns:
87 BluetoothUUID instance
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)