mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
Split test_python_types.cpp into builtin_casters, stl and pytypes
This commit is contained in:
parent
bdfb50f384
commit
83e328f58c
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
136
tests/test_builtin_casters.cpp
Normal file
136
tests/test_builtin_casters.cpp
Normal 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()); });
|
||||||
|
}
|
221
tests/test_builtin_casters.py
Normal file
221
tests/test_builtin_casters.py
Normal 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)"
|
@ -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 */
|
||||||
|
@ -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
44
tests/test_class.py
Normal 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
|
||||||
|
"""
|
@ -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()
|
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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])
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -1,695 +0,0 @@
|
|||||||
/*
|
|
||||||
tests/test_python_types.cpp -- singleton design pattern, static functions and
|
|
||||||
variables, passing and interacting with Python types
|
|
||||||
|
|
||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
||||||
|
|
||||||
All rights reserved. Use of this source code is governed by a
|
|
||||||
BSD-style license that can be found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "pybind11_tests.h"
|
|
||||||
#include "constructor_stats.h"
|
|
||||||
#include <pybind11/stl.h>
|
|
||||||
#include <pybind11/complex.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <io.h>
|
|
||||||
# include <fcntl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# pragma warning(push)
|
|
||||||
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class ExamplePythonTypes {
|
|
||||||
public:
|
|
||||||
static ExamplePythonTypes *new_instance() {
|
|
||||||
auto *ptr = new ExamplePythonTypes();
|
|
||||||
print_created(ptr, "via new_instance");
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
~ExamplePythonTypes() { print_destroyed(this); }
|
|
||||||
|
|
||||||
/* Create and return a Python dictionary */
|
|
||||||
py::dict get_dict() {
|
|
||||||
py::dict dict;
|
|
||||||
dict[py::str("key")] = py::str("value");
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create and return a Python set */
|
|
||||||
py::set get_set() {
|
|
||||||
py::set set;
|
|
||||||
set.add(py::str("key1"));
|
|
||||||
set.add("key2");
|
|
||||||
set.add(std::string("key3"));
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create and return a C++ dictionary */
|
|
||||||
std::map<std::string, std::string> get_dict_2() {
|
|
||||||
std::map<std::string, std::string> result;
|
|
||||||
result["key"] = "value";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create and return a C++ set */
|
|
||||||
std::set<std::string> get_set_2() {
|
|
||||||
std::set<std::string> result;
|
|
||||||
result.insert("key1");
|
|
||||||
result.insert("key2");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create, manipulate, and return a Python list */
|
|
||||||
py::list get_list() {
|
|
||||||
py::list list;
|
|
||||||
list.append("value");
|
|
||||||
py::print("Entry at position 0:", list[0]);
|
|
||||||
list[0] = py::str("overwritten");
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* C++ STL data types are automatically casted */
|
|
||||||
std::vector<std::wstring> get_list_2() {
|
|
||||||
std::vector<std::wstring> list;
|
|
||||||
list.push_back(L"value");
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* C++ STL data types are automatically casted */
|
|
||||||
std::array<std::string, 2> get_array() {
|
|
||||||
return std::array<std::string, 2> {{ "array entry 1" , "array entry 2"}};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::valarray<int> get_valarray() {
|
|
||||||
return std::valarray<int>({ 1, 4, 9 });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Easily iterate over a dictionary using a C++11 range-based for loop */
|
|
||||||
void print_dict(py::dict dict) {
|
|
||||||
for (auto item : dict)
|
|
||||||
py::print("key: {}, value={}"_s.format(item.first, item.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Easily iterate over a set using a C++11 range-based for loop */
|
|
||||||
void print_set(py::set set) {
|
|
||||||
for (auto item : set)
|
|
||||||
py::print("key:", item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Easily iterate over a list using a C++11 range-based for loop */
|
|
||||||
void print_list(py::list list) {
|
|
||||||
int index = 0;
|
|
||||||
for (auto item : list)
|
|
||||||
py::print("list item {}: {}"_s.format(index++, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STL data types (such as maps) are automatically casted from Python */
|
|
||||||
void print_dict_2(const std::map<std::string, std::string> &dict) {
|
|
||||||
for (auto item : dict)
|
|
||||||
py::print("key: {}, value={}"_s.format(item.first, item.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STL data types (such as sets) are automatically casted from Python */
|
|
||||||
void print_set_2(const std::set<std::string> &set) {
|
|
||||||
for (auto item : set)
|
|
||||||
py::print("key:", item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STL data types (such as vectors) are automatically casted from Python */
|
|
||||||
void print_list_2(std::vector<std::wstring> &list) {
|
|
||||||
int index = 0;
|
|
||||||
for (auto item : list)
|
|
||||||
py::print("list item {}: {}"_s.format(index++, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pybind automatically translates between C++11 and Python tuples */
|
|
||||||
std::pair<std::string, bool> pair_passthrough(std::pair<bool, std::string> input) {
|
|
||||||
return std::make_pair(input.second, input.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pybind automatically translates between C++11 and Python tuples */
|
|
||||||
std::tuple<int, std::string, bool> tuple_passthrough(std::tuple<bool, std::string, int> input) {
|
|
||||||
return std::make_tuple(std::get<2>(input), std::get<1>(input), std::get<0>(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* STL data types (such as arrays) are automatically casted from Python */
|
|
||||||
void print_array(std::array<std::string, 2> &array) {
|
|
||||||
int index = 0;
|
|
||||||
for (auto item : array)
|
|
||||||
py::print("array item {}: {}"_s.format(index++, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_valarray(std::valarray<int> &varray) {
|
|
||||||
int index = 0;
|
|
||||||
for (auto item : varray)
|
|
||||||
py::print("valarray item {}: {}"_s.format(index++, item));
|
|
||||||
}
|
|
||||||
|
|
||||||
void throw_exception() {
|
|
||||||
throw std::runtime_error("This exception was intentionally thrown.");
|
|
||||||
}
|
|
||||||
|
|
||||||
py::bytes get_bytes_from_string() {
|
|
||||||
return (py::bytes) std::string("foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
py::bytes get_bytes_from_str() {
|
|
||||||
return (py::bytes) py::str("bar", 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
py::str get_str_from_string() {
|
|
||||||
return (py::str) std::string("baz");
|
|
||||||
}
|
|
||||||
|
|
||||||
py::str get_str_from_bytes() {
|
|
||||||
return (py::str) py::bytes("boo", 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_print(const py::object& obj) {
|
|
||||||
py::print(py::str(obj));
|
|
||||||
py::print(py::repr(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int value;
|
|
||||||
static const int value2;
|
|
||||||
};
|
|
||||||
|
|
||||||
int ExamplePythonTypes::value = 0;
|
|
||||||
const int ExamplePythonTypes::value2 = 5;
|
|
||||||
|
|
||||||
struct MoveOutContainer {
|
|
||||||
struct Value { int value; };
|
|
||||||
|
|
||||||
std::list<Value> move_list() const { return {{0}, {1}, {2}}; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UnregisteredType { };
|
|
||||||
|
|
||||||
// Class that can be move- and copy-constructed, but not assigned
|
|
||||||
struct NoAssign {
|
|
||||||
int value;
|
|
||||||
|
|
||||||
explicit NoAssign(int value = 0) : value(value) {}
|
|
||||||
NoAssign(const NoAssign &) = default;
|
|
||||||
NoAssign(NoAssign &&) = default;
|
|
||||||
|
|
||||||
NoAssign &operator=(const NoAssign &) = delete;
|
|
||||||
NoAssign &operator=(NoAssign &&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Increments on copy
|
|
||||||
struct IncrIntWrapper {
|
|
||||||
int i;
|
|
||||||
IncrIntWrapper(int i) : i(i) {}
|
|
||||||
IncrIntWrapper(const IncrIntWrapper ©) : i(copy.i + 1) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::reference_wrapper<IncrIntWrapper>> incr_int_wrappers() {
|
|
||||||
static IncrIntWrapper x1(1), x2(2);
|
|
||||||
std::vector<std::reference_wrapper<IncrIntWrapper>> r;
|
|
||||||
r.emplace_back(x1);
|
|
||||||
r.emplace_back(x2);
|
|
||||||
return r;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Issue #528: templated constructor
|
|
||||||
struct TplCtorClass {
|
|
||||||
template <typename T> TplCtorClass(const T &) { }
|
|
||||||
bool operator==(const TplCtorClass &) const { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template <>
|
|
||||||
struct hash<TplCtorClass> { size_t operator()(const TplCtorClass &) const { return 0; } };
|
|
||||||
}
|
|
||||||
|
|
||||||
test_initializer python_types([](py::module &m) {
|
|
||||||
/* No constructor is explicitly defined below. An exception is raised when
|
|
||||||
trying to construct it directly from Python */
|
|
||||||
py::class_<ExamplePythonTypes>(m, "ExamplePythonTypes", "Example 2 documentation")
|
|
||||||
.def("get_dict", &ExamplePythonTypes::get_dict, "Return a Python dictionary")
|
|
||||||
.def("get_dict_2", &ExamplePythonTypes::get_dict_2, "Return a C++ dictionary")
|
|
||||||
.def("get_list", &ExamplePythonTypes::get_list, "Return a Python list")
|
|
||||||
.def("get_list_2", &ExamplePythonTypes::get_list_2, "Return a C++ list")
|
|
||||||
.def("get_set", &ExamplePythonTypes::get_set, "Return a Python set")
|
|
||||||
.def("get_set2", &ExamplePythonTypes::get_set_2, "Return a C++ set")
|
|
||||||
.def("get_array", &ExamplePythonTypes::get_array, "Return a C++ array")
|
|
||||||
.def("get_valarray", &ExamplePythonTypes::get_valarray, "Return a C++ valarray")
|
|
||||||
.def("print_dict", &ExamplePythonTypes::print_dict, "Print entries of a Python dictionary")
|
|
||||||
.def("print_dict_2", &ExamplePythonTypes::print_dict_2, "Print entries of a C++ dictionary")
|
|
||||||
.def("print_set", &ExamplePythonTypes::print_set, "Print entries of a Python set")
|
|
||||||
.def("print_set_2", &ExamplePythonTypes::print_set_2, "Print entries of a C++ set")
|
|
||||||
.def("print_list", &ExamplePythonTypes::print_list, "Print entries of a Python list")
|
|
||||||
.def("print_list_2", &ExamplePythonTypes::print_list_2, "Print entries of a C++ list")
|
|
||||||
.def("print_array", &ExamplePythonTypes::print_array, "Print entries of a C++ array")
|
|
||||||
.def("print_valarray", &ExamplePythonTypes::print_valarray, "Print entries of a C++ valarray")
|
|
||||||
.def("pair_passthrough", &ExamplePythonTypes::pair_passthrough, "Return a pair in reversed order")
|
|
||||||
.def("tuple_passthrough", &ExamplePythonTypes::tuple_passthrough, "Return a triple in reversed order")
|
|
||||||
.def("throw_exception", &ExamplePythonTypes::throw_exception, "Throw an exception")
|
|
||||||
.def("get_bytes_from_string", &ExamplePythonTypes::get_bytes_from_string, "py::bytes from std::string")
|
|
||||||
.def("get_bytes_from_str", &ExamplePythonTypes::get_bytes_from_str, "py::bytes from py::str")
|
|
||||||
.def("get_str_from_string", &ExamplePythonTypes::get_str_from_string, "py::str from std::string")
|
|
||||||
.def("get_str_from_bytes", &ExamplePythonTypes::get_str_from_bytes, "py::str from py::bytes")
|
|
||||||
.def("test_print", &ExamplePythonTypes::test_print, "test the print function")
|
|
||||||
.def_static("new_instance", &ExamplePythonTypes::new_instance, "Return an instance")
|
|
||||||
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
|
|
||||||
.def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)");
|
|
||||||
|
|
||||||
m.def("test_print_function", []() {
|
|
||||||
py::print("Hello, World!");
|
|
||||||
py::print(1, 2.0, "three", true, std::string("-- multiple args"));
|
|
||||||
auto args = py::make_tuple("and", "a", "custom", "separator");
|
|
||||||
py::print("*args", *args, "sep"_a="-");
|
|
||||||
py::print("no new line here", "end"_a=" -- ");
|
|
||||||
py::print("next print");
|
|
||||||
|
|
||||||
auto py_stderr = py::module::import("sys").attr("stderr");
|
|
||||||
py::print("this goes to stderr", "file"_a=py_stderr);
|
|
||||||
|
|
||||||
py::print("flush", "flush"_a=true);
|
|
||||||
|
|
||||||
py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this"));
|
|
||||||
});
|
|
||||||
|
|
||||||
py::class_<NoAssign>(m, "NoAssign", "Class with no C++ assignment operators")
|
|
||||||
.def(py::init<>())
|
|
||||||
.def(py::init<int>());
|
|
||||||
|
|
||||||
m.def("test_print_failure", []() { py::print(42, UnregisteredType()); });
|
|
||||||
#if !defined(NDEBUG)
|
|
||||||
m.attr("debug_enabled") = true;
|
|
||||||
#else
|
|
||||||
m.attr("debug_enabled") = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m.def("test_str_format", []() {
|
|
||||||
auto s1 = "{} + {} = {}"_s.format(1, 2, 3);
|
|
||||||
auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3);
|
|
||||||
return py::make_tuple(s1, s2);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("test_dict_keyword_constructor", []() {
|
|
||||||
auto d1 = py::dict("x"_a=1, "y"_a=2);
|
|
||||||
auto d2 = py::dict("z"_a=3, **d1);
|
|
||||||
return d2;
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("test_accessor_api", [](py::object o) {
|
|
||||||
auto d = py::dict();
|
|
||||||
|
|
||||||
d["basic_attr"] = o.attr("basic_attr");
|
|
||||||
|
|
||||||
auto l = py::list();
|
|
||||||
for (const auto &item : o.attr("begin_end")) {
|
|
||||||
l.append(item);
|
|
||||||
}
|
|
||||||
d["begin_end"] = l;
|
|
||||||
|
|
||||||
d["operator[object]"] = o.attr("d")["operator[object]"_s];
|
|
||||||
d["operator[char *]"] = o.attr("d")["operator[char *]"];
|
|
||||||
|
|
||||||
d["attr(object)"] = o.attr("sub").attr("attr_obj");
|
|
||||||
d["attr(char *)"] = o.attr("sub").attr("attr_char");
|
|
||||||
try {
|
|
||||||
o.attr("sub").attr("missing").ptr();
|
|
||||||
} catch (const py::error_already_set &) {
|
|
||||||
d["missing_attr_ptr"] = "raised"_s;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
o.attr("missing").attr("doesn't matter");
|
|
||||||
} catch (const py::error_already_set &) {
|
|
||||||
d["missing_attr_chain"] = "raised"_s;
|
|
||||||
}
|
|
||||||
|
|
||||||
d["is_none"] = o.attr("basic_attr").is_none();
|
|
||||||
|
|
||||||
d["operator()"] = o.attr("func")(1);
|
|
||||||
d["operator*"] = o.attr("func")(*o.attr("begin_end"));
|
|
||||||
|
|
||||||
return d;
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("test_tuple_accessor", [](py::tuple existing_t) {
|
|
||||||
try {
|
|
||||||
existing_t[0] = 1;
|
|
||||||
} catch (const py::error_already_set &) {
|
|
||||||
// --> Python system error
|
|
||||||
// Only new tuples (refcount == 1) are mutable
|
|
||||||
auto new_t = py::tuple(3);
|
|
||||||
for (size_t i = 0; i < new_t.size(); ++i) {
|
|
||||||
new_t[i] = i;
|
|
||||||
}
|
|
||||||
return new_t;
|
|
||||||
}
|
|
||||||
return py::tuple();
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("test_accessor_assignment", []() {
|
|
||||||
auto l = py::list(1);
|
|
||||||
l[0] = 0;
|
|
||||||
|
|
||||||
auto d = py::dict();
|
|
||||||
d["get"] = l[0];
|
|
||||||
auto var = l[0];
|
|
||||||
d["deferred_get"] = var;
|
|
||||||
l[0] = 1;
|
|
||||||
d["set"] = l[0];
|
|
||||||
var = 99; // this assignment should not overwrite l[0]
|
|
||||||
d["deferred_set"] = l[0];
|
|
||||||
d["var"] = var;
|
|
||||||
|
|
||||||
return d;
|
|
||||||
});
|
|
||||||
|
|
||||||
bool has_optional = false, has_exp_optional = false;
|
|
||||||
#ifdef PYBIND11_HAS_OPTIONAL
|
|
||||||
has_optional = true;
|
|
||||||
using opt_int = std::optional<int>;
|
|
||||||
using opt_no_assign = std::optional<NoAssign>;
|
|
||||||
m.def("double_or_zero", [](const opt_int& x) -> int {
|
|
||||||
return x.value_or(0) * 2;
|
|
||||||
});
|
|
||||||
m.def("half_or_none", [](int x) -> opt_int {
|
|
||||||
return x ? opt_int(x / 2) : opt_int();
|
|
||||||
});
|
|
||||||
m.def("test_nullopt", [](opt_int x) {
|
|
||||||
return x.value_or(42);
|
|
||||||
}, py::arg_v("x", std::nullopt, "None"));
|
|
||||||
m.def("test_no_assign", [](const opt_no_assign &x) {
|
|
||||||
return x ? x->value : 42;
|
|
||||||
}, py::arg_v("x", std::nullopt, "None"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PYBIND11_HAS_EXP_OPTIONAL
|
|
||||||
has_exp_optional = true;
|
|
||||||
using exp_opt_int = std::experimental::optional<int>;
|
|
||||||
using exp_opt_no_assign = std::experimental::optional<NoAssign>;
|
|
||||||
m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int {
|
|
||||||
return x.value_or(0) * 2;
|
|
||||||
});
|
|
||||||
m.def("half_or_none_exp", [](int x) -> exp_opt_int {
|
|
||||||
return x ? exp_opt_int(x / 2) : exp_opt_int();
|
|
||||||
});
|
|
||||||
m.def("test_nullopt_exp", [](exp_opt_int x) {
|
|
||||||
return x.value_or(42);
|
|
||||||
}, py::arg_v("x", std::experimental::nullopt, "None"));
|
|
||||||
m.def("test_no_assign_exp", [](const exp_opt_no_assign &x) {
|
|
||||||
return x ? x->value : 42;
|
|
||||||
}, py::arg_v("x", std::experimental::nullopt, "None"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m.attr("has_optional") = has_optional;
|
|
||||||
m.attr("has_exp_optional") = has_exp_optional;
|
|
||||||
|
|
||||||
#ifdef PYBIND11_HAS_VARIANT
|
|
||||||
struct visitor {
|
|
||||||
const char *operator()(int) { return "int"; }
|
|
||||||
const char *operator()(std::string) { return "std::string"; }
|
|
||||||
const char *operator()(double) { return "double"; }
|
|
||||||
const char *operator()(std::nullptr_t) { return "std::nullptr_t"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
m.def("load_variant", [](std::variant<int, std::string, double, std::nullptr_t> v) {
|
|
||||||
return std::visit(visitor(), v);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("load_variant_2pass", [](std::variant<double, int> v) {
|
|
||||||
return std::visit(visitor(), v);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("cast_variant", []() {
|
|
||||||
using V = std::variant<int, std::string>;
|
|
||||||
return py::make_tuple(V(5), V("Hello"));
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m.def("test_default_constructors", []() {
|
|
||||||
return py::dict(
|
|
||||||
"str"_a=py::str(),
|
|
||||||
"bool"_a=py::bool_(),
|
|
||||||
"int"_a=py::int_(),
|
|
||||||
"float"_a=py::float_(),
|
|
||||||
"tuple"_a=py::tuple(),
|
|
||||||
"list"_a=py::list(),
|
|
||||||
"dict"_a=py::dict(),
|
|
||||||
"set"_a=py::set()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("test_converting_constructors", [](py::dict d) {
|
|
||||||
return py::dict(
|
|
||||||
"str"_a=py::str(d["str"]),
|
|
||||||
"bool"_a=py::bool_(d["bool"]),
|
|
||||||
"int"_a=py::int_(d["int"]),
|
|
||||||
"float"_a=py::float_(d["float"]),
|
|
||||||
"tuple"_a=py::tuple(d["tuple"]),
|
|
||||||
"list"_a=py::list(d["list"]),
|
|
||||||
"dict"_a=py::dict(d["dict"]),
|
|
||||||
"set"_a=py::set(d["set"]),
|
|
||||||
"memoryview"_a=py::memoryview(d["memoryview"])
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("test_cast_functions", [](py::dict d) {
|
|
||||||
// When converting between Python types, obj.cast<T>() should be the same as T(obj)
|
|
||||||
return py::dict(
|
|
||||||
"str"_a=d["str"].cast<py::str>(),
|
|
||||||
"bool"_a=d["bool"].cast<py::bool_>(),
|
|
||||||
"int"_a=d["int"].cast<py::int_>(),
|
|
||||||
"float"_a=d["float"].cast<py::float_>(),
|
|
||||||
"tuple"_a=d["tuple"].cast<py::tuple>(),
|
|
||||||
"list"_a=d["list"].cast<py::list>(),
|
|
||||||
"dict"_a=d["dict"].cast<py::dict>(),
|
|
||||||
"set"_a=d["set"].cast<py::set>(),
|
|
||||||
"memoryview"_a=d["memoryview"].cast<py::memoryview>()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
py::class_<MoveOutContainer::Value>(m, "MoveOutContainerValue")
|
|
||||||
.def_readonly("value", &MoveOutContainer::Value::value);
|
|
||||||
|
|
||||||
py::class_<MoveOutContainer>(m, "MoveOutContainer")
|
|
||||||
.def(py::init<>())
|
|
||||||
.def_property_readonly("move_list", &MoveOutContainer::move_list);
|
|
||||||
|
|
||||||
m.def("get_implicit_casting", []() {
|
|
||||||
py::dict d;
|
|
||||||
d["char*_i1"] = "abc";
|
|
||||||
const char *c2 = "abc";
|
|
||||||
d["char*_i2"] = c2;
|
|
||||||
d["char*_e"] = py::cast(c2);
|
|
||||||
d["char*_p"] = py::str(c2);
|
|
||||||
|
|
||||||
d["int_i1"] = 42;
|
|
||||||
int i = 42;
|
|
||||||
d["int_i2"] = i;
|
|
||||||
i++;
|
|
||||||
d["int_e"] = py::cast(i);
|
|
||||||
i++;
|
|
||||||
d["int_p"] = py::int_(i);
|
|
||||||
|
|
||||||
d["str_i1"] = std::string("str");
|
|
||||||
std::string s2("str1");
|
|
||||||
d["str_i2"] = s2;
|
|
||||||
s2[3] = '2';
|
|
||||||
d["str_e"] = py::cast(s2);
|
|
||||||
s2[3] = '3';
|
|
||||||
d["str_p"] = py::str(s2);
|
|
||||||
|
|
||||||
py::list l(2);
|
|
||||||
l[0] = 3;
|
|
||||||
l[1] = py::cast(6);
|
|
||||||
l.append(9);
|
|
||||||
l.append(py::cast(12));
|
|
||||||
l.append(py::int_(15));
|
|
||||||
|
|
||||||
return py::dict(
|
|
||||||
"d"_a=d,
|
|
||||||
"l"_a=l
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
m.def("string_roundtrip", [](const char *s) { return s; });
|
|
||||||
|
|
||||||
// Some test characters in utf16 and utf32 encodings. The last one (the 𝐀) contains a null byte
|
|
||||||
char32_t a32 = 0x61 /*a*/, z32 = 0x7a /*z*/, ib32 = 0x203d /*‽*/, cake32 = 0x1f382 /*🎂*/, mathbfA32 = 0x1d400 /*𝐀*/;
|
|
||||||
char16_t b16 = 0x62 /*b*/, z16 = 0x7a, ib16 = 0x203d, cake16_1 = 0xd83c, cake16_2 = 0xdf82, mathbfA16_1 = 0xd835, mathbfA16_2 = 0xdc00;
|
|
||||||
std::wstring wstr;
|
|
||||||
wstr.push_back(0x61); // a
|
|
||||||
wstr.push_back(0x2e18); // ⸘
|
|
||||||
if (sizeof(wchar_t) == 2) { wstr.push_back(mathbfA16_1); wstr.push_back(mathbfA16_2); } // 𝐀, utf16
|
|
||||||
else { wstr.push_back((wchar_t) mathbfA32); } // 𝐀, utf32
|
|
||||||
wstr.push_back(0x7a); // z
|
|
||||||
|
|
||||||
m.def("good_utf8_string", []() { return std::string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8‽ 🎂 𝐀
|
|
||||||
m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // b‽🎂𝐀z
|
|
||||||
m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // a𝐀🎂‽z
|
|
||||||
m.def("good_wchar_string", [=]() { return wstr; }); // a‽𝐀z
|
|
||||||
m.def("bad_utf8_string", []() { return std::string("abc\xd0" "def"); });
|
|
||||||
m.def("bad_utf16_string", [=]() { return std::u16string({ b16, char16_t(0xd800), z16 }); });
|
|
||||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger UnicodeDecodeError
|
|
||||||
if (PY_MAJOR_VERSION >= 3)
|
|
||||||
m.def("bad_utf32_string", [=]() { return std::u32string({ a32, char32_t(0xd800), z32 }); });
|
|
||||||
if (PY_MAJOR_VERSION >= 3 || sizeof(wchar_t) == 2)
|
|
||||||
m.def("bad_wchar_string", [=]() { return std::wstring({ wchar_t(0x61), wchar_t(0xd800) }); });
|
|
||||||
m.def("u8_Z", []() -> char { return 'Z'; });
|
|
||||||
m.def("u8_eacute", []() -> char { return '\xe9'; });
|
|
||||||
m.def("u16_ibang", [=]() -> char16_t { return ib16; });
|
|
||||||
m.def("u32_mathbfA", [=]() -> char32_t { return mathbfA32; });
|
|
||||||
m.def("wchar_heart", []() -> wchar_t { return 0x2665; });
|
|
||||||
|
|
||||||
m.attr("wchar_size") = py::cast(sizeof(wchar_t));
|
|
||||||
m.def("ord_char", [](char c) -> int { return static_cast<unsigned char>(c); });
|
|
||||||
m.def("ord_char16", [](char16_t c) -> uint16_t { return c; });
|
|
||||||
m.def("ord_char32", [](char32_t c) -> uint32_t { return c; });
|
|
||||||
m.def("ord_wchar", [](wchar_t c) -> int { return c; });
|
|
||||||
|
|
||||||
m.def("strlen", [](char *s) { return strlen(s); });
|
|
||||||
m.def("string_length", [](std::string s) { return s.length(); });
|
|
||||||
|
|
||||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
|
||||||
m.def("return_none_char", []() -> const char * { return nullptr; });
|
|
||||||
m.def("return_none_bool", []() -> bool * { return nullptr; });
|
|
||||||
m.def("return_none_int", []() -> int * { return nullptr; });
|
|
||||||
m.def("return_none_float", []() -> float * { return nullptr; });
|
|
||||||
|
|
||||||
m.def("defer_none_cstring", [](char *) { return false; });
|
|
||||||
m.def("defer_none_cstring", [](py::none) { return true; });
|
|
||||||
m.def("defer_none_custom", [](ExamplePythonTypes *) { return false; });
|
|
||||||
m.def("defer_none_custom", [](py::none) { return true; });
|
|
||||||
// void and optional, however, don't defer:
|
|
||||||
m.def("nodefer_none_void", [](void *) { return true; });
|
|
||||||
m.def("nodefer_none_void", [](py::none) { return false; });
|
|
||||||
#ifdef PYBIND11_HAS_OPTIONAL
|
|
||||||
m.def("nodefer_none_optional", [](std::optional<int>) { return true; });
|
|
||||||
m.def("nodefer_none_optional", [](py::none) { return false; });
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PYBIND11_HAS_STRING_VIEW
|
|
||||||
m.attr("has_string_view") = true;
|
|
||||||
m.def("string_view_print", [](std::string_view s) { py::print(s, s.size()); });
|
|
||||||
m.def("string_view16_print", [](std::u16string_view s) { py::print(s, s.size()); });
|
|
||||||
m.def("string_view32_print", [](std::u32string_view s) { py::print(s, s.size()); });
|
|
||||||
m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; });
|
|
||||||
m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
|
|
||||||
m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
|
|
||||||
m.def("string_view_return", []() { return std::string_view(u8"utf8 secret \U0001f382"); });
|
|
||||||
m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); });
|
|
||||||
m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); });
|
|
||||||
#else
|
|
||||||
m.attr("has_string_view") = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m.def("return_capsule_with_destructor",
|
|
||||||
[]() {
|
|
||||||
py::print("creating capsule");
|
|
||||||
return py::capsule([]() {
|
|
||||||
py::print("destructing capsule");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
m.def("return_capsule_with_destructor_2",
|
|
||||||
[]() {
|
|
||||||
py::print("creating capsule");
|
|
||||||
return py::capsule((void *) 1234, [](void *ptr) {
|
|
||||||
py::print("destructing capsule: {}"_s.format((size_t) ptr));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
m.def("return_capsule_with_name_and_destructor_3",
|
|
||||||
[]() {
|
|
||||||
py::print("creating capsule");
|
|
||||||
auto capsule=py::capsule((void *) 1234, "pointer type description",
|
|
||||||
[](PyObject *ptr) {
|
|
||||||
if (ptr) {
|
|
||||||
py::print("destructing capsule");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
auto name = capsule.name();
|
|
||||||
void *contents = capsule;
|
|
||||||
py::print("created capsule with name --{}-- and contents {}"_s.format(name,(size_t) contents));
|
|
||||||
return capsule;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
m.def("load_nullptr_t", [](std::nullptr_t) {}); // not useful, but it should still compile
|
|
||||||
m.def("cast_nullptr_t", []() { return std::nullptr_t{}; });
|
|
||||||
|
|
||||||
struct IntWrapper { int i; IntWrapper(int i) : i(i) { } };
|
|
||||||
py::class_<IntWrapper>(m, "IntWrapper")
|
|
||||||
.def(py::init<int>())
|
|
||||||
.def("__repr__", [](const IntWrapper &p) { return "IntWrapper[" + std::to_string(p.i) + "]"; });
|
|
||||||
|
|
||||||
// #171: Can't return reference wrappers (or STL datastructures containing them)
|
|
||||||
// Also used to test #848: reference_wrapper shouldn't allow None
|
|
||||||
m.def("return_vec_of_reference_wrapper", [](std::reference_wrapper<IntWrapper> p4) {
|
|
||||||
IntWrapper *p1 = new IntWrapper{1};
|
|
||||||
IntWrapper *p2 = new IntWrapper{2};
|
|
||||||
IntWrapper *p3 = new IntWrapper{3};
|
|
||||||
std::vector<std::reference_wrapper<IntWrapper>> v;
|
|
||||||
v.push_back(std::ref(*p1));
|
|
||||||
v.push_back(std::ref(*p2));
|
|
||||||
v.push_back(std::ref(*p3));
|
|
||||||
v.push_back(p4);
|
|
||||||
return v;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reference-wrapper to non-generic type caster type:
|
|
||||||
m.def("refwrap_int", [](std::reference_wrapper<int> p) { return 10 * p.get(); });
|
|
||||||
|
|
||||||
// Not currently supported (std::pair caster has return-by-value cast operator);
|
|
||||||
// triggers static_assert failure.
|
|
||||||
//m.def("refwrap_pair", [](std::reference_wrapper<std::pair<int, int>>) { });
|
|
||||||
|
|
||||||
// Test that copying/referencing is working as expected with reference_wrappers:
|
|
||||||
py::class_<IncrIntWrapper>(m, "IncrIntWrapper")
|
|
||||||
.def(py::init<int>())
|
|
||||||
.def_readonly("i", &IncrIntWrapper::i);
|
|
||||||
|
|
||||||
m.def("refwrap_list_refs", []() {
|
|
||||||
py::list l;
|
|
||||||
for (auto &f : incr_int_wrappers()) l.append(py::cast(f, py::return_value_policy::reference));
|
|
||||||
return l;
|
|
||||||
});
|
|
||||||
m.def("refwrap_list_copies", []() {
|
|
||||||
py::list l;
|
|
||||||
for (auto &f : incr_int_wrappers()) l.append(py::cast(f, py::return_value_policy::copy));
|
|
||||||
return l;
|
|
||||||
});
|
|
||||||
m.def("refwrap_iiw", [](const IncrIntWrapper &w) { return w.i; });
|
|
||||||
m.def("refwrap_call_iiw", [](IncrIntWrapper &w, py::function f) {
|
|
||||||
py::list l;
|
|
||||||
l.append(f(std::ref(w)));
|
|
||||||
l.append(f(std::cref(w)));
|
|
||||||
IncrIntWrapper x(w.i);
|
|
||||||
l.append(f(std::ref(x)));
|
|
||||||
IncrIntWrapper y(w.i);
|
|
||||||
auto r3 = std::ref(y);
|
|
||||||
l.append(f(r3));
|
|
||||||
return l;
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Issue #484: number conversion generates unhandled exceptions
|
|
||||||
m.def("test_complex", [](float x) { return "{}"_s.format(x); });
|
|
||||||
m.def("test_complex", [](std::complex<float> x) { return "({}, {})"_s.format(x.real(), x.imag()); });
|
|
||||||
|
|
||||||
/// Issue #528: templated constructor
|
|
||||||
m.def("tpl_ctor_vector", [](std::vector<TplCtorClass> &) {});
|
|
||||||
m.def("tpl_ctor_map", [](std::unordered_map<TplCtorClass, TplCtorClass> &) {});
|
|
||||||
m.def("tpl_ctor_set", [](std::unordered_set<TplCtorClass> &) {});
|
|
||||||
#if defined(PYBIND11_HAS_OPTIONAL)
|
|
||||||
m.def("tpl_constr_optional", [](std::optional<TplCtorClass> &) {});
|
|
||||||
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
|
||||||
m.def("tpl_constr_optional", [](std::experimental::optional<TplCtorClass> &) {});
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# pragma warning(pop)
|
|
||||||
#endif
|
|
@ -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
264
tests/test_pytypes.cpp
Normal 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
211
tests/test_pytypes.py
Normal 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)"
|
||||||
|
)
|
@ -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
163
tests/test_stl.cpp
Normal 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
133
tests/test_stl.py
Normal 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)]"
|
Loading…
Reference in New Issue
Block a user