人类国际奥委会

flying-ioc的Python项目详细描述


人类控制反转(ioc)-用python编写

Inversion of control

如何使用

pip安装飞行ioc

from flying_ioc import *

ioc = IocManager()

ioc.set_class(cls=HelperWrapper, singleton=True)
ioc.set_class(cls=GRHelperService, singleton=True)
ioc.set_class(name='api', cls=GRApiClient, singleton=True, thread_local=True)

gr_service: GRHelperService = ioc.GRHelperService
gr_service.start()

功能

  • 支持将对象作为ioc管理器的属性获取
  • 用参数的类名初始化类参数,如果不存在则用参数名初始化
  • 支持继承-初始化父类所需的参数
  • 支持值、类、工厂的映射
  • 支持单线程、每线程类等映射配置
  • 支持@notinject decorator

属性

gr_service: GRHelperService = ioc.GRHelperService
gr_service.start()

初始化类

class ClassA:
    pass

class ClassB:
    pass

class ClassC:
    pass

class ExampleClass:
    def __init__(self, arg1: ClassA, arg2, arg3: ClassC):
        assert arg1.__class__ == ClassA
        assert arg2.__class__ == ClassB
        assert arg3.__class__ == ClassC


def test_arguments():
    ioc = IocManager()
    ioc.set_class(cls=ClassA)
    ioc.set_class(name='arg2', cls=ClassB)
    ioc.set_class(name='arg3', cls=ClassC)

    ioc.set_class(cls=ExampleClass)

    assert ioc.ExampleClass.__class__ == ExampleClass

支持继承

class ClassA:
    pass

class ClassB:
    pass

class ClassC:
    pass

class ParentD:
    def __init__(self, arg1: ClassA, **kwargs):
        self._arg1 = arg1

class ParentE(ParentD):
    def __init__(self, arg2: ClassB, **kwargs):
        super().__init__(**kwargs)
        self._arg2 = arg2

class ExampleClass(ParentE):
    def __init__(self, arg3: ClassC, **kwargs):
        super().__init__(**kwargs)
        assert self._arg1.__class__ == ClassA
        assert self._arg2.__class__ == ClassB
        assert arg3.__class__ == ClassC

def test_arguments():
    ioc = IocManager()
    ioc.set_class(cls=ClassA)
    ioc.set_class(cls=ClassB)
    ioc.set_class(cls=ClassC)

    ioc.set_class(cls=ExampleClass)

    assert ioc.ExampleClass.__class__ == ExampleClass

class ClassA:
    pass


class ExampleClass:
    def __init__(self, value_text, value_class):
        assert value_text == 'Some text'
        assert value_class.__class__ == ClassA


def test_arguments():
    ioc = IocManager()
    ioc.set_value(name='value_text', value='Some text')
    ioc.set_value(name='value_class', value=ClassA())

    ioc.set_class(cls=ExampleClass)

    assert ioc.ExampleClass.__class__ == ExampleClass

工厂

class ClassA:
    pass

class ClassB:
    pass

class ClassC:
    pass

class Factory(IocFactory):
    @staticmethod
    def get_instance(ioc_manager: IocManager, name: str, frame_info: inspect.FrameInfo):
        if frame_info.function == 'test_factory_1':
            return ioc_manager.ClassA

        if name == 'factory1':
            return ioc_manager.ClassB

        return ioc_manager.ClassC

ioc = IocManager()
ioc.set_class(cls=ClassA)
ioc.set_class(cls=ClassB)
ioc.set_class(cls=ClassC)
ioc.set_factory(name='factory1', cls=Factory)
ioc.set_factory(name='factory2', cls=Factory)

def test_factory_1():
    assert ioc.factory1.__class__ == ClassA
    assert ioc.factory2.__class__ == ClassA

def test_factory_2():
    assert ioc.factory1.__class__ == ClassB
    assert ioc.factory2.__class__ == ClassC

单重态

class ClassA:
    pass

class ClassB:
    pass

def test_singleton():
    ioc = IocManager()
    ioc.set_class(cls=ClassA)
    ioc.set_class(cls=ClassB, singleton=True)

    assert ioc.ClassA != ioc.ClassA
    assert ioc.ClassB == ioc.ClassB

每个线程的类

class ClassA:
        pass

def _set_vars(ioc: IocManager, storage: dict):
    def wrapped():
        storage['singleton1'] = ioc.singleton1
        storage['singleton2'] = ioc.singleton2

    return wrapped

def test_class_per_thread():
    ioc = IocManager()

    ioc.set_class(name='singleton1', cls=ClassA, singleton=True)
    ioc.set_class(name='singleton2', cls=ClassA, singleton=True, thread_local=True)

    assert ioc.singleton1 == ioc.singleton1
    assert ioc.singleton2 == ioc.singleton2

    thread_storage = {}
    thread = threading.Thread(target=_set_vars(ioc, thread_storage))
    thread.start()
    thread.join()

    assert ioc.singleton1 == thread_storage['singleton1']
    assert ioc.singleton2 != thread_storage['singleton2']

@不注入装饰器

在下面的示例中,@notinject decorator阻止ioc管理器在初始化example class时将arg1添加到kwargs参数中,父类需要arg1参数。

在本例中删除@notinject装饰符将导致异常。

@nonject decorator接受在初始化过程中要跳过的参数名列表。

class ClassA:
    pass

class ClassB:
    pass

class Parent:
    def __init__(self, arg1: ClassA, **kwargs):
        super().__init__(**kwargs)
        self._arg1 = arg1

@NotInject(['arg1'])
class ExampleClass(Parent):
    def __init__(self, arg2: ClassB, **kwargs):
        arg1 = ClassA()
        super().__init__(arg1, **kwargs)
        assert self._arg1 == arg1
        assert arg2.__class__ == ClassB

def test_not_inject():
    ioc = IocManager()
    ioc.set_class(cls=ClassA)
    ioc.set_class(cls=ClassB)

    ioc.set_class(cls=ExampleClass)

    assert ioc.ExampleClass.__class__ == ExampleClass

例外情况

IOC管理器引发两种类型的异常:

  • attributeerror-尝试从IOC管理器获取未定义的属性时
  • 类型错误-在以下情况下:
    • IOC管理器缺少初始化 类或IT父类
    • 初始化类时,会提供同一参数的多个实例 它的父类-由用户和ioc管理器注入。本期 可以使用@notinject decorator进行解析
class ClassA:
    pass

class ClassB:
    pass

class ClassC:
    pass

class Parent:
    def __init__(self, arg1: ClassA, **kwargs):
        super().__init__(**kwargs)
        self._arg1 = arg1

class ExampleClass1(Parent):
    def __init__(self, arg2: ClassB, **kwargs):
        arg1 = ClassA()
        super().__init__(arg1, **kwargs)
        assert self._arg1 == arg1
        assert arg2.__class__ == ClassB

class ExampleClass2:
    def __init__(self, arg1: ClassC):
        pass

class ExampleClass3(ExampleClass2):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

ioc = IocManager()
ioc.set_class(cls=ClassA)
ioc.set_class(cls=ClassB)

def test_exception_container_not_defined():
    with pytest.raises(AttributeError) as e:
        ioc.NotExists
    assert e.value.args[0] == "Name 'NotExists' does not exist"

def test_exception_missing_not_inject():
    with pytest.raises(TypeError) as e:
        ioc.set_class(cls=ExampleClass1)
        ioc.ExampleClass1
    assert e.value.args[0] == "__init__() got multiple values for argument 'arg1'"

def test_exception_arg_is_not_defined():
    with pytest.raises(TypeError) as e:
        ioc.set_class(cls=ExampleClass2)
        ioc.ExampleClass2
    assert e.value.args[0].args[0] == "Can't get a container neither by class name ClassC, neither by arg name arg1"

def test_exception_arg_for_parent_is_not_defined():
    with pytest.raises(TypeError) as e:
        ioc.set_class(cls=ExampleClass3)
        ioc.ExampleClass3
    assert e.value.args[0] == "__init__() missing 1 required positional argument: 'arg1'"

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
IntelliJ IDEA Java代码样式:注释和枚举之间的空间   java车辆路径问题或工具教程未在eclipse上运行   从HTML表单发布时,java非英语UTF 8字符会发生变化   使用MultiResourceItemReader时,java Spring批处理在CustomLineMapper中获取文件名   java配置JAXB解组器来处理同时具有acessor方法和字段的类   java在JpaRepository findAll()方法中返回对象的浅拷贝   java如何对docx文件进行碎片整理,以便与ApachePOI3.8一起使用?   java如何将一维数组分解成行?   java为什么这个图形对象不是绘画?(我做错了什么?)   如何在Java代码中将Java函数应用于Scala选项   使用IntelliJ REST客户端测试Spring MVC POST导致415   weka java代码kmeans群集   java中为什么Sin(30)=0.98803116240928618   java创建Android警报通知   数组可能是Java For循环问题