Module connpy.tests.test_hooks
Tests for connpy.hooks module — MethodHook and ClassHook.
Classes
class TestClassHook-
Expand source code
class TestClassHook: def test_creates_instance(self): """ClassHook still creates instances normally.""" @ClassHook class MyClass: def __init__(self, value): self.value = value obj = MyClass(42) assert obj.value == 42 def test_modify_future_instances(self): """modify() affects all future instances.""" @ClassHook class MyClass: def __init__(self): self.x = 1 def set_x_to_99(instance): instance.x = 99 MyClass.modify(set_x_to_99) obj = MyClass() assert obj.x == 99 def test_modify_does_not_affect_past(self): """modify() does not affect already-created instances.""" @ClassHook class MyClass: def __init__(self): self.x = 1 old_obj = MyClass() def set_x_to_99(instance): instance.x = 99 MyClass.modify(set_x_to_99) assert old_obj.x == 1 # Not affected assert MyClass().x == 99 # New instance IS affected def test_instance_modify(self): """instance.modify() only affects that specific instance.""" @ClassHook class MyClass: def __init__(self): self.x = 1 obj1 = MyClass() obj2 = MyClass() obj1.modify(lambda inst: setattr(inst, 'x', 999)) assert obj1.x == 999 assert obj2.x == 1 def test_multiple_deferred_hooks(self): """Multiple modify() calls apply in order.""" @ClassHook class MyClass: def __init__(self): self.log = [] MyClass.modify(lambda inst: inst.log.append("first")) MyClass.modify(lambda inst: inst.log.append("second")) obj = MyClass() assert obj.log == ["first", "second"] def test_getattr_delegation(self): """ClassHook delegates attribute access to the wrapped class.""" @ClassHook class MyClass: class_var = "hello" def __init__(self): pass assert MyClass.class_var == "hello"Methods
def test_creates_instance(self)-
Expand source code
def test_creates_instance(self): """ClassHook still creates instances normally.""" @ClassHook class MyClass: def __init__(self, value): self.value = value obj = MyClass(42) assert obj.value == 42ClassHook still creates instances normally.
def test_getattr_delegation(self)-
Expand source code
def test_getattr_delegation(self): """ClassHook delegates attribute access to the wrapped class.""" @ClassHook class MyClass: class_var = "hello" def __init__(self): pass assert MyClass.class_var == "hello"ClassHook delegates attribute access to the wrapped class.
def test_instance_modify(self)-
Expand source code
def test_instance_modify(self): """instance.modify() only affects that specific instance.""" @ClassHook class MyClass: def __init__(self): self.x = 1 obj1 = MyClass() obj2 = MyClass() obj1.modify(lambda inst: setattr(inst, 'x', 999)) assert obj1.x == 999 assert obj2.x == 1instance.modify() only affects that specific instance.
def test_modify_does_not_affect_past(self)-
Expand source code
def test_modify_does_not_affect_past(self): """modify() does not affect already-created instances.""" @ClassHook class MyClass: def __init__(self): self.x = 1 old_obj = MyClass() def set_x_to_99(instance): instance.x = 99 MyClass.modify(set_x_to_99) assert old_obj.x == 1 # Not affected assert MyClass().x == 99 # New instance IS affectedmodify() does not affect already-created instances.
def test_modify_future_instances(self)-
Expand source code
def test_modify_future_instances(self): """modify() affects all future instances.""" @ClassHook class MyClass: def __init__(self): self.x = 1 def set_x_to_99(instance): instance.x = 99 MyClass.modify(set_x_to_99) obj = MyClass() assert obj.x == 99modify() affects all future instances.
def test_multiple_deferred_hooks(self)-
Expand source code
def test_multiple_deferred_hooks(self): """Multiple modify() calls apply in order.""" @ClassHook class MyClass: def __init__(self): self.log = [] MyClass.modify(lambda inst: inst.log.append("first")) MyClass.modify(lambda inst: inst.log.append("second")) obj = MyClass() assert obj.log == ["first", "second"]Multiple modify() calls apply in order.
class TestMethodHook-
Expand source code
class TestMethodHook: def test_basic_call(self): """Decorated function executes normally.""" @MethodHook def add(a, b): return a + b assert add(2, 3) == 5 def test_pre_hook_modifies_args(self): """Pre-hook can modify arguments before execution.""" @MethodHook def greet(name): return f"Hello {name}" def uppercase_hook(name): return (name.upper(),), {} greet.register_pre_hook(uppercase_hook) assert greet("world") == "Hello WORLD" def test_post_hook_modifies_result(self): """Post-hook can modify the return value.""" @MethodHook def compute(x): return x * 2 def double_result(*args, **kwargs): return kwargs["result"] * 2 compute.register_post_hook(double_result) assert compute(5) == 20 # 5*2=10, then 10*2=20 def test_multiple_pre_hooks_order(self): """Pre-hooks execute in registration order.""" calls = [] @MethodHook def func(x): return x def hook1(x): calls.append("hook1") return (x,), {} def hook2(x): calls.append("hook2") return (x,), {} func.register_pre_hook(hook1) func.register_pre_hook(hook2) func(1) assert calls == ["hook1", "hook2"] def test_multiple_post_hooks_order(self): """Post-hooks execute in registration order.""" calls = [] @MethodHook def func(x): return x def hook1(*args, **kwargs): calls.append("hook1") return kwargs["result"] def hook2(*args, **kwargs): calls.append("hook2") return kwargs["result"] func.register_post_hook(hook1) func.register_post_hook(hook2) func(1) assert calls == ["hook1", "hook2"] def test_pre_hook_exception_continues(self, capsys): """If a pre-hook raises, the function still executes.""" @MethodHook def func(x): return x + 1 def bad_hook(x): raise RuntimeError("broken hook") func.register_pre_hook(bad_hook) # Should not raise — the hook error is printed but execution continues result = func(5) assert result == 6 def test_post_hook_exception_continues(self, capsys): """If a post-hook raises, the result is still returned.""" @MethodHook def func(x): return x + 1 def bad_hook(*args, **kwargs): raise RuntimeError("broken post hook") func.register_post_hook(bad_hook) result = func(5) assert result == 6 def test_method_hook_as_instance_method(self): """MethodHook works as a descriptor on a class.""" class MyClass: @MethodHook def double(self, x): return x * 2 obj = MyClass() assert obj.double(5) == 10 def test_method_hook_instance_hook_registration(self): """Can register hooks via instance method access.""" class MyClass: @MethodHook def process(self, x): return x def add_ten(*args, **kwargs): return kwargs["result"] + 10 obj = MyClass() obj.process.register_post_hook(add_ten) assert obj.process(5) == 15Methods
def test_basic_call(self)-
Expand source code
def test_basic_call(self): """Decorated function executes normally.""" @MethodHook def add(a, b): return a + b assert add(2, 3) == 5Decorated function executes normally.
def test_method_hook_as_instance_method(self)-
Expand source code
def test_method_hook_as_instance_method(self): """MethodHook works as a descriptor on a class.""" class MyClass: @MethodHook def double(self, x): return x * 2 obj = MyClass() assert obj.double(5) == 10MethodHook works as a descriptor on a class.
def test_method_hook_instance_hook_registration(self)-
Expand source code
def test_method_hook_instance_hook_registration(self): """Can register hooks via instance method access.""" class MyClass: @MethodHook def process(self, x): return x def add_ten(*args, **kwargs): return kwargs["result"] + 10 obj = MyClass() obj.process.register_post_hook(add_ten) assert obj.process(5) == 15Can register hooks via instance method access.
def test_multiple_post_hooks_order(self)-
Expand source code
def test_multiple_post_hooks_order(self): """Post-hooks execute in registration order.""" calls = [] @MethodHook def func(x): return x def hook1(*args, **kwargs): calls.append("hook1") return kwargs["result"] def hook2(*args, **kwargs): calls.append("hook2") return kwargs["result"] func.register_post_hook(hook1) func.register_post_hook(hook2) func(1) assert calls == ["hook1", "hook2"]Post-hooks execute in registration order.
def test_multiple_pre_hooks_order(self)-
Expand source code
def test_multiple_pre_hooks_order(self): """Pre-hooks execute in registration order.""" calls = [] @MethodHook def func(x): return x def hook1(x): calls.append("hook1") return (x,), {} def hook2(x): calls.append("hook2") return (x,), {} func.register_pre_hook(hook1) func.register_pre_hook(hook2) func(1) assert calls == ["hook1", "hook2"]Pre-hooks execute in registration order.
def test_post_hook_exception_continues(self, capsys)-
Expand source code
def test_post_hook_exception_continues(self, capsys): """If a post-hook raises, the result is still returned.""" @MethodHook def func(x): return x + 1 def bad_hook(*args, **kwargs): raise RuntimeError("broken post hook") func.register_post_hook(bad_hook) result = func(5) assert result == 6If a post-hook raises, the result is still returned.
def test_post_hook_modifies_result(self)-
Expand source code
def test_post_hook_modifies_result(self): """Post-hook can modify the return value.""" @MethodHook def compute(x): return x * 2 def double_result(*args, **kwargs): return kwargs["result"] * 2 compute.register_post_hook(double_result) assert compute(5) == 20 # 5*2=10, then 10*2=20Post-hook can modify the return value.
def test_pre_hook_exception_continues(self, capsys)-
Expand source code
def test_pre_hook_exception_continues(self, capsys): """If a pre-hook raises, the function still executes.""" @MethodHook def func(x): return x + 1 def bad_hook(x): raise RuntimeError("broken hook") func.register_pre_hook(bad_hook) # Should not raise — the hook error is printed but execution continues result = func(5) assert result == 6If a pre-hook raises, the function still executes.
def test_pre_hook_modifies_args(self)-
Expand source code
def test_pre_hook_modifies_args(self): """Pre-hook can modify arguments before execution.""" @MethodHook def greet(name): return f"Hello {name}" def uppercase_hook(name): return (name.upper(),), {} greet.register_pre_hook(uppercase_hook) assert greet("world") == "Hello WORLD"Pre-hook can modify arguments before execution.