From 83e328f58c18799d370520ff016aa33163fb1e8d Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Fri, 9 Jun 2017 00:44:49 +0200 Subject: [PATCH] Split test_python_types.cpp into builtin_casters, stl and pytypes --- docs/advanced/cast/stl.rst | 2 +- docs/advanced/pycpp/object.rst | 2 +- tests/CMakeLists.txt | 6 +- tests/pybind11_tests.cpp | 32 +- tests/pybind11_tests.h | 39 +- tests/test_builtin_casters.cpp | 136 ++++ tests/test_builtin_casters.py | 221 ++++++ tests/test_callbacks.cpp | 7 +- tests/{test_class_args.cpp => test_class.cpp} | 22 +- tests/test_class.py | 44 ++ tests/test_class_args.py | 8 - tests/test_exceptions.cpp | 4 + tests/test_exceptions.py | 8 + tests/test_inheritance.cpp | 4 +- tests/test_modules.py | 1 + tests/test_python_types.cpp | 695 ----------------- tests/test_python_types.py | 715 ------------------ tests/test_pytypes.cpp | 264 +++++++ tests/test_pytypes.py | 211 ++++++ tests/test_sequences_and_iterators.py | 1 - tests/test_stl.cpp | 163 ++++ tests/test_stl.py | 133 ++++ 22 files changed, 1276 insertions(+), 1442 deletions(-) create mode 100644 tests/test_builtin_casters.cpp create mode 100644 tests/test_builtin_casters.py rename tests/{test_class_args.cpp => test_class.cpp} (86%) create mode 100644 tests/test_class.py delete mode 100644 tests/test_class_args.py delete mode 100644 tests/test_python_types.cpp delete mode 100644 tests/test_python_types.py create mode 100644 tests/test_pytypes.cpp create mode 100644 tests/test_pytypes.py create mode 100644 tests/test_stl.cpp create mode 100644 tests/test_stl.py diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst index 47c2a9670..23e67516b 100644 --- a/docs/advanced/cast/stl.rst +++ b/docs/advanced/cast/stl.rst @@ -23,7 +23,7 @@ next sections for more details and alternative approaches that avoid this. .. seealso:: - The file :file:`tests/test_python_types.cpp` contains a complete + The file :file:`tests/test_stl.cpp` contains a complete example that demonstrates how to pass STL data types in more detail. C++17 library containers diff --git a/docs/advanced/pycpp/object.rst b/docs/advanced/pycpp/object.rst index ae58876de..c9728e922 100644 --- a/docs/advanced/pycpp/object.rst +++ b/docs/advanced/pycpp/object.rst @@ -90,7 +90,7 @@ Generalized unpacking according to PEP448_ is also supported: .. seealso:: - The file :file:`tests/test_python_types.cpp` contains a complete + The file :file:`tests/test_pytypes.cpp` contains a complete example that demonstrates passing native Python types in more detail. The file :file:`tests/test_callbacks.cpp` presents a few examples of calling Python functions from C++, including keywords arguments and unpacking. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index be223f577..a57896d44 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,10 +28,11 @@ endif() set(PYBIND11_TEST_FILES test_alias_initialization.cpp test_buffers.cpp + test_builtin_casters.cpp test_call_policies.cpp test_callbacks.cpp test_chrono.cpp - test_class_args.cpp + test_class.cpp test_constants_and_functions.cpp test_copy_move.cpp test_docstring_options.cpp @@ -50,9 +51,10 @@ set(PYBIND11_TEST_FILES test_opaque_types.cpp test_operator_overloading.cpp test_pickling.cpp - test_python_types.cpp + test_pytypes.cpp test_sequences_and_iterators.cpp test_smart_ptr.cpp + test_stl.cpp test_stl_binders.cpp test_virtual_functions.cpp ) diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp index 1e243501a..81fe8bfbe 100644 --- a/tests/pybind11_tests.cpp +++ b/tests/pybind11_tests.cpp @@ -10,6 +10,9 @@ #include "pybind11_tests.h" #include "constructor_stats.h" +#include +#include + /* For testing purposes, we define a static global variable here in a function that each individual test .cpp calls with its initialization lambda. It's convenient here because we can just not @@ -28,8 +31,15 @@ std::list> &initializers() { return inits; } -test_initializer::test_initializer(std::function initializer) { - initializers().push_back(std::move(initializer)); +test_initializer::test_initializer(Initializer init) { + initializers().push_back(init); +} + +test_initializer::test_initializer(const char *submodule_name, Initializer init) { + initializers().push_back([=](py::module &parent) { + auto m = parent.def_submodule(submodule_name); + init(m); + }); } void bind_ConstructorStats(py::module &m) { @@ -57,6 +67,24 @@ PYBIND11_MODULE(pybind11_tests, m) { bind_ConstructorStats(m); +#if !defined(NDEBUG) + m.attr("debug_enabled") = true; +#else + m.attr("debug_enabled") = false; +#endif + + py::class_(m, "UserType", "A `py::class_` type for testing") + .def(py::init<>()) + .def(py::init()) + .def("get_value", &UserType::value, "Get value using a method") + .def_property_readonly("value", &UserType::value, "Get value using a property") + .def("__repr__", [](const UserType& u) { return "UserType({})"_s.format(u.value()); }); + + py::class_(m, "IncType") + .def(py::init<>()) + .def(py::init()) + .def("__repr__", [](const IncType& u) { return "IncType({})"_s.format(u.value()); }); + for (const auto &initializer : initializers()) initializer(m); diff --git a/tests/pybind11_tests.h b/tests/pybind11_tests.h index c11b687b2..dd8d15931 100644 --- a/tests/pybind11_tests.h +++ b/tests/pybind11_tests.h @@ -1,12 +1,45 @@ #pragma once #include -#include -#include namespace py = pybind11; using namespace pybind11::literals; class test_initializer { + using Initializer = void (*)(py::module &); + public: - test_initializer(std::function initializer); + test_initializer(Initializer init); + test_initializer(const char *submodule_name, Initializer init); +}; + +#define TEST_SUBMODULE(name, variable) \ + void test_submodule_##name(py::module &); \ + test_initializer name(#name, test_submodule_##name); \ + void test_submodule_##name(py::module &variable) + + +/// Dummy type which is not exported anywhere -- something to trigger a conversion error +struct UnregisteredType { }; + +/// A user-defined type which is exported and can be used by any test +class UserType { +public: + UserType() = default; + UserType(int i) : i(i) { } + + int value() const { return i; } + +private: + int i = -1; +}; + +/// Like UserType, but increments `value` on copy for quick reference vs. copy tests +class IncType : public UserType { +public: + using UserType::UserType; + IncType() = default; + IncType(const IncType &other) : IncType(other.value() + 1) { } + IncType(IncType &&) = delete; + IncType &operator=(const IncType &) = delete; + IncType &operator=(IncType &&) = delete; }; diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp new file mode 100644 index 000000000..33fe689a0 --- /dev/null +++ b/tests/test_builtin_casters.cpp @@ -0,0 +1,136 @@ +/* + tests/test_builtin_casters.cpp -- Casters available without any additional headers + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +TEST_SUBMODULE(builtin_casters, m) { + // test_simple_string + m.def("string_roundtrip", [](const char *s) { return s; }); + + // test_unicode_conversion + // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte + char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*β€½*/, cake32 = 0x1f382 /*πŸŽ‚*/, mathbfA32 = 0x1d400 /*𝐀*/; + char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; + std::wstring wstr; + wstr.push_back(0x61); // a + wstr.push_back(0x2e18); // ⸘ + if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 + else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 + wstr.push_back(0x7a); // z + + m.def("good_utf8_string", []() { return std::string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8β€½ πŸŽ‚ 𝐀 + m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // bβ€½πŸŽ‚π€z + m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // aπ€πŸŽ‚β€½z + m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z + m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); + m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); + // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError + if (PY_MAJOR_VERSION >= 3) + m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); }); + if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2) + m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); + m.def("u8_Z", []() -> char { return 'Z'; }); + m.def("u8_eacute", []() -> char { return '\xe9'; }); + m.def("u16_ibang", [=]() -> char16_t { return ib16; }); + m.def("u32_mathbfA", [=]() -> char32_t { return mathbfA32; }); + m.def("wchar_heart", []() -> wchar_t { return 0x2665; }); + + // test_single_char_arguments + m.attr("wchar_size") = py::cast(sizeof(wchar_t)); + m.def("ord_char", [](char c) -> int { return static_cast(c); }); + m.def("ord_char16", [](char16_t c) -> uint16_t { return c; }); + m.def("ord_char32", [](char32_t c) -> uint32_t { return c; }); + m.def("ord_wchar", [](wchar_t c) -> int { return c; }); + + // test_bytes_to_string + m.def("strlen", [](char *s) { return strlen(s); }); + m.def("string_length", [](std::string s) { return s.length(); }); + + // test_string_view +#ifdef PYBIND11_HAS_STRING_VIEW + m.attr("has_string_view") = true; + m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); + m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); }); + m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); }); + m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); + m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); + m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); + m.def("string_view_return", []() { return std::string_view(u8"utf8 secret \U0001f382"); }); + m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); + m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); +#endif + + // test_tuple + m.def("pair_passthrough", [](std::pair input) { + return std::make_pair(input.second, input.first); + }, "Return a pair in reversed order"); + m.def("tuple_passthrough", [](std::tuple input) { + return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); + }, "Return a triple in reversed order"); + + + // test_builtins_cast_return_none + m.def("return_none_string", []() -> std::string * { return nullptr; }); + m.def("return_none_char", []() -> const char * { return nullptr; }); + m.def("return_none_bool", []() -> bool * { return nullptr; }); + m.def("return_none_int", []() -> int * { return nullptr; }); + m.def("return_none_float", []() -> float * { return nullptr; }); + + // test_none_deferred + m.def("defer_none_cstring", [](char *) { return false; }); + m.def("defer_none_cstring", [](py::none) { return true; }); + m.def("defer_none_custom", [](UserType *) { return false; }); + m.def("defer_none_custom", [](py::none) { return true; }); + m.def("nodefer_none_void", [](void *) { return true; }); + m.def("nodefer_none_void", [](py::none) { return false; }); + + // test_void_caster + m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile + m.def("cast_nullptr_t", []() { return std::nullptr_t{}; }); + + // test_reference_wrapper + m.def("refwrap_builtin", [](std::reference_wrapper p) { return 10 * p.get(); }); + m.def("refwrap_usertype", [](std::reference_wrapper p) { return p.get().value(); }); + // Not currently supported (std::pair caster has return-by-value cast operator); + // triggers static_assert failure. + //m.def("refwrap_pair", [](std::reference_wrapper>) { }); + + m.def("refwrap_list", [](bool copy) { + static IncType x1(1), x2(2); + py::list l; + for (auto &f : {std::ref(x1), std::ref(x2)}) { + l.append(py::cast(f, copy ? py::return_value_policy::copy + : py::return_value_policy::reference)); + } + return l; + }, "copy"_a); + + m.def("refwrap_iiw", [](const IncType &w) { return w.value(); }); + m.def("refwrap_call_iiw", [](IncType &w, py::function f) { + py::list l; + l.append(f(std::ref(w))); + l.append(f(std::cref(w))); + IncType x(w.value()); + l.append(f(std::ref(x))); + IncType y(w.value()); + auto r3 = std::ref(y); + l.append(f(r3)); + return l; + }); + + // test_complex + m.def("complex_cast", [](float x) { return "{}"_s.format(x); }); + m.def("complex_cast", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); +} diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py new file mode 100644 index 000000000..59af0ee9a --- /dev/null +++ b/tests/test_builtin_casters.py @@ -0,0 +1,221 @@ +# Python < 3 needs this: coding=utf-8 +import pytest + +from pybind11_tests import builtin_casters as m +from pybind11_tests import UserType, IncType + + +def test_simple_string(): + assert m.string_roundtrip("const char *") == "const char *" + + +def test_unicode_conversion(): + """Tests unicode conversion and error reporting.""" + assert m.good_utf8_string() == u"Say utf8β€½ πŸŽ‚ 𝐀" + assert m.good_utf16_string() == u"bβ€½πŸŽ‚π€z" + assert m.good_utf32_string() == u"aπ€πŸŽ‚β€½z" + assert m.good_wchar_string() == u"aβΈ˜π€z" + + with pytest.raises(UnicodeDecodeError): + m.bad_utf8_string() + + with pytest.raises(UnicodeDecodeError): + m.bad_utf16_string() + + # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7) + if hasattr(m, "bad_utf32_string"): + with pytest.raises(UnicodeDecodeError): + m.bad_utf32_string() + if hasattr(m, "bad_wchar_string"): + with pytest.raises(UnicodeDecodeError): + m.bad_wchar_string() + + assert m.u8_Z() == 'Z' + assert m.u8_eacute() == u'Γ©' + assert m.u16_ibang() == u'β€½' + assert m.u32_mathbfA() == u'𝐀' + assert m.wchar_heart() == u'β™₯' + + +def test_single_char_arguments(): + """Tests failures for passing invalid inputs to char-accepting functions""" + 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 m.ord_char(u'a') == 0x61 # simple ASCII + assert m.ord_char(u'Γ©') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char + with pytest.raises(ValueError) as excinfo: + assert m.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 m.ord_char(u'ab') + assert str(excinfo.value) == toolong_message + + assert m.ord_char16(u'a') == 0x61 + assert m.ord_char16(u'Γ©') == 0xE9 + assert m.ord_char16(u'Δ€') == 0x100 + assert m.ord_char16(u'β€½') == 0x203d + assert m.ord_char16(u'β™₯') == 0x2665 + with pytest.raises(ValueError) as excinfo: + assert m.ord_char16(u'πŸŽ‚') == 0x1F382 # requires surrogate pair + assert str(excinfo.value) == toobig_message(0x10000) + with pytest.raises(ValueError) as excinfo: + assert m.ord_char16(u'aa') + assert str(excinfo.value) == toolong_message + + assert m.ord_char32(u'a') == 0x61 + assert m.ord_char32(u'Γ©') == 0xE9 + assert m.ord_char32(u'Δ€') == 0x100 + assert m.ord_char32(u'β€½') == 0x203d + assert m.ord_char32(u'β™₯') == 0x2665 + assert m.ord_char32(u'πŸŽ‚') == 0x1F382 + with pytest.raises(ValueError) as excinfo: + assert m.ord_char32(u'aa') + assert str(excinfo.value) == toolong_message + + assert m.ord_wchar(u'a') == 0x61 + assert m.ord_wchar(u'Γ©') == 0xE9 + assert m.ord_wchar(u'Δ€') == 0x100 + assert m.ord_wchar(u'β€½') == 0x203d + assert m.ord_wchar(u'β™₯') == 0x2665 + if m.wchar_size == 2: + with pytest.raises(ValueError) as excinfo: + assert m.ord_wchar(u'πŸŽ‚') == 0x1F382 # requires surrogate pair + assert str(excinfo.value) == toobig_message(0x10000) + else: + assert m.ord_wchar(u'πŸŽ‚') == 0x1F382 + with pytest.raises(ValueError) as excinfo: + assert m.ord_wchar(u'aa') + assert str(excinfo.value) == toolong_message + + +def test_bytes_to_string(): + """Tests the ability to pass bytes to C++ string-accepting functions. Note that this is + one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" + # Issue #816 + import sys + byte = bytes if sys.version_info[0] < 3 else str + + assert m.strlen(byte("hi")) == 2 + assert m.string_length(byte("world")) == 5 + assert m.string_length(byte("a\x00b")) == 3 + assert m.strlen(byte("a\x00b")) == 1 # C-string limitation + + # passing in a utf8 encoded string should work + assert m.string_length(u'πŸ’©'.encode("utf8")) == 4 + + +@pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no ") +def test_string_view(capture): + """Tests support for C++17 string_view arguments and return values""" + assert m.string_view_chars("Hi") == [72, 105] + assert m.string_view_chars("Hi πŸŽ‚") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] + assert m.string_view16_chars("Hi πŸŽ‚") == [72, 105, 32, 0xd83c, 0xdf82] + assert m.string_view32_chars("Hi πŸŽ‚") == [72, 105, 32, 127874] + + assert m.string_view_return() == "utf8 secret πŸŽ‚" + assert m.string_view16_return() == "utf16 secret πŸŽ‚" + assert m.string_view32_return() == "utf32 secret πŸŽ‚" + + with capture: + m.string_view_print("Hi") + m.string_view_print("utf8 πŸŽ‚") + m.string_view16_print("utf16 πŸŽ‚") + m.string_view32_print("utf32 πŸŽ‚") + assert capture == """ + Hi 2 + utf8 πŸŽ‚ 9 + utf16 πŸŽ‚ 8 + utf32 πŸŽ‚ 7 + """ + + with capture: + m.string_view_print("Hi, ascii") + m.string_view_print("Hi, utf8 πŸŽ‚") + m.string_view16_print("Hi, utf16 πŸŽ‚") + m.string_view32_print("Hi, utf32 πŸŽ‚") + assert capture == """ + Hi, ascii 9 + Hi, utf8 πŸŽ‚ 13 + Hi, utf16 πŸŽ‚ 12 + Hi, utf32 πŸŽ‚ 11 + """ + + +def test_tuple(doc): + """std::pair <-> tuple & std::tuple <-> tuple""" + assert m.pair_passthrough((True, "test")) == ("test", True) + assert m.tuple_passthrough((True, "test", 5)) == (5, "test", True) + # Any sequence can be cast to a std::pair or std::tuple + assert m.pair_passthrough([True, "test"]) == ("test", True) + assert m.tuple_passthrough([True, "test", 5]) == (5, "test", True) + + assert doc(m.pair_passthrough) == """ + pair_passthrough(arg0: Tuple[bool, str]) -> Tuple[str, bool] + + Return a pair in reversed order + """ + assert doc(m.tuple_passthrough) == """ + tuple_passthrough(arg0: Tuple[bool, str, int]) -> Tuple[int, str, bool] + + Return a triple in reversed order + """ + + +def test_builtins_cast_return_none(): + """Casters produced with PYBIND11_TYPE_CASTER() should convert nullptr to None""" + 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_none_deferred(): + """None passed as various argument types should defer to other overloads""" + assert not m.defer_none_cstring("abc") + assert m.defer_none_cstring(None) + assert not m.defer_none_custom(UserType()) + assert m.defer_none_custom(None) + assert m.nodefer_none_void(None) + + +def test_void_caster(): + assert m.load_nullptr_t(None) is None + assert m.cast_nullptr_t() is None + + +def test_reference_wrapper(): + """std::reference_wrapper for builtin and user types""" + assert m.refwrap_builtin(42) == 420 + assert m.refwrap_usertype(UserType(42)) == 42 + + with pytest.raises(TypeError) as excinfo: + m.refwrap_builtin(None) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + m.refwrap_usertype(None) + assert "incompatible function arguments" in str(excinfo.value) + + a1 = m.refwrap_list(copy=True) + a2 = m.refwrap_list(copy=True) + assert [x.value for x in a1] == [2, 3] + assert [x.value for x in a2] == [2, 3] + assert not a1[0] is a2[0] and not a1[1] is a2[1] + + b1 = m.refwrap_list(copy=False) + b2 = m.refwrap_list(copy=False) + assert [x.value for x in b1] == [1, 2] + assert [x.value for x in b2] == [1, 2] + assert b1[0] is b2[0] and b1[1] is b2[1] + + assert m.refwrap_iiw(IncType(5)) == 5 + assert m.refwrap_call_iiw(IncType(10), m.refwrap_iiw) == [10, 10, 10, 10] + + +def test_complex_cast(): + """std::complex casts""" + assert m.complex_cast(1) == "1.0" + assert m.complex_cast(2j) == "(0.0, 2.0)" diff --git a/tests/test_callbacks.cpp b/tests/test_callbacks.cpp index 41110087c..f26f6c33f 100644 --- a/tests/test_callbacks.cpp +++ b/tests/test_callbacks.cpp @@ -71,9 +71,6 @@ struct Payload { } }; -/// Something to trigger a conversion error -struct Unregistered {}; - class AbstractBase { public: virtual unsigned int func() = 0; @@ -144,11 +141,11 @@ test_initializer callbacks([](py::module &m) { }); m.def("test_arg_conversion_error1", [](py::function f) { - f(234, Unregistered(), "kw"_a=567); + f(234, UnregisteredType(), "kw"_a=567); }); m.def("test_arg_conversion_error2", [](py::function f) { - f(234, "expected_name"_a=Unregistered(), "kw"_a=567); + f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567); }); /* Test cleanup of lambda closure */ diff --git a/tests/test_class_args.cpp b/tests/test_class.cpp similarity index 86% rename from tests/test_class_args.cpp rename to tests/test_class.cpp index e18b39db2..1af9740e9 100644 --- a/tests/test_class_args.cpp +++ b/tests/test_class.cpp @@ -1,5 +1,5 @@ /* - tests/test_class_args.cpp -- tests that various way of defining a class work + tests/test_class.cpp -- test py::class_ definitions and basic functionality Copyright (c) 2016 Wenzel Jakob @@ -8,7 +8,22 @@ */ #include "pybind11_tests.h" +#include "constructor_stats.h" +TEST_SUBMODULE(class_, m) { + // test_instance + struct NoConstructor { + static NoConstructor *new_instance() { + auto *ptr = new NoConstructor(); + print_created(ptr, "via new_instance"); + return ptr; + } + ~NoConstructor() { print_destroyed(this); } + }; + + py::class_(m, "NoConstructor") + .def_static("new_instance", &NoConstructor::new_instance, "Return an instance"); +} template class BreaksBase {}; template class BreaksTramp : public BreaksBase {}; @@ -61,8 +76,3 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared); //template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {}; //typedef py::class_, BreaksBase<-6>, BreaksBase<-7>> Breaks8; //CHECK_BROKEN(8); - -test_initializer class_args([](py::module &m) { - // Just test that this compiled okay - m.def("class_args_noop", []() {}); -}); diff --git a/tests/test_class.py b/tests/test_class.py new file mode 100644 index 000000000..6e8491374 --- /dev/null +++ b/tests/test_class.py @@ -0,0 +1,44 @@ +import pytest + +from pybind11_tests import class_ as m +from pybind11_tests import UserType, ConstructorStats + + +def test_repr(): + # In Python 3.3+, repr() accesses __qualname__ + assert "pybind11_type" in repr(type(UserType)) + assert "UserType" in repr(UserType) + + +def test_instance(msg): + with pytest.raises(TypeError) as excinfo: + m.NoConstructor() + assert msg(excinfo.value) == "m.class_.NoConstructor: No constructor defined!" + + instance = m.NoConstructor.new_instance() + + cstats = ConstructorStats.get(m.NoConstructor) + assert cstats.alive() == 1 + del instance + assert cstats.alive() == 0 + + +def test_docstrings(doc): + assert doc(UserType) == "A `py::class_` type for testing" + assert UserType.__name__ == "UserType" + assert UserType.__module__ == "pybind11_tests" + assert UserType.get_value.__name__ == "get_value" + assert UserType.get_value.__module__ == "pybind11_tests" + + assert doc(UserType.get_value) == """ + get_value(self: m.UserType) -> int + + Get value using a method + """ + assert doc(UserType.value) == "Get value using a property" + + assert doc(m.NoConstructor.new_instance) == """ + new_instance() -> m.class_.NoConstructor + + Return an instance + """ diff --git a/tests/test_class_args.py b/tests/test_class_args.py deleted file mode 100644 index 40cbcec9f..000000000 --- a/tests/test_class_args.py +++ /dev/null @@ -1,8 +0,0 @@ - - -def test_class_args(): - """There's basically nothing to test here; just make sure the code compiled - and declared its definition - """ - from pybind11_tests import class_args_noop - class_args_noop() diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp index ea6bdb9f8..01efc700e 100644 --- a/tests/test_exceptions.cpp +++ b/tests/test_exceptions.cpp @@ -108,6 +108,10 @@ struct PythonCallInDestructor { }; test_initializer custom_exceptions([](py::module &m) { + m.def("throw_std_exception", []() { + throw std::runtime_error("This exception was intentionally thrown."); + }); + // make a new custom exception and use it as a translation target static py::exception ex(m, "MyException"); py::register_exception_translator([](std::exception_ptr p) { diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 887ba644e..15d47876f 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,6 +1,14 @@ import pytest +def test_std_exception(msg): + from pybind11_tests import throw_std_exception + + with pytest.raises(RuntimeError) as excinfo: + throw_std_exception() + assert msg(excinfo.value) == "This exception was intentionally thrown." + + def test_error_already_set(msg): from pybind11_tests import throw_already_set diff --git a/tests/test_inheritance.cpp b/tests/test_inheritance.cpp index 6129b3fcc..fce537d22 100644 --- a/tests/test_inheritance.cpp +++ b/tests/test_inheritance.cpp @@ -97,8 +97,6 @@ test_initializer inheritance([](py::module &m) { m.def("return_none", []() -> BaseClass* { return nullptr; }); m.def("test_isinstance", [](py::list l) { - struct Unregistered { }; // checks missing type_info code path - return py::make_tuple( py::isinstance(l[0]), py::isinstance(l[1]), @@ -106,7 +104,7 @@ test_initializer inheritance([](py::module &m) { py::isinstance(l[3]), py::isinstance(l[4]), py::isinstance(l[5]), - py::isinstance(l[6]) + py::isinstance(l[6]) ); }); diff --git a/tests/test_modules.py b/tests/test_modules.py index 1cb177f9a..17c00c8bc 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -59,6 +59,7 @@ def test_pydoc(): import pybind11_tests import pydoc + assert pybind11_tests.__name__ == "pybind11_tests" assert pybind11_tests.__doc__ == "pybind11 test module" assert pydoc.text.docmodule(pybind11_tests) diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp deleted file mode 100644 index 46a23ee61..000000000 --- a/tests/test_python_types.cpp +++ /dev/null @@ -1,695 +0,0 @@ -/* - tests/test_python_types.cpp -- singleton design pattern, static functions and - variables, passing and interacting with Python types - - Copyright (c) 2016 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#include "pybind11_tests.h" -#include "constructor_stats.h" -#include -#include - -#ifdef _WIN32 -# include -# include -#endif - -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#endif - -class ExamplePythonTypes { -public: - static ExamplePythonTypes *new_instance() { - auto *ptr = new ExamplePythonTypes(); - print_created(ptr, "via new_instance"); - return ptr; - } - ~ExamplePythonTypes() { print_destroyed(this); } - - /* Create and return a Python dictionary */ - py::dict get_dict() { - py::dict dict; - dict[py::str("key")] = py::str("value"); - return dict; - } - - /* Create and return a Python set */ - py::set get_set() { - py::set set; - set.add(py::str("key1")); - set.add("key2"); - set.add(std::string("key3")); - return set; - } - - /* Create and return a C++ dictionary */ - std::map get_dict_2() { - std::map result; - result["key"] = "value"; - return result; - } - - /* Create and return a C++ set */ - std::set get_set_2() { - std::set result; - result.insert("key1"); - result.insert("key2"); - return result; - } - - /* Create, manipulate, and return a Python list */ - py::list get_list() { - py::list list; - list.append("value"); - py::print("Entry at position 0:", list[0]); - list[0] = py::str("overwritten"); - return list; - } - - /* C++ STL data types are automatically casted */ - std::vector get_list_2() { - std::vector list; - list.push_back(L"value"); - return list; - } - - /* C++ STL data types are automatically casted */ - std::array get_array() { - return std::array {{ "array entry 1" , "array entry 2"}}; - } - - std::valarray get_valarray() { - return std::valarray({ 1, 4, 9 }); - } - - /* Easily iterate over a dictionary using a C++11 range-based for loop */ - void print_dict(py::dict dict) { - for (auto item : dict) - py::print("key: {}, value={}"_s.format(item.first, item.second)); - } - - /* Easily iterate over a set using a C++11 range-based for loop */ - void print_set(py::set set) { - for (auto item : set) - py::print("key:", item); - } - - /* Easily iterate over a list using a C++11 range-based for loop */ - void print_list(py::list list) { - int index = 0; - for (auto item : list) - py::print("list item {}: {}"_s.format(index++, item)); - } - - /* STL data types (such as maps) are automatically casted from Python */ - void print_dict_2(const std::map &dict) { - for (auto item : dict) - py::print("key: {}, value={}"_s.format(item.first, item.second)); - } - - /* STL data types (such as sets) are automatically casted from Python */ - void print_set_2(const std::set &set) { - for (auto item : set) - py::print("key:", item); - } - - /* STL data types (such as vectors) are automatically casted from Python */ - void print_list_2(std::vector &list) { - int index = 0; - for (auto item : list) - py::print("list item {}: {}"_s.format(index++, item)); - } - - /* pybind automatically translates between C++11 and Python tuples */ - std::pair pair_passthrough(std::pair input) { - return std::make_pair(input.second, input.first); - } - - /* pybind automatically translates between C++11 and Python tuples */ - std::tuple tuple_passthrough(std::tuple input) { - return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input)); - } - - /* STL data types (such as arrays) are automatically casted from Python */ - void print_array(std::array &array) { - int index = 0; - for (auto item : array) - py::print("array item {}: {}"_s.format(index++, item)); - } - - void print_valarray(std::valarray &varray) { - int index = 0; - for (auto item : varray) - py::print("valarray item {}: {}"_s.format(index++, item)); - } - - void throw_exception() { - throw std::runtime_error("This exception was intentionally thrown."); - } - - py::bytes get_bytes_from_string() { - return (py::bytes) std::string("foo"); - } - - py::bytes get_bytes_from_str() { - return (py::bytes) py::str("bar", 3); - } - - py::str get_str_from_string() { - return (py::str) std::string("baz"); - } - - py::str get_str_from_bytes() { - return (py::str) py::bytes("boo", 3); - } - - void test_print(const py::object& obj) { - py::print(py::str(obj)); - py::print(py::repr(obj)); - } - - static int value; - static const int value2; -}; - -int ExamplePythonTypes::value = 0; -const int ExamplePythonTypes::value2 = 5; - -struct MoveOutContainer { - struct Value { int value; }; - - std::list move_list() const { return {{0}, {1}, {2}}; } -}; - -struct UnregisteredType { }; - -// Class that can be move- and copy-constructed, but not assigned -struct NoAssign { - int value; - - explicit NoAssign(int value = 0) : value(value) {} - NoAssign(const NoAssign &) = default; - NoAssign(NoAssign &&) = default; - - NoAssign &operator=(const NoAssign &) = delete; - NoAssign &operator=(NoAssign &&) = delete; -}; - -// Increments on copy -struct IncrIntWrapper { - int i; - IncrIntWrapper(int i) : i(i) {} - IncrIntWrapper(const IncrIntWrapper ©) : i(copy.i + 1) {} -}; - -std::vector> incr_int_wrappers() { - static IncrIntWrapper x1(1), x2(2); - std::vector> r; - r.emplace_back(x1); - r.emplace_back(x2); - return r; -}; - -/// Issue #528: templated constructor -struct TplCtorClass { - template TplCtorClass(const T &) { } - bool operator==(const TplCtorClass &) const { return true; } -}; - -namespace std { - template <> - struct hash { size_t operator()(const TplCtorClass &) const { return 0; } }; -} - -test_initializer python_types([](py::module &m) { - /* No constructor is explicitly defined below. An exception is raised when - trying to construct it directly from Python */ - py::class_(m, "ExamplePythonTypes", "Example 2 documentation") - .def("get_dict", &ExamplePythonTypes::get_dict, "Return a Python dictionary") - .def("get_dict_2", &ExamplePythonTypes::get_dict_2, "Return a C++ dictionary") - .def("get_list", &ExamplePythonTypes::get_list, "Return a Python list") - .def("get_list_2", &ExamplePythonTypes::get_list_2, "Return a C++ list") - .def("get_set", &ExamplePythonTypes::get_set, "Return a Python set") - .def("get_set2", &ExamplePythonTypes::get_set_2, "Return a C++ set") - .def("get_array", &ExamplePythonTypes::get_array, "Return a C++ array") - .def("get_valarray", &ExamplePythonTypes::get_valarray, "Return a C++ valarray") - .def("print_dict", &ExamplePythonTypes::print_dict, "Print entries of a Python dictionary") - .def("print_dict_2", &ExamplePythonTypes::print_dict_2, "Print entries of a C++ dictionary") - .def("print_set", &ExamplePythonTypes::print_set, "Print entries of a Python set") - .def("print_set_2", &ExamplePythonTypes::print_set_2, "Print entries of a C++ set") - .def("print_list", &ExamplePythonTypes::print_list, "Print entries of a Python list") - .def("print_list_2", &ExamplePythonTypes::print_list_2, "Print entries of a C++ list") - .def("print_array", &ExamplePythonTypes::print_array, "Print entries of a C++ array") - .def("print_valarray", &ExamplePythonTypes::print_valarray, "Print entries of a C++ valarray") - .def("pair_passthrough", &ExamplePythonTypes::pair_passthrough, "Return a pair in reversed order") - .def("tuple_passthrough", &ExamplePythonTypes::tuple_passthrough, "Return a triple in reversed order") - .def("throw_exception", &ExamplePythonTypes::throw_exception, "Throw an exception") - .def("get_bytes_from_string", &ExamplePythonTypes::get_bytes_from_string, "py::bytes from std::string") - .def("get_bytes_from_str", &ExamplePythonTypes::get_bytes_from_str, "py::bytes from py::str") - .def("get_str_from_string", &ExamplePythonTypes::get_str_from_string, "py::str from std::string") - .def("get_str_from_bytes", &ExamplePythonTypes::get_str_from_bytes, "py::str from py::bytes") - .def("test_print", &ExamplePythonTypes::test_print, "test the print function") - .def_static("new_instance", &ExamplePythonTypes::new_instance, "Return an instance") - .def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member") - .def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)"); - - m.def("test_print_function", []() { - py::print("Hello, World!"); - py::print(1, 2.0, "three", true, std::string("-- multiple args")); - auto args = py::make_tuple("and", "a", "custom", "separator"); - py::print("*args", *args, "sep"_a="-"); - py::print("no new line here", "end"_a=" -- "); - py::print("next print"); - - auto py_stderr = py::module::import("sys").attr("stderr"); - py::print("this goes to stderr", "file"_a=py_stderr); - - py::print("flush", "flush"_a=true); - - py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this")); - }); - - py::class_(m, "NoAssign", "Class with no C++ assignment operators") - .def(py::init<>()) - .def(py::init()); - - m.def("test_print_failure", []() { py::print(42, UnregisteredType()); }); -#if !defined(NDEBUG) - m.attr("debug_enabled") = true; -#else - m.attr("debug_enabled") = false; -#endif - - m.def("test_str_format", []() { - auto s1 = "{} + {} = {}"_s.format(1, 2, 3); - auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3); - return py::make_tuple(s1, s2); - }); - - m.def("test_dict_keyword_constructor", []() { - auto d1 = py::dict("x"_a=1, "y"_a=2); - auto d2 = py::dict("z"_a=3, **d1); - return d2; - }); - - m.def("test_accessor_api", [](py::object o) { - auto d = py::dict(); - - d["basic_attr"] = o.attr("basic_attr"); - - auto l = py::list(); - for (const auto &item : o.attr("begin_end")) { - l.append(item); - } - d["begin_end"] = l; - - d["operator[object]"] = o.attr("d")["operator[object]"_s]; - d["operator[char *]"] = o.attr("d")["operator[char *]"]; - - d["attr(object)"] = o.attr("sub").attr("attr_obj"); - d["attr(char *)"] = o.attr("sub").attr("attr_char"); - try { - o.attr("sub").attr("missing").ptr(); - } catch (const py::error_already_set &) { - d["missing_attr_ptr"] = "raised"_s; - } - try { - o.attr("missing").attr("doesn't matter"); - } catch (const py::error_already_set &) { - d["missing_attr_chain"] = "raised"_s; - } - - d["is_none"] = o.attr("basic_attr").is_none(); - - d["operator()"] = o.attr("func")(1); - d["operator*"] = o.attr("func")(*o.attr("begin_end")); - - return d; - }); - - m.def("test_tuple_accessor", [](py::tuple existing_t) { - try { - existing_t[0] = 1; - } catch (const py::error_already_set &) { - // --> Python system error - // Only new tuples (refcount == 1) are mutable - auto new_t = py::tuple(3); - for (size_t i = 0; i < new_t.size(); ++i) { - new_t[i] = i; - } - return new_t; - } - return py::tuple(); - }); - - m.def("test_accessor_assignment", []() { - auto l = py::list(1); - l[0] = 0; - - auto d = py::dict(); - d["get"] = l[0]; - auto var = l[0]; - d["deferred_get"] = var; - l[0] = 1; - d["set"] = l[0]; - var = 99; // this assignment should not overwrite l[0] - d["deferred_set"] = l[0]; - d["var"] = var; - - return d; - }); - - bool has_optional = false, has_exp_optional = false; -#ifdef PYBIND11_HAS_OPTIONAL - has_optional = true; - using opt_int = std::optional; - using opt_no_assign = std::optional; - m.def("double_or_zero", [](const opt_int& x) -> int { - return x.value_or(0) * 2; - }); - m.def("half_or_none", [](int x) -> opt_int { - return x ? opt_int(x / 2) : opt_int(); - }); - m.def("test_nullopt", [](opt_int x) { - return x.value_or(42); - }, py::arg_v("x", std::nullopt, "None")); - m.def("test_no_assign", [](const opt_no_assign &x) { - return x ? x->value : 42; - }, py::arg_v("x", std::nullopt, "None")); -#endif - -#ifdef PYBIND11_HAS_EXP_OPTIONAL - has_exp_optional = true; - using exp_opt_int = std::experimental::optional; - using exp_opt_no_assign = std::experimental::optional; - m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int { - return x.value_or(0) * 2; - }); - m.def("half_or_none_exp", [](int x) -> exp_opt_int { - return x ? exp_opt_int(x / 2) : exp_opt_int(); - }); - m.def("test_nullopt_exp", [](exp_opt_int x) { - return x.value_or(42); - }, py::arg_v("x", std::experimental::nullopt, "None")); - m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) { - return x ? x->value : 42; - }, py::arg_v("x", std::experimental::nullopt, "None")); -#endif - - m.attr("has_optional") = has_optional; - m.attr("has_exp_optional") = has_exp_optional; - -#ifdef PYBIND11_HAS_VARIANT - struct visitor { - const char *operator()(int) { return "int"; } - const char *operator()(std::string) { return "std::string"; } - const char *operator()(double) { return "double"; } - const char *operator()(std::nullptr_t) { return "std::nullptr_t"; } - }; - - m.def("load_variant", [](std::variant v) { - return std::visit(visitor(), v); - }); - - m.def("load_variant_2pass", [](std::variant v) { - return std::visit(visitor(), v); - }); - - m.def("cast_variant", []() { - using V = std::variant; - return py::make_tuple(V(5), V("Hello")); - }); -#endif - - m.def("test_default_constructors", []() { - return py::dict( - "str"_a=py::str(), - "bool"_a=py::bool_(), - "int"_a=py::int_(), - "float"_a=py::float_(), - "tuple"_a=py::tuple(), - "list"_a=py::list(), - "dict"_a=py::dict(), - "set"_a=py::set() - ); - }); - - m.def("test_converting_constructors", [](py::dict d) { - return py::dict( - "str"_a=py::str(d["str"]), - "bool"_a=py::bool_(d["bool"]), - "int"_a=py::int_(d["int"]), - "float"_a=py::float_(d["float"]), - "tuple"_a=py::tuple(d["tuple"]), - "list"_a=py::list(d["list"]), - "dict"_a=py::dict(d["dict"]), - "set"_a=py::set(d["set"]), - "memoryview"_a=py::memoryview(d["memoryview"]) - ); - }); - - m.def("test_cast_functions", [](py::dict d) { - // When converting between Python types, obj.cast() should be the same as T(obj) - return py::dict( - "str"_a=d["str"].cast(), - "bool"_a=d["bool"].cast(), - "int"_a=d["int"].cast(), - "float"_a=d["float"].cast(), - "tuple"_a=d["tuple"].cast(), - "list"_a=d["list"].cast(), - "dict"_a=d["dict"].cast(), - "set"_a=d["set"].cast(), - "memoryview"_a=d["memoryview"].cast() - ); - }); - - py::class_(m, "MoveOutContainerValue") - .def_readonly("value", &MoveOutContainer::Value::value); - - py::class_(m, "MoveOutContainer") - .def(py::init<>()) - .def_property_readonly("move_list", &MoveOutContainer::move_list); - - m.def("get_implicit_casting", []() { - py::dict d; - d["char*_i1"] = "abc"; - const char *c2 = "abc"; - d["char*_i2"] = c2; - d["char*_e"] = py::cast(c2); - d["char*_p"] = py::str(c2); - - d["int_i1"] = 42; - int i = 42; - d["int_i2"] = i; - i++; - d["int_e"] = py::cast(i); - i++; - d["int_p"] = py::int_(i); - - d["str_i1"] = std::string("str"); - std::string s2("str1"); - d["str_i2"] = s2; - s2[3] = '2'; - d["str_e"] = py::cast(s2); - s2[3] = '3'; - d["str_p"] = py::str(s2); - - py::list l(2); - l[0] = 3; - l[1] = py::cast(6); - l.append(9); - l.append(py::cast(12)); - l.append(py::int_(15)); - - return py::dict( - "d"_a=d, - "l"_a=l - ); - }); - - m.def("string_roundtrip", [](const char *s) { return s; }); - - // Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte - char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*β€½*/, cake32 = 0x1f382 /*πŸŽ‚*/, mathbfA32 = 0x1d400 /*𝐀*/; - char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00; - std::wstring wstr; - wstr.push_back(0x61); // a - wstr.push_back(0x2e18); // ⸘ - if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16 - else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32 - wstr.push_back(0x7a); // z - - m.def("good_utf8_string", []() { return std::string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8β€½ πŸŽ‚ 𝐀 - m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // bβ€½πŸŽ‚π€z - m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // aπ€πŸŽ‚β€½z - m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z - m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); }); - m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); }); - // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError - if (PY_MAJOR_VERSION >= 3) - m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); }); - if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2) - m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); }); - m.def("u8_Z", []() -> char { return 'Z'; }); - m.def("u8_eacute", []() -> char { return '\xe9'; }); - m.def("u16_ibang", [=]() -> char16_t { return ib16; }); - m.def("u32_mathbfA", [=]() -> char32_t { return mathbfA32; }); - m.def("wchar_heart", []() -> wchar_t { return 0x2665; }); - - m.attr("wchar_size") = py::cast(sizeof(wchar_t)); - m.def("ord_char", [](char c) -> int { return static_cast(c); }); - m.def("ord_char16", [](char16_t c) -> uint16_t { return c; }); - m.def("ord_char32", [](char32_t c) -> uint32_t { return c; }); - m.def("ord_wchar", [](wchar_t c) -> int { return c; }); - - m.def("strlen", [](char *s) { return strlen(s); }); - m.def("string_length", [](std::string s) { return s.length(); }); - - m.def("return_none_string", []() -> std::string * { return nullptr; }); - m.def("return_none_char", []() -> const char * { return nullptr; }); - m.def("return_none_bool", []() -> bool * { return nullptr; }); - m.def("return_none_int", []() -> int * { return nullptr; }); - m.def("return_none_float", []() -> float * { return nullptr; }); - - m.def("defer_none_cstring", [](char *) { return false; }); - m.def("defer_none_cstring", [](py::none) { return true; }); - m.def("defer_none_custom", [](ExamplePythonTypes *) { return false; }); - m.def("defer_none_custom", [](py::none) { return true; }); - // void and optional, however, don't defer: - m.def("nodefer_none_void", [](void *) { return true; }); - m.def("nodefer_none_void", [](py::none) { return false; }); -#ifdef PYBIND11_HAS_OPTIONAL - m.def("nodefer_none_optional", [](std::optional) { return true; }); - m.def("nodefer_none_optional", [](py::none) { return false; }); -#endif - -#ifdef PYBIND11_HAS_STRING_VIEW - m.attr("has_string_view") = true; - m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); }); - m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); }); - m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); }); - m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; }); - m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); - m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; }); - m.def("string_view_return", []() { return std::string_view(u8"utf8 secret \U0001f382"); }); - m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); }); - m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); }); -#else - m.attr("has_string_view") = false; -#endif - - m.def("return_capsule_with_destructor", - []() { - py::print("creating capsule"); - return py::capsule([]() { - py::print("destructing capsule"); - }); - } - ); - - m.def("return_capsule_with_destructor_2", - []() { - py::print("creating capsule"); - return py::capsule((void *) 1234, [](void *ptr) { - py::print("destructing capsule: {}"_s.format((size_t) ptr)); - }); - } - ); - - m.def("return_capsule_with_name_and_destructor_3", - []() { - py::print("creating capsule"); - auto capsule=py::capsule((void *) 1234, "pointer type description", - [](PyObject *ptr) { - if (ptr) { - py::print("destructing capsule"); - } - }); - auto name = capsule.name(); - void *contents = capsule; - py::print("created capsule with name --{}-- and contents {}"_s.format(name,(size_t) contents)); - return capsule; - } - ); - - m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile - m.def("cast_nullptr_t", []() { return std::nullptr_t{}; }); - - struct IntWrapper { int i; IntWrapper(int i) : i(i) { } }; - py::class_(m, "IntWrapper") - .def(py::init()) - .def("__repr__", [](const IntWrapper &p) { return "IntWrapper[" + std::to_string(p.i) + "]"; }); - - // #171: Can't return reference wrappers (or STL datastructures containing them) - // Also used to test #848: reference_wrapper shouldn't allow None - m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper p4) { - IntWrapper *p1 = new IntWrapper{1}; - IntWrapper *p2 = new IntWrapper{2}; - IntWrapper *p3 = new IntWrapper{3}; - std::vector> v; - v.push_back(std::ref(*p1)); - v.push_back(std::ref(*p2)); - v.push_back(std::ref(*p3)); - v.push_back(p4); - return v; - }); - - // Reference-wrapper to non-generic type caster type: - m.def("refwrap_int", [](std::reference_wrapper p) { return 10 * p.get(); }); - - // Not currently supported (std::pair caster has return-by-value cast operator); - // triggers static_assert failure. - //m.def("refwrap_pair", [](std::reference_wrapper>) { }); - - // Test that copying/referencing is working as expected with reference_wrappers: - py::class_(m, "IncrIntWrapper") - .def(py::init()) - .def_readonly("i", &IncrIntWrapper::i); - - m.def("refwrap_list_refs", []() { - py::list l; - for (auto &f : incr_int_wrappers()) l.append(py::cast(f, py::return_value_policy::reference)); - return l; - }); - m.def("refwrap_list_copies", []() { - py::list l; - for (auto &f : incr_int_wrappers()) l.append(py::cast(f, py::return_value_policy::copy)); - return l; - }); - m.def("refwrap_iiw", [](const IncrIntWrapper &w) { return w.i; }); - m.def("refwrap_call_iiw", [](IncrIntWrapper &w, py::function f) { - py::list l; - l.append(f(std::ref(w))); - l.append(f(std::cref(w))); - IncrIntWrapper x(w.i); - l.append(f(std::ref(x))); - IncrIntWrapper y(w.i); - auto r3 = std::ref(y); - l.append(f(r3)); - return l; - }); - - /// Issue #484: number conversion generates unhandled exceptions - m.def("test_complex", [](float x) { return "{}"_s.format(x); }); - m.def("test_complex", [](std::complex x) { return "({}, {})"_s.format(x.real(), x.imag()); }); - - /// Issue #528: templated constructor - m.def("tpl_ctor_vector", [](std::vector &) {}); - m.def("tpl_ctor_map", [](std::unordered_map &) {}); - m.def("tpl_ctor_set", [](std::unordered_set &) {}); -#if defined(PYBIND11_HAS_OPTIONAL) - m.def("tpl_constr_optional", [](std::optional &) {}); -#elif defined(PYBIND11_HAS_EXP_OPTIONAL) - m.def("tpl_constr_optional", [](std::experimental::optional &) {}); -#endif -}); - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif diff --git a/tests/test_python_types.py b/tests/test_python_types.py deleted file mode 100644 index 79326028e..000000000 --- a/tests/test_python_types.py +++ /dev/null @@ -1,715 +0,0 @@ -# Python < 3 needs this: coding=utf-8 -import pytest -import pybind11_tests - -from pybind11_tests import (ExamplePythonTypes, ConstructorStats, has_optional, has_exp_optional, - has_string_view) - - -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, test_print_failure, debug_enabled - - 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" - - with pytest.raises(RuntimeError) as excinfo: - test_print_failure() - assert str(excinfo.value) == "make_tuple(): unable to convert " + ( - "argument of type 'UnregisteredType' to Python object" - if debug_enabled else - "arguments to Python object (compile in debug mode for details)" - ) - - -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 ') -def test_optional(): - from pybind11_tests import (double_or_zero, half_or_none, test_nullopt, - test_no_assign, NoAssign) - - 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 - - assert test_no_assign() == 42 - assert test_no_assign(None) == 42 - assert test_no_assign(NoAssign(43)) == 43 - pytest.raises(TypeError, test_no_assign, 43) - - -@pytest.mark.skipif(not has_exp_optional, reason='no ') -def test_exp_optional(): - from pybind11_tests import (double_or_zero_exp, half_or_none_exp, test_nullopt_exp, - test_no_assign_exp, NoAssign) - - 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 - - assert test_no_assign_exp() == 42 - assert test_no_assign_exp(None) == 42 - assert test_no_assign_exp(NoAssign(43)) == 43 - pytest.raises(TypeError, test_no_assign_exp, 43) - - -@pytest.mark.skipif(not hasattr(pybind11_tests, "load_variant"), reason='no ') -def test_variant(doc): - from pybind11_tests import load_variant, load_variant_2pass, cast_variant - - assert load_variant(1) == "int" - assert load_variant("1") == "std::string" - assert load_variant(1.0) == "double" - assert load_variant(None) == "std::nullptr_t" - - assert load_variant_2pass(1) == "int" - assert load_variant_2pass(1.0) == "double" - - assert cast_variant() == (5, "Hello") - - assert doc(load_variant) == "load_variant(arg0: Union[int, str, float, None]) -> str" - - -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_simple_string(): - from pybind11_tests import string_roundtrip - - assert string_roundtrip("const char *") == "const char *" - - -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_bytes_to_string(): - """Tests the ability to pass bytes to C++ string-accepting functions. Note that this is - one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" - # Issue #816 - from pybind11_tests import strlen, string_length - import sys - byte = bytes if sys.version_info[0] < 3 else str - - assert strlen(byte("hi")) == 2 - assert string_length(byte("world")) == 5 - assert string_length(byte("a\x00b")) == 3 - assert strlen(byte("a\x00b")) == 1 # C-string limitation - - # passing in a utf8 encoded string should work - assert string_length(u'πŸ’©'.encode("utf8")) == 4 - - -@pytest.mark.skipif(not has_string_view, reason='no ') -def test_string_view(capture): - """Tests support for C++17 string_view arguments and return values""" - from pybind11_tests import (string_view_print, string_view16_print, string_view32_print, - string_view_chars, string_view16_chars, string_view32_chars, - string_view_return, string_view16_return, string_view32_return) - - assert string_view_chars("Hi") == [72, 105] - assert string_view_chars("Hi πŸŽ‚") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82] - assert string_view16_chars("Hi πŸŽ‚") == [72, 105, 32, 0xd83c, 0xdf82] - assert string_view32_chars("Hi πŸŽ‚") == [72, 105, 32, 127874] - - assert string_view_return() == "utf8 secret πŸŽ‚" - assert string_view16_return() == "utf16 secret πŸŽ‚" - assert string_view32_return() == "utf32 secret πŸŽ‚" - - with capture: - string_view_print("Hi") - string_view_print("utf8 πŸŽ‚") - string_view16_print("utf16 πŸŽ‚") - string_view32_print("utf32 πŸŽ‚") - - assert capture == """ - Hi 2 - utf8 πŸŽ‚ 9 - utf16 πŸŽ‚ 8 - utf32 πŸŽ‚ 7 - """ - - with capture: - string_view_print("Hi, ascii") - string_view_print("Hi, utf8 πŸŽ‚") - string_view16_print("Hi, utf16 πŸŽ‚") - string_view32_print("Hi, utf32 πŸŽ‚") - assert capture == """ - Hi, ascii 9 - Hi, utf8 πŸŽ‚ 13 - Hi, utf16 πŸŽ‚ 12 - Hi, utf32 πŸŽ‚ 11 - """ - - -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_none_deferred(): - """None passed as various argument types should defer to other overloads""" - import pybind11_tests as m - - assert not m.defer_none_cstring("abc") - assert m.defer_none_cstring(None) - assert not m.defer_none_custom(m.ExamplePythonTypes.new_instance()) - assert m.defer_none_custom(None) - assert m.nodefer_none_void(None) - if has_optional: - assert m.nodefer_none_optional(None) - - -def test_capsule_with_destructor(capture): - import pybind11_tests as m - pytest.gc_collect() - 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 - """ - - with capture: - a = m.return_capsule_with_name_and_destructor_3() - del a - pytest.gc_collect() - assert capture.unordered == """ - created capsule with name --pointer type description-- and contents 1234 - creating capsule - destructing capsule - """ - - -def test_void_caster(): - import pybind11_tests as m - assert m.load_nullptr_t(None) is None - assert m.cast_nullptr_t() is None - - -def test_reference_wrapper(): - """std::reference_wrapper tests. - - #171: Can't return reference wrappers (or STL data structures containing them) - #848: std::reference_wrapper accepts nullptr / None arguments [but shouldn't] - (no issue): reference_wrappers should work for types with custom type casters - """ - from pybind11_tests import (IntWrapper, return_vec_of_reference_wrapper, refwrap_int, - IncrIntWrapper, refwrap_iiw, refwrap_call_iiw, - refwrap_list_copies, refwrap_list_refs) - - # 171: - assert str(return_vec_of_reference_wrapper(IntWrapper(4))) == \ - "[IntWrapper[1], IntWrapper[2], IntWrapper[3], IntWrapper[4]]" - - # 848: - with pytest.raises(TypeError) as excinfo: - return_vec_of_reference_wrapper(None) - assert "incompatible function arguments" in str(excinfo.value) - - assert refwrap_int(42) == 420 - - a1 = refwrap_list_copies() - a2 = refwrap_list_copies() - assert [x.i for x in a1] == [2, 3] - assert [x.i for x in a2] == [2, 3] - assert not a1[0] is a2[0] and not a1[1] is a2[1] - - b1 = refwrap_list_refs() - b2 = refwrap_list_refs() - assert [x.i for x in b1] == [1, 2] - assert [x.i for x in b2] == [1, 2] - assert b1[0] is b2[0] and b1[1] is b2[1] - - assert refwrap_iiw(IncrIntWrapper(5)) == 5 - assert refwrap_call_iiw(IncrIntWrapper(10), refwrap_iiw) == [10, 10, 10, 10] - - -def test_complex_cast(): - """#484: number conversion generates unhandled exceptions""" - from pybind11_tests import test_complex - - assert test_complex(1) == "1.0" - assert test_complex(2j) == "(0.0, 2.0)" diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp new file mode 100644 index 000000000..edc1e995c --- /dev/null +++ b/tests/test_pytypes.cpp @@ -0,0 +1,264 @@ +/* + tests/test_pytypes.cpp -- Python type casters + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" + + +TEST_SUBMODULE(pytypes, m) { + // test_list + m.def("get_list", []() { + py::list list; + list.append("value"); + py::print("Entry at position 0:", list[0]); + list[0] = py::str("overwritten"); + return list; + }); + m.def("print_list", [](py::list list) { + int index = 0; + for (auto item : list) + py::print("list item {}: {}"_s.format(index++, item)); + }); + + // test_set + m.def("get_set", []() { + py::set set; + set.add(py::str("key1")); + set.add("key2"); + set.add(std::string("key3")); + return set; + }); + m.def("print_set", [](py::set set) { + for (auto item : set) + py::print("key:", item); + }); + + // test_dict + m.def("get_dict", []() { return py::dict("key"_a="value"); }); + m.def("print_dict", [](py::dict dict) { + for (auto item : dict) + py::print("key: {}, value={}"_s.format(item.first, item.second)); + }); + m.def("dict_keyword_constructor", []() { + auto d1 = py::dict("x"_a=1, "y"_a=2); + auto d2 = py::dict("z"_a=3, **d1); + return d2; + }); + + // test_str + m.def("str_from_string", []() { return py::str(std::string("baz")); }); + m.def("str_from_bytes", []() { return py::str(py::bytes("boo", 3)); }); + m.def("str_from_object", [](const py::object& obj) { return py::str(obj); }); + m.def("repr_from_object", [](const py::object& obj) { return py::repr(obj); }); + + m.def("str_format", []() { + auto s1 = "{} + {} = {}"_s.format(1, 2, 3); + auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3); + return py::make_tuple(s1, s2); + }); + + // test_bytes + m.def("bytes_from_string", []() { return py::bytes(std::string("foo")); }); + m.def("bytes_from_str", []() { return py::bytes(py::str("bar", 3)); }); + + // test_capsule + m.def("return_capsule_with_destructor", []() { + py::print("creating capsule"); + return py::capsule([]() { + py::print("destructing capsule"); + }); + }); + + m.def("return_capsule_with_destructor_2", []() { + py::print("creating capsule"); + return py::capsule((void *) 1234, [](void *ptr) { + py::print("destructing capsule: {}"_s.format((size_t) ptr)); + }); + }); + + m.def("return_capsule_with_name_and_destructor", []() { + auto capsule = py::capsule((void *) 1234, "pointer type description", [](PyObject *ptr) { + if (ptr) { + auto name = PyCapsule_GetName(ptr); + py::print("destructing capsule ({}, '{}')"_s.format( + (size_t) PyCapsule_GetPointer(ptr, name), name + )); + } + }); + void *contents = capsule; + py::print("created capsule ({}, '{}')"_s.format((size_t) contents, capsule.name())); + return capsule; + }); + + // test_accessors + m.def("accessor_api", [](py::object o) { + auto d = py::dict(); + + d["basic_attr"] = o.attr("basic_attr"); + + auto l = py::list(); + for (const auto &item : o.attr("begin_end")) { + l.append(item); + } + d["begin_end"] = l; + + d["operator[object]"] = o.attr("d")["operator[object]"_s]; + d["operator[char *]"] = o.attr("d")["operator[char *]"]; + + d["attr(object)"] = o.attr("sub").attr("attr_obj"); + d["attr(char *)"] = o.attr("sub").attr("attr_char"); + try { + o.attr("sub").attr("missing").ptr(); + } catch (const py::error_already_set &) { + d["missing_attr_ptr"] = "raised"_s; + } + try { + o.attr("missing").attr("doesn't matter"); + } catch (const py::error_already_set &) { + d["missing_attr_chain"] = "raised"_s; + } + + d["is_none"] = o.attr("basic_attr").is_none(); + + d["operator()"] = o.attr("func")(1); + d["operator*"] = o.attr("func")(*o.attr("begin_end")); + + return d; + }); + + m.def("tuple_accessor", [](py::tuple existing_t) { + try { + existing_t[0] = 1; + } catch (const py::error_already_set &) { + // --> Python system error + // Only new tuples (refcount == 1) are mutable + auto new_t = py::tuple(3); + for (size_t i = 0; i < new_t.size(); ++i) { + new_t[i] = i; + } + return new_t; + } + return py::tuple(); + }); + + m.def("accessor_assignment", []() { + auto l = py::list(1); + l[0] = 0; + + auto d = py::dict(); + d["get"] = l[0]; + auto var = l[0]; + d["deferred_get"] = var; + l[0] = 1; + d["set"] = l[0]; + var = 99; // this assignment should not overwrite l[0] + d["deferred_set"] = l[0]; + d["var"] = var; + + return d; + }); + + // test_constructors + m.def("default_constructors", []() { + return py::dict( + "str"_a=py::str(), + "bool"_a=py::bool_(), + "int"_a=py::int_(), + "float"_a=py::float_(), + "tuple"_a=py::tuple(), + "list"_a=py::list(), + "dict"_a=py::dict(), + "set"_a=py::set() + ); + }); + + m.def("converting_constructors", [](py::dict d) { + return py::dict( + "str"_a=py::str(d["str"]), + "bool"_a=py::bool_(d["bool"]), + "int"_a=py::int_(d["int"]), + "float"_a=py::float_(d["float"]), + "tuple"_a=py::tuple(d["tuple"]), + "list"_a=py::list(d["list"]), + "dict"_a=py::dict(d["dict"]), + "set"_a=py::set(d["set"]), + "memoryview"_a=py::memoryview(d["memoryview"]) + ); + }); + + m.def("cast_functions", [](py::dict d) { + // When converting between Python types, obj.cast() should be the same as T(obj) + return py::dict( + "str"_a=d["str"].cast(), + "bool"_a=d["bool"].cast(), + "int"_a=d["int"].cast(), + "float"_a=d["float"].cast(), + "tuple"_a=d["tuple"].cast(), + "list"_a=d["list"].cast(), + "dict"_a=d["dict"].cast(), + "set"_a=d["set"].cast(), + "memoryview"_a=d["memoryview"].cast() + ); + }); + + m.def("get_implicit_casting", []() { + py::dict d; + d["char*_i1"] = "abc"; + const char *c2 = "abc"; + d["char*_i2"] = c2; + d["char*_e"] = py::cast(c2); + d["char*_p"] = py::str(c2); + + d["int_i1"] = 42; + int i = 42; + d["int_i2"] = i; + i++; + d["int_e"] = py::cast(i); + i++; + d["int_p"] = py::int_(i); + + d["str_i1"] = std::string("str"); + std::string s2("str1"); + d["str_i2"] = s2; + s2[3] = '2'; + d["str_e"] = py::cast(s2); + s2[3] = '3'; + d["str_p"] = py::str(s2); + + py::list l(2); + l[0] = 3; + l[1] = py::cast(6); + l.append(9); + l.append(py::cast(12)); + l.append(py::int_(15)); + + return py::dict( + "d"_a=d, + "l"_a=l + ); + }); + + // test_print + m.def("print_function", []() { + py::print("Hello, World!"); + py::print(1, 2.0, "three", true, std::string("-- multiple args")); + auto args = py::make_tuple("and", "a", "custom", "separator"); + py::print("*args", *args, "sep"_a="-"); + py::print("no new line here", "end"_a=" -- "); + py::print("next print"); + + auto py_stderr = py::module::import("sys").attr("stderr"); + py::print("this goes to stderr", "file"_a=py_stderr); + + py::print("flush", "flush"_a=true); + + py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this")); + }); + + m.def("print_failure", []() { py::print(42, UnregisteredType()); }); +} diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py new file mode 100644 index 000000000..1c75c47de --- /dev/null +++ b/tests/test_pytypes.py @@ -0,0 +1,211 @@ +import pytest +import sys + +from pybind11_tests import pytypes as m +from pybind11_tests import debug_enabled + + +def test_list(capture, doc): + with capture: + l = m.get_list() + assert l == ["overwritten"] + + l.append("value2") + m.print_list(l) + assert capture.unordered == """ + Entry at position 0: value + list item 0: overwritten + list item 1: value2 + """ + + assert doc(m.get_list) == "get_list() -> list" + assert doc(m.print_list) == "print_list(arg0: list) -> None" + + +def test_set(capture, doc): + s = m.get_set() + assert s == {"key1", "key2", "key3"} + + with capture: + s.add("key4") + m.print_set(s) + assert capture.unordered == """ + key: key1 + key: key2 + key: key3 + key: key4 + """ + + assert doc(m.get_list) == "get_list() -> list" + assert doc(m.print_list) == "print_list(arg0: list) -> None" + + +def test_dict(capture, doc): + d = m.get_dict() + assert d == {"key": "value"} + + with capture: + d["key2"] = "value2" + m.print_dict(d) + assert capture.unordered == """ + key: key, value=value + key: key2, value=value2 + """ + + assert doc(m.get_dict) == "get_dict() -> dict" + assert doc(m.print_dict) == "print_dict(arg0: dict) -> None" + + assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3} + + +def test_str(doc): + assert m.str_from_string().encode().decode() == "baz" + assert m.str_from_bytes().encode().decode() == "boo" + + assert doc(m.str_from_bytes) == "str_from_bytes() -> str" + + class A(object): + def __str__(self): + return "this is a str" + + def __repr__(self): + return "this is a repr" + + assert m.str_from_object(A()) == "this is a str" + assert m.repr_from_object(A()) == "this is a repr" + + s1, s2 = m.str_format() + assert s1 == "1 + 2 = 3" + assert s1 == s2 + + +def test_bytes(doc): + assert m.bytes_from_string().decode() == "foo" + assert m.bytes_from_str().decode() == "bar" + + assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( + "bytes" if sys.version_info[0] == 3 else "str" + ) + + +def test_capsule(capture): + pytest.gc_collect() + 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 + """ + + with capture: + a = m.return_capsule_with_name_and_destructor() + del a + pytest.gc_collect() + assert capture.unordered == """ + created capsule (1234, 'pointer type description') + destructing capsule (1234, 'pointer type description') + """ + + +def test_accessors(): + 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 = m.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 m.tuple_accessor(tuple()) == (0, 1, 2) + + d = m.accessor_assignment() + assert d["get"] == 0 + assert d["deferred_get"] == 0 + assert d["set"] == 1 + assert d["deferred_set"] == 1 + assert d["var"] == 99 + + +def test_constructors(): + """C++ default and converting constructors are equivalent to type calls in Python""" + types = [str, bool, int, float, tuple, list, dict, set] + expected = {t.__name__: t() for t in types} + assert m.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 m.converting_constructors(inputs) == expected + assert m.cast_functions(inputs) == expected + + +def test_implicit_casting(): + """Tests implicit casting when assigning or appending to dicts and lists.""" + z = m.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_print(capture): + with capture: + m.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" + + with pytest.raises(RuntimeError) as excinfo: + m.print_failure() + assert str(excinfo.value) == "make_tuple(): unable to convert " + ( + "argument of type 'UnregisteredType' to Python object" + if debug_enabled else + "arguments to Python object (compile in debug mode for details)" + ) diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index 012e97d97..2ce2e60f9 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -163,4 +163,3 @@ def test_iterator_rvp(): assert list(m.make_iterator_1()) == [1, 2, 3] assert list(m.make_iterator_2()) == [1, 2, 3] assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2())) - diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp new file mode 100644 index 000000000..528809768 --- /dev/null +++ b/tests/test_stl.cpp @@ -0,0 +1,163 @@ +/* + tests/test_stl.cpp -- STL type casters + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include + +// Class that can be move- and copy-constructed, but not assigned +struct NoAssign { + int value; + + explicit NoAssign(int value = 0) : value(value) { } + NoAssign(const NoAssign &) = default; + NoAssign(NoAssign &&) = default; + + NoAssign &operator=(const NoAssign &) = delete; + NoAssign &operator=(NoAssign &&) = delete; +}; + +/// Issue #528: templated constructor +struct TplCtorClass { + template TplCtorClass(const T &) { } + bool operator==(const TplCtorClass &) const { return true; } +}; + +namespace std { + template <> + struct hash { size_t operator()(const TplCtorClass &) const { return 0; } }; +} + + +TEST_SUBMODULE(stl, m) { + // test_vector + m.def("cast_vector", []() { return std::vector{1}; }); + m.def("load_vector", [](const std::vector &v) { return v.at(0) == 1 && v.at(1) == 2; }); + + // test_array + m.def("cast_array", []() { return std::array {{1 , 2}}; }); + m.def("load_array", [](const std::array &a) { return a[0] == 1 && a[1] == 2; }); + + // test_valarray + m.def("cast_valarray", []() { return std::valarray{1, 4, 9}; }); + m.def("load_valarray", [](const std::valarray& v) { + return v.size() == 3 && v[0] == 1 && v[1] == 4 && v[2] == 9; + }); + + // test_map + m.def("cast_map", []() { return std::map{{"key", "value"}}; }); + m.def("load_map", [](const std::map &map) { + return map.at("key") == "value" && map.at("key2") == "value2"; + }); + + // test_set + m.def("cast_set", []() { return std::set{"key1", "key2"}; }); + m.def("load_set", [](const std::set &set) { + return set.count("key1") && set.count("key2") && set.count("key3"); + }); + + struct MoveOutContainer { + struct Value { int value; }; + + std::list move_list() const { return {{0}, {1}, {2}}; } + }; + + py::class_(m, "MoveOutContainerValue") + .def_readonly("value", &MoveOutContainer::Value::value); + + py::class_(m, "MoveOutContainer") + .def(py::init<>()) + .def_property_readonly("move_list", &MoveOutContainer::move_list); + + py::class_(m, "NoAssign", "Class with no C++ assignment operators") + .def(py::init<>()) + .def(py::init()); + +#ifdef PYBIND11_HAS_OPTIONAL + m.attr("has_optional") = true; + + using opt_int = std::optional; + using opt_no_assign = std::optional; + m.def("double_or_zero", [](const opt_int& x) -> int { + return x.value_or(0) * 2; + }); + m.def("half_or_none", [](int x) -> opt_int { + return x ? opt_int(x / 2) : opt_int(); + }); + m.def("test_nullopt", [](opt_int x) { + return x.value_or(42); + }, py::arg_v("x", std::nullopt, "None")); + m.def("test_no_assign", [](const opt_no_assign &x) { + return x ? x->value : 42; + }, py::arg_v("x", std::nullopt, "None")); + + m.def("nodefer_none_optional", [](std::optional) { return true; }); + m.def("nodefer_none_optional", [](py::none) { return false; }); +#endif + +#ifdef PYBIND11_HAS_EXP_OPTIONAL + m.attr("has_exp_optional") = true; + + using exp_opt_int = std::experimental::optional; + using exp_opt_no_assign = std::experimental::optional; + m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int { + return x.value_or(0) * 2; + }); + m.def("half_or_none_exp", [](int x) -> exp_opt_int { + return x ? exp_opt_int(x / 2) : exp_opt_int(); + }); + m.def("test_nullopt_exp", [](exp_opt_int x) { + return x.value_or(42); + }, py::arg_v("x", std::experimental::nullopt, "None")); + m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) { + return x ? x->value : 42; + }, py::arg_v("x", std::experimental::nullopt, "None")); +#endif + +#ifdef PYBIND11_HAS_VARIANT + struct visitor { + const char *operator()(int) { return "int"; } + const char *operator()(std::string) { return "std::string"; } + const char *operator()(double) { return "double"; } + const char *operator()(std::nullptr_t) { return "std::nullptr_t"; } + }; + + m.def("load_variant", [](std::variant v) { + return std::visit(visitor(), v); + }); + + m.def("load_variant_2pass", [](std::variant v) { + return std::visit(visitor(), v); + }); + + m.def("cast_variant", []() { + using V = std::variant; + return py::make_tuple(V(5), V("Hello")); + }); +#endif + + /// #528: templated constructor + m.def("tpl_ctor_vector", [](std::vector &) {}); + m.def("tpl_ctor_map", [](std::unordered_map &) {}); + m.def("tpl_ctor_set", [](std::unordered_set &) {}); +#if defined(PYBIND11_HAS_OPTIONAL) + m.def("tpl_constr_optional", [](std::optional &) {}); +#elif defined(PYBIND11_HAS_EXP_OPTIONAL) + m.def("tpl_constr_optional", [](std::experimental::optional &) {}); +#endif + + // test_vec_of_reference_wrapper + // #171: Can't return STL structures containing reference wrapper + m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper p4) { + static UserType p1{1}, p2{2}, p3{3}; + return std::vector> { + std::ref(p1), std::ref(p2), std::ref(p3), p4 + }; + }); + +} diff --git a/tests/test_stl.py b/tests/test_stl.py new file mode 100644 index 000000000..1c982583b --- /dev/null +++ b/tests/test_stl.py @@ -0,0 +1,133 @@ +import pytest + +from pybind11_tests import stl as m +from pybind11_tests import UserType + + +def test_vector(doc): + """std::vector <-> list""" + l = m.cast_vector() + assert l == [1] + l.append(2) + assert m.load_vector(l) + assert m.load_vector(tuple(l)) + + assert doc(m.cast_vector) == "cast_vector() -> List[int]" + assert doc(m.load_vector) == "load_vector(arg0: List[int]) -> bool" + + +def test_array(doc): + """std::array <-> list""" + l = m.cast_array() + assert l == [1, 2] + assert m.load_array(l) + + assert doc(m.cast_array) == "cast_array() -> List[int[2]]" + assert doc(m.load_array) == "load_array(arg0: List[int[2]]) -> bool" + + +def test_valarray(doc): + """std::valarray <-> list""" + l = m.cast_valarray() + assert l == [1, 4, 9] + assert m.load_valarray(l) + + assert doc(m.cast_valarray) == "cast_valarray() -> List[int]" + assert doc(m.load_valarray) == "load_valarray(arg0: List[int]) -> bool" + + +def test_map(doc): + """std::map <-> dict""" + d = m.cast_map() + assert d == {"key": "value"} + d["key2"] = "value2" + assert m.load_map(d) + + assert doc(m.cast_map) == "cast_map() -> Dict[str, str]" + assert doc(m.load_map) == "load_map(arg0: Dict[str, str]) -> bool" + + +def test_set(doc): + """std::set <-> set""" + s = m.cast_set() + assert s == {"key1", "key2"} + s.add("key3") + assert m.load_set(s) + + assert doc(m.cast_set) == "cast_set() -> Set[str]" + assert doc(m.load_set) == "load_set(arg0: Set[str]) -> bool" + + +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.""" + c = m.MoveOutContainer() + moved_out_list = c.move_list + assert [x.value for x in moved_out_list] == [0, 1, 2] + + +@pytest.mark.skipif(not hasattr(m, "has_optional"), reason='no ') +def test_optional(): + assert m.double_or_zero(None) == 0 + assert m.double_or_zero(42) == 84 + pytest.raises(TypeError, m.double_or_zero, 'foo') + + assert m.half_or_none(0) is None + assert m.half_or_none(42) == 21 + pytest.raises(TypeError, m.half_or_none, 'foo') + + assert m.test_nullopt() == 42 + assert m.test_nullopt(None) == 42 + assert m.test_nullopt(42) == 42 + assert m.test_nullopt(43) == 43 + + assert m.test_no_assign() == 42 + assert m.test_no_assign(None) == 42 + assert m.test_no_assign(m.NoAssign(43)) == 43 + pytest.raises(TypeError, m.test_no_assign, 43) + + assert m.nodefer_none_optional(None) + + +@pytest.mark.skipif(not hasattr(m, "has_exp_optional"), reason='no ') +def test_exp_optional(): + assert m.double_or_zero_exp(None) == 0 + assert m.double_or_zero_exp(42) == 84 + pytest.raises(TypeError, m.double_or_zero_exp, 'foo') + + assert m.half_or_none_exp(0) is None + assert m.half_or_none_exp(42) == 21 + pytest.raises(TypeError, m.half_or_none_exp, 'foo') + + assert m.test_nullopt_exp() == 42 + assert m.test_nullopt_exp(None) == 42 + assert m.test_nullopt_exp(42) == 42 + assert m.test_nullopt_exp(43) == 43 + + assert m.test_no_assign_exp() == 42 + assert m.test_no_assign_exp(None) == 42 + assert m.test_no_assign_exp(m.NoAssign(43)) == 43 + pytest.raises(TypeError, m.test_no_assign_exp, 43) + + +@pytest.mark.skipif(not hasattr(m, "load_variant"), reason='no ') +def test_variant(doc): + assert m.load_variant(1) == "int" + assert m.load_variant("1") == "std::string" + assert m.load_variant(1.0) == "double" + assert m.load_variant(None) == "std::nullptr_t" + + assert m.load_variant_2pass(1) == "int" + assert m.load_variant_2pass(1.0) == "double" + + assert m.cast_variant() == (5, "Hello") + + assert doc(m.load_variant) == "load_variant(arg0: Union[int, str, float, None]) -> str" + + +def test_vec_of_reference_wrapper(): + """#171: Can't return reference wrappers (or STL structures containing them)""" + assert str(m.return_vec_of_reference_wrapper(UserType(4))) == \ + "[UserType(1), UserType(2), UserType(3), UserType(4)]"