Wenzel Jakob | 0671002 | 2018-09-01 01:09:16 +0200 | [diff] [blame] | 1 | from __future__ import division |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 2 | import pytest |
| 3 | import sys |
| 4 | |
| 5 | from pybind11_tests import pytypes as m |
| 6 | from pybind11_tests import debug_enabled |
| 7 | |
| 8 | |
| 9 | def test_list(capture, doc): |
| 10 | with capture: |
Jason Rhinelander | 5c7a290 | 2017-10-24 20:39:46 -0300 | [diff] [blame] | 11 | lst = m.get_list() |
| 12 | assert lst == ["overwritten"] |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 13 | |
Jason Rhinelander | 5c7a290 | 2017-10-24 20:39:46 -0300 | [diff] [blame] | 14 | lst.append("value2") |
| 15 | m.print_list(lst) |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 16 | assert capture.unordered == """ |
| 17 | Entry at position 0: value |
| 18 | list item 0: overwritten |
| 19 | list item 1: value2 |
| 20 | """ |
| 21 | |
| 22 | assert doc(m.get_list) == "get_list() -> list" |
| 23 | assert doc(m.print_list) == "print_list(arg0: list) -> None" |
| 24 | |
| 25 | |
| 26 | def test_set(capture, doc): |
| 27 | s = m.get_set() |
| 28 | assert s == {"key1", "key2", "key3"} |
| 29 | |
| 30 | with capture: |
| 31 | s.add("key4") |
| 32 | m.print_set(s) |
| 33 | assert capture.unordered == """ |
| 34 | key: key1 |
| 35 | key: key2 |
| 36 | key: key3 |
| 37 | key: key4 |
| 38 | """ |
| 39 | |
| 40 | assert doc(m.get_list) == "get_list() -> list" |
| 41 | assert doc(m.print_list) == "print_list(arg0: list) -> None" |
| 42 | |
| 43 | |
| 44 | def test_dict(capture, doc): |
| 45 | d = m.get_dict() |
| 46 | assert d == {"key": "value"} |
| 47 | |
| 48 | with capture: |
| 49 | d["key2"] = "value2" |
| 50 | m.print_dict(d) |
| 51 | assert capture.unordered == """ |
| 52 | key: key, value=value |
| 53 | key: key2, value=value2 |
| 54 | """ |
| 55 | |
| 56 | assert doc(m.get_dict) == "get_dict() -> dict" |
| 57 | assert doc(m.print_dict) == "print_dict(arg0: dict) -> None" |
| 58 | |
| 59 | assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3} |
| 60 | |
| 61 | |
| 62 | def test_str(doc): |
| 63 | assert m.str_from_string().encode().decode() == "baz" |
| 64 | assert m.str_from_bytes().encode().decode() == "boo" |
| 65 | |
| 66 | assert doc(m.str_from_bytes) == "str_from_bytes() -> str" |
| 67 | |
| 68 | class A(object): |
| 69 | def __str__(self): |
| 70 | return "this is a str" |
| 71 | |
| 72 | def __repr__(self): |
| 73 | return "this is a repr" |
| 74 | |
| 75 | assert m.str_from_object(A()) == "this is a str" |
| 76 | assert m.repr_from_object(A()) == "this is a repr" |
| 77 | |
| 78 | s1, s2 = m.str_format() |
| 79 | assert s1 == "1 + 2 = 3" |
| 80 | assert s1 == s2 |
| 81 | |
| 82 | |
| 83 | def test_bytes(doc): |
| 84 | assert m.bytes_from_string().decode() == "foo" |
| 85 | assert m.bytes_from_str().decode() == "bar" |
| 86 | |
| 87 | assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( |
| 88 | "bytes" if sys.version_info[0] == 3 else "str" |
| 89 | ) |
| 90 | |
| 91 | |
| 92 | def test_capsule(capture): |
| 93 | pytest.gc_collect() |
| 94 | with capture: |
| 95 | a = m.return_capsule_with_destructor() |
| 96 | del a |
| 97 | pytest.gc_collect() |
| 98 | assert capture.unordered == """ |
| 99 | creating capsule |
| 100 | destructing capsule |
| 101 | """ |
| 102 | |
| 103 | with capture: |
| 104 | a = m.return_capsule_with_destructor_2() |
| 105 | del a |
| 106 | pytest.gc_collect() |
| 107 | assert capture.unordered == """ |
| 108 | creating capsule |
| 109 | destructing capsule: 1234 |
| 110 | """ |
| 111 | |
| 112 | with capture: |
| 113 | a = m.return_capsule_with_name_and_destructor() |
| 114 | del a |
| 115 | pytest.gc_collect() |
| 116 | assert capture.unordered == """ |
| 117 | created capsule (1234, 'pointer type description') |
| 118 | destructing capsule (1234, 'pointer type description') |
| 119 | """ |
| 120 | |
| 121 | |
| 122 | def test_accessors(): |
| 123 | class SubTestObject: |
| 124 | attr_obj = 1 |
| 125 | attr_char = 2 |
| 126 | |
| 127 | class TestObject: |
| 128 | basic_attr = 1 |
| 129 | begin_end = [1, 2, 3] |
| 130 | d = {"operator[object]": 1, "operator[char *]": 2} |
| 131 | sub = SubTestObject() |
| 132 | |
| 133 | def func(self, x, *args): |
| 134 | return self.basic_attr + x + sum(args) |
| 135 | |
| 136 | d = m.accessor_api(TestObject()) |
| 137 | assert d["basic_attr"] == 1 |
| 138 | assert d["begin_end"] == [1, 2, 3] |
| 139 | assert d["operator[object]"] == 1 |
| 140 | assert d["operator[char *]"] == 2 |
| 141 | assert d["attr(object)"] == 1 |
| 142 | assert d["attr(char *)"] == 2 |
| 143 | assert d["missing_attr_ptr"] == "raised" |
| 144 | assert d["missing_attr_chain"] == "raised" |
| 145 | assert d["is_none"] is False |
| 146 | assert d["operator()"] == 2 |
| 147 | assert d["operator*"] == 7 |
Dean Moldovan | 2cf87a5 | 2017-09-10 12:21:21 +0200 | [diff] [blame] | 148 | assert d["implicit_list"] == [1, 2, 3] |
| 149 | assert all(x in TestObject.__dict__ for x in d["implicit_dict"]) |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 150 | |
| 151 | assert m.tuple_accessor(tuple()) == (0, 1, 2) |
| 152 | |
| 153 | d = m.accessor_assignment() |
| 154 | assert d["get"] == 0 |
| 155 | assert d["deferred_get"] == 0 |
| 156 | assert d["set"] == 1 |
| 157 | assert d["deferred_set"] == 1 |
| 158 | assert d["var"] == 99 |
| 159 | |
| 160 | |
| 161 | def test_constructors(): |
| 162 | """C++ default and converting constructors are equivalent to type calls in Python""" |
| 163 | types = [str, bool, int, float, tuple, list, dict, set] |
| 164 | expected = {t.__name__: t() for t in types} |
| 165 | assert m.default_constructors() == expected |
| 166 | |
| 167 | data = { |
| 168 | str: 42, |
| 169 | bool: "Not empty", |
| 170 | int: "42", |
| 171 | float: "+1e3", |
| 172 | tuple: range(3), |
| 173 | list: range(3), |
| 174 | dict: [("two", 2), ("one", 1), ("three", 3)], |
| 175 | set: [4, 4, 5, 6, 6, 6], |
| 176 | memoryview: b'abc' |
| 177 | } |
| 178 | inputs = {k.__name__: v for k, v in data.items()} |
| 179 | expected = {k.__name__: k(v) for k, v in data.items()} |
Jason Rhinelander | 373da82 | 2017-08-03 19:27:04 -0400 | [diff] [blame] | 180 | |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 181 | assert m.converting_constructors(inputs) == expected |
| 182 | assert m.cast_functions(inputs) == expected |
| 183 | |
Jason Rhinelander | 373da82 | 2017-08-03 19:27:04 -0400 | [diff] [blame] | 184 | # Converting constructors and cast functions should just reference rather |
| 185 | # than copy when no conversion is needed: |
| 186 | noconv1 = m.converting_constructors(expected) |
| 187 | for k in noconv1: |
| 188 | assert noconv1[k] is expected[k] |
| 189 | |
| 190 | noconv2 = m.cast_functions(expected) |
| 191 | for k in noconv2: |
| 192 | assert noconv2[k] is expected[k] |
| 193 | |
Dean Moldovan | 83e328f | 2017-06-09 00:44:49 +0200 | [diff] [blame] | 194 | |
| 195 | def test_implicit_casting(): |
| 196 | """Tests implicit casting when assigning or appending to dicts and lists.""" |
| 197 | z = m.get_implicit_casting() |
| 198 | assert z['d'] == { |
| 199 | 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc', |
| 200 | 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3', |
| 201 | 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44 |
| 202 | } |
| 203 | assert z['l'] == [3, 6, 9, 12, 15] |
| 204 | |
| 205 | |
| 206 | def test_print(capture): |
| 207 | with capture: |
| 208 | m.print_function() |
| 209 | assert capture == """ |
| 210 | Hello, World! |
| 211 | 1 2.0 three True -- multiple args |
| 212 | *args-and-a-custom-separator |
| 213 | no new line here -- next print |
| 214 | flush |
| 215 | py::print + str.format = this |
| 216 | """ |
| 217 | assert capture.stderr == "this goes to stderr" |
| 218 | |
| 219 | with pytest.raises(RuntimeError) as excinfo: |
| 220 | m.print_failure() |
| 221 | assert str(excinfo.value) == "make_tuple(): unable to convert " + ( |
| 222 | "argument of type 'UnregisteredType' to Python object" |
| 223 | if debug_enabled else |
| 224 | "arguments to Python object (compile in debug mode for details)" |
| 225 | ) |
Bruce Merry | 37de2da | 2017-08-30 14:22:00 +0200 | [diff] [blame] | 226 | |
| 227 | |
| 228 | def test_hash(): |
| 229 | class Hashable(object): |
| 230 | def __init__(self, value): |
| 231 | self.value = value |
| 232 | |
| 233 | def __hash__(self): |
| 234 | return self.value |
| 235 | |
| 236 | class Unhashable(object): |
| 237 | __hash__ = None |
| 238 | |
| 239 | assert m.hash_function(Hashable(42)) == 42 |
| 240 | with pytest.raises(TypeError): |
| 241 | m.hash_function(Unhashable()) |
Wenzel Jakob | 0671002 | 2018-09-01 01:09:16 +0200 | [diff] [blame] | 242 | |
| 243 | |
| 244 | def test_number_protocol(): |
| 245 | for a, b in [(1, 1), (3, 5)]: |
| 246 | li = [a == b, a != b, a < b, a <= b, a > b, a >= b, a + b, |
| 247 | a - b, a * b, a / b, a | b, a & b, a ^ b, a >> b, a << b] |
| 248 | assert m.test_number_protocol(a, b) == li |
Wenzel Jakob | b4b2292 | 2018-09-01 01:19:16 +0200 | [diff] [blame^] | 249 | |
| 250 | |
| 251 | def test_list_slicing(): |
| 252 | li = list(range(100)) |
| 253 | assert li[::2] == m.test_list_slicing(li) |