程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

Properly add type annotations to a services container

发布于2025-01-02 16:54     阅读(495)     评论(0)     点赞(11)     收藏(0)


I am trying to add type annotations to a "service container" with relatively strict typing, but am struggling to define the type hints correctly.

Basically, the services container is a glorified dictionary, where the key should be an ABC type, or a Protocol (i.e. the "abstract class"). The dictionary value is a generic ServiceEntry[T] where T is a subclass of the key ("abstract class").

I've included simplified implementation below, but the full code can be found in this gist: https://gist.github.com/NixonInnes/6b7cf851cd2715aafbf32c4fb54405ac

This is my service entry implementation:

class ServiceLife(Enum):
    SINGLETON = 1
    TRANSIENT = 2

class ServiceEntry[T]:
    service_class: type[T]
    service_life: ServiceLife
    instance: T | None

    def __init__(self, service_class: type[T], service_life: ServiceLife):
        self.service_class = service_class
        self.service_life = service_life
        self.instance = None
        self.lock = threading.Lock()

And this is a simplified implementation of the service container:

class ServiceContainer(IServiceContainer):
    def __init__(self):
        ...
        self.__services: dict[type[Any], ServiceEntry[Any]] = {}

    def _set_service[T](self, key: type[T], value: ServiceEntry[T]) -> None:
        with self.__lock:
            self.__services[key] = value
    
    def _get_service[T](self, key: type[T]) -> ServiceEntry[T]:
        with self.__lock:
            return self.__services[key]

    @override
    def register[T](
        self,
        abstract_class: type[T],
        service_class: type[T],
        service_life: ServiceLife = ServiceLife.TRANSIENT,
    ):
        assert issubclass(
            service_class, abstract_class
        ), f"'{service_class.__name__}' does not implement '{abstract_class.__name__}'"
        
        service = ServiceEntry[T](service_class, service_life)
        self._set_service(abstract_class, service)

    @override
    def get[T](self, abstract_class: type[T], **overrides: Any) -> T: 

        try:
            service = self._get_service(abstract_class)
            ...
            with service.lock:
                ...
                instance = self._create_instance(service, overrides)
            ...
            return instance
    
    def _create_instance[T](self, service_entry: ServiceEntry[T], overrides: dict[str, Any]) -> T:
        signature = inspect.signature(service_entry.service_class.__init__)
        type_hints = get_type_hints(service_entry.service_class.__init__)
        kwargs = {}
        for name, param in signature.parameters.items():
        ...
        return service_entry.service_class(**kwargs)

I would like to be able to satisfy type checkers so that when I do something like the below, it is able to infer the output service type and ideally detect an invalid registration (i.e. the service_class is not a subclass of the abstract_class)

class IFoo(ABC):
    ...

class Foo(IFoo):
    pass

@runtime_checkable
class IBar(Prototype):
    ...

class Bar(IBar):
    pass


services = ServiceContainer()

services.register(IFoo, Foo)
services.register(IBar, Bar)

foo_service = services.get(IFoo)
bar_service = services.get(IBar)

Maybe im being too ambitious with the static typing system?


解决方案


暂无回答



所属网站分类: 技术文章 > 问答

作者:黑洞官方问答小能手

链接:https://www.pythonheidong.com/blog/article/2046696/77791330e2c5061a685b/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

11 0
收藏该文
已收藏

评论内容:(最多支持255个字符)