Split test_python_types.cpp into builtin_casters, stl and pytypes

This commit is contained in:
Dean Moldovan 2017-06-09 00:44:49 +02:00
parent bdfb50f384
commit 83e328f58c
22 changed files with 1276 additions and 1442 deletions

View File

@ -23,7 +23,7 @@ next sections for more details and alternative approaches that avoid this.
.. seealso:: .. seealso::
The file :file:`tests/test_python_types.cpp` contains a complete The file :file:`tests/test_stl.cpp` contains a complete
example that demonstrates how to pass STL data types in more detail. example that demonstrates how to pass STL data types in more detail.
C++17 library containers C++17 library containers

View File

@ -90,7 +90,7 @@ Generalized unpacking according to PEP448_ is also supported:
.. seealso:: .. seealso::
The file :file:`tests/test_python_types.cpp` contains a complete The file :file:`tests/test_pytypes.cpp` contains a complete
example that demonstrates passing native Python types in more detail. The example that demonstrates passing native Python types in more detail. The
file :file:`tests/test_callbacks.cpp` presents a few examples of calling file :file:`tests/test_callbacks.cpp` presents a few examples of calling
Python functions from C++, including keywords arguments and unpacking. Python functions from C++, including keywords arguments and unpacking.

View File

@ -28,10 +28,11 @@ endif()
set(PYBIND11_TEST_FILES set(PYBIND11_TEST_FILES
test_alias_initialization.cpp test_alias_initialization.cpp
test_buffers.cpp test_buffers.cpp
test_builtin_casters.cpp
test_call_policies.cpp test_call_policies.cpp
test_callbacks.cpp test_callbacks.cpp
test_chrono.cpp test_chrono.cpp
test_class_args.cpp test_class.cpp
test_constants_and_functions.cpp test_constants_and_functions.cpp
test_copy_move.cpp test_copy_move.cpp
test_docstring_options.cpp test_docstring_options.cpp
@ -50,9 +51,10 @@ set(PYBIND11_TEST_FILES
test_opaque_types.cpp test_opaque_types.cpp
test_operator_overloading.cpp test_operator_overloading.cpp
test_pickling.cpp test_pickling.cpp
test_python_types.cpp test_pytypes.cpp
test_sequences_and_iterators.cpp test_sequences_and_iterators.cpp
test_smart_ptr.cpp test_smart_ptr.cpp
test_stl.cpp
test_stl_binders.cpp test_stl_binders.cpp
test_virtual_functions.cpp test_virtual_functions.cpp
) )

View File

@ -10,6 +10,9 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
#include "constructor_stats.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 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 test .cpp calls with its initialization lambda. It's convenient here because we can just not
@ -28,8 +31,15 @@ std::list<std::function<void(py::module &)>> &initializers() {
return inits; return inits;
} }
test_initializer::test_initializer(std::function<void(py::module &)> initializer) { test_initializer::test_initializer(Initializer init) {
initializers().push_back(std::move(initializer)); 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) { void bind_ConstructorStats(py::module &m) {
@ -57,6 +67,24 @@ PYBIND11_MODULE(pybind11_tests, m) {
bind_ConstructorStats(m); 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()) for (const auto &initializer : initializers())
initializer(m); initializer(m);

View File

@ -1,12 +1,45 @@
#pragma once #pragma once
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <functional>
#include <list>
namespace py = pybind11; namespace py = pybind11;
using namespace pybind11::literals; using namespace pybind11::literals;
class test_initializer { class test_initializer {
using Initializer = void (*)(py::module &);
public: 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;
}; };

View File

@ -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()); });
}

View File

@ -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)"

View File

@ -71,9 +71,6 @@ struct Payload {
} }
}; };
/// Something to trigger a conversion error
struct Unregistered {};
class AbstractBase { class AbstractBase {
public: public:
virtual unsigned int func() = 0; virtual unsigned int func() = 0;
@ -144,11 +141,11 @@ test_initializer callbacks([](py::module &m) {
}); });
m.def("test_arg_conversion_error1", [](py::function f) { 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) { 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 */ /* Test cleanup of lambda closure */

View File

@ -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> Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
@ -8,7 +8,22 @@
*/ */
#include "pybind11_tests.h" #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 BreaksBase {};
template <int N> class BreaksTramp : public BreaksBase<N> {}; template <int N> class BreaksTramp : public BreaksBase<N> {};
@ -61,8 +76,3 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
//template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {}; //template <> struct BreaksBase<-8> : BreaksBase<-6>, BreaksBase<-7> {};
//typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8; //typedef py::class_<BreaksBase<-8>, BreaksBase<-6>, BreaksBase<-7>> Breaks8;
//CHECK_BROKEN(8); //CHECK_BROKEN(8);
test_initializer class_args([](py::module &m) {
// Just test that this compiled okay
m.def("class_args_noop", []() {});
});

44
tests/test_class.py Normal file
View File

@ -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
"""

View File

@ -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()

View File

@ -108,6 +108,10 @@ struct PythonCallInDestructor {
}; };
test_initializer custom_exceptions([](py::module &m) { 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 // make a new custom exception and use it as a translation target
static py::exception<MyException> ex(m, "MyException"); static py::exception<MyException> ex(m, "MyException");
py::register_exception_translator([](std::exception_ptr p) { py::register_exception_translator([](std::exception_ptr p) {

View File

@ -1,6 +1,14 @@
import pytest 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): def test_error_already_set(msg):
from pybind11_tests import throw_already_set from pybind11_tests import throw_already_set

View File

@ -97,8 +97,6 @@ test_initializer inheritance([](py::module &m) {
m.def("return_none", []() -> BaseClass* { return nullptr; }); m.def("return_none", []() -> BaseClass* { return nullptr; });
m.def("test_isinstance", [](py::list l) { m.def("test_isinstance", [](py::list l) {
struct Unregistered { }; // checks missing type_info code path
return py::make_tuple( return py::make_tuple(
py::isinstance<py::tuple>(l[0]), py::isinstance<py::tuple>(l[0]),
py::isinstance<py::dict>(l[1]), py::isinstance<py::dict>(l[1]),
@ -106,7 +104,7 @@ test_initializer inheritance([](py::module &m) {
py::isinstance<Pet>(l[3]), py::isinstance<Pet>(l[3]),
py::isinstance<Dog>(l[4]), py::isinstance<Dog>(l[4]),
py::isinstance<Rabbit>(l[5]), py::isinstance<Rabbit>(l[5]),
py::isinstance<Unregistered>(l[6]) py::isinstance<UnregisteredType>(l[6])
); );
}); });

View File

@ -59,6 +59,7 @@ def test_pydoc():
import pybind11_tests import pybind11_tests
import pydoc import pydoc
assert pybind11_tests.__name__ == "pybind11_tests"
assert pybind11_tests.__doc__ == "pybind11 test module" assert pybind11_tests.__doc__ == "pybind11 test module"
assert pydoc.text.docmodule(pybind11_tests) assert pydoc.text.docmodule(pybind11_tests)

View File

@ -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 &copy) : 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

View File

@ -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)"

264
tests/test_pytypes.cpp Normal file
View File

@ -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()); });
}

211
tests/test_pytypes.py Normal file
View File

@ -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)"
)

View File

@ -163,4 +163,3 @@ def test_iterator_rvp():
assert list(m.make_iterator_1()) == [1, 2, 3] assert list(m.make_iterator_1()) == [1, 2, 3]
assert list(m.make_iterator_2()) == [1, 2, 3] assert list(m.make_iterator_2()) == [1, 2, 3]
assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2())) assert not isinstance(m.make_iterator_1(), type(m.make_iterator_2()))

163
tests/test_stl.cpp Normal file
View File

@ -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
};
});
}

133
tests/test_stl.py Normal file
View File

@ -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)]"