blob: 93c42c22b8b2f45040302732407995ad1d689792 [file] [log] [blame]
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02001import pytest
Jason Rhinelander391c7542017-07-25 16:47:36 -04002from pybind11_tests import callbacks as m
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02003
4
Dean Moldovan665e8802016-08-12 22:28:31 +02005def test_callbacks():
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02006 from functools import partial
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02007
8 def func1():
Dean Moldovan665e8802016-08-12 22:28:31 +02009 return "func1"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020010
11 def func2(a, b, c, d):
Dean Moldovan665e8802016-08-12 22:28:31 +020012 return "func2", a, b, c, d
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020013
14 def func3(a):
Dean Moldovan665e8802016-08-12 22:28:31 +020015 return "func3({})".format(a)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020016
Jason Rhinelander391c7542017-07-25 16:47:36 -040017 assert m.test_callback1(func1) == "func1"
18 assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
19 assert m.test_callback1(partial(func2, 1, 2, 3, 4)) == ("func2", 1, 2, 3, 4)
20 assert m.test_callback1(partial(func3, "partial")) == "func3(partial)"
21 assert m.test_callback3(lambda i: i + 1) == "func(43) = 44"
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020022
Jason Rhinelander391c7542017-07-25 16:47:36 -040023 f = m.test_callback4()
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020024 assert f(43) == 44
Jason Rhinelander391c7542017-07-25 16:47:36 -040025 f = m.test_callback5()
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020026 assert f(number=43) == 44
27
28
Jason Rhinelandera01b6b82017-04-24 12:29:42 -040029def test_bound_method_callback():
Jason Rhinelandera01b6b82017-04-24 12:29:42 -040030 # Bound Python method:
31 class MyClass:
32 def double(self, val):
33 return 2 * val
34
35 z = MyClass()
Jason Rhinelander391c7542017-07-25 16:47:36 -040036 assert m.test_callback3(z.double) == "func(43) = 86"
Jason Rhinelandera01b6b82017-04-24 12:29:42 -040037
Jason Rhinelander391c7542017-07-25 16:47:36 -040038 z = m.CppBoundMethodTest()
39 assert m.test_callback3(z.triple) == "func(43) = 129"
Jason Rhinelandera01b6b82017-04-24 12:29:42 -040040
41
Dean Moldovanc743e1b2016-08-29 03:05:42 +020042def test_keyword_args_and_generalized_unpacking():
Dean Moldovanc743e1b2016-08-29 03:05:42 +020043
44 def f(*args, **kwargs):
45 return args, kwargs
46
Jason Rhinelander391c7542017-07-25 16:47:36 -040047 assert m.test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {})
48 assert m.test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2})
49 assert m.test_keyword_args(f) == ((), {"x": 10, "y": 20})
50 assert m.test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4})
51 assert m.test_unpacking_and_keywords2(f) == (
Dean Moldovanc743e1b2016-08-29 03:05:42 +020052 ("positional", 1, 2, 3, 4, 5),
53 {"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
54 )
55
56 with pytest.raises(TypeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -040057 m.test_unpacking_error1(f)
Dean Moldovanc743e1b2016-08-29 03:05:42 +020058 assert "Got multiple values for keyword argument" in str(excinfo.value)
59
60 with pytest.raises(TypeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -040061 m.test_unpacking_error2(f)
Dean Moldovanc743e1b2016-08-29 03:05:42 +020062 assert "Got multiple values for keyword argument" in str(excinfo.value)
63
64 with pytest.raises(RuntimeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -040065 m.test_arg_conversion_error1(f)
Dean Moldovanc743e1b2016-08-29 03:05:42 +020066 assert "Unable to convert call argument" in str(excinfo.value)
67
68 with pytest.raises(RuntimeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -040069 m.test_arg_conversion_error2(f)
Dean Moldovanc743e1b2016-08-29 03:05:42 +020070 assert "Unable to convert call argument" in str(excinfo.value)
71
72
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020073def test_lambda_closure_cleanup():
Jason Rhinelander391c7542017-07-25 16:47:36 -040074 m.test_cleanup()
75 cstats = m.payload_cstats()
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020076 assert cstats.alive() == 0
77 assert cstats.copy_constructions == 1
78 assert cstats.move_constructions >= 1
79
80
Dean Moldovan665e8802016-08-12 22:28:31 +020081def test_cpp_function_roundtrip():
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020082 """Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer"""
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020083
Jason Rhinelander391c7542017-07-25 16:47:36 -040084 assert m.test_dummy_function(m.dummy_function) == "matches dummy_function: eval(1) = 2"
85 assert (m.test_dummy_function(m.roundtrip(m.dummy_function)) ==
86 "matches dummy_function: eval(1) = 2")
87 assert m.roundtrip(None, expect_none=True) is None
88 assert (m.test_dummy_function(lambda x: x + 2) ==
89 "can't convert to function pointer: eval(1) = 3")
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020090
Dean Moldovan665e8802016-08-12 22:28:31 +020091 with pytest.raises(TypeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -040092 m.test_dummy_function(m.dummy_function2)
Wenzel Jakobe99ebae2016-09-12 11:44:37 +090093 assert "incompatible function arguments" in str(excinfo.value)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020094
Dean Moldovan665e8802016-08-12 22:28:31 +020095 with pytest.raises(TypeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -040096 m.test_dummy_function(lambda x, y: x + y)
Dean Moldovan665e8802016-08-12 22:28:31 +020097 assert any(s in str(excinfo.value) for s in ("missing 1 required positional argument",
98 "takes exactly 2 arguments"))
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020099
100
101def test_function_signatures(doc):
Jason Rhinelander391c7542017-07-25 16:47:36 -0400102 assert doc(m.test_callback3) == "test_callback3(arg0: Callable[[int], int]) -> str"
103 assert doc(m.test_callback4) == "test_callback4() -> Callable[[int], int]"
Lunderbergc7fcde72017-02-22 14:00:59 -0500104
105
106def test_movable_object():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400107 assert m.callback_with_movable(lambda _: None) is True