Coverage for src/bluetooth_sig/gatt/uuid_registry.py: 90%
341 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-28 01:26 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-28 01:26 +0000
1"""UUID registry loading from Bluetooth SIG YAML files."""
3from __future__ import annotations
5import logging
6import threading
8from bluetooth_sig.registry.gss import GssRegistry
9from bluetooth_sig.registry.uuids.units import UnitsRegistry
10from bluetooth_sig.types import CharacteristicInfo, ServiceInfo
11from bluetooth_sig.types.base_types import SIGInfo
12from bluetooth_sig.types.registry.descriptor_types import DescriptorInfo
13from bluetooth_sig.types.registry.gss_characteristic import GssCharacteristicSpec
14from bluetooth_sig.types.uuid import BluetoothUUID
16from ..registry.utils import find_bluetooth_sig_path, load_yaml_uuids, normalize_uuid_string
17from ..types.registry import CharacteristicSpec, FieldInfo, UnitMetadata
19__all__ = [
20 "UuidRegistry",
21 "get_uuid_registry",
22]
24logger = logging.getLogger(__name__)
27class UuidRegistry: # pylint: disable=too-many-instance-attributes
28 """Registry for Bluetooth SIG UUIDs with canonical storage + alias indices.
30 This registry stores a number of internal caches and mappings which
31 legitimately exceed the default pylint instance attribute limit. The
32 complexity is intentional and centralised; an inline disable is used to
33 avoid noisy global configuration changes.
34 """
36 _instance: UuidRegistry | None = None
37 _class_lock = threading.RLock()
39 @classmethod
40 def get_instance(cls) -> UuidRegistry:
41 """Return the process-wide UuidRegistry singleton instance."""
42 if cls._instance is None:
43 with cls._class_lock:
44 if cls._instance is None:
45 cls._instance = cls()
46 return cls._instance
48 def __init__(self) -> None:
49 """Initialize the UUID registry."""
50 self._lock = threading.RLock()
51 self._loaded = False
52 self._load_error: Exception | None = None
54 # Canonical storage: normalized_uuid -> domain types (single source of truth)
55 self._services: dict[str, ServiceInfo] = {}
56 self._characteristics: dict[str, CharacteristicInfo] = {}
57 self._descriptors: dict[str, DescriptorInfo] = {}
59 # Lightweight alias indices: alias -> normalized_uuid
60 self._service_aliases: dict[str, str] = {}
61 self._characteristic_aliases: dict[str, str] = {}
62 self._descriptor_aliases: dict[str, str] = {}
64 # Preserve SIG entries overridden at runtime so we can restore them
65 self._service_overrides: dict[str, ServiceInfo] = {}
66 self._characteristic_overrides: dict[str, CharacteristicInfo] = {}
67 self._descriptor_overrides: dict[str, DescriptorInfo] = {}
69 # Track runtime-registered UUIDs (replaces origin field checks)
70 self._runtime_uuids: set[str] = set()
72 self._gss_registry: GssRegistry | None = None
74 def _ensure_loaded(self) -> None:
75 """Ensure the registry has loaded its YAML data exactly once."""
76 with self._lock:
77 if self._loaded:
78 return
79 if self._load_error is not None:
80 raise RuntimeError("UUID registry failed to load SIG data") from self._load_error
81 try:
82 self._load_uuids()
83 except Exception as exc: # pylint: disable=broad-exception-caught
84 self._load_error = exc
85 raise RuntimeError("UUID registry failed to load SIG data") from exc
86 self._loaded = True
88 def ensure_loaded(self) -> None:
89 """Public API to eagerly load UUID registry data."""
90 self._ensure_loaded()
92 def _store_service(self, info: ServiceInfo) -> None:
93 """Store service info with canonical storage + aliases."""
94 canonical_key = info.uuid.normalized
96 # Store once in canonical location
97 self._services[canonical_key] = info
99 # Create lightweight alias mappings (normalized to lowercase)
100 aliases = self._generate_aliases(info)
101 for alias in aliases:
102 self._service_aliases[alias.lower()] = canonical_key
104 def _store_characteristic(self, info: CharacteristicInfo) -> None:
105 """Store characteristic info with canonical storage + aliases."""
106 canonical_key = info.uuid.normalized
108 # Store once in canonical location
109 self._characteristics[canonical_key] = info
111 # Create lightweight alias mappings (normalized to lowercase)
112 aliases = self._generate_aliases(info)
113 for alias in aliases:
114 self._characteristic_aliases[alias.lower()] = canonical_key
116 def _store_descriptor(self, info: DescriptorInfo) -> None:
117 """Store descriptor info with canonical storage + aliases."""
118 canonical_key = info.uuid.normalized
120 # Store once in canonical location
121 self._descriptors[canonical_key] = info
123 # Create lightweight alias mappings (normalized to lowercase)
124 aliases = self._generate_aliases(info)
125 for alias in aliases:
126 self._descriptor_aliases[alias.lower()] = canonical_key
128 def _generate_aliases(self, info: SIGInfo) -> set[str]:
129 """Generate name/ID-based alias keys for domain info types (UUID variations handled by BluetoothUUID)."""
130 aliases: set[str] = {
131 info.name.lower(),
132 }
134 if info.id:
135 aliases.add(info.id)
137 if info.id and "service" in info.id:
138 service_name = info.id.replace("org.bluetooth.service.", "")
139 if service_name.endswith("_service"):
140 service_name = service_name[:-8] # Remove _service
141 service_name = service_name.replace("_", " ").title()
142 aliases.add(service_name)
143 # Also add "Service" suffix if not present
144 if not service_name.endswith(" Service"):
145 aliases.add(service_name + " Service")
146 elif info.id and "characteristic" in info.id:
147 char_name = info.id.replace("org.bluetooth.characteristic.", "")
148 char_name = char_name.replace("_", " ").title()
149 aliases.add(char_name)
151 # Add space-separated words from name
152 name_words = info.name.replace("_", " ").replace("-", " ")
153 if " " in name_words:
154 aliases.add(name_words.title())
155 aliases.add(name_words.lower())
157 # Remove empty strings, None values, and the canonical key itself
158 canonical_key = info.uuid.normalized
159 return {alias for alias in aliases if alias and alias.strip() and alias != canonical_key}
161 def _load_uuids(self) -> None: # pylint: disable=too-many-branches
162 """Load all UUIDs from YAML files."""
163 base_path = find_bluetooth_sig_path()
164 if not base_path:
165 return
167 # Load service UUIDs
168 service_yaml = base_path / "service_uuids.yaml"
169 if service_yaml.exists():
170 for uuid_info in load_yaml_uuids(service_yaml):
171 uuid = normalize_uuid_string(uuid_info["uuid"])
173 bt_uuid = BluetoothUUID(uuid)
174 info = ServiceInfo(
175 uuid=bt_uuid,
176 name=uuid_info["name"],
177 id=uuid_info.get("id", ""),
178 )
179 self._store_service(info)
181 # Load characteristic UUIDs
182 characteristic_yaml = base_path / "characteristic_uuids.yaml"
183 if characteristic_yaml.exists():
184 for uuid_info in load_yaml_uuids(characteristic_yaml):
185 uuid = normalize_uuid_string(uuid_info["uuid"])
187 bt_uuid = BluetoothUUID(uuid)
188 char_info = CharacteristicInfo(
189 uuid=bt_uuid,
190 name=uuid_info["name"],
191 id=uuid_info.get("id", ""),
192 unit="", # Will be set from unit mappings if available
193 )
194 self._store_characteristic(char_info)
196 # Load descriptor UUIDs
197 descriptor_yaml = base_path / "descriptors.yaml"
198 if descriptor_yaml.exists():
199 for uuid_info in load_yaml_uuids(descriptor_yaml):
200 uuid = normalize_uuid_string(uuid_info["uuid"])
202 bt_uuid = BluetoothUUID(uuid)
203 desc_info = DescriptorInfo(
204 uuid=bt_uuid,
205 name=uuid_info["name"],
206 id=uuid_info.get("id", ""),
207 )
208 self._store_descriptor(desc_info)
210 # Load GSS specifications
211 self._gss_registry = GssRegistry.get_instance()
212 self._load_gss_characteristic_info()
214 # TODO: Remove when bluetooth_sig submodule includes these UUIDs.
215 # Analog (0x2A58) and Digital (0x2A56) are present in service specs
216 # (Automation IO) but absent from the assigned-number YAML files.
217 _yaml_absent: list[tuple[BluetoothUUID, str, str]] = [
218 (BluetoothUUID(0x2A56), "Digital", "org.bluetooth.characteristic.digital"),
219 (BluetoothUUID(0x2A58), "Analog", "org.bluetooth.characteristic.analog"),
220 ]
221 for _bt_uuid, _name, _identifier in _yaml_absent:
222 if _bt_uuid.normalized not in self._characteristics:
223 self._store_characteristic(
224 CharacteristicInfo(
225 uuid=_bt_uuid,
226 name=_name,
227 id=_identifier,
228 unit="",
229 python_type=None,
230 )
231 )
233 def _load_gss_characteristic_info(self) -> None:
234 """Load GSS specs and update characteristics with extracted info."""
235 if self._gss_registry is None:
236 return
238 all_specs = self._gss_registry.get_all_specs()
240 # Group by identifier to avoid duplicate processing
241 processed_ids: set[str] = set()
242 for spec in all_specs.values():
243 if spec.identifier in processed_ids:
244 continue
245 processed_ids.add(spec.identifier)
247 # Extract unit and value_type from structure
248 char_data = {
249 "structure": [
250 {
251 "field": f.field,
252 "type": f.type,
253 "size": f.size,
254 "description": f.description,
255 }
256 for f in spec.structure
257 ]
258 }
259 unit, value_type = self._gss_registry.extract_info_from_gss(char_data)
261 # Multi-field structs have per-field units; no single representative
262 # unit, and the first field's scalar wire type (e.g. int) is not
263 # representative of the struct-valued characteristic.
264 if len(spec.structure) > 1:
265 unit = None
266 value_type = None
268 if unit or value_type:
269 self._update_characteristic_with_gss_info(spec.name, spec.identifier, unit, value_type)
271 def _update_characteristic_with_gss_info(
272 self, char_name: str, char_id: str, unit: str | None, python_type: type | None
273 ) -> None:
274 """Update existing characteristic with GSS info."""
275 with self._lock:
276 # Find the canonical entry by checking aliases (normalized to lowercase)
277 canonical_uuid = None
278 for search_key in [char_name, char_id]:
279 canonical_uuid = self._characteristic_aliases.get(search_key.lower())
280 if canonical_uuid:
281 break
283 if not canonical_uuid or canonical_uuid not in self._characteristics:
284 return
286 # Get existing info and create updated version
287 existing_info = self._characteristics[canonical_uuid]
289 # Use provided python_type or keep existing
290 new_python_type = python_type if python_type is not None else existing_info.python_type
292 # Create updated CharacteristicInfo (immutable, so create new instance)
293 updated_info = CharacteristicInfo(
294 uuid=existing_info.uuid,
295 name=existing_info.name,
296 id=existing_info.id,
297 unit=unit or existing_info.unit,
298 python_type=new_python_type,
299 )
301 # Update canonical store (aliases remain the same since UUID/name/id unchanged)
302 self._characteristics[canonical_uuid] = updated_info
304 def _convert_bluetooth_unit_to_readable(self, unit_spec: str) -> str:
305 """Convert Bluetooth SIG unit specification to human-readable symbol.
307 Args:
308 unit_spec: Unit specification (e.g., "thermodynamic_temperature.degree_celsius")
310 Returns:
311 Human-readable symbol (e.g., "°C"), or unit_spec if no mapping found
312 """
313 unit_spec = unit_spec.rstrip(".").lower()
314 unit_id = f"org.bluetooth.unit.{unit_spec}"
316 units_registry = UnitsRegistry.get_instance()
317 unit_info = units_registry.get_info(unit_id)
318 if unit_info and unit_info.symbol:
319 return unit_info.symbol
321 return unit_spec
323 def register_characteristic( # pylint: disable=too-many-arguments,too-many-positional-arguments
324 self,
325 uuid: BluetoothUUID,
326 name: str,
327 identifier: str | None = None,
328 unit: str | None = None,
329 python_type: type | str | None = None,
330 override: bool = False,
331 ) -> None:
332 """Register a custom characteristic at runtime.
334 Args:
335 uuid: The Bluetooth UUID for the characteristic
336 name: Human-readable name
337 identifier: Optional identifier (auto-generated if not provided)
338 unit: Optional unit of measurement
339 python_type: Optional Python type for the value
340 override: If True, allow overriding existing entries
341 """
342 self._ensure_loaded()
343 with self._lock:
344 canonical_key = uuid.normalized
346 # Check for conflicts with existing entries
347 if canonical_key in self._characteristics:
348 # Check if it's a SIG characteristic (not in runtime set)
349 if canonical_key not in self._runtime_uuids:
350 if not override:
351 raise ValueError(
352 f"UUID {uuid} conflicts with existing SIG "
353 "characteristic entry. Use override=True to replace."
354 )
355 # Preserve original SIG entry for restoration
356 self._characteristic_overrides.setdefault(canonical_key, self._characteristics[canonical_key])
357 elif not override:
358 # Runtime entry already exists
359 raise ValueError(
360 f"UUID {uuid} already registered as runtime characteristic. Use override=True to replace."
361 )
363 info = CharacteristicInfo(
364 uuid=uuid,
365 name=name,
366 id=identifier or f"runtime.characteristic.{name.lower().replace(' ', '_')}",
367 unit=unit or "",
368 python_type=python_type,
369 )
371 # Track as runtime-registered UUID
372 self._runtime_uuids.add(canonical_key)
374 self._store_characteristic(info)
376 def register_service(
377 self,
378 uuid: BluetoothUUID,
379 name: str,
380 identifier: str | None = None,
381 override: bool = False,
382 ) -> None:
383 """Register a custom service at runtime.
385 Args:
386 uuid: The Bluetooth UUID for the service
387 name: Human-readable name
388 identifier: Optional identifier (auto-generated if not provided)
389 override: If True, allow overriding existing entries
390 """
391 self._ensure_loaded()
392 with self._lock:
393 canonical_key = uuid.normalized
395 # Check for conflicts with existing entries
396 if canonical_key in self._services:
397 # Check if it's a SIG service (not in runtime set)
398 if canonical_key not in self._runtime_uuids:
399 if not override:
400 raise ValueError(
401 f"UUID {uuid} conflicts with existing SIG service entry. Use override=True to replace."
402 )
403 # Preserve original SIG entry for restoration
404 self._service_overrides.setdefault(canonical_key, self._services[canonical_key])
405 elif not override:
406 # Runtime entry already exists
407 raise ValueError(
408 f"UUID {uuid} already registered as runtime service. Use override=True to replace."
409 )
411 info = ServiceInfo(
412 uuid=uuid,
413 name=name,
414 id=identifier or f"runtime.service.{name.lower().replace(' ', '_')}",
415 )
417 # Track as runtime-registered UUID
418 self._runtime_uuids.add(canonical_key)
420 self._store_service(info)
422 def get_service_info(self, key: str | BluetoothUUID) -> ServiceInfo | None:
423 """Get information about a service by UUID, name, or ID."""
424 self._ensure_loaded()
425 with self._lock:
426 # Convert BluetoothUUID to canonical key
427 if isinstance(key, BluetoothUUID):
428 canonical_key = key.normalized
429 # Direct canonical lookup
430 if canonical_key in self._services:
431 return self._services[canonical_key]
432 else:
433 search_key = str(key).strip()
435 # Try UUID normalization first
436 try:
437 bt_uuid = BluetoothUUID(search_key)
438 canonical_key = bt_uuid.normalized
439 if canonical_key in self._services:
440 return self._services[canonical_key]
441 except ValueError:
442 pass # UUID normalization failed, continue to alias lookup
444 # Check alias index (normalized to lowercase)
445 alias_key = self._service_aliases.get(search_key.lower())
446 if alias_key and alias_key in self._services:
447 return self._services[alias_key]
449 return None
451 def get_characteristic_info(self, identifier: str | BluetoothUUID) -> CharacteristicInfo | None:
452 """Get information about a characteristic by UUID, name, or ID."""
453 self._ensure_loaded()
454 with self._lock:
455 # Convert BluetoothUUID to canonical key
456 if isinstance(identifier, BluetoothUUID):
457 canonical_key = identifier.normalized
458 # Direct canonical lookup
459 if canonical_key in self._characteristics:
460 return self._characteristics[canonical_key]
461 else:
462 search_key = str(identifier).strip()
464 # Try UUID normalization first
465 try:
466 bt_uuid = BluetoothUUID(search_key)
467 canonical_key = bt_uuid.normalized
468 if canonical_key in self._characteristics:
469 return self._characteristics[canonical_key]
470 except ValueError:
471 pass # UUID normalization failed, continue to alias lookup
473 # Check alias index (normalized to lowercase)
474 alias_key = self._characteristic_aliases.get(search_key.lower())
475 if alias_key and alias_key in self._characteristics:
476 return self._characteristics[alias_key]
478 return None
480 def get_descriptor_info(self, identifier: str | BluetoothUUID) -> DescriptorInfo | None:
481 """Get information about a descriptor by UUID, name, or ID."""
482 self._ensure_loaded()
483 with self._lock:
484 # Convert BluetoothUUID to canonical key
485 if isinstance(identifier, BluetoothUUID):
486 canonical_key = identifier.normalized
487 # Direct canonical lookup
488 if canonical_key in self._descriptors:
489 return self._descriptors[canonical_key]
490 else:
491 search_key = str(identifier).strip()
493 # Try UUID normalization first
494 try:
495 bt_uuid = BluetoothUUID(search_key)
496 canonical_key = bt_uuid.normalized
497 if canonical_key in self._descriptors:
498 return self._descriptors[canonical_key]
499 except ValueError:
500 pass # UUID normalization failed, continue to alias lookup
502 # Check alias index (normalized to lowercase)
503 alias_key = self._descriptor_aliases.get(search_key.lower())
504 if alias_key and alias_key in self._descriptors:
505 return self._descriptors[alias_key]
507 return None
509 def get_gss_spec(self, identifier: str | BluetoothUUID) -> GssCharacteristicSpec | None:
510 """Get the full GSS characteristic specification with all field metadata.
512 This provides access to the complete YAML structure including all fields,
513 their units, resolutions, ranges, and presence conditions.
515 Args:
516 identifier: Characteristic name, ID, or UUID
518 Returns:
519 GssCharacteristicSpec with full field structure, or None if not found
521 Example::
522 gss = get_uuid_registry().get_gss_spec("Location and Speed")
523 if gss:
524 for field in gss.structure:
525 print(f"{field.python_name}: unit={field.unit_id}, resolution={field.resolution}")
527 """
528 self._ensure_loaded()
529 if self._gss_registry is None:
530 return None
532 with self._lock:
533 # Try direct lookup by name or ID
534 if isinstance(identifier, str):
535 spec = self._gss_registry.get_spec(identifier)
536 if spec:
537 return spec
539 # Try to get CharacteristicInfo to find the ID
540 char_info = self.get_characteristic_info(identifier)
541 if char_info:
542 spec = self._gss_registry.get_spec(char_info.id)
543 if spec:
544 return spec
545 else:
546 # Look up by UUID
547 char_info = self.get_characteristic_info(identifier)
548 if char_info:
549 spec = self._gss_registry.get_spec(char_info.name)
550 if spec:
551 return spec
552 spec = self._gss_registry.get_spec(char_info.id)
553 if spec:
554 return spec
556 return None
558 def resolve_characteristic_spec(self, characteristic_name: str) -> CharacteristicSpec | None: # pylint: disable=too-many-locals
559 """Resolve characteristic specification with rich YAML metadata.
561 This method provides detailed characteristic information including data types,
562 field sizes, units, and descriptions by cross-referencing multiple YAML sources.
564 Args:
565 characteristic_name: Name of the characteristic (e.g., "Temperature", "Battery Level")
567 Returns:
568 CharacteristicSpec with full metadata, or None if not found
570 Example::
571 spec = get_uuid_registry().resolve_characteristic_spec("Temperature")
572 if spec:
573 print(f"UUID: {spec.uuid}, Unit: {spec.unit_symbol}, Type: {spec.data_type}")
575 """
576 self._ensure_loaded()
577 with self._lock:
578 # 1. Get UUID from characteristic registry
579 char_info = self.get_characteristic_info(characteristic_name)
580 if not char_info:
581 return None
583 # 2. Get typed GSS specification if available
584 gss_spec = self.get_gss_spec(characteristic_name)
586 # 3. Extract metadata from GSS specification
587 data_type = None
588 field_size = None
589 unit_id = None
590 unit_symbol = None
591 unit_readable_name = None
592 base_unit = None
593 resolution_text = None
594 description = None
596 if gss_spec:
597 description = gss_spec.description
599 # Only set data_type for single-field characteristics
600 # Multi-field characteristics have complex structures and no single data type
601 if len(gss_spec.structure) == 1:
602 # Use primary field for metadata extraction
603 primary = gss_spec.primary_field
604 if primary:
605 data_type = primary.type
606 field_size = str(primary.fixed_size) if primary.fixed_size else primary.size
608 # Use FieldSpec's unit_id property (auto-parsed from description)
609 if primary.unit_id:
610 unit_id = f"org.bluetooth.unit.{primary.unit_id}"
611 unit_symbol = self._convert_bluetooth_unit_to_readable(primary.unit_id)
612 # Preserve the human-readable long-form name
613 unit_info_obj = UnitsRegistry.get_instance().get_info(unit_id)
614 if unit_info_obj:
615 unit_readable_name = unit_info_obj.readable_name
616 base_unit = unit_id
618 # Get resolution from FieldSpec
619 if primary.resolution is not None:
620 resolution_text = f"Resolution: {primary.resolution}"
622 # 4. Use existing unit from CharacteristicInfo if GSS didn't provide one.
623 # Multi-field structs have per-field units; don't promote one to top-level.
624 is_multi_field = gss_spec is not None and len(gss_spec.structure) > 1
625 if not unit_symbol and char_info.unit and not is_multi_field:
626 unit_symbol = char_info.unit
628 return CharacteristicSpec(
629 uuid=char_info.uuid,
630 name=char_info.name,
631 field_info=FieldInfo(data_type=data_type, field_size=field_size),
632 unit_info=UnitMetadata(
633 unit_id=unit_id,
634 unit_symbol=unit_symbol,
635 unit_name=unit_readable_name,
636 base_unit=base_unit,
637 resolution_text=resolution_text,
638 ),
639 description=description,
640 structure=gss_spec.structure if gss_spec else [],
641 )
643 def get_signed_from_data_type(self, data_type: str | None) -> bool:
644 """Determine if data type is signed from GSS data type.
646 Args:
647 data_type: GSS data type string (e.g., "sint16", "float32", "uint8")
649 Returns:
650 True if the type represents signed values, False otherwise
652 """
653 if not data_type:
654 return False
655 # Comprehensive signed type detection
656 signed_types = {"float32", "float64", "medfloat16", "medfloat32"}
657 return data_type.startswith("sint") or data_type in signed_types
659 @staticmethod
660 def get_byte_order_hint() -> str:
661 """Get byte order hint for Bluetooth SIG specifications.
663 Returns:
664 "little" - Bluetooth SIG uses little-endian by convention
666 """
667 return "little"
669 def clear_custom_registrations(self) -> None:
670 """Clear all custom registrations (for testing)."""
671 with self._lock:
672 # Use runtime_uuids set to identify what to remove
673 runtime_keys = list(self._runtime_uuids)
675 # Remove runtime entries from canonical stores
676 for key in runtime_keys:
677 self._services.pop(key, None)
678 self._characteristics.pop(key, None)
679 self._descriptors.pop(key, None)
681 # Remove corresponding aliases (alias -> canonical_key where canonical_key is runtime)
682 runtime_service_aliases = [
683 alias for alias, canonical in self._service_aliases.items() if canonical in runtime_keys
684 ]
685 runtime_char_aliases = [
686 alias for alias, canonical in self._characteristic_aliases.items() if canonical in runtime_keys
687 ]
688 runtime_desc_aliases = [
689 alias for alias, canonical in self._descriptor_aliases.items() if canonical in runtime_keys
690 ]
692 for alias in runtime_service_aliases:
693 del self._service_aliases[alias]
694 for alias in runtime_char_aliases:
695 del self._characteristic_aliases[alias]
696 for alias in runtime_desc_aliases:
697 del self._descriptor_aliases[alias]
699 # Restore any preserved SIG entries that were overridden
700 for key in runtime_keys:
701 original = self._service_overrides.pop(key, None)
702 if original is not None:
703 self._store_service(original)
704 original = self._characteristic_overrides.pop(key, None)
705 if original is not None:
706 self._store_characteristic(original)
707 original = self._descriptor_overrides.pop(key, None)
708 if original is not None:
709 self._store_descriptor(original)
711 # Clear the runtime tracking set
712 self._runtime_uuids.clear()
715def get_uuid_registry() -> UuidRegistry:
716 """Return the process-wide UUID registry singleton instance."""
717 return UuidRegistry.get_instance()