Split test_python_types.cpp into builtin_casters, stl and pytypes
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index be223f5..a57896d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -28,10 +28,11 @@
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 @@
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 1e24350..81fe8bf 100644
--- a/tests/pybind11_tests.cpp
+++ b/tests/pybind11_tests.cpp
@@ -10,6 +10,9 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
+#include <functional>
+#include <list>
+
/*
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 @@
return inits;
}
-test_initializer::test_initializer(std::function<void(py::module &)> 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 @@
bind_ConstructorStats(m);
+#if !defined(NDEBUG)
+ m.attr("debug_enabled") = true;
+#else
+ m.attr("debug_enabled") = false;
+#endif
+
+ py::class_<UserType>(m, "UserType", "A `py::class_` type for testing")
+ .def(py::init<>())
+ .def(py::init<int>())
+ .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_<IncType, UserType>(m, "IncType")
+ .def(py::init<>())
+ .def(py::init<int>())
+ .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 c11b687..dd8d159 100644
--- a/tests/pybind11_tests.h
+++ b/tests/pybind11_tests.h
@@ -1,12 +1,45 @@
#pragma once
#include <pybind11/pybind11.h>
-#include <functional>
-#include <list>
namespace py = pybind11;
using namespace pybind11::literals;
class test_initializer {
+ using Initializer = void (*)(py::module &);
+
public:
- test_initializer(std::function<void(py::module &)> 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 0000000..33fe689
--- /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 <wenzel.jakob@epfl.ch>
+
+ 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 <pybind11/complex.h>
+
+#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<unsigned char>(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<bool, std::string> input) {
+ return std::make_pair(input.second, input.first);
+ }, "Return a pair in reversed order");
+ m.def("tuple_passthrough", [](std::tuple<bool, std::string, int> 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<int> p) { return 10 * p.get(); });
+ m.def("refwrap_usertype", [](std::reference_wrapper<UserType> 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<std::pair<int, int>>) { });
+
+ 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<float> 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 0000000..59af0ee
--- /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 <string_view>")
+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 4111008..f26f6c3 100644
--- a/tests/test_callbacks.cpp
+++ b/tests/test_callbacks.cpp
@@ -71,9 +71,6 @@
}
};
-/// Something to trigger a conversion error
-struct Unregistered {};
-
class AbstractBase {
public:
virtual unsigned int func() = 0;
@@ -144,11 +141,11 @@
});
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 e18b39d..1af9740 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 <wenzel.jakob@epfl.ch>
@@ -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_<NoConstructor>(m, "NoConstructor")
+ .def_static("new_instance", &NoConstructor::new_instance, "Return an instance");
+}
template <int N> class BreaksBase {};
template <int N> class BreaksTramp : public BreaksBase<N> {};
@@ -61,8 +76,3 @@
//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
//typedef py::class_<BreaksBase<-8>, 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 0000000..6e84913
--- /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 40cbcec..0000000
--- 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 ea6bdb9..01efc70 100644
--- a/tests/test_exceptions.cpp
+++ b/tests/test_exceptions.cpp
@@ -108,6 +108,10 @@
};
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<MyException> ex(m, "MyException");
py::register_exception_translator([](std::exception_ptr p) {
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index 887ba64..15d4787 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 6129b3f..fce537d 100644
--- a/tests/test_inheritance.cpp
+++ b/tests/test_inheritance.cpp
@@ -97,8 +97,6 @@
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<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]),
@@ -106,7 +104,7 @@
py::isinstance<Pet>(l[3]),
py::isinstance<Dog>(l[4]),
py::isinstance<Rabbit>(l[5]),
- py::isinstance<Unregistered>(l[6])
+ py::isinstance<UnregisteredType>(l[6])
);
});
diff --git a/tests/test_modules.py b/tests/test_modules.py
index 1cb177f..17c00c8 100644
--- a/tests/test_modules.py
+++ b/tests/test_modules.py
@@ -59,6 +59,7 @@
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 46a23ee..0000000
--- 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 <wenzel.jakob@epfl.ch>
-
- 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 <pybind11/stl.h>
-#include <pybind11/complex.h>
-
-#ifdef _WIN32
-# include <io.h>
-# include <fcntl.h>
-#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<std::string, std::string> get_dict_2() {
- std::map<std::string, std::string> result;
- result["key"] = "value";
- return result;
- }
-
- /* Create and return a C++ set */
- std::set<std::string> get_set_2() {
- std::set<std::string> 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<std::wstring> get_list_2() {
- std::vector<std::wstring> list;
- list.push_back(L"value");
- return list;
- }
-
- /* C++ STL data types are automatically casted */
- std::array<std::string, 2> get_array() {
- return std::array<std::string, 2> {{ "array entry 1" , "array entry 2"}};
- }
-
- std::valarray<int> get_valarray() {
- return std::valarray<int>({ 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<std::string, std::string> &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<std::string> &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<std::wstring> &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<std::string, bool> pair_passthrough(std::pair<bool, std::string> input) {
- return std::make_pair(input.second, input.first);
- }
-
- /* pybind automatically translates between C++11 and Python tuples */
- std::tuple<int, std::string, bool> tuple_passthrough(std::tuple<bool, std::string, int> 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<std::string, 2> &array) {
- int index = 0;
- for (auto item : array)
- py::print("array item {}: {}"_s.format(index++, item));
- }
-
- void print_valarray(std::valarray<int> &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<Value> 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<std::reference_wrapper<IncrIntWrapper>> incr_int_wrappers() {
- static IncrIntWrapper x1(1), x2(2);
- std::vector<std::reference_wrapper<IncrIntWrapper>> r;
- r.emplace_back(x1);
- r.emplace_back(x2);
- return r;
-};
-
-/// Issue #528: templated constructor
-struct TplCtorClass {
- template <typename T> TplCtorClass(const T &) { }
- bool operator==(const TplCtorClass &) const { return true; }
-};
-
-namespace std {
- template <>
- struct hash<TplCtorClass> { 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_<ExamplePythonTypes>(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_<NoAssign>(m, "NoAssign", "Class with no C++ assignment operators")
- .def(py::init<>())
- .def(py::init<int>());
-
- 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<int>;
- using opt_no_assign = std::optional<NoAssign>;
- 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<int>;
- using exp_opt_no_assign = std::experimental::optional<NoAssign>;
- 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<int, std::string, double, std::nullptr_t> v) {
- return std::visit(visitor(), v);
- });
-
- m.def("load_variant_2pass", [](std::variant<double, int> v) {
- return std::visit(visitor(), v);
- });
-
- m.def("cast_variant", []() {
- using V = std::variant<int, std::string>;
- 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<T>() should be the same as T(obj)
- return py::dict(
- "str"_a=d["str"].cast<py::str>(),
- "bool"_a=d["bool"].cast<py::bool_>(),
- "int"_a=d["int"].cast<py::int_>(),
- "float"_a=d["float"].cast<py::float_>(),
- "tuple"_a=d["tuple"].cast<py::tuple>(),
- "list"_a=d["list"].cast<py::list>(),
- "dict"_a=d["dict"].cast<py::dict>(),
- "set"_a=d["set"].cast<py::set>(),
- "memoryview"_a=d["memoryview"].cast<py::memoryview>()
- );
- });
-
- py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue")
- .def_readonly("value", &MoveOutContainer::Value::value);
-
- py::class_<MoveOutContainer>(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<unsigned char>(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<int>) { 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_<IntWrapper>(m, "IntWrapper")
- .def(py::init<int>())
- .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<IntWrapper> p4) {
- IntWrapper *p1 = new IntWrapper{1};
- IntWrapper *p2 = new IntWrapper{2};
- IntWrapper *p3 = new IntWrapper{3};
- std::vector<std::reference_wrapper<IntWrapper>> 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<int> 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<std::pair<int, int>>) { });
-
- // Test that copying/referencing is working as expected with reference_wrappers:
- py::class_<IncrIntWrapper>(m, "IncrIntWrapper")
- .def(py::init<int>())
- .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<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
-
- /// Issue #528: templated constructor
- m.def("tpl_ctor_vector", [](std::vector<TplCtorClass> &) {});
- m.def("tpl_ctor_map", [](std::unordered_map<TplCtorClass, TplCtorClass> &) {});
- m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
-#if defined(PYBIND11_HAS_OPTIONAL)
- m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
-#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
- m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {});
-#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 7932602..0000000
--- 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 <optional>')
-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 <experimental/optional>')
-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 <variant>')
-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 <string_view>')
-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<T> 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 0000000..edc1e99
--- /dev/null
+++ b/tests/test_pytypes.cpp
@@ -0,0 +1,264 @@
+/*
+ tests/test_pytypes.cpp -- Python type casters
+
+ Copyright (c) 2017 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ 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<T>() should be the same as T(obj)
+ return py::dict(
+ "str"_a=d["str"].cast<py::str>(),
+ "bool"_a=d["bool"].cast<py::bool_>(),
+ "int"_a=d["int"].cast<py::int_>(),
+ "float"_a=d["float"].cast<py::float_>(),
+ "tuple"_a=d["tuple"].cast<py::tuple>(),
+ "list"_a=d["list"].cast<py::list>(),
+ "dict"_a=d["dict"].cast<py::dict>(),
+ "set"_a=d["set"].cast<py::set>(),
+ "memoryview"_a=d["memoryview"].cast<py::memoryview>()
+ );
+ });
+
+ 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 0000000..1c75c47
--- /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 012e97d..2ce2e60 100644
--- a/tests/test_sequences_and_iterators.py
+++ b/tests/test_sequences_and_iterators.py
@@ -163,4 +163,3 @@
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 0000000..5288097
--- /dev/null
+++ b/tests/test_stl.cpp
@@ -0,0 +1,163 @@
+/*
+ tests/test_stl.cpp -- STL type casters
+
+ Copyright (c) 2017 Wenzel Jakob <wenzel.jakob@epfl.ch>
+
+ 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 <pybind11/stl.h>
+
+// 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 <typename T> TplCtorClass(const T &) { }
+ bool operator==(const TplCtorClass &) const { return true; }
+};
+
+namespace std {
+ template <>
+ struct hash<TplCtorClass> { size_t operator()(const TplCtorClass &) const { return 0; } };
+}
+
+
+TEST_SUBMODULE(stl, m) {
+ // test_vector
+ m.def("cast_vector", []() { return std::vector<int>{1}; });
+ m.def("load_vector", [](const std::vector<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });
+
+ // test_array
+ m.def("cast_array", []() { return std::array<int, 2> {{1 , 2}}; });
+ m.def("load_array", [](const std::array<int, 2> &a) { return a[0] == 1 && a[1] == 2; });
+
+ // test_valarray
+ m.def("cast_valarray", []() { return std::valarray<int>{1, 4, 9}; });
+ m.def("load_valarray", [](const std::valarray<int>& v) {
+ return v.size() == 3 && v[0] == 1 && v[1] == 4 && v[2] == 9;
+ });
+
+ // test_map
+ m.def("cast_map", []() { return std::map<std::string, std::string>{{"key", "value"}}; });
+ m.def("load_map", [](const std::map<std::string, std::string> &map) {
+ return map.at("key") == "value" && map.at("key2") == "value2";
+ });
+
+ // test_set
+ m.def("cast_set", []() { return std::set<std::string>{"key1", "key2"}; });
+ m.def("load_set", [](const std::set<std::string> &set) {
+ return set.count("key1") && set.count("key2") && set.count("key3");
+ });
+
+ struct MoveOutContainer {
+ struct Value { int value; };
+
+ std::list<Value> move_list() const { return {{0}, {1}, {2}}; }
+ };
+
+ py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue")
+ .def_readonly("value", &MoveOutContainer::Value::value);
+
+ py::class_<MoveOutContainer>(m, "MoveOutContainer")
+ .def(py::init<>())
+ .def_property_readonly("move_list", &MoveOutContainer::move_list);
+
+ py::class_<NoAssign>(m, "NoAssign", "Class with no C++ assignment operators")
+ .def(py::init<>())
+ .def(py::init<int>());
+
+#ifdef PYBIND11_HAS_OPTIONAL
+ m.attr("has_optional") = true;
+
+ using opt_int = std::optional<int>;
+ using opt_no_assign = std::optional<NoAssign>;
+ 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<int>) { 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<int>;
+ using exp_opt_no_assign = std::experimental::optional<NoAssign>;
+ 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<int, std::string, double, std::nullptr_t> v) {
+ return std::visit(visitor(), v);
+ });
+
+ m.def("load_variant_2pass", [](std::variant<double, int> v) {
+ return std::visit(visitor(), v);
+ });
+
+ m.def("cast_variant", []() {
+ using V = std::variant<int, std::string>;
+ return py::make_tuple(V(5), V("Hello"));
+ });
+#endif
+
+ /// #528: templated constructor
+ m.def("tpl_ctor_vector", [](std::vector<TplCtorClass> &) {});
+ m.def("tpl_ctor_map", [](std::unordered_map<TplCtorClass, TplCtorClass> &) {});
+ m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
+#if defined(PYBIND11_HAS_OPTIONAL)
+ m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
+#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
+ m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {});
+#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<UserType> p4) {
+ static UserType p1{1}, p2{2}, p3{3};
+ return std::vector<std::reference_wrapper<UserType>> {
+ 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 0000000..1c98258
--- /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 <optional>')
+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 <experimental/optional>')
+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 <variant>')
+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)]"