def test_callable_after(self): def after1(proxy, ret_val): """ after在被代理的方法调用之后调用,用来改变输出 :param proxy: 代理实例 :param ret_val: 被代理的方法返回值 :return: """ if ret_val < 10: return 10 if ret_val < 100: Return(100) return ret_val def after2(proxy, ret_val): return ret_val // 2 aa = A() a = Proxy(aa, after=[ AOP.Hook(after1, ["__call__"]), AOP.Hook(after2, ["__call__"]) ]) # 传入1,得到结果1*3,走到after1之后被改成了10,走到after2的时候被改成了5 assert a(1) == 5 # 传入4,得到结果4*3,走到after1直接被返回了100,不再走after2 assert a(4) == 100 # 传入70,得到结果70*3,无修改通过after1,随后在after2被改成105 assert a(70) == 105
def test_callable_before(self): def before1(proxy, a): """ before在被代理的方法调用之前调用,用来改变输入 :param proxy: 代理实例 :param a: 被代理的方法传入的参数,需要与代理的方法形参列表保持一致 :return: """ if a == 1: a = 5 return (a, ), dict() if a == 3: Return(1000) def before2(proxy, a): return (a // 2, ), dict() aa = A() a = Proxy(aa, before=[ AOP.Hook(before1, ["__call__"]), AOP.Hook(before2, ["__call__"]) ]) # 传入1,走到before1之后被改成了5,走到before2的时候被改成了2, 最后得到结果2*3 assert a(1) == 6 # 传入3,走到before1直接被返回了1000,不再走before2和被代理的方法。 assert a(3) == 1000 # 传入7,无修改通过before1, 随后在before2被改成3,最后得到结果3*3 assert a(7) == 9
def test_customize(self): class FunProxy(Proxy): proxy_methods = ["fun", "bar"] def common(proxy, name): return 2 aa = A() a = FunProxy( aa, before=[AOP.Hook(lambda *args, **kwargs: None, ["fun"])], after=[AOP.Hook(common, ["__getitem__", "__call__", "fun"])]) assert a.b == 3 assert aa.b == 3 assert a.__dict__ == {"b": 3} assert aa.__dict__ == {"b": 3} a.b = 5 assert a.b == 5 assert aa.b == 5 assert a.__dict__ == {"b": 5} assert aa.__dict__ == {"b": 5} assert a["333"] == 2 assert aa["333"] == 1 assert a(3) == 2 assert aa(3) == 9 assert a.fun(3, 4) == 2 assert aa.fun(3, 4) == 12 assert a.bar(3, 4) == 7
def test_nomal(self): aa = A() def common(proxy, name, value=None): if name == "b": Return(2) a = Proxy( aa, before=[ AOP.Hook(common, ["__getattribute__", "__setattr__", "__delattr__"]) ]) assert aa.b == 3 assert a.b == 2 a.b = 10 assert aa.b == 3 a.c = 10 assert aa.c == 10 assert a.c == 10 del a.b assert hasattr(a, "b") assert hasattr(aa, "b") del a.c assert not hasattr(a, "c") assert not hasattr(aa, "c")
def prop_alias(mock_obj_prefix=None, mock_factory_prefix="factories"): """ prop mark装饰器别名,指定要mock对象及工厂对象名字的前缀,减少装饰器长度。 :param mock_obj_prefix: :param mock_factory_prefix: :return: """ return Proxy(MarkDecorator(Mark("prop", (), {})), before=[ AOP.Hook(wrapper(mock_obj_prefix, mock_factory_prefix), ["__call__"]) ])
def get_store(cls, self_or_cls, **callargs): conn, cur = cls.init_sqlite with super(SqliteDriverMixin, cls).get_store(self_or_cls, **callargs) as self_or_cls: cur = conn.cursor() if hasattr(self_or_cls, "_need_proxy") \ and self_or_cls._need_proxy("store"): store = SqliteProxy( cur, before=[AOP.Hook(execute_before, ["execute"])]) self_or_cls = proxy(self_or_cls, prop_name="store", prop=store) try: yield self_or_cls finally: conn.commit()
def proxy(obj, prop, prop_name): """ 为 object 对象代理一个属性 :param object obj: 被代理的对象 :param object prop: 代理返回的属性 :param str prop_name: 被代理的属性名 :return: 被代理之后的对象 :rtype: object """ def common(_proxy, name, value=None): # pylint: disable=W0613 """ 用于 Hook 的钩子函数 """ if name == prop_name: Return(prop) return Proxy(obj, before=[ AOP.Hook(common, ["__getattribute__", "__setattr__", "__delattr__"]), ])
def proxy(obj, prop, prop_name): """ 为object对象代理一个属性 :param obj: :param prop: 属性 :param prop_name: 属性名 :return: """ assert isinstance(prop_name, str), "prop_name must be string!" def common(proxy, name, value=None): if name == prop_name: if value: raise RuntimeError(f"{prop_name} readonly!") else: Return(prop) return Proxy( obj, before=[ AOP.Hook(common, ["__getattribute__", "__setattr__", "__delattr__"]), ])
def test_object_del(self): aa = A() assert hasattr(aa, "b") AOP.object_del(aa, "b") assert not hasattr(aa, "b")