# -*- coding: utf-8 -*- import pytest from pybind11_tests import callbacks as m from threading import Thread def test_callbacks(): from functools import partial def func1(): return "func1" def func2(a, b, c, d): return "func2", a, b, c, d def func3(a): return "func3({})".format(a) assert m.test_callback1(func1) == "func1" assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5) assert m.test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4) assert m.test_callback1(partial(func3, "partial")) == "func3(partial)" assert m.test_callback3(lambda i: i + 1) == "func(43) = 44" f = m.test_callback4() assert f(43) == 44 f = m.test_callback5() assert f(number=43) == 44 def test_bound_method_callback(): # Bound Python method: class MyClass: def double(self, val): return 2 * val z = MyClass() assert m.test_callback3(z.double) == "func(43) = 86" z = m.CppBoundMethodTest() assert m.test_callback3(z.triple) == "func(43) = 129" def test_keyword_args_and_generalized_unpacking(): def f(*args, **kwargs): return args, kwargs assert m.test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {}) assert m.test_dict_unpacking(f) == ( ("positional", 1), {"key": "value", "a": 1, "b": 2}, ) assert m.test_keyword_args(f) == ((), {"x": 10, "y": 20}) assert m.test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4}) assert m.test_unpacking_and_keywords2(f) == ( ("positional", 1, 2, 3, 4, 5), {"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5}, ) with pytest.raises(TypeError) as excinfo: m.test_unpacking_error1(f) assert "Got multiple values for keyword argument" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.test_unpacking_error2(f) assert "Got multiple values for keyword argument" in str(excinfo.value) with pytest.raises(RuntimeError) as excinfo: m.test_arg_conversion_error1(f) assert "Unable to convert call argument" in str(excinfo.value) with pytest.raises(RuntimeError) as excinfo: m.test_arg_conversion_error2(f) assert "Unable to convert call argument" in str(excinfo.value) def test_lambda_closure_cleanup(): m.test_cleanup() cstats = m.payload_cstats() assert cstats.alive() == 0 assert cstats.copy_constructions == 1 assert cstats.move_constructions >= 1 def test_cpp_function_roundtrip(): """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer""" assert ( m.test_dummy_function(m.dummy_function) == "matches dummy_function: eval(1) = 2" ) assert ( m.test_dummy_function(m.roundtrip(m.dummy_function)) == "matches dummy_function: eval(1) = 2" ) assert m.roundtrip(None, expect_none=True) is None assert ( m.test_dummy_function(lambda x: x + 2) == "can't convert to function pointer: eval(1) = 3" ) with pytest.raises(TypeError) as excinfo: m.test_dummy_function(m.dummy_function2) assert "incompatible function arguments" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.test_dummy_function(lambda x, y: x + y) assert any( s in str(excinfo.value) for s in ("missing 1 required positional argument", "takes exactly 2 arguments") ) def test_function_signatures(doc): assert doc(m.test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str" assert doc(m.test_callback4) == "test_callback4() -> Callable[[int], int]" def test_movable_object(): assert m.callback_with_movable(lambda _: None) is True def test_async_callbacks(): # serves as state for async callback class Item: def __init__(self, value): self.value = value res = [] # generate stateful lambda that will store result in `res` def gen_f(): s = Item(3) return lambda j: res.append(s.value + j) # do some work async work = [1, 2, 3, 4] m.test_async_callback(gen_f(), work) # wait until work is done from time import sleep sleep(0.5) assert sum(res) == sum([x + 3 for x in work]) def test_async_async_callbacks(): t = Thread(target=test_async_callbacks) t.start() t.join() href='#n20'>20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151