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::
The file :file:`tests/test_python_types.cpp` contains a complete
The file :file:`tests/test_stl.cpp` contains a complete
example that demonstrates how to pass STL data types in more detail.
C++17 library containers

View File

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

View File

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

View File

@ -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 @@ std::list<std::function<void(py::module &)>> &initializers() {
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 @@ PYBIND11_MODULE(pybind11_tests, m) {
bind_ConstructorStats(m);
#if !defined(NDEBUG)
m.attr("debug_enabled") = true;
#else
m.attr("debug_enabled") = false;
#endif
py::class_<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);

View File

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

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 {
public:
virtual unsigned int func() = 0;
@ -144,11 +141,11 @@ test_initializer callbacks([](py::module &m) {
});
m.def("test_arg_conversion_error1", [](py::function f) {
f(234, Unregistered(), "kw"_a=567);
f(234, UnregisteredType(), "kw"_a=567);
});
m.def("test_arg_conversion_error2", [](py::function f) {
f(234, "expected_name"_a=Unregistered(), "kw"_a=567);
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
});
/* Test cleanup of lambda closure */

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>
@ -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 @@ CHECK_HOLDER(6, shared); CHECK_HOLDER(7, shared); CHECK_HOLDER(8, shared);
//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", []() {});
});

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) {
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) {

View File

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

View File

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

View File

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

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_2()) == [1, 2, 3]
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)]"