项目文件夹

文件
wehub-resource-sync 593b94c120
pytest / Unit Tests (push) Has been cancelled
pytest / Integration (integration_tests_a) (push) Has been cancelled
pytest / Integration (integration_tests_b) (push) Has been cancelled
pytest / Integration (integration_tests_c) (push) Has been cancelled
pytest / Integration (integration_tests_d) (push) Has been cancelled
pytest / Integration (integration_tests_e) (push) Has been cancelled
pytest / Integration (integration_tests_f) (push) Has been cancelled
pytest / Integration (integration_tests_g) (push) Has been cancelled
pytest / Integration (integration_tests_h) (push) Has been cancelled
pytest / Integration (integration_tests_i) (push) Has been cancelled
pytest / Integration (integration_tests_j) (push) Has been cancelled
pytest / Distributed (distributed_a) (push) Has been cancelled
pytest / Distributed (distributed_b) (push) Has been cancelled
pytest / Distributed (distributed_c) (push) Has been cancelled
pytest / Distributed (distributed_d) (push) Has been cancelled
pytest / Distributed (distributed_e) (push) Has been cancelled
pytest / Distributed (distributed_f) (push) Has been cancelled
pytest / Minimal Install (push) Has been cancelled
pytest / Event File (push) Has been cancelled
pytest (slow) / py-slow (push) Has been cancelled
Publish JSON Schema / publish-schema (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:49:20 +08:00

118 行
3.6 KiB
Python

#! /usr/bin/env python
# Copyright (c) 2023 Predibase, Inc., 2020 Uber Technologies, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
from collections import UserDict
from typing import Generic, TypeVar
# Legacy default keys for backward compatibility.
# New code should use the explicit `default` parameter instead.
DEFAULT_KEYS = ["None", "none", "null", None]
T = TypeVar("T")
class Registry(UserDict, Generic[T]):
"""Type-safe registry with optional parent delegation and mock support.
Items are considered to exist in the registry if they are in either the
registry itself or its parent. Supports:
- Generic typing: Registry[EncoderType], Registry[CombinerType]
- Parent delegation for hierarchical registries
- register() decorator for clean registration
- unregister() for testing and dynamic removal
- Mock support via context manager
"""
def __init__(self, source=None):
init_data = None
parent = {}
if isinstance(source, Registry):
parent = source
else:
init_data = source
self.parent = parent
super().__init__(init_data)
def __getitem__(self, key: str) -> T:
if self.parent and key not in self.data:
return self.parent.__getitem__(key)
return self.data.__getitem__(key)
def __contains__(self, key: str):
return key in self.data or key in self.parent
def __len__(self) -> int:
return len(self.data) + len(self.parent)
def __iter__(self):
return self._merged().__iter__()
def keys(self):
return self._merged().keys()
def values(self):
return self._merged().values()
def items(self):
return self._merged().items()
def _merged(self):
return {**self.parent, **self.data}
def register(self, name: str, default: bool = False):
"""Register a class in the registry via decorator.
Args:
name: Registration key.
default: If True, also register under None/"none"/"null" keys.
"""
def wrap(cls):
self[name] = cls
if default:
for key in DEFAULT_KEYS:
self[key] = cls
return cls
return wrap
def unregister(self, name: str):
"""Remove a registered item. Useful for testing.
Args:
name: Key to remove.
Raises:
KeyError if name is not registered.
"""
if name in self.data:
del self.data[name]
else:
raise KeyError(f"'{name}' is not registered")
def get_default(self) -> T | None:
"""Get the default registered item (registered with default=True)."""
for key in DEFAULT_KEYS:
if key in self.data:
return self.data[key]
return None
def list_registered(self) -> list[str]:
"""List all registered names (excluding default key aliases)."""
return [k for k in self._merged() if k not in DEFAULT_KEYS]