summaryrefslogtreecommitdiff
path: root/ext/pybind11/tests/test_python_types.py
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pybind11/tests/test_python_types.py')
-rw-r--r--ext/pybind11/tests/test_python_types.py402
1 files changed, 402 insertions, 0 deletions
diff --git a/ext/pybind11/tests/test_python_types.py b/ext/pybind11/tests/test_python_types.py
new file mode 100644
index 000000000..9fe1ef71e
--- /dev/null
+++ b/ext/pybind11/tests/test_python_types.py
@@ -0,0 +1,402 @@
+import pytest
+
+from pybind11_tests import ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional
+
+
+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
+
+
+def test_docs(doc):
+ assert doc(ExamplePythonTypes) == "Example 2 documentation"
+ 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]