Coverage for src / bluetooth_sig / gatt / descriptors / registry.py: 88%

33 statements  

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

1"""Descriptor registry and resolution.""" 

2 

3from __future__ import annotations 

4 

5import logging 

6from typing import ClassVar 

7 

8from ...types.uuid import BluetoothUUID 

9from .base import BaseDescriptor 

10 

11logger = logging.getLogger(__name__) 

12 

13 

14class DescriptorRegistry: 

15 """Registry for descriptor classes.""" 

16 

17 _registry: ClassVar[dict[str, type[BaseDescriptor]]] = {} 

18 

19 @classmethod 

20 def register(cls, descriptor_class: type[BaseDescriptor]) -> None: 

21 """Register a descriptor class.""" 

22 # Create an instance to resolve the UUID 

23 try: 

24 instance = descriptor_class() 

25 uuid_str = str(instance.uuid) 

26 cls._registry[uuid_str] = descriptor_class 

27 except (ValueError, TypeError, AttributeError): 

28 # If we can't create an instance or resolve UUID, skip registration 

29 logger.warning("Failed to register descriptor class %s", descriptor_class.__name__) 

30 

31 @classmethod 

32 def get_descriptor_class(cls, uuid: str | BluetoothUUID | int) -> type[BaseDescriptor] | None: 

33 """Get descriptor class for UUID. 

34 

35 Args: 

36 uuid: The descriptor UUID 

37 

38 Returns: 

39 Descriptor class if found, None otherwise 

40 

41 Raises: 

42 ValueError: If uuid format is invalid 

43 """ 

44 # Convert to BluetoothUUID and use full form for lookup (let ValueError propagate) 

45 uuid_obj = BluetoothUUID(uuid) 

46 full_uuid_str = uuid_obj.dashed_form 

47 return cls._registry.get(full_uuid_str) 

48 

49 @classmethod 

50 def create_descriptor(cls, uuid: str | BluetoothUUID | int) -> BaseDescriptor | None: 

51 """Create descriptor instance for UUID. 

52 

53 Args: 

54 uuid: The descriptor UUID 

55 

56 Returns: 

57 Descriptor instance if found, None otherwise 

58 

59 Raises: 

60 ValueError: If uuid format is invalid 

61 """ 

62 descriptor_class = cls.get_descriptor_class(uuid) 

63 if descriptor_class: 

64 try: 

65 return descriptor_class() 

66 except (ValueError, TypeError, AttributeError): 

67 return None 

68 return None 

69 

70 @classmethod 

71 def list_registered_descriptors(cls) -> list[str]: 

72 """List all registered descriptor UUIDs.""" 

73 return list(cls._registry.keys())