| # Python < 3 needs this: coding=utf-8 |
| import pytest |
| |
| from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional |
| |
| |
| def test_repr(): |
| # In Python 3.3+, repr() accesses __qualname__ |
| assert "pybind11_type" in repr(type(ExamplePythonTypes)) |
| assert "ExamplePythonTypes" in repr(ExamplePythonTypes) |
| |
| |
| def test_static(): |
| ExamplePythonTypes.value = 15 |
| assert ExamplePythonTypes.value == 15 |
| assert ExamplePythonTypes.value2 == 5 |
| |
| with pytest.raises(AttributeError) as excinfo: |
| ExamplePythonTypes.value2 = 15 |
| assert str(excinfo.value) == "can't set attribute" |
| |
| |
| def test_instance(capture): |
| with pytest.raises(TypeError) as excinfo: |
| ExamplePythonTypes() |
| assert str(excinfo.value) == "pybind11_tests.ExamplePythonTypes: No constructor defined!" |
| |
| instance = ExamplePythonTypes.new_instance() |
| |
| with capture: |
| dict_result = instance.get_dict() |
| dict_result['key2'] = 'value2' |
| instance.print_dict(dict_result) |
| assert capture.unordered == """ |
| key: key, value=value |
| key: key2, value=value2 |
| """ |
| with capture: |
| dict_result = instance.get_dict_2() |
| dict_result['key2'] = 'value2' |
| instance.print_dict_2(dict_result) |
| assert capture.unordered == """ |
| key: key, value=value |
| key: key2, value=value2 |
| """ |
| with capture: |
| set_result = instance.get_set() |
| set_result.add('key4') |
| instance.print_set(set_result) |
| assert capture.unordered == """ |
| key: key1 |
| key: key2 |
| key: key3 |
| key: key4 |
| """ |
| with capture: |
| set_result = instance.get_set2() |
| set_result.add('key3') |
| instance.print_set_2(set_result) |
| assert capture.unordered == """ |
| key: key1 |
| key: key2 |
| key: key3 |
| """ |
| with capture: |
| list_result = instance.get_list() |
| list_result.append('value2') |
| instance.print_list(list_result) |
| assert capture.unordered == """ |
| Entry at position 0: value |
| list item 0: overwritten |
| list item 1: value2 |
| """ |
| with capture: |
| list_result = instance.get_list_2() |
| list_result.append('value2') |
| instance.print_list_2(list_result) |
| assert capture.unordered == """ |
| list item 0: value |
| list item 1: value2 |
| """ |
| with capture: |
| list_result = instance.get_list_2() |
| list_result.append('value2') |
| instance.print_list_2(tuple(list_result)) |
| assert capture.unordered == """ |
| list item 0: value |
| list item 1: value2 |
| """ |
| array_result = instance.get_array() |
| assert array_result == ['array entry 1', 'array entry 2'] |
| with capture: |
| instance.print_array(array_result) |
| assert capture.unordered == """ |
| array item 0: array entry 1 |
| array item 1: array entry 2 |
| """ |
| varray_result = instance.get_valarray() |
| assert varray_result == [1, 4, 9] |
| with capture: |
| instance.print_valarray(varray_result) |
| assert capture.unordered == """ |
| valarray item 0: 1 |
| valarray item 1: 4 |
| valarray item 2: 9 |
| """ |
| with pytest.raises(RuntimeError) as excinfo: |
| instance.throw_exception() |
| assert str(excinfo.value) == "This exception was intentionally thrown." |
| |
| assert instance.pair_passthrough((True, "test")) == ("test", True) |
| assert instance.tuple_passthrough((True, "test", 5)) == (5, "test", True) |
| # Any sequence can be cast to a std::pair or std::tuple |
| assert instance.pair_passthrough([True, "test"]) == ("test", True) |
| assert instance.tuple_passthrough([True, "test", 5]) == (5, "test", True) |
| |
| assert instance.get_bytes_from_string().decode() == "foo" |
| assert instance.get_bytes_from_str().decode() == "bar" |
| assert instance.get_str_from_string().encode().decode() == "baz" |
| assert instance.get_str_from_bytes().encode().decode() == "boo" |
| |
| class A(object): |
| def __str__(self): |
| return "this is a str" |
| |
| def __repr__(self): |
| return "this is a repr" |
| |
| with capture: |
| instance.test_print(A()) |
| assert capture == """ |
| this is a str |
| this is a repr |
| """ |
| |
| cstats = ConstructorStats.get(ExamplePythonTypes) |
| assert cstats.alive() == 1 |
| del instance |
| assert cstats.alive() == 0 |
| |
| |
| # PyPy does not seem to propagate the tp_docs field at the moment |
| def test_class_docs(doc): |
| assert doc(ExamplePythonTypes) == "Example 2 documentation" |
| |
| |
| def test_method_docs(doc): |
| assert doc(ExamplePythonTypes.get_dict) == """ |
| get_dict(self: m.ExamplePythonTypes) -> dict |
| |
| Return a Python dictionary |
| """ |
| assert doc(ExamplePythonTypes.get_dict_2) == """ |
| get_dict_2(self: m.ExamplePythonTypes) -> Dict[str, str] |
| |
| Return a C++ dictionary |
| """ |
| assert doc(ExamplePythonTypes.get_list) == """ |
| get_list(self: m.ExamplePythonTypes) -> list |
| |
| Return a Python list |
| """ |
| assert doc(ExamplePythonTypes.get_list_2) == """ |
| get_list_2(self: m.ExamplePythonTypes) -> List[str] |
| |
| Return a C++ list |
| """ |
| assert doc(ExamplePythonTypes.get_dict) == """ |
| get_dict(self: m.ExamplePythonTypes) -> dict |
| |
| Return a Python dictionary |
| """ |
| assert doc(ExamplePythonTypes.get_set) == """ |
| get_set(self: m.ExamplePythonTypes) -> set |
| |
| Return a Python set |
| """ |
| assert doc(ExamplePythonTypes.get_set2) == """ |
| get_set2(self: m.ExamplePythonTypes) -> Set[str] |
| |
| Return a C++ set |
| """ |
| assert doc(ExamplePythonTypes.get_array) == """ |
| get_array(self: m.ExamplePythonTypes) -> List[str[2]] |
| |
| Return a C++ array |
| """ |
| assert doc(ExamplePythonTypes.get_valarray) == """ |
| get_valarray(self: m.ExamplePythonTypes) -> List[int] |
| |
| Return a C++ valarray |
| """ |
| assert doc(ExamplePythonTypes.print_dict) == """ |
| print_dict(self: m.ExamplePythonTypes, arg0: dict) -> None |
| |
| Print entries of a Python dictionary |
| """ |
| assert doc(ExamplePythonTypes.print_dict_2) == """ |
| print_dict_2(self: m.ExamplePythonTypes, arg0: Dict[str, str]) -> None |
| |
| Print entries of a C++ dictionary |
| """ |
| assert doc(ExamplePythonTypes.print_set) == """ |
| print_set(self: m.ExamplePythonTypes, arg0: set) -> None |
| |
| Print entries of a Python set |
| """ |
| assert doc(ExamplePythonTypes.print_set_2) == """ |
| print_set_2(self: m.ExamplePythonTypes, arg0: Set[str]) -> None |
| |
| Print entries of a C++ set |
| """ |
| assert doc(ExamplePythonTypes.print_list) == """ |
| print_list(self: m.ExamplePythonTypes, arg0: list) -> None |
| |
| Print entries of a Python list |
| """ |
| assert doc(ExamplePythonTypes.print_list_2) == """ |
| print_list_2(self: m.ExamplePythonTypes, arg0: List[str]) -> None |
| |
| Print entries of a C++ list |
| """ |
| assert doc(ExamplePythonTypes.print_array) == """ |
| print_array(self: m.ExamplePythonTypes, arg0: List[str[2]]) -> None |
| |
| Print entries of a C++ array |
| """ |
| assert doc(ExamplePythonTypes.pair_passthrough) == """ |
| pair_passthrough(self: m.ExamplePythonTypes, arg0: Tuple[bool, str]) -> Tuple[str, bool] |
| |
| Return a pair in reversed order |
| """ |
| assert doc(ExamplePythonTypes.tuple_passthrough) == """ |
| tuple_passthrough(self: m.ExamplePythonTypes, arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool] |
| |
| Return a triple in reversed order |
| """ # noqa: E501 line too long |
| assert doc(ExamplePythonTypes.throw_exception) == """ |
| throw_exception(self: m.ExamplePythonTypes) -> None |
| |
| Throw an exception |
| """ |
| assert doc(ExamplePythonTypes.new_instance) == """ |
| new_instance() -> m.ExamplePythonTypes |
| |
| Return an instance |
| """ |
| |
| |
| def test_module(): |
| import pybind11_tests |
| |
| assert pybind11_tests.__name__ == "pybind11_tests" |
| assert ExamplePythonTypes.__name__ == "ExamplePythonTypes" |
| assert ExamplePythonTypes.__module__ == "pybind11_tests" |
| assert ExamplePythonTypes.get_set.__name__ == "get_set" |
| assert ExamplePythonTypes.get_set.__module__ == "pybind11_tests" |
| |
| |
| def test_print(capture): |
| from pybind11_tests import test_print_function |
| |
| with capture: |
| test_print_function() |
| assert capture == """ |
| Hello, World! |
| 1 2.0 three True -- multiple args |
| *args-and-a-custom-separator |
| no new line here -- next print |
| flush |
| py::print + str.format = this |
| """ |
| assert capture.stderr == "this goes to stderr" |
| |
| |
| def test_str_api(): |
| from pybind11_tests import test_str_format |
| |
| s1, s2 = test_str_format() |
| assert s1 == "1 + 2 = 3" |
| assert s1 == s2 |
| |
| |
| def test_dict_api(): |
| from pybind11_tests import test_dict_keyword_constructor |
| |
| assert test_dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3} |
| |
| |
| def test_accessors(): |
| from pybind11_tests import test_accessor_api, test_tuple_accessor, test_accessor_assignment |
| |
| class SubTestObject: |
| attr_obj = 1 |
| attr_char = 2 |
| |
| class TestObject: |
| basic_attr = 1 |
| begin_end = [1, 2, 3] |
| d = {"operator[object]": 1, "operator[char *]": 2} |
| sub = SubTestObject() |
| |
| def func(self, x, *args): |
| return self.basic_attr + x + sum(args) |
| |
| d = test_accessor_api(TestObject()) |
| assert d["basic_attr"] == 1 |
| assert d["begin_end"] == [1, 2, 3] |
| assert d["operator[object]"] == 1 |
| assert d["operator[char *]"] == 2 |
| assert d["attr(object)"] == 1 |
| assert d["attr(char *)"] == 2 |
| assert d["missing_attr_ptr"] == "raised" |
| assert d["missing_attr_chain"] == "raised" |
| assert d["is_none"] is False |
| assert d["operator()"] == 2 |
| assert d["operator*"] == 7 |
| |
| assert test_tuple_accessor(tuple()) == (0, 1, 2) |
| |
| d = test_accessor_assignment() |
| assert d["get"] == 0 |
| assert d["deferred_get"] == 0 |
| assert d["set"] == 1 |
| assert d["deferred_set"] == 1 |
| assert d["var"] == 99 |
| |
| |
| @pytest.mark.skipif(not has_optional, reason='no <optional>') |
| def test_optional(): |
| from pybind11_tests import double_or_zero, half_or_none, test_nullopt |
| |
| assert double_or_zero(None) == 0 |
| assert double_or_zero(42) == 84 |
| pytest.raises(TypeError, double_or_zero, 'foo') |
| |
| assert half_or_none(0) is None |
| assert half_or_none(42) == 21 |
| pytest.raises(TypeError, half_or_none, 'foo') |
| |
| assert test_nullopt() == 42 |
| assert test_nullopt(None) == 42 |
| assert test_nullopt(42) == 42 |
| assert test_nullopt(43) == 43 |
| |
| |
| @pytest.mark.skipif(not has_exp_optional, reason='no <experimental/optional>') |
| def test_exp_optional(): |
| from pybind11_tests import double_or_zero_exp, half_or_none_exp, test_nullopt_exp |
| |
| assert double_or_zero_exp(None) == 0 |
| assert double_or_zero_exp(42) == 84 |
| pytest.raises(TypeError, double_or_zero_exp, 'foo') |
| |
| assert half_or_none_exp(0) is None |
| assert half_or_none_exp(42) == 21 |
| pytest.raises(TypeError, half_or_none_exp, 'foo') |
| |
| assert test_nullopt_exp() == 42 |
| assert test_nullopt_exp(None) == 42 |
| assert test_nullopt_exp(42) == 42 |
| assert test_nullopt_exp(43) == 43 |
| |
| |
| def test_constructors(): |
| """C++ default and converting constructors are equivalent to type calls in Python""" |
| from pybind11_tests import (test_default_constructors, test_converting_constructors, |
| test_cast_functions) |
| |
| types = [str, bool, int, float, tuple, list, dict, set] |
| expected = {t.__name__: t() for t in types} |
| assert test_default_constructors() == expected |
| |
| data = { |
| str: 42, |
| bool: "Not empty", |
| int: "42", |
| float: "+1e3", |
| tuple: range(3), |
| list: range(3), |
| dict: [("two", 2), ("one", 1), ("three", 3)], |
| set: [4, 4, 5, 6, 6, 6], |
| memoryview: b'abc' |
| } |
| inputs = {k.__name__: v for k, v in data.items()} |
| expected = {k.__name__: k(v) for k, v in data.items()} |
| assert test_converting_constructors(inputs) == expected |
| assert test_cast_functions(inputs) == expected |
| |
| |
| def test_move_out_container(): |
| """Properties use the `reference_internal` policy by default. If the underlying function |
| returns an rvalue, the policy is automatically changed to `move` to avoid referencing |
| a temporary. In case the return value is a container of user-defined types, the policy |
| also needs to be applied to the elements, not just the container.""" |
| from pybind11_tests import MoveOutContainer |
| |
| c = MoveOutContainer() |
| moved_out_list = c.move_list |
| assert [x.value for x in moved_out_list] == [0, 1, 2] |
| |
| |
| def test_implicit_casting(): |
| """Tests implicit casting when assigning or appending to dicts and lists.""" |
| from pybind11_tests import get_implicit_casting |
| |
| z = get_implicit_casting() |
| assert z['d'] == { |
| 'char*_i1': 'abc', 'char*_i2': 'abc', 'char*_e': 'abc', 'char*_p': 'abc', |
| 'str_i1': 'str', 'str_i2': 'str1', 'str_e': 'str2', 'str_p': 'str3', |
| 'int_i1': 42, 'int_i2': 42, 'int_e': 43, 'int_p': 44 |
| } |
| assert z['l'] == [3, 6, 9, 12, 15] |
| |
| |
| def test_unicode_conversion(): |
| """Tests unicode conversion and error reporting.""" |
| import pybind11_tests |
| from pybind11_tests import (good_utf8_string, bad_utf8_string, |
| good_utf16_string, bad_utf16_string, |
| good_utf32_string, # bad_utf32_string, |
| good_wchar_string, # bad_wchar_string, |
| u8_Z, u8_eacute, u16_ibang, u32_mathbfA, wchar_heart) |
| |
| assert good_utf8_string() == u"Say utf8β½ π π" |
| assert good_utf16_string() == u"bβ½ππz" |
| assert good_utf32_string() == u"aππβ½z" |
| assert good_wchar_string() == u"aβΈπz" |
| |
| with pytest.raises(UnicodeDecodeError): |
| bad_utf8_string() |
| |
| with pytest.raises(UnicodeDecodeError): |
| bad_utf16_string() |
| |
| # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7) |
| if hasattr(pybind11_tests, "bad_utf32_string"): |
| with pytest.raises(UnicodeDecodeError): |
| pybind11_tests.bad_utf32_string() |
| if hasattr(pybind11_tests, "bad_wchar_string"): |
| with pytest.raises(UnicodeDecodeError): |
| pybind11_tests.bad_wchar_string() |
| |
| assert u8_Z() == 'Z' |
| assert u8_eacute() == u'Γ©' |
| assert u16_ibang() == u'β½' |
| assert u32_mathbfA() == u'π' |
| assert wchar_heart() == u'β₯' |
| |
| |
| def test_single_char_arguments(): |
| """Tests failures for passing invalid inputs to char-accepting functions""" |
| from pybind11_tests import ord_char, ord_char16, ord_char32, ord_wchar, wchar_size |
| |
| def toobig_message(r): |
| return "Character code point not in range({0:#x})".format(r) |
| toolong_message = "Expected a character, but multi-character string found" |
| |
| assert ord_char(u'a') == 0x61 # simple ASCII |
| assert ord_char(u'Γ©') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_char(u'Δ') == 0x100 # requires 2 bytes, doesn't fit in a char |
| assert str(excinfo.value) == toobig_message(0x100) |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_char(u'ab') |
| assert str(excinfo.value) == toolong_message |
| |
| assert ord_char16(u'a') == 0x61 |
| assert ord_char16(u'Γ©') == 0xE9 |
| assert ord_char16(u'Δ') == 0x100 |
| assert ord_char16(u'β½') == 0x203d |
| assert ord_char16(u'β₯') == 0x2665 |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_char16(u'π') == 0x1F382 # requires surrogate pair |
| assert str(excinfo.value) == toobig_message(0x10000) |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_char16(u'aa') |
| assert str(excinfo.value) == toolong_message |
| |
| assert ord_char32(u'a') == 0x61 |
| assert ord_char32(u'Γ©') == 0xE9 |
| assert ord_char32(u'Δ') == 0x100 |
| assert ord_char32(u'β½') == 0x203d |
| assert ord_char32(u'β₯') == 0x2665 |
| assert ord_char32(u'π') == 0x1F382 |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_char32(u'aa') |
| assert str(excinfo.value) == toolong_message |
| |
| assert ord_wchar(u'a') == 0x61 |
| assert ord_wchar(u'Γ©') == 0xE9 |
| assert ord_wchar(u'Δ') == 0x100 |
| assert ord_wchar(u'β½') == 0x203d |
| assert ord_wchar(u'β₯') == 0x2665 |
| if wchar_size == 2: |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_wchar(u'π') == 0x1F382 # requires surrogate pair |
| assert str(excinfo.value) == toobig_message(0x10000) |
| else: |
| assert ord_wchar(u'π') == 0x1F382 |
| with pytest.raises(ValueError) as excinfo: |
| assert ord_wchar(u'aa') |
| assert str(excinfo.value) == toolong_message |
| |
| |
| def test_builtins_cast_return_none(): |
| """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None""" |
| import pybind11_tests as m |
| |
| assert m.return_none_string() is None |
| assert m.return_none_char() is None |
| assert m.return_none_bool() is None |
| assert m.return_none_int() is None |
| assert m.return_none_float() is None |
| |
| |
| def test_capsule_with_destructor(capture): |
| import pybind11_tests as m |
| with capture: |
| a = m.return_capsule_with_destructor() |
| del a |
| pytest.gc_collect() |
| assert capture.unordered == """ |
| creating capsule |
| destructing capsule |
| """ |
| |
| with capture: |
| a = m.return_capsule_with_destructor_2() |
| del a |
| pytest.gc_collect() |
| assert capture.unordered == """ |
| creating capsule |
| destructing capsule: 1234 |
| """ |