summaryrefslogtreecommitdiff
path: root/ext/pybind11/tests
diff options
context:
space:
mode:
authorBobby R. Bruce <bbruce@ucdavis.edu>2019-09-23 13:52:58 -0700
committerBobby R. Bruce <bbruce@ucdavis.edu>2019-09-24 21:40:15 +0000
commitf97cf54db7a6f7642cc9fd122f23c4396c39bcf0 (patch)
tree17d2ed22a1114cb138500d46afddb3bafcc2b418 /ext/pybind11/tests
parent9235ae56c282d5a02ada3ed9b4e0fe2ee5738bde (diff)
downloadgem5-f97cf54db7a6f7642cc9fd122f23c4396c39bcf0.tar.xz
ext: Updated Pybind11 to version 2.4.1.
This updates Pybind11 from version 2.2.1 to version 2.4.1. This fixes warning/error received when "<experiment/optional>" is used when compiling using c++14 with clang. It should be noted that "ext/pybind11/include/pybind11/std.h" has been changed to include a fix added by commit ba42457254cc362eddc099f22b60d469cc6369e0. This is necessary to avoid build errors. Built: Linux (gcc, c++11) and MacOS (clang, c++14). Tested: Ran quick tests for X86, ARM, and RISC-V. Deprecates: https://gem5-review.googlesource.com/c/public/gem5/+/21019 Change-Id: Ie9783511cb6be50136076a55330e645f4f36d075 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/21119 Reviewed-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com> Maintainer: Jason Lowe-Power <jason@lowepower.com> Maintainer: Andreas Sandberg <andreas.sandberg@arm.com> Tested-by: kokoro <noreply+kokoro@google.com>
Diffstat (limited to 'ext/pybind11/tests')
-rw-r--r--ext/pybind11/tests/CMakeLists.txt33
-rw-r--r--ext/pybind11/tests/conftest.py33
-rw-r--r--ext/pybind11/tests/constructor_stats.h2
-rw-r--r--ext/pybind11/tests/cross_module_gil_utils.cpp73
-rw-r--r--ext/pybind11/tests/pytest.ini1
-rw-r--r--ext/pybind11/tests/test_async.cpp26
-rw-r--r--ext/pybind11/tests/test_async.py23
-rw-r--r--ext/pybind11/tests/test_buffers.cpp4
-rw-r--r--ext/pybind11/tests/test_buffers.py18
-rw-r--r--ext/pybind11/tests/test_builtin_casters.cpp14
-rw-r--r--ext/pybind11/tests/test_builtin_casters.py20
-rw-r--r--ext/pybind11/tests/test_call_policies.cpp2
-rw-r--r--ext/pybind11/tests/test_callbacks.cpp19
-rw-r--r--ext/pybind11/tests/test_callbacks.py29
-rw-r--r--ext/pybind11/tests/test_chrono.cpp8
-rw-r--r--ext/pybind11/tests/test_chrono.py75
-rw-r--r--ext/pybind11/tests/test_class.cpp67
-rw-r--r--ext/pybind11/tests/test_class.py52
-rw-r--r--ext/pybind11/tests/test_constants_and_functions.cpp14
-rw-r--r--ext/pybind11/tests/test_copy_move.cpp2
-rw-r--r--ext/pybind11/tests/test_eigen.cpp14
-rw-r--r--ext/pybind11/tests/test_eigen.py23
-rw-r--r--ext/pybind11/tests/test_embed/CMakeLists.txt9
-rw-r--r--ext/pybind11/tests/test_embed/catch.cpp8
-rw-r--r--ext/pybind11/tests/test_embed/external_module.cpp23
-rw-r--r--ext/pybind11/tests/test_embed/test_interpreter.cpp17
-rw-r--r--ext/pybind11/tests/test_enum.cpp24
-rw-r--r--ext/pybind11/tests/test_enum.py97
-rw-r--r--ext/pybind11/tests/test_exceptions.cpp32
-rw-r--r--ext/pybind11/tests/test_exceptions.py4
-rw-r--r--ext/pybind11/tests/test_factory_constructors.cpp3
-rw-r--r--ext/pybind11/tests/test_gil_scoped.cpp52
-rw-r--r--ext/pybind11/tests/test_gil_scoped.py85
-rw-r--r--ext/pybind11/tests/test_iostream.py11
-rw-r--r--ext/pybind11/tests/test_kwargs_and_defaults.cpp33
-rw-r--r--ext/pybind11/tests/test_kwargs_and_defaults.py54
-rw-r--r--ext/pybind11/tests/test_local_bindings.py4
-rw-r--r--ext/pybind11/tests/test_methods_and_attributes.cpp26
-rw-r--r--ext/pybind11/tests/test_methods_and_attributes.py48
-rw-r--r--ext/pybind11/tests/test_multiple_inheritance.cpp6
-rw-r--r--ext/pybind11/tests/test_numpy_array.cpp95
-rw-r--r--ext/pybind11/tests/test_numpy_array.py55
-rw-r--r--ext/pybind11/tests/test_numpy_dtypes.cpp23
-rw-r--r--ext/pybind11/tests/test_numpy_dtypes.py18
-rw-r--r--ext/pybind11/tests/test_opaque_types.cpp10
-rw-r--r--ext/pybind11/tests/test_opaque_types.py18
-rw-r--r--ext/pybind11/tests/test_operator_overloading.cpp25
-rw-r--r--ext/pybind11/tests/test_operator_overloading.py18
-rw-r--r--ext/pybind11/tests/test_pickling.py6
-rw-r--r--ext/pybind11/tests/test_pytypes.cpp38
-rw-r--r--ext/pybind11/tests/test_pytypes.py35
-rw-r--r--ext/pybind11/tests/test_sequences_and_iterators.cpp19
-rw-r--r--ext/pybind11/tests/test_sequences_and_iterators.py19
-rw-r--r--ext/pybind11/tests/test_smart_ptr.cpp100
-rw-r--r--ext/pybind11/tests/test_smart_ptr.py68
-rw-r--r--ext/pybind11/tests/test_stl.cpp46
-rw-r--r--ext/pybind11/tests/test_stl.py67
-rw-r--r--ext/pybind11/tests/test_stl_binders.py52
-rw-r--r--ext/pybind11/tests/test_tagbased_polymorphic.cpp136
-rw-r--r--ext/pybind11/tests/test_tagbased_polymorphic.py20
-rw-r--r--ext/pybind11/tests/test_union.cpp22
-rw-r--r--ext/pybind11/tests/test_union.py8
-rw-r--r--ext/pybind11/tests/test_virtual_functions.cpp37
-rw-r--r--ext/pybind11/tests/test_virtual_functions.py8
64 files changed, 1890 insertions, 141 deletions
diff --git a/ext/pybind11/tests/CMakeLists.txt b/ext/pybind11/tests/CMakeLists.txt
index 25e06662c..765c47adb 100644
--- a/ext/pybind11/tests/CMakeLists.txt
+++ b/ext/pybind11/tests/CMakeLists.txt
@@ -26,6 +26,7 @@ endif()
# Full set of test files (you can override these; see below)
set(PYBIND11_TEST_FILES
+ test_async.cpp
test_buffers.cpp
test_builtin_casters.cpp
test_call_policies.cpp
@@ -40,6 +41,7 @@ set(PYBIND11_TEST_FILES
test_eval.cpp
test_exceptions.cpp
test_factory_constructors.cpp
+ test_gil_scoped.cpp
test_iostream.cpp
test_kwargs_and_defaults.cpp
test_local_bindings.cpp
@@ -57,6 +59,8 @@ set(PYBIND11_TEST_FILES
test_smart_ptr.cpp
test_stl.cpp
test_stl_binders.cpp
+ test_tagbased_polymorphic.cpp
+ test_union.cpp
test_virtual_functions.cpp
)
@@ -68,6 +72,13 @@ if (PYBIND11_TEST_OVERRIDE)
set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE})
endif()
+# Skip test_async for Python < 3.5
+list(FIND PYBIND11_TEST_FILES test_async.cpp PYBIND11_TEST_FILES_ASYNC_I)
+if((PYBIND11_TEST_FILES_ASYNC_I GREATER -1) AND ("${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" VERSION_LESS 3.5))
+ message(STATUS "Skipping test_async because Python version ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR} < 3.5")
+ list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_ASYNC_I})
+endif()
+
string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")
# Contains the set of test files that require pybind11_cross_module_tests to be
@@ -80,6 +91,10 @@ set(PYBIND11_CROSS_MODULE_TESTS
test_stl_binders.py
)
+set(PYBIND11_CROSS_MODULE_GIL_TESTS
+ test_gil_scoped.py
+)
+
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
# skip message).
@@ -89,7 +104,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
# produces a fatal error if loaded from a pre-3.0 cmake.
if (NOT CMAKE_VERSION VERSION_LESS 3.0)
- find_package(Eigen3 QUIET CONFIG)
+ find_package(Eigen3 3.2.7 QUIET CONFIG)
if (EIGEN3_FOUND)
if (EIGEN3_VERSION_STRING AND NOT EIGEN3_VERSION_STRING VERSION_LESS 3.3.1)
set(PYBIND11_EIGEN_VIA_TARGET 1)
@@ -99,7 +114,7 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
if (NOT EIGEN3_FOUND)
# Couldn't load via target, so fall back to allowing module mode finding, which will pick up
# tools/FindEigen3.cmake
- find_package(Eigen3 QUIET)
+ find_package(Eigen3 3.2.7 QUIET)
endif()
if(EIGEN3_FOUND)
@@ -123,14 +138,14 @@ find_package(Boost 1.56)
function(pybind11_enable_warnings target_name)
if(MSVC)
target_compile_options(${target_name} PRIVATE /W4)
- else()
- target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual)
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
+ target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated)
endif()
if(PYBIND11_WERROR)
if(MSVC)
target_compile_options(${target_name} PRIVATE /WX)
- else()
+ elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
target_compile_options(${target_name} PRIVATE -Werror)
endif()
endif()
@@ -147,6 +162,14 @@ foreach(t ${PYBIND11_CROSS_MODULE_TESTS})
endif()
endforeach()
+foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS})
+ list(FIND PYBIND11_PYTEST_FILES ${t} i)
+ if (i GREATER -1)
+ list(APPEND test_targets cross_module_gil_utils)
+ break()
+ endif()
+endforeach()
+
set(testdir ${CMAKE_CURRENT_SOURCE_DIR})
foreach(target ${test_targets})
set(test_files ${PYBIND11_TEST_FILES})
diff --git a/ext/pybind11/tests/conftest.py b/ext/pybind11/tests/conftest.py
index f4c228260..57f681c66 100644
--- a/ext/pybind11/tests/conftest.py
+++ b/ext/pybind11/tests/conftest.py
@@ -17,6 +17,11 @@ _unicode_marker = re.compile(r'u(\'[^\']*\')')
_long_marker = re.compile(r'([0-9])L')
_hexadecimal = re.compile(r'0x[0-9a-fA-F]+')
+# test_async.py requires support for async and await
+collect_ignore = []
+if sys.version_info[:2] < (3, 5):
+ collect_ignore.append("test_async.py")
+
def _strip_and_dedent(s):
"""For triple-quote strings"""
@@ -75,7 +80,7 @@ class Capture(object):
self.capfd.readouterr()
return self
- def __exit__(self, *_):
+ def __exit__(self, *args):
self.out, self.err = self.capfd.readouterr()
def __eq__(self, other):
@@ -185,7 +190,7 @@ def gc_collect():
gc.collect()
-def pytest_namespace():
+def pytest_configure():
"""Add import suppression and test requirements to `pytest` namespace"""
try:
import numpy as np
@@ -202,19 +207,17 @@ def pytest_namespace():
pypy = platform.python_implementation() == "PyPy"
skipif = pytest.mark.skipif
- return {
- 'suppress': suppress,
- 'requires_numpy': skipif(not np, reason="numpy is not installed"),
- 'requires_scipy': skipif(not np, reason="scipy is not installed"),
- 'requires_eigen_and_numpy': skipif(not have_eigen or not np,
- reason="eigen and/or numpy are not installed"),
- 'requires_eigen_and_scipy': skipif(not have_eigen or not scipy,
- reason="eigen and/or scipy are not installed"),
- 'unsupported_on_pypy': skipif(pypy, reason="unsupported on PyPy"),
- 'unsupported_on_py2': skipif(sys.version_info.major < 3,
- reason="unsupported on Python 2.x"),
- 'gc_collect': gc_collect
- }
+ pytest.suppress = suppress
+ pytest.requires_numpy = skipif(not np, reason="numpy is not installed")
+ pytest.requires_scipy = skipif(not np, reason="scipy is not installed")
+ pytest.requires_eigen_and_numpy = skipif(not have_eigen or not np,
+ reason="eigen and/or numpy are not installed")
+ pytest.requires_eigen_and_scipy = skipif(
+ not have_eigen or not scipy, reason="eigen and/or scipy are not installed")
+ pytest.unsupported_on_pypy = skipif(pypy, reason="unsupported on PyPy")
+ pytest.unsupported_on_py2 = skipif(sys.version_info.major < 3,
+ reason="unsupported on Python 2.x")
+ pytest.gc_collect = gc_collect
def _test_import_pybind11():
diff --git a/ext/pybind11/tests/constructor_stats.h b/ext/pybind11/tests/constructor_stats.h
index babded032..f026e70f9 100644
--- a/ext/pybind11/tests/constructor_stats.h
+++ b/ext/pybind11/tests/constructor_stats.h
@@ -180,7 +180,7 @@ public:
}
}
}
- catch (std::out_of_range) {}
+ catch (const std::out_of_range &) {}
if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
auto &cs1 = get(*t1);
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
diff --git a/ext/pybind11/tests/cross_module_gil_utils.cpp b/ext/pybind11/tests/cross_module_gil_utils.cpp
new file mode 100644
index 000000000..07db9f6e4
--- /dev/null
+++ b/ext/pybind11/tests/cross_module_gil_utils.cpp
@@ -0,0 +1,73 @@
+/*
+ tests/cross_module_gil_utils.cpp -- tools for acquiring GIL from a different module
+
+ Copyright (c) 2019 Google LLC
+
+ 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/pybind11.h>
+#include <cstdint>
+
+// This file mimics a DSO that makes pybind11 calls but does not define a
+// PYBIND11_MODULE. The purpose is to test that such a DSO can create a
+// py::gil_scoped_acquire when the running thread is in a GIL-released state.
+//
+// Note that we define a Python module here for convenience, but in general
+// this need not be the case. The typical scenario would be a DSO that implements
+// shared logic used internally by multiple pybind11 modules.
+
+namespace {
+
+namespace py = pybind11;
+void gil_acquire() { py::gil_scoped_acquire gil; }
+
+constexpr char kModuleName[] = "cross_module_gil_utils";
+
+#if PY_MAJOR_VERSION >= 3
+struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ kModuleName,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#else
+PyMethodDef module_methods[] = {
+ {NULL, NULL, 0, NULL}
+};
+#endif
+
+} // namespace
+
+extern "C" PYBIND11_EXPORT
+#if PY_MAJOR_VERSION >= 3
+PyObject* PyInit_cross_module_gil_utils()
+#else
+void initcross_module_gil_utils()
+#endif
+{
+
+ PyObject* m =
+#if PY_MAJOR_VERSION >= 3
+ PyModule_Create(&moduledef);
+#else
+ Py_InitModule(kModuleName, module_methods);
+#endif
+
+ if (m != NULL) {
+ static_assert(
+ sizeof(&gil_acquire) == sizeof(void*),
+ "Function pointer must have the same size as void*");
+ PyModule_AddObject(m, "gil_acquire_funcaddr",
+ PyLong_FromVoidPtr(reinterpret_cast<void*>(&gil_acquire)));
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+}
diff --git a/ext/pybind11/tests/pytest.ini b/ext/pybind11/tests/pytest.ini
index 1e44f0a05..f209964a4 100644
--- a/ext/pybind11/tests/pytest.ini
+++ b/ext/pybind11/tests/pytest.ini
@@ -13,3 +13,4 @@ filterwarnings =
ignore::ImportWarning
# bogus numpy ABI warning (see numpy/#432)
ignore:.*numpy.dtype size changed.*:RuntimeWarning
+ ignore:.*numpy.ufunc size changed.*:RuntimeWarning
diff --git a/ext/pybind11/tests/test_async.cpp b/ext/pybind11/tests/test_async.cpp
new file mode 100644
index 000000000..f0ad0d535
--- /dev/null
+++ b/ext/pybind11/tests/test_async.cpp
@@ -0,0 +1,26 @@
+/*
+ tests/test_async.cpp -- __await__ support
+
+ Copyright (c) 2019 Google Inc.
+
+ 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(async_module, m) {
+ struct DoesNotSupportAsync {};
+ py::class_<DoesNotSupportAsync>(m, "DoesNotSupportAsync")
+ .def(py::init<>());
+ struct SupportsAsync {};
+ py::class_<SupportsAsync>(m, "SupportsAsync")
+ .def(py::init<>())
+ .def("__await__", [](const SupportsAsync& self) -> py::object {
+ static_cast<void>(self);
+ py::object loop = py::module::import("asyncio.events").attr("get_event_loop")();
+ py::object f = loop.attr("create_future")();
+ f.attr("set_result")(5);
+ return f.attr("__await__")();
+ });
+}
diff --git a/ext/pybind11/tests/test_async.py b/ext/pybind11/tests/test_async.py
new file mode 100644
index 000000000..e1c959d60
--- /dev/null
+++ b/ext/pybind11/tests/test_async.py
@@ -0,0 +1,23 @@
+import asyncio
+import pytest
+from pybind11_tests import async_module as m
+
+
+@pytest.fixture
+def event_loop():
+ loop = asyncio.new_event_loop()
+ yield loop
+ loop.close()
+
+
+async def get_await_result(x):
+ return await x
+
+
+def test_await(event_loop):
+ assert 5 == event_loop.run_until_complete(get_await_result(m.SupportsAsync()))
+
+
+def test_await_missing(event_loop):
+ with pytest.raises(TypeError):
+ event_loop.run_until_complete(get_await_result(m.DoesNotSupportAsync()))
diff --git a/ext/pybind11/tests/test_buffers.cpp b/ext/pybind11/tests/test_buffers.cpp
index 5be717730..433dfeee6 100644
--- a/ext/pybind11/tests/test_buffers.cpp
+++ b/ext/pybind11/tests/test_buffers.cpp
@@ -78,7 +78,7 @@ TEST_SUBMODULE(buffers, m) {
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
.def(py::init<ssize_t, ssize_t>())
/// Construct from a buffer
- .def(py::init([](py::buffer b) {
+ .def(py::init([](py::buffer const b) {
py::buffer_info info = b.request();
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
throw std::runtime_error("Incompatible buffer format!");
@@ -107,7 +107,7 @@ TEST_SUBMODULE(buffers, m) {
return py::buffer_info(
m.data(), /* Pointer to buffer */
{ m.rows(), m.cols() }, /* Buffer dimensions */
- { sizeof(float) * size_t(m.rows()), /* Strides (in bytes) for each index */
+ { sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
sizeof(float) }
);
})
diff --git a/ext/pybind11/tests/test_buffers.py b/ext/pybind11/tests/test_buffers.py
index c348be5dd..f006552bf 100644
--- a/ext/pybind11/tests/test_buffers.py
+++ b/ext/pybind11/tests/test_buffers.py
@@ -36,17 +36,21 @@ def test_from_python():
# https://bitbucket.org/pypy/pypy/issues/2444
@pytest.unsupported_on_pypy
def test_to_python():
- mat = m.Matrix(5, 5)
- assert memoryview(mat).shape == (5, 5)
+ mat = m.Matrix(5, 4)
+ assert memoryview(mat).shape == (5, 4)
assert mat[2, 3] == 0
- mat[2, 3] = 4
+ mat[2, 3] = 4.0
+ mat[3, 2] = 7.0
assert mat[2, 3] == 4
+ assert mat[3, 2] == 7
+ assert struct.unpack_from('f', mat, (3 * 4 + 2) * 4) == (7, )
+ assert struct.unpack_from('f', mat, (2 * 4 + 3) * 4) == (4, )
mat2 = np.array(mat, copy=False)
- assert mat2.shape == (5, 5)
- assert abs(mat2).sum() == 4
- assert mat2[2, 3] == 4
+ assert mat2.shape == (5, 4)
+ assert abs(mat2).sum() == 11
+ assert mat2[2, 3] == 4 and mat2[3, 2] == 7
mat2[2, 3] = 5
assert mat2[2, 3] == 5
@@ -58,7 +62,7 @@ def test_to_python():
del mat2 # holds a mat reference
pytest.gc_collect()
assert cstats.alive() == 0
- assert cstats.values() == ["5x5 matrix"]
+ assert cstats.values() == ["5x4 matrix"]
assert cstats.copy_constructions == 0
# assert cstats.move_constructions >= 0 # Don't invoke any
assert cstats.copy_assignments == 0
diff --git a/ext/pybind11/tests/test_builtin_casters.cpp b/ext/pybind11/tests/test_builtin_casters.cpp
index b73e96ea5..e026127f8 100644
--- a/ext/pybind11/tests/test_builtin_casters.cpp
+++ b/ext/pybind11/tests/test_builtin_casters.cpp
@@ -50,7 +50,9 @@ TEST_SUBMODULE(builtin_casters, m) {
// 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_char_lv", [](char &c) -> int { return static_cast<unsigned char>(c); });
m.def("ord_char16", [](char16_t c) -> uint16_t { return c; });
+ m.def("ord_char16_lv", [](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; });
@@ -153,4 +155,16 @@ TEST_SUBMODULE(builtin_casters, m) {
// 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()); });
+
+ // test int vs. long (Python 2)
+ m.def("int_cast", []() {return (int) 42;});
+ m.def("long_cast", []() {return (long) 42;});
+ m.def("longlong_cast", []() {return ULLONG_MAX;});
+
+ /// test void* cast operator
+ m.def("test_void_caster", []() -> bool {
+ void *v = (void *) 0xabcd;
+ py::object o = py::cast(v);
+ return py::cast<void *>(o) == v;
+ });
}
diff --git a/ext/pybind11/tests/test_builtin_casters.py b/ext/pybind11/tests/test_builtin_casters.py
index bc094a381..73cc465f5 100644
--- a/ext/pybind11/tests/test_builtin_casters.py
+++ b/ext/pybind11/tests/test_builtin_casters.py
@@ -44,6 +44,7 @@ def test_single_char_arguments():
toolong_message = "Expected a character, but multi-character string found"
assert m.ord_char(u'a') == 0x61 # simple ASCII
+ assert m.ord_char_lv(u'b') == 0x62
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
@@ -54,9 +55,11 @@ def test_single_char_arguments():
assert m.ord_char16(u'a') == 0x61
assert m.ord_char16(u'é') == 0xE9
+ assert m.ord_char16_lv(u'ê') == 0xEA
assert m.ord_char16(u'Ä€') == 0x100
assert m.ord_char16(u'‽') == 0x203d
assert m.ord_char16(u'♥') == 0x2665
+ assert m.ord_char16_lv(u'♡') == 0x2661
with pytest.raises(ValueError) as excinfo:
assert m.ord_char16(u'🎂') == 0x1F382 # requires surrogate pair
assert str(excinfo.value) == toobig_message(0x10000)
@@ -320,3 +323,20 @@ def test_numpy_bool():
assert convert(np.bool_(False)) is False
assert noconvert(np.bool_(True)) is True
assert noconvert(np.bool_(False)) is False
+
+
+def test_int_long():
+ """In Python 2, a C++ int should return a Python int rather than long
+ if possible: longs are not always accepted where ints are used (such
+ as the argument to sys.exit()). A C++ long long is always a Python
+ long."""
+
+ import sys
+ must_be_long = type(getattr(sys, 'maxint', 1) + 1)
+ assert isinstance(m.int_cast(), int)
+ assert isinstance(m.long_cast(), int)
+ assert isinstance(m.longlong_cast(), must_be_long)
+
+
+def test_void_caster_2():
+ assert m.test_void_caster()
diff --git a/ext/pybind11/tests/test_call_policies.cpp b/ext/pybind11/tests/test_call_policies.cpp
index 8642188f9..fd2455783 100644
--- a/ext/pybind11/tests/test_call_policies.cpp
+++ b/ext/pybind11/tests/test_call_policies.cpp
@@ -36,6 +36,8 @@ TEST_SUBMODULE(call_policies, m) {
class Child {
public:
Child() { py::print("Allocating child."); }
+ Child(const Child &) = default;
+ Child(Child &&) = default;
~Child() { py::print("Releasing child."); }
};
py::class_<Child>(m, "Child")
diff --git a/ext/pybind11/tests/test_callbacks.cpp b/ext/pybind11/tests/test_callbacks.cpp
index 273eacc30..71b88c44c 100644
--- a/ext/pybind11/tests/test_callbacks.cpp
+++ b/ext/pybind11/tests/test_callbacks.cpp
@@ -10,6 +10,7 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.h>
+#include <thread>
int dummy_function(int i) { return i + 1; }
@@ -146,4 +147,22 @@ TEST_SUBMODULE(callbacks, m) {
py::class_<CppBoundMethodTest>(m, "CppBoundMethodTest")
.def(py::init<>())
.def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; });
+
+ // test async Python callbacks
+ using callback_f = std::function<void(int)>;
+ m.def("test_async_callback", [](callback_f f, py::list work) {
+ // make detached thread that calls `f` with piece of work after a little delay
+ auto start_f = [f](int j) {
+ auto invoke_f = [f, j] {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ f(j);
+ };
+ auto t = std::thread(std::move(invoke_f));
+ t.detach();
+ };
+
+ // spawn worker threads
+ for (auto i : work)
+ start_f(py::cast<int>(i));
+ });
}
diff --git a/ext/pybind11/tests/test_callbacks.py b/ext/pybind11/tests/test_callbacks.py
index 93c42c22b..6439c8e72 100644
--- a/ext/pybind11/tests/test_callbacks.py
+++ b/ext/pybind11/tests/test_callbacks.py
@@ -1,5 +1,6 @@
import pytest
from pybind11_tests import callbacks as m
+from threading import Thread
def test_callbacks():
@@ -105,3 +106,31 @@ def test_function_signatures(doc):
def test_movable_object():
assert m.callback_with_movable(lambda _: None) is True
+
+
+def test_async_callbacks():
+ # serves as state for async callback
+ class Item:
+ def __init__(self, value):
+ self.value = value
+
+ res = []
+
+ # generate stateful lambda that will store result in `res`
+ def gen_f():
+ s = Item(3)
+ return lambda j: res.append(s.value + j)
+
+ # do some work async
+ work = [1, 2, 3, 4]
+ m.test_async_callback(gen_f(), work)
+ # wait until work is done
+ from time import sleep
+ sleep(0.5)
+ assert sum(res) == sum([x + 3 for x in work])
+
+
+def test_async_async_callbacks():
+ t = Thread(target=test_async_callbacks)
+ t.start()
+ t.join()
diff --git a/ext/pybind11/tests/test_chrono.cpp b/ext/pybind11/tests/test_chrono.cpp
index 195a93bba..899d08d8d 100644
--- a/ext/pybind11/tests/test_chrono.cpp
+++ b/ext/pybind11/tests/test_chrono.cpp
@@ -14,6 +14,10 @@
TEST_SUBMODULE(chrono, m) {
using system_time = std::chrono::system_clock::time_point;
using steady_time = std::chrono::steady_clock::time_point;
+
+ using timespan = std::chrono::duration<int64_t, std::nano>;
+ using timestamp = std::chrono::time_point<std::chrono::system_clock, timespan>;
+
// test_chrono_system_clock
// Return the current time off the wall clock
m.def("test_chrono1", []() { return std::chrono::system_clock::now(); });
@@ -44,4 +48,8 @@ TEST_SUBMODULE(chrono, m) {
// Float durations (issue #719)
m.def("test_chrono_float_diff", [](std::chrono::duration<float> a, std::chrono::duration<float> b) {
return a - b; });
+
+ m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp {
+ return start + delta;
+ });
}
diff --git a/ext/pybind11/tests/test_chrono.py b/ext/pybind11/tests/test_chrono.py
index 2b75bd191..55c954406 100644
--- a/ext/pybind11/tests/test_chrono.py
+++ b/ext/pybind11/tests/test_chrono.py
@@ -40,6 +40,62 @@ def test_chrono_system_clock_roundtrip():
assert diff.microseconds == 0
+def test_chrono_system_clock_roundtrip_date():
+ date1 = datetime.date.today()
+
+ # Roundtrip the time
+ datetime2 = m.test_chrono2(date1)
+ date2 = datetime2.date()
+ time2 = datetime2.time()
+
+ # The returned value should be a datetime
+ assert isinstance(datetime2, datetime.datetime)
+ assert isinstance(date2, datetime.date)
+ assert isinstance(time2, datetime.time)
+
+ # They should be identical (no information lost on roundtrip)
+ diff = abs(date1 - date2)
+ assert diff.days == 0
+ assert diff.seconds == 0
+ assert diff.microseconds == 0
+
+ # Year, Month & Day should be the same after the round trip
+ assert date1.year == date2.year
+ assert date1.month == date2.month
+ assert date1.day == date2.day
+
+ # There should be no time information
+ assert time2.hour == 0
+ assert time2.minute == 0
+ assert time2.second == 0
+ assert time2.microsecond == 0
+
+
+def test_chrono_system_clock_roundtrip_time():
+ time1 = datetime.datetime.today().time()
+
+ # Roundtrip the time
+ datetime2 = m.test_chrono2(time1)
+ date2 = datetime2.date()
+ time2 = datetime2.time()
+
+ # The returned value should be a datetime
+ assert isinstance(datetime2, datetime.datetime)
+ assert isinstance(date2, datetime.date)
+ assert isinstance(time2, datetime.time)
+
+ # Hour, Minute, Second & Microsecond should be the same after the round trip
+ assert time1.hour == time2.hour
+ assert time1.minute == time2.minute
+ assert time1.second == time2.second
+ assert time1.microsecond == time2.microsecond
+
+ # There should be no date information (i.e. date = python base date)
+ assert date2.year == 1970
+ assert date2.month == 1
+ assert date2.day == 1
+
+
def test_chrono_duration_roundtrip():
# Get the difference between two times (a timedelta)
@@ -70,6 +126,19 @@ def test_chrono_duration_subtraction_equivalence():
assert cpp_diff.microseconds == diff.microseconds
+def test_chrono_duration_subtraction_equivalence_date():
+
+ date1 = datetime.date.today()
+ date2 = datetime.date.today()
+
+ diff = date2 - date1
+ cpp_diff = m.test_chrono4(date2, date1)
+
+ assert cpp_diff.days == diff.days
+ assert cpp_diff.seconds == diff.seconds
+ assert cpp_diff.microseconds == diff.microseconds
+
+
def test_chrono_steady_clock():
time1 = m.test_chrono5()
assert isinstance(time1, datetime.timedelta)
@@ -99,3 +168,9 @@ def test_floating_point_duration():
diff = m.test_chrono_float_diff(43.789012, 1.123456)
assert diff.seconds == 42
assert 665556 <= diff.microseconds <= 665557
+
+
+def test_nano_timepoint():
+ time = datetime.datetime.now()
+ time1 = m.test_nano_timepoint(time, datetime.timedelta(seconds=60))
+ assert(time1 == time + datetime.timedelta(seconds=60))
diff --git a/ext/pybind11/tests/test_class.cpp b/ext/pybind11/tests/test_class.cpp
index 222190617..499d0cc51 100644
--- a/ext/pybind11/tests/test_class.cpp
+++ b/ext/pybind11/tests/test_class.cpp
@@ -10,10 +10,27 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include "local_bindings.h"
+#include <pybind11/stl.h>
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier
+#endif
+
+// test_brace_initialization
+struct NoBraceInitialization {
+ NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
+ template <typename T>
+ NoBraceInitialization(std::initializer_list<T> l) : vec(l) {}
+
+ std::vector<int> vec;
+};
TEST_SUBMODULE(class_, m) {
// test_instance
struct NoConstructor {
+ NoConstructor() = default;
+ NoConstructor(const NoConstructor &) = default;
+ NoConstructor(NoConstructor &&) = default;
static NoConstructor *new_instance() {
auto *ptr = new NoConstructor();
print_created(ptr, "via new_instance");
@@ -82,7 +99,12 @@ TEST_SUBMODULE(class_, m) {
m.def("dog_bark", [](const Dog &dog) { return dog.bark(); });
// test_automatic_upcasting
- struct BaseClass { virtual ~BaseClass() {} };
+ struct BaseClass {
+ BaseClass() = default;
+ BaseClass(const BaseClass &) = default;
+ BaseClass(BaseClass &&) = default;
+ virtual ~BaseClass() {}
+ };
struct DerivedClass1 : BaseClass { };
struct DerivedClass2 : BaseClass { };
@@ -291,6 +313,12 @@ TEST_SUBMODULE(class_, m) {
.def(py::init<int, const std::string &>())
.def_readwrite("field1", &BraceInitialization::field1)
.def_readwrite("field2", &BraceInitialization::field2);
+ // We *don't* want to construct using braces when the given constructor argument maps to a
+ // constructor, because brace initialization could go to the wrong place (in particular when
+ // there is also an `initializer_list<T>`-accept constructor):
+ py::class_<NoBraceInitialization>(m, "NoBraceInitialization")
+ .def(py::init<std::vector<int>>())
+ .def_readonly("vec", &NoBraceInitialization::vec);
// test_reentrant_implicit_conversion_failure
// #1035: issue with runaway reentrant implicit conversion
@@ -302,6 +330,43 @@ TEST_SUBMODULE(class_, m) {
.def(py::init<const BogusImplicitConversion &>());
py::implicitly_convertible<int, BogusImplicitConversion>();
+
+ // test_qualname
+ // #1166: nested class docstring doesn't show nested name
+ // Also related: tests that __qualname__ is set properly
+ struct NestBase {};
+ struct Nested {};
+ py::class_<NestBase> base(m, "NestBase");
+ base.def(py::init<>());
+ py::class_<Nested>(base, "Nested")
+ .def(py::init<>())
+ .def("fn", [](Nested &, int, NestBase &, Nested &) {})
+ .def("fa", [](Nested &, int, NestBase &, Nested &) {},
+ "a"_a, "b"_a, "c"_a);
+ base.def("g", [](NestBase &, Nested &) {});
+ base.def("h", []() { return NestBase(); });
+
+ // test_error_after_conversion
+ // The second-pass path through dispatcher() previously didn't
+ // remember which overload was used, and would crash trying to
+ // generate a useful error message
+
+ struct NotRegistered {};
+ struct StringWrapper { std::string str; };
+ m.def("test_error_after_conversions", [](int) {});
+ m.def("test_error_after_conversions",
+ [](StringWrapper) -> NotRegistered { return {}; });
+ py::class_<StringWrapper>(m, "StringWrapper").def(py::init<std::string>());
+ py::implicitly_convertible<std::string, StringWrapper>();
+
+ #if defined(PYBIND11_CPP17)
+ struct alignas(1024) Aligned {
+ std::uintptr_t ptr() const { return (uintptr_t) this; }
+ };
+ py::class_<Aligned>(m, "Aligned")
+ .def(py::init<>())
+ .def("ptr", &Aligned::ptr);
+ #endif
}
template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; };
diff --git a/ext/pybind11/tests/test_class.py b/ext/pybind11/tests/test_class.py
index 412d6798e..ed63ca853 100644
--- a/ext/pybind11/tests/test_class.py
+++ b/ext/pybind11/tests/test_class.py
@@ -44,6 +44,31 @@ def test_docstrings(doc):
"""
+def test_qualname(doc):
+ """Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we
+ backport the attribute) and that generated docstrings properly use it and the module name"""
+ assert m.NestBase.__qualname__ == "NestBase"
+ assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
+
+ assert doc(m.NestBase.__init__) == """
+ __init__(self: m.class_.NestBase) -> None
+ """
+ assert doc(m.NestBase.g) == """
+ g(self: m.class_.NestBase, arg0: m.class_.NestBase.Nested) -> None
+ """
+ assert doc(m.NestBase.Nested.__init__) == """
+ __init__(self: m.class_.NestBase.Nested) -> None
+ """
+ assert doc(m.NestBase.Nested.fn) == """
+ fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
+ """ # noqa: E501 line too long
+ assert doc(m.NestBase.Nested.fa) == """
+ fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None
+ """ # noqa: E501 line too long
+ assert m.NestBase.__module__ == "pybind11_tests.class_"
+ assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"
+
+
def test_inheritance(msg):
roger = m.Rabbit('Rabbit')
assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
@@ -203,6 +228,12 @@ def test_brace_initialization():
assert a.field1 == 123
assert a.field2 == "test"
+ # Tests that a non-simple class doesn't get brace initialization (if the
+ # class defines an initializer_list constructor, in particular, it would
+ # win over the expected constructor).
+ b = m.NoBraceInitialization([123, 456])
+ assert b.vec == [123, 456]
+
@pytest.unsupported_on_pypy
def test_class_refcount():
@@ -229,7 +260,22 @@ def test_reentrant_implicit_conversion_failure(msg):
# ensure that there is no runaway reentrant implicit conversion (#1035)
with pytest.raises(TypeError) as excinfo:
m.BogusImplicitConversion(0)
- assert msg(excinfo.value) == '''__init__(): incompatible constructor arguments. The following argument types are supported:
- 1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion)
+ assert msg(excinfo.value) == '''
+ __init__(): incompatible constructor arguments. The following argument types are supported:
+ 1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion)
+
+ Invoked with: 0
+ '''
+
+
+def test_error_after_conversions():
+ with pytest.raises(TypeError) as exc_info:
+ m.test_error_after_conversions("hello")
+ assert str(exc_info.value).startswith(
+ "Unable to convert function return value to a Python type!")
+
-Invoked with: 0'''
+def test_aligned():
+ if hasattr(m, "Aligned"):
+ p = m.Aligned().ptr()
+ assert p % 1024 == 0
diff --git a/ext/pybind11/tests/test_constants_and_functions.cpp b/ext/pybind11/tests/test_constants_and_functions.cpp
index 8c9ef7f67..e8ec74b7b 100644
--- a/ext/pybind11/tests/test_constants_and_functions.cpp
+++ b/ext/pybind11/tests/test_constants_and_functions.cpp
@@ -49,7 +49,14 @@ namespace test_exc_sp {
int f1(int x) noexcept { return x+1; }
int f2(int x) noexcept(true) { return x+2; }
int f3(int x) noexcept(false) { return x+3; }
+#if defined(__GNUG__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated"
+#endif
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
+#if defined(__GNUG__)
+# pragma GCC diagnostic pop
+#endif
struct C {
int m1(int x) noexcept { return x-1; }
int m2(int x) const noexcept { return x-2; }
@@ -57,8 +64,15 @@ struct C {
int m4(int x) const noexcept(true) { return x-4; }
int m5(int x) noexcept(false) { return x-5; }
int m6(int x) const noexcept(false) { return x-6; }
+#if defined(__GNUG__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated"
+#endif
int m7(int x) throw() { return x-7; }
int m8(int x) const throw() { return x-8; }
+#if defined(__GNUG__)
+# pragma GCC diagnostic pop
+#endif
};
}
diff --git a/ext/pybind11/tests/test_copy_move.cpp b/ext/pybind11/tests/test_copy_move.cpp
index 94113e3af..98d5e0a0b 100644
--- a/ext/pybind11/tests/test_copy_move.cpp
+++ b/ext/pybind11/tests/test_copy_move.cpp
@@ -86,7 +86,7 @@ template <> struct type_caster<CopyOnlyInt> {
protected:
CopyOnlyInt value;
public:
- static PYBIND11_DESCR name() { return _("CopyOnlyInt"); }
+ static constexpr auto name = _("CopyOnlyInt");
bool load(handle src, bool) { value = CopyOnlyInt(src.cast<int>()); return true; }
static handle cast(const CopyOnlyInt &m, return_value_policy r, handle p) { return pybind11::cast(m.value, r, p); }
static handle cast(const CopyOnlyInt *src, return_value_policy policy, handle parent) {
diff --git a/ext/pybind11/tests/test_eigen.cpp b/ext/pybind11/tests/test_eigen.cpp
index 17b156ce4..aba088d72 100644
--- a/ext/pybind11/tests/test_eigen.cpp
+++ b/ext/pybind11/tests/test_eigen.cpp
@@ -11,6 +11,11 @@
#include "constructor_stats.h"
#include <pybind11/eigen.h>
#include <pybind11/stl.h>
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4996) // C4996: std::unary_negation is deprecated
+#endif
+
#include <Eigen/Cholesky>
using MatrixXdR = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
@@ -119,7 +124,7 @@ TEST_SUBMODULE(eigen, m) {
// This one accepts a matrix of any stride:
m.def("add_any", [](py::EigenDRef<Eigen::MatrixXd> x, int r, int c, double v) { x(r,c) += v; });
- // Return mutable references (numpy maps into eigen varibles)
+ // Return mutable references (numpy maps into eigen variables)
m.def("get_cm_ref", []() { return Eigen::Ref<Eigen::MatrixXd>(get_cm()); });
m.def("get_rm_ref", []() { return Eigen::Ref<MatrixXdR>(get_rm()); });
// The same references, but non-mutable (numpy maps into eigen variables, but is !writeable)
@@ -288,6 +293,13 @@ TEST_SUBMODULE(eigen, m) {
m.def("iss738_f1", &adjust_matrix<const Eigen::Ref<const Eigen::MatrixXd> &>, py::arg().noconvert());
m.def("iss738_f2", &adjust_matrix<const Eigen::Ref<const Eigen::Matrix<double, -1, -1, Eigen::RowMajor>> &>, py::arg().noconvert());
+ // test_issue1105
+ // Issue #1105: when converting from a numpy two-dimensional (Nx1) or (1xN) value into a dense
+ // eigen Vector or RowVector, the argument would fail to load because the numpy copy would fail:
+ // numpy won't broadcast a Nx1 into a 1-dimensional vector.
+ m.def("iss1105_col", [](Eigen::VectorXd) { return true; });
+ m.def("iss1105_row", [](Eigen::RowVectorXd) { return true; });
+
// test_named_arguments
// Make sure named arguments are working properly:
m.def("matrix_multiply", [](const py::EigenDRef<const Eigen::MatrixXd> A, const py::EigenDRef<const Eigen::MatrixXd> B)
diff --git a/ext/pybind11/tests/test_eigen.py b/ext/pybind11/tests/test_eigen.py
index 4ac8cbf5d..55d935173 100644
--- a/ext/pybind11/tests/test_eigen.py
+++ b/ext/pybind11/tests/test_eigen.py
@@ -19,7 +19,7 @@ def assert_equal_ref(mat):
def assert_sparse_equal_ref(sparse_mat):
- assert_equal_ref(sparse_mat.todense())
+ assert_equal_ref(sparse_mat.toarray())
def test_fixed():
@@ -181,8 +181,7 @@ def test_negative_stride_from_python(msg):
double_threer(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[float32[1, 3], flags.writeable]) -> None
- Invoked with: array([ 5., 4., 3.], dtype=float32)
- """ # noqa: E501 line too long
+ Invoked with: """ + repr(np.array([ 5., 4., 3.], dtype='float32')) # noqa: E501 line too long
with pytest.raises(TypeError) as excinfo:
m.double_threec(second_col)
@@ -190,8 +189,7 @@ def test_negative_stride_from_python(msg):
double_threec(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[float32[3, 1], flags.writeable]) -> None
- Invoked with: array([ 7., 4., 1.], dtype=float32)
- """ # noqa: E501 line too long
+ Invoked with: """ + repr(np.array([ 7., 4., 1.], dtype='float32')) # noqa: E501 line too long
def test_nonunit_stride_to_python():
@@ -672,6 +670,21 @@ def test_issue738():
assert np.all(m.iss738_f2(np.array([[1.], [2], [3]])) == np.array([[1.], [12], [23]]))
+def test_issue1105():
+ """Issue 1105: 1xN or Nx1 input arrays weren't accepted for eigen
+ compile-time row vectors or column vector"""
+ assert m.iss1105_row(np.ones((1, 7)))
+ assert m.iss1105_col(np.ones((7, 1)))
+
+ # These should still fail (incompatible dimensions):
+ with pytest.raises(TypeError) as excinfo:
+ m.iss1105_row(np.ones((7, 1)))
+ assert "incompatible function arguments" in str(excinfo.value)
+ with pytest.raises(TypeError) as excinfo:
+ m.iss1105_col(np.ones((1, 7)))
+ assert "incompatible function arguments" in str(excinfo.value)
+
+
def test_custom_operator_new():
"""Using Eigen types as member variables requires a class-specific
operator new with proper alignment"""
diff --git a/ext/pybind11/tests/test_embed/CMakeLists.txt b/ext/pybind11/tests/test_embed/CMakeLists.txt
index 0a43e0e22..8b4f1f843 100644
--- a/ext/pybind11/tests/test_embed/CMakeLists.txt
+++ b/ext/pybind11/tests/test_embed/CMakeLists.txt
@@ -5,7 +5,9 @@ if(${PYTHON_MODULE_EXTENSION} MATCHES "pypy")
endif()
find_package(Catch 1.9.3)
-if(NOT CATCH_FOUND)
+if(CATCH_FOUND)
+ message(STATUS "Building interpreter tests using Catch v${CATCH_VERSION}")
+else()
message(STATUS "Catch not detected. Interpreter tests will be skipped. Install Catch headers"
" manually or use `cmake -DDOWNLOAD_CATCH=1` to fetch them automatically.")
return()
@@ -31,4 +33,9 @@ target_link_libraries(test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT})
add_custom_target(cpptest COMMAND $<TARGET_FILE:test_embed>
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+pybind11_add_module(external_module THIN_LTO external_module.cpp)
+set_target_properties(external_module PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+add_dependencies(cpptest external_module)
+
add_dependencies(check cpptest)
diff --git a/ext/pybind11/tests/test_embed/catch.cpp b/ext/pybind11/tests/test_embed/catch.cpp
index cface485d..dd137385c 100644
--- a/ext/pybind11/tests/test_embed/catch.cpp
+++ b/ext/pybind11/tests/test_embed/catch.cpp
@@ -3,12 +3,18 @@
#include <pybind11/embed.h>
+#ifdef _MSC_VER
+// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch
+// 2.0.1; this should be fixed in the next catch release after 2.0.1).
+# pragma warning(disable: 4996)
+#endif
+
#define CATCH_CONFIG_RUNNER
#include <catch.hpp>
namespace py = pybind11;
-int main(int argc, const char *argv[]) {
+int main(int argc, char *argv[]) {
py::scoped_interpreter guard{};
auto result = Catch::Session().run(argc, argv);
diff --git a/ext/pybind11/tests/test_embed/external_module.cpp b/ext/pybind11/tests/test_embed/external_module.cpp
new file mode 100644
index 000000000..e9a6058b1
--- /dev/null
+++ b/ext/pybind11/tests/test_embed/external_module.cpp
@@ -0,0 +1,23 @@
+#include <pybind11/pybind11.h>
+
+namespace py = pybind11;
+
+/* Simple test module/test class to check that the referenced internals data of external pybind11
+ * modules aren't preserved over a finalize/initialize.
+ */
+
+PYBIND11_MODULE(external_module, m) {
+ class A {
+ public:
+ A(int value) : v{value} {};
+ int v;
+ };
+
+ py::class_<A>(m, "A")
+ .def(py::init<int>())
+ .def_readwrite("value", &A::v);
+
+ m.def("internals_at", []() {
+ return reinterpret_cast<uintptr_t>(&py::detail::get_internals());
+ });
+}
diff --git a/ext/pybind11/tests/test_embed/test_interpreter.cpp b/ext/pybind11/tests/test_embed/test_interpreter.cpp
index 6b5f051f2..222bd565f 100644
--- a/ext/pybind11/tests/test_embed/test_interpreter.cpp
+++ b/ext/pybind11/tests/test_embed/test_interpreter.cpp
@@ -1,4 +1,11 @@
#include <pybind11/embed.h>
+
+#ifdef _MSC_VER
+// Silence MSVC C++17 deprecation warning from Catch regarding std::uncaught_exceptions (up to catch
+// 2.0.1; this should be fixed in the next catch release after 2.0.1).
+# pragma warning(disable: 4996)
+#endif
+
#include <catch.hpp>
#include <thread>
@@ -94,7 +101,8 @@ bool has_pybind11_internals_builtin() {
};
bool has_pybind11_internals_static() {
- return py::detail::get_internals_ptr() != nullptr;
+ auto **&ipp = py::detail::get_internals_pp();
+ return ipp && *ipp;
}
TEST_CASE("Restart the interpreter") {
@@ -102,6 +110,11 @@ TEST_CASE("Restart the interpreter") {
REQUIRE(py::module::import("widget_module").attr("add")(1, 2).cast<int>() == 3);
REQUIRE(has_pybind11_internals_builtin());
REQUIRE(has_pybind11_internals_static());
+ REQUIRE(py::module::import("external_module").attr("A")(123).attr("value").cast<int>() == 123);
+
+ // local and foreign module internals should point to the same internals:
+ REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) ==
+ py::module::import("external_module").attr("internals_at")().cast<uintptr_t>());
// Restart the interpreter.
py::finalize_interpreter();
@@ -116,6 +129,8 @@ TEST_CASE("Restart the interpreter") {
pybind11::detail::get_internals();
REQUIRE(has_pybind11_internals_builtin());
REQUIRE(has_pybind11_internals_static());
+ REQUIRE(reinterpret_cast<uintptr_t>(*py::detail::get_internals_pp()) ==
+ py::module::import("external_module").attr("internals_at")().cast<uintptr_t>());
// Make sure that an interpreter with no get_internals() created until finalize still gets the
// internals destroyed
diff --git a/ext/pybind11/tests/test_enum.cpp b/ext/pybind11/tests/test_enum.cpp
index 49f31ba1f..315308920 100644
--- a/ext/pybind11/tests/test_enum.cpp
+++ b/ext/pybind11/tests/test_enum.cpp
@@ -13,11 +13,13 @@ TEST_SUBMODULE(enums, m) {
// test_unscoped_enum
enum UnscopedEnum {
EOne = 1,
- ETwo
+ ETwo,
+ EThree
};
- py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic())
- .value("EOne", EOne)
- .value("ETwo", ETwo)
+ py::enum_<UnscopedEnum>(m, "UnscopedEnum", py::arithmetic(), "An unscoped enumeration")
+ .value("EOne", EOne, "Docstring for EOne")
+ .value("ETwo", ETwo, "Docstring for ETwo")
+ .value("EThree", EThree, "Docstring for EThree")
.export_values();
// test_scoped_enum
@@ -68,4 +70,18 @@ TEST_SUBMODULE(enums, m) {
m.def("test_enum_to_int", [](int) { });
m.def("test_enum_to_uint", [](uint32_t) { });
m.def("test_enum_to_long_long", [](long long) { });
+
+ // test_duplicate_enum_name
+ enum SimpleEnum
+ {
+ ONE, TWO, THREE
+ };
+
+ m.def("register_bad_enum", [m]() {
+ py::enum_<SimpleEnum>(m, "SimpleEnum")
+ .value("ONE", SimpleEnum::ONE) //NOTE: all value function calls are called with the same first parameter value
+ .value("ONE", SimpleEnum::TWO)
+ .value("ONE", SimpleEnum::THREE)
+ .export_values();
+ });
}
diff --git a/ext/pybind11/tests/test_enum.py b/ext/pybind11/tests/test_enum.py
index d8eff5278..7fe9b618d 100644
--- a/ext/pybind11/tests/test_enum.py
+++ b/ext/pybind11/tests/test_enum.py
@@ -6,9 +6,22 @@ def test_unscoped_enum():
assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne"
assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo"
assert str(m.EOne) == "UnscopedEnum.EOne"
+
+ # name property
+ assert m.UnscopedEnum.EOne.name == "EOne"
+ assert m.UnscopedEnum.ETwo.name == "ETwo"
+ assert m.EOne.name == "EOne"
+ # name readonly
+ with pytest.raises(AttributeError):
+ m.UnscopedEnum.EOne.name = ""
+ # name returns a copy
+ foo = m.UnscopedEnum.EOne.name
+ foo = "bar"
+ assert m.UnscopedEnum.EOne.name == "EOne"
+
# __members__ property
assert m.UnscopedEnum.__members__ == \
- {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo}
+ {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree}
# __members__ readonly
with pytest.raises(AttributeError):
m.UnscopedEnum.__members__ = {}
@@ -16,12 +29,57 @@ def test_unscoped_enum():
foo = m.UnscopedEnum.__members__
foo["bar"] = "baz"
assert m.UnscopedEnum.__members__ == \
- {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo}
+ {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree}
+
+ for docstring_line in '''An unscoped enumeration
+
+Members:
+
+ EOne : Docstring for EOne
+
+ ETwo : Docstring for ETwo
- # no TypeError exception for unscoped enum ==/!= int comparisons
+ EThree : Docstring for EThree'''.split('\n'):
+ assert docstring_line in m.UnscopedEnum.__doc__
+
+ # Unscoped enums will accept ==/!= int comparisons
y = m.UnscopedEnum.ETwo
assert y == 2
+ assert 2 == y
assert y != 3
+ assert 3 != y
+ # Compare with None
+ assert (y != None) # noqa: E711
+ assert not (y == None) # noqa: E711
+ # Compare with an object
+ assert (y != object())
+ assert not (y == object())
+ # Compare with string
+ assert y != "2"
+ assert "2" != y
+ assert not ("2" == y)
+ assert not (y == "2")
+
+ with pytest.raises(TypeError):
+ y < object()
+
+ with pytest.raises(TypeError):
+ y <= object()
+
+ with pytest.raises(TypeError):
+ y > object()
+
+ with pytest.raises(TypeError):
+ y >= object()
+
+ with pytest.raises(TypeError):
+ y | object()
+
+ with pytest.raises(TypeError):
+ y & object()
+
+ with pytest.raises(TypeError):
+ y ^ object()
assert int(m.UnscopedEnum.ETwo) == 2
assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo"
@@ -40,17 +98,37 @@ def test_unscoped_enum():
assert not (m.UnscopedEnum.ETwo < m.UnscopedEnum.EOne)
assert not (2 < m.UnscopedEnum.EOne)
+ # arithmetic
+ assert m.UnscopedEnum.EOne & m.UnscopedEnum.EThree == m.UnscopedEnum.EOne
+ assert m.UnscopedEnum.EOne | m.UnscopedEnum.ETwo == m.UnscopedEnum.EThree
+ assert m.UnscopedEnum.EOne ^ m.UnscopedEnum.EThree == m.UnscopedEnum.ETwo
+
def test_scoped_enum():
assert m.test_scoped_enum(m.ScopedEnum.Three) == "ScopedEnum::Three"
z = m.ScopedEnum.Two
assert m.test_scoped_enum(z) == "ScopedEnum::Two"
- # expected TypeError exceptions for scoped enum ==/!= int comparisons
+ # Scoped enums will *NOT* accept ==/!= int comparisons (Will always return False)
+ assert not z == 3
+ assert not 3 == z
+ assert z != 3
+ assert 3 != z
+ # Compare with None
+ assert (z != None) # noqa: E711
+ assert not (z == None) # noqa: E711
+ # Compare with an object
+ assert (z != object())
+ assert not (z == object())
+ # Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions)
+ with pytest.raises(TypeError):
+ z > 3
with pytest.raises(TypeError):
- assert z == 2
+ z < 3
with pytest.raises(TypeError):
- assert z != 3
+ z >= 3
+ with pytest.raises(TypeError):
+ z <= 3
# order
assert m.ScopedEnum.Two < m.ScopedEnum.Three
@@ -100,6 +178,7 @@ def test_binary_operators():
assert int(m.Flags.Read | m.Flags.Execute) == 5
assert int(m.Flags.Write | m.Flags.Execute) == 3
assert int(m.Flags.Write | 1) == 3
+ assert ~m.Flags.Write == -3
state = m.Flags.Read | m.Flags.Write
assert (state & m.Flags.Read) != 0
@@ -119,3 +198,9 @@ def test_enum_to_int():
m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode)
m.test_enum_to_long_long(m.Flags.Read)
m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode)
+
+
+def test_duplicate_enum_name():
+ with pytest.raises(ValueError) as excinfo:
+ m.register_bad_enum()
+ assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!'
diff --git a/ext/pybind11/tests/test_exceptions.cpp b/ext/pybind11/tests/test_exceptions.cpp
index ae28abb48..d30139037 100644
--- a/ext/pybind11/tests/test_exceptions.cpp
+++ b/ext/pybind11/tests/test_exceptions.cpp
@@ -9,7 +9,7 @@
#include "pybind11_tests.h"
-// A type that should be raised as an exeption in Python
+// A type that should be raised as an exception in Python
class MyException : public std::exception {
public:
explicit MyException(const char * m) : message{m} {}
@@ -118,10 +118,38 @@ TEST_SUBMODULE(exceptions, m) {
m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
m.def("exception_matches", []() {
py::dict foo;
- try { foo["bar"]; }
+ try {
+ // Assign to a py::object to force read access of nonexistent dict entry
+ py::object o = foo["bar"];
+ }
catch (py::error_already_set& ex) {
if (!ex.matches(PyExc_KeyError)) throw;
+ return true;
+ }
+ return false;
+ });
+ m.def("exception_matches_base", []() {
+ py::dict foo;
+ try {
+ // Assign to a py::object to force read access of nonexistent dict entry
+ py::object o = foo["bar"];
}
+ catch (py::error_already_set &ex) {
+ if (!ex.matches(PyExc_Exception)) throw;
+ return true;
+ }
+ return false;
+ });
+ m.def("modulenotfound_exception_matches_base", []() {
+ try {
+ // On Python >= 3.6, this raises a ModuleNotFoundError, a subclass of ImportError
+ py::module::import("nonexistent");
+ }
+ catch (py::error_already_set &ex) {
+ if (!ex.matches(PyExc_ImportError)) throw;
+ return true;
+ }
+ return false;
});
m.def("throw_already_set", [](bool err) {
diff --git a/ext/pybind11/tests/test_exceptions.py b/ext/pybind11/tests/test_exceptions.py
index 8d37c09b8..6edff9fe4 100644
--- a/ext/pybind11/tests/test_exceptions.py
+++ b/ext/pybind11/tests/test_exceptions.py
@@ -48,7 +48,9 @@ def test_python_call_in_catch():
def test_exception_matches():
- m.exception_matches()
+ assert m.exception_matches()
+ assert m.exception_matches_base()
+ assert m.modulenotfound_exception_matches_base()
def test_custom(msg):
diff --git a/ext/pybind11/tests/test_factory_constructors.cpp b/ext/pybind11/tests/test_factory_constructors.cpp
index fb33377b2..5cfbfdc3f 100644
--- a/ext/pybind11/tests/test_factory_constructors.cpp
+++ b/ext/pybind11/tests/test_factory_constructors.cpp
@@ -13,7 +13,7 @@
#include <cmath>
// Classes for testing python construction via C++ factory function:
-// Not publically constructible, copyable, or movable:
+// Not publicly constructible, copyable, or movable:
class TestFactory1 {
friend class TestFactoryHelper;
TestFactory1() : value("(empty)") { print_default_created(this); }
@@ -285,6 +285,7 @@ TEST_SUBMODULE(factory_constructors, m) {
// test_reallocations
// Class that has verbose operator_new/operator_delete calls
struct NoisyAlloc {
+ NoisyAlloc(const NoisyAlloc &) = default;
NoisyAlloc(int i) { py::print(py::str("NoisyAlloc(int {})").format(i)); }
NoisyAlloc(double d) { py::print(py::str("NoisyAlloc(double {})").format(d)); }
~NoisyAlloc() { py::print("~NoisyAlloc()"); }
diff --git a/ext/pybind11/tests/test_gil_scoped.cpp b/ext/pybind11/tests/test_gil_scoped.cpp
new file mode 100644
index 000000000..76c17fdc7
--- /dev/null
+++ b/ext/pybind11/tests/test_gil_scoped.cpp
@@ -0,0 +1,52 @@
+/*
+ tests/test_gil_scoped.cpp -- acquire and release gil
+
+ Copyright (c) 2017 Borja Zarco (Google LLC) <bzarco@google.com>
+
+ 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/functional.h>
+
+
+class VirtClass {
+public:
+ virtual ~VirtClass() {}
+ virtual void virtual_func() {}
+ virtual void pure_virtual_func() = 0;
+};
+
+class PyVirtClass : public VirtClass {
+ void virtual_func() override {
+ PYBIND11_OVERLOAD(void, VirtClass, virtual_func,);
+ }
+ void pure_virtual_func() override {
+ PYBIND11_OVERLOAD_PURE(void, VirtClass, pure_virtual_func,);
+ }
+};
+
+TEST_SUBMODULE(gil_scoped, m) {
+ py::class_<VirtClass, PyVirtClass>(m, "VirtClass")
+ .def(py::init<>())
+ .def("virtual_func", &VirtClass::virtual_func)
+ .def("pure_virtual_func", &VirtClass::pure_virtual_func);
+
+ m.def("test_callback_py_obj",
+ [](py::object func) { func(); });
+ m.def("test_callback_std_func",
+ [](const std::function<void()> &func) { func(); });
+ m.def("test_callback_virtual_func",
+ [](VirtClass &virt) { virt.virtual_func(); });
+ m.def("test_callback_pure_virtual_func",
+ [](VirtClass &virt) { virt.pure_virtual_func(); });
+ m.def("test_cross_module_gil",
+ []() {
+ auto cm = py::module::import("cross_module_gil_utils");
+ auto gil_acquire = reinterpret_cast<void (*)()>(
+ PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr()));
+ py::gil_scoped_release gil_release;
+ gil_acquire();
+ });
+}
diff --git a/ext/pybind11/tests/test_gil_scoped.py b/ext/pybind11/tests/test_gil_scoped.py
new file mode 100644
index 000000000..1548337cc
--- /dev/null
+++ b/ext/pybind11/tests/test_gil_scoped.py
@@ -0,0 +1,85 @@
+import multiprocessing
+import threading
+from pybind11_tests import gil_scoped as m
+
+
+def _run_in_process(target, *args, **kwargs):
+ """Runs target in process and returns its exitcode after 10s (None if still alive)."""
+ process = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
+ process.daemon = True
+ try:
+ process.start()
+ # Do not need to wait much, 10s should be more than enough.
+ process.join(timeout=10)
+ return process.exitcode
+ finally:
+ if process.is_alive():
+ process.terminate()
+
+
+def _python_to_cpp_to_python():
+ """Calls different C++ functions that come back to Python."""
+ class ExtendedVirtClass(m.VirtClass):
+ def virtual_func(self):
+ pass
+
+ def pure_virtual_func(self):
+ pass
+
+ extended = ExtendedVirtClass()
+ m.test_callback_py_obj(lambda: None)
+ m.test_callback_std_func(lambda: None)
+ m.test_callback_virtual_func(extended)
+ m.test_callback_pure_virtual_func(extended)
+
+
+def _python_to_cpp_to_python_from_threads(num_threads, parallel=False):
+ """Calls different C++ functions that come back to Python, from Python threads."""
+ threads = []
+ for _ in range(num_threads):
+ thread = threading.Thread(target=_python_to_cpp_to_python)
+ thread.daemon = True
+ thread.start()
+ if parallel:
+ threads.append(thread)
+ else:
+ thread.join()
+ for thread in threads:
+ thread.join()
+
+
+def test_python_to_cpp_to_python_from_thread():
+ """Makes sure there is no GIL deadlock when running in a thread.
+
+ It runs in a separate process to be able to stop and assert if it deadlocks.
+ """
+ assert _run_in_process(_python_to_cpp_to_python_from_threads, 1) == 0
+
+
+def test_python_to_cpp_to_python_from_thread_multiple_parallel():
+ """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel.
+
+ It runs in a separate process to be able to stop and assert if it deadlocks.
+ """
+ assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=True) == 0
+
+
+def test_python_to_cpp_to_python_from_thread_multiple_sequential():
+ """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially.
+
+ It runs in a separate process to be able to stop and assert if it deadlocks.
+ """
+ assert _run_in_process(_python_to_cpp_to_python_from_threads, 8, parallel=False) == 0
+
+
+def test_python_to_cpp_to_python_from_process():
+ """Makes sure there is no GIL deadlock when using processes.
+
+ This test is for completion, but it was never an issue.
+ """
+ assert _run_in_process(_python_to_cpp_to_python) == 0
+
+
+def test_cross_module_gil():
+ """Makes sure that the GIL can be acquired by another module from a GIL-released state."""
+ m.test_cross_module_gil() # Should not raise a SIGSEGV
diff --git a/ext/pybind11/tests/test_iostream.py b/ext/pybind11/tests/test_iostream.py
index 3364849a4..27095b270 100644
--- a/ext/pybind11/tests/test_iostream.py
+++ b/ext/pybind11/tests/test_iostream.py
@@ -54,6 +54,17 @@ def test_captured(capsys):
assert stderr == msg
+def test_captured_large_string(capsys):
+ # Make this bigger than the buffer used on the C++ side: 1024 chars
+ msg = "I've been redirected to Python, I hope!"
+ msg = msg * (1024 // len(msg) + 1)
+
+ m.captured_output_default(msg)
+ stdout, stderr = capsys.readouterr()
+ assert stdout == msg
+ assert stderr == ''
+
+
def test_guard_capture(capsys):
msg = "I've been redirected to Python, I hope!"
m.guard_output(msg)
diff --git a/ext/pybind11/tests/test_kwargs_and_defaults.cpp b/ext/pybind11/tests/test_kwargs_and_defaults.cpp
index 165f8017e..6563fb9ad 100644
--- a/ext/pybind11/tests/test_kwargs_and_defaults.cpp
+++ b/ext/pybind11/tests/test_kwargs_and_defaults.cpp
@@ -8,6 +8,7 @@
*/
#include "pybind11_tests.h"
+#include "constructor_stats.h"
#include <pybind11/stl.h>
TEST_SUBMODULE(kwargs_and_defaults, m) {
@@ -33,7 +34,9 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a=0);
// test_args_and_kwargs
- m.def("args_function", [](py::args args) -> py::tuple { return args; });
+ m.def("args_function", [](py::args args) -> py::tuple {
+ return std::move(args);
+ });
m.def("args_kwargs_function", [](py::args args, py::kwargs kwargs) {
return py::make_tuple(args, kwargs);
});
@@ -53,6 +56,34 @@ TEST_SUBMODULE(kwargs_and_defaults, m) {
m.def("mixed_plus_args_kwargs_defaults", mixed_plus_both,
py::arg("i") = 1, py::arg("j") = 3.14159);
+ // test_args_refcount
+ // PyPy needs a garbage collection to get the reference count values to match CPython's behaviour
+ #ifdef PYPY_VERSION
+ #define GC_IF_NEEDED ConstructorStats::gc()
+ #else
+ #define GC_IF_NEEDED
+ #endif
+ m.def("arg_refcount_h", [](py::handle h) { GC_IF_NEEDED; return h.ref_count(); });
+ m.def("arg_refcount_h", [](py::handle h, py::handle, py::handle) { GC_IF_NEEDED; return h.ref_count(); });
+ m.def("arg_refcount_o", [](py::object o) { GC_IF_NEEDED; return o.ref_count(); });
+ m.def("args_refcount", [](py::args a) {
+ GC_IF_NEEDED;
+ py::tuple t(a.size());
+ for (size_t i = 0; i < a.size(); i++)
+ // Use raw Python API here to avoid an extra, intermediate incref on the tuple item:
+ t[i] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<ssize_t>(i)));
+ return t;
+ });
+ m.def("mixed_args_refcount", [](py::object o, py::args a) {
+ GC_IF_NEEDED;
+ py::tuple t(a.size() + 1);
+ t[0] = o.ref_count();
+ for (size_t i = 0; i < a.size(); i++)
+ // Use raw Python API here to avoid an extra, intermediate incref on the tuple item:
+ t[i + 1] = (int) Py_REFCNT(PyTuple_GET_ITEM(a.ptr(), static_cast<ssize_t>(i)));
+ return t;
+ });
+
// pybind11 won't allow these to be bound: args and kwargs, if present, must be at the end.
// Uncomment these to test that the static_assert is indeed working:
// m.def("bad_args1", [](py::args, int) {});
diff --git a/ext/pybind11/tests/test_kwargs_and_defaults.py b/ext/pybind11/tests/test_kwargs_and_defaults.py
index 733fe8593..27a05a024 100644
--- a/ext/pybind11/tests/test_kwargs_and_defaults.py
+++ b/ext/pybind11/tests/test_kwargs_and_defaults.py
@@ -5,11 +5,11 @@ from pybind11_tests import kwargs_and_defaults as m
def test_function_signatures(doc):
assert doc(m.kw_func0) == "kw_func0(arg0: int, arg1: int) -> str"
assert doc(m.kw_func1) == "kw_func1(x: int, y: int) -> str"
- assert doc(m.kw_func2) == "kw_func2(x: int=100, y: int=200) -> str"
- assert doc(m.kw_func3) == "kw_func3(data: str='Hello world!') -> None"
- assert doc(m.kw_func4) == "kw_func4(myList: List[int]=[13, 17]) -> str"
- assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int=300) -> str"
- assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int=0) -> str"
+ assert doc(m.kw_func2) == "kw_func2(x: int = 100, y: int = 200) -> str"
+ assert doc(m.kw_func3) == "kw_func3(data: str = 'Hello world!') -> None"
+ assert doc(m.kw_func4) == "kw_func4(myList: List[int] = [13, 17]) -> str"
+ assert doc(m.kw_func_udl) == "kw_func_udl(x: int, y: int = 300) -> str"
+ assert doc(m.kw_func_udl_z) == "kw_func_udl_z(x: int, y: int = 0) -> str"
assert doc(m.args_function) == "args_function(*args) -> tuple"
assert doc(m.args_kwargs_function) == "args_kwargs_function(*args, **kwargs) -> tuple"
assert doc(m.KWClass.foo0) == \
@@ -93,7 +93,7 @@ def test_mixed_args_and_kwargs(msg):
assert mpakd(1, i=1)
assert msg(excinfo.value) == """
mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported:
- 1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple
+ 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple
Invoked with: 1; kwargs: i=1
""" # noqa: E501 line too long
@@ -101,7 +101,47 @@ def test_mixed_args_and_kwargs(msg):
assert mpakd(1, 2, j=1)
assert msg(excinfo.value) == """
mixed_plus_args_kwargs_defaults(): incompatible function arguments. The following argument types are supported:
- 1. (i: int=1, j: float=3.14159, *args, **kwargs) -> tuple
+ 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple
Invoked with: 1, 2; kwargs: j=1
""" # noqa: E501 line too long
+
+
+def test_args_refcount():
+ """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
+ arguments"""
+ refcount = m.arg_refcount_h
+
+ myval = 54321
+ expected = refcount(myval)
+ assert m.arg_refcount_h(myval) == expected
+ assert m.arg_refcount_o(myval) == expected + 1
+ assert m.arg_refcount_h(myval) == expected
+ assert refcount(myval) == expected
+
+ assert m.mixed_plus_args(1, 2.0, "a", myval) == (1, 2.0, ("a", myval))
+ assert refcount(myval) == expected
+
+ assert m.mixed_plus_kwargs(3, 4.0, a=1, b=myval) == (3, 4.0, {"a": 1, "b": myval})
+ assert refcount(myval) == expected
+
+ assert m.args_function(-1, myval) == (-1, myval)
+ assert refcount(myval) == expected
+
+ assert m.mixed_plus_args_kwargs(5, 6.0, myval, a=myval) == (5, 6.0, (myval,), {"a": myval})
+ assert refcount(myval) == expected
+
+ assert m.args_kwargs_function(7, 8, myval, a=1, b=myval) == \
+ ((7, 8, myval), {"a": 1, "b": myval})
+ assert refcount(myval) == expected
+
+ exp3 = refcount(myval, myval, myval)
+ assert m.args_refcount(myval, myval, myval) == (exp3, exp3, exp3)
+ assert refcount(myval) == expected
+
+ # This function takes the first arg as a `py::object` and the rest as a `py::args`. Unlike the
+ # previous case, when we have both positional and `py::args` we need to construct a new tuple
+ # for the `py::args`; in the previous case, we could simply inc_ref and pass on Python's input
+ # tuple without having to inc_ref the individual elements, but here we can't, hence the extra
+ # refs.
+ assert m.mixed_args_refcount(myval, myval, myval) == (exp3 + 3, exp3 + 3, exp3 + 3)
diff --git a/ext/pybind11/tests/test_local_bindings.py b/ext/pybind11/tests/test_local_bindings.py
index b3dc3619c..b380376e2 100644
--- a/ext/pybind11/tests/test_local_bindings.py
+++ b/ext/pybind11/tests/test_local_bindings.py
@@ -220,7 +220,7 @@ def test_cross_module_calls():
c, d = m.MixGL2(3), cm.MixGL2(4)
with pytest.raises(TypeError) as excinfo:
m.get_gl_value(c)
- assert "incompatible function arguments" in str(excinfo)
+ assert "incompatible function arguments" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
m.get_gl_value(d)
- assert "incompatible function arguments" in str(excinfo)
+ assert "incompatible function arguments" in str(excinfo.value)
diff --git a/ext/pybind11/tests/test_methods_and_attributes.cpp b/ext/pybind11/tests/test_methods_and_attributes.cpp
index cd15869f4..c7b82f13d 100644
--- a/ext/pybind11/tests/test_methods_and_attributes.cpp
+++ b/ext/pybind11/tests/test_methods_and_attributes.cpp
@@ -11,6 +11,11 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
+#if !defined(PYBIND11_OVERLOAD_CAST)
+template <typename... Args>
+using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
+#endif
+
class ExampleMandA {
public:
ExampleMandA() { print_default_created(this); }
@@ -242,15 +247,16 @@ TEST_SUBMODULE(methods_and_attributes, m) {
.def("overloaded_const", py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<float, float>(&ExampleMandA::overloaded, py::const_))
#else
- .def("overloaded", static_cast<py::str (ExampleMandA::*)()>(&ExampleMandA::overloaded))
- .def("overloaded", static_cast<py::str (ExampleMandA::*)(int)>(&ExampleMandA::overloaded))
- .def("overloaded", static_cast<py::str (ExampleMandA::*)(int, float)>(&ExampleMandA::overloaded))
+ // Use both the traditional static_cast method and the C++11 compatible overload_cast_
+ .def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded))
+ .def("overloaded", overload_cast_<int>()(&ExampleMandA::overloaded))
+ .def("overloaded", overload_cast_<int, float>()(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, int)>(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
- .def("overloaded_float", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
- .def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int ) const>(&ExampleMandA::overloaded))
- .def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, float) const>(&ExampleMandA::overloaded))
+ .def("overloaded_float", overload_cast_<float, float>()(&ExampleMandA::overloaded))
+ .def("overloaded_const", overload_cast_<int >()(&ExampleMandA::overloaded, py::const_))
+ .def("overloaded_const", overload_cast_<int, float>()(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, int) const>(&ExampleMandA::overloaded))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, int) const>(&ExampleMandA::overloaded))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, float) const>(&ExampleMandA::overloaded))
@@ -279,12 +285,20 @@ TEST_SUBMODULE(methods_and_attributes, m) {
.def(py::init<>())
.def_readonly("def_readonly", &TestProperties::value)
.def_readwrite("def_readwrite", &TestProperties::value)
+ .def_property("def_writeonly", nullptr,
+ [](TestProperties& s,int v) { s.value = v; } )
+ .def_property("def_property_writeonly", nullptr, &TestProperties::set)
.def_property_readonly("def_property_readonly", &TestProperties::get)
.def_property("def_property", &TestProperties::get, &TestProperties::set)
+ .def_property("def_property_impossible", nullptr, nullptr)
.def_readonly_static("def_readonly_static", &TestProperties::static_value)
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
+ .def_property_static("def_writeonly_static", nullptr,
+ [](py::object, int v) { TestProperties::static_value = v; })
.def_property_readonly_static("def_property_readonly_static",
[](py::object) { return TestProperties::static_get(); })
+ .def_property_static("def_property_writeonly_static", nullptr,
+ [](py::object, int v) { return TestProperties::static_set(v); })
.def_property_static("def_property_static",
[](py::object) { return TestProperties::static_get(); },
[](py::object, int v) { TestProperties::static_set(v); })
diff --git a/ext/pybind11/tests/test_methods_and_attributes.py b/ext/pybind11/tests/test_methods_and_attributes.py
index 9fd9cb75c..f1c862be8 100644
--- a/ext/pybind11/tests/test_methods_and_attributes.py
+++ b/ext/pybind11/tests/test_methods_and_attributes.py
@@ -98,23 +98,52 @@ def test_properties():
instance.def_property = 3
assert instance.def_property == 3
+ with pytest.raises(AttributeError) as excinfo:
+ dummy = instance.def_property_writeonly # noqa: F841 unused var
+ assert "unreadable attribute" in str(excinfo.value)
+
+ instance.def_property_writeonly = 4
+ assert instance.def_property_readonly == 4
+
+ with pytest.raises(AttributeError) as excinfo:
+ dummy = instance.def_property_impossible # noqa: F841 unused var
+ assert "unreadable attribute" in str(excinfo.value)
+
+ with pytest.raises(AttributeError) as excinfo:
+ instance.def_property_impossible = 5
+ assert "can't set attribute" in str(excinfo.value)
+
def test_static_properties():
assert m.TestProperties.def_readonly_static == 1
with pytest.raises(AttributeError) as excinfo:
m.TestProperties.def_readonly_static = 2
- assert "can't set attribute" in str(excinfo)
+ assert "can't set attribute" in str(excinfo.value)
m.TestProperties.def_readwrite_static = 2
assert m.TestProperties.def_readwrite_static == 2
- assert m.TestProperties.def_property_readonly_static == 2
with pytest.raises(AttributeError) as excinfo:
- m.TestProperties.def_property_readonly_static = 3
- assert "can't set attribute" in str(excinfo)
+ dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var
+ assert "unreadable attribute" in str(excinfo.value)
+
+ m.TestProperties.def_writeonly_static = 3
+ assert m.TestProperties.def_readonly_static == 3
+
+ assert m.TestProperties.def_property_readonly_static == 3
+ with pytest.raises(AttributeError) as excinfo:
+ m.TestProperties.def_property_readonly_static = 99
+ assert "can't set attribute" in str(excinfo.value)
+
+ m.TestProperties.def_property_static = 4
+ assert m.TestProperties.def_property_static == 4
+
+ with pytest.raises(AttributeError) as excinfo:
+ dummy = m.TestProperties.def_property_writeonly_static
+ assert "unreadable attribute" in str(excinfo.value)
- m.TestProperties.def_property_static = 3
- assert m.TestProperties.def_property_static == 3
+ m.TestProperties.def_property_writeonly_static = 5
+ assert m.TestProperties.def_property_static == 5
# Static property read and write via instance
instance = m.TestProperties()
@@ -127,6 +156,13 @@ def test_static_properties():
assert m.TestProperties.def_readwrite_static == 2
assert instance.def_readwrite_static == 2
+ with pytest.raises(AttributeError) as excinfo:
+ dummy = instance.def_property_writeonly_static # noqa: F841 unused var
+ assert "unreadable attribute" in str(excinfo.value)
+
+ instance.def_property_writeonly_static = 4
+ assert instance.def_property_static == 4
+
# It should be possible to override properties in derived classes
assert m.TestPropertiesOverride().def_readonly == 99
assert m.TestPropertiesOverride.def_readonly_static == 99
diff --git a/ext/pybind11/tests/test_multiple_inheritance.cpp b/ext/pybind11/tests/test_multiple_inheritance.cpp
index 35f9d9c4e..ba1674fb2 100644
--- a/ext/pybind11/tests/test_multiple_inheritance.cpp
+++ b/ext/pybind11/tests/test_multiple_inheritance.cpp
@@ -130,8 +130,8 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// test_mi_unaligned_base
// test_mi_base_return
// Issue #801: invalid casting to derived type with MI bases
- struct I801B1 { int a = 1; virtual ~I801B1() = default; };
- struct I801B2 { int b = 2; virtual ~I801B2() = default; };
+ struct I801B1 { int a = 1; I801B1() = default; I801B1(const I801B1 &) = default; virtual ~I801B1() = default; };
+ struct I801B2 { int b = 2; I801B2() = default; I801B2(const I801B2 &) = default; virtual ~I801B2() = default; };
struct I801C : I801B1, I801B2 {};
struct I801D : I801C {}; // Indirect MI
// Unregistered classes:
@@ -205,7 +205,7 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// test_diamond_inheritance
// Issue #959: segfault when constructing diamond inheritance instance
// All of these have int members so that there will be various unequal pointers involved.
- struct B { int b; virtual ~B() = default; };
+ struct B { int b; B() = default; B(const B&) = default; virtual ~B() = default; };
struct C0 : public virtual B { int c0; };
struct C1 : public virtual B { int c1; };
struct D : public C0, public C1 { int d; };
diff --git a/ext/pybind11/tests/test_numpy_array.cpp b/ext/pybind11/tests/test_numpy_array.cpp
index 2046c0e03..156a3bfa8 100644
--- a/ext/pybind11/tests/test_numpy_array.cpp
+++ b/ext/pybind11/tests/test_numpy_array.cpp
@@ -14,6 +14,67 @@
#include <cstdint>
+// Size / dtype checks.
+struct DtypeCheck {
+ py::dtype numpy{};
+ py::dtype pybind11{};
+};
+
+template <typename T>
+DtypeCheck get_dtype_check(const char* name) {
+ py::module np = py::module::import("numpy");
+ DtypeCheck check{};
+ check.numpy = np.attr("dtype")(np.attr(name));
+ check.pybind11 = py::dtype::of<T>();
+ return check;
+}
+
+std::vector<DtypeCheck> get_concrete_dtype_checks() {
+ return {
+ // Normalization
+ get_dtype_check<std::int8_t>("int8"),
+ get_dtype_check<std::uint8_t>("uint8"),
+ get_dtype_check<std::int16_t>("int16"),
+ get_dtype_check<std::uint16_t>("uint16"),
+ get_dtype_check<std::int32_t>("int32"),
+ get_dtype_check<std::uint32_t>("uint32"),
+ get_dtype_check<std::int64_t>("int64"),
+ get_dtype_check<std::uint64_t>("uint64")
+ };
+}
+
+struct DtypeSizeCheck {
+ std::string name{};
+ int size_cpp{};
+ int size_numpy{};
+ // For debugging.
+ py::dtype dtype{};
+};
+
+template <typename T>
+DtypeSizeCheck get_dtype_size_check() {
+ DtypeSizeCheck check{};
+ check.name = py::type_id<T>();
+ check.size_cpp = sizeof(T);
+ check.dtype = py::dtype::of<T>();
+ check.size_numpy = check.dtype.attr("itemsize").template cast<int>();
+ return check;
+}
+
+std::vector<DtypeSizeCheck> get_platform_dtype_size_checks() {
+ return {
+ get_dtype_size_check<short>(),
+ get_dtype_size_check<unsigned short>(),
+ get_dtype_size_check<int>(),
+ get_dtype_size_check<unsigned int>(),
+ get_dtype_size_check<long>(),
+ get_dtype_size_check<unsigned long>(),
+ get_dtype_size_check<long long>(),
+ get_dtype_size_check<unsigned long long>(),
+ };
+}
+
+// Arrays.
using arr = py::array;
using arr_t = py::array_t<uint16_t, 0>;
static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
@@ -68,10 +129,33 @@ template <typename T, typename T2> py::handle auxiliaries(T &&r, T2 &&r2) {
return l.release();
}
+// note: declaration at local scope would create a dangling reference!
+static int data_i = 42;
+
TEST_SUBMODULE(numpy_array, sm) {
try { py::module::import("numpy"); }
catch (...) { return; }
+ // test_dtypes
+ py::class_<DtypeCheck>(sm, "DtypeCheck")
+ .def_readonly("numpy", &DtypeCheck::numpy)
+ .def_readonly("pybind11", &DtypeCheck::pybind11)
+ .def("__repr__", [](const DtypeCheck& self) {
+ return py::str("<DtypeCheck numpy={} pybind11={}>").format(
+ self.numpy, self.pybind11);
+ });
+ sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);
+
+ py::class_<DtypeSizeCheck>(sm, "DtypeSizeCheck")
+ .def_readonly("name", &DtypeSizeCheck::name)
+ .def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
+ .def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
+ .def("__repr__", [](const DtypeSizeCheck& self) {
+ return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>").format(
+ self.name, self.size_cpp, self.size_numpy, self.dtype);
+ });
+ sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);
+
// test_array_attributes
sm.def("ndim", [](const arr& a) { return a.ndim(); });
sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); });
@@ -102,6 +186,11 @@ TEST_SUBMODULE(numpy_array, sm) {
sm.def("make_f_array", [] { return py::array_t<float>({ 2, 2 }, { 4, 8 }); });
sm.def("make_c_array", [] { return py::array_t<float>({ 2, 2 }, { 8, 4 }); });
+ // test_empty_shaped_array
+ sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
+ // test numpy scalars (empty shape, ndim==0)
+ sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); });
+
// test_wrap
sm.def("wrap", [](py::array a) {
return py::array(
@@ -292,4 +381,10 @@ TEST_SUBMODULE(numpy_array, sm) {
std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
return a;
});
+
+#if PY_MAJOR_VERSION >= 3
+ sm.def("index_using_ellipsis", [](py::array a) {
+ return a[py::make_tuple(0, py::ellipsis(), 0)];
+ });
+#endif
}
diff --git a/ext/pybind11/tests/test_numpy_array.py b/ext/pybind11/tests/test_numpy_array.py
index 27433934f..d0a6324df 100644
--- a/ext/pybind11/tests/test_numpy_array.py
+++ b/ext/pybind11/tests/test_numpy_array.py
@@ -7,6 +7,21 @@ with pytest.suppress(ImportError):
import numpy as np
+def test_dtypes():
+ # See issue #1328.
+ # - Platform-dependent sizes.
+ for size_check in m.get_platform_dtype_size_checks():
+ print(size_check)
+ assert size_check.size_cpp == size_check.size_numpy, size_check
+ # - Concrete sizes.
+ for check in m.get_concrete_dtype_checks():
+ print(check)
+ assert check.numpy == check.pybind11, check
+ if check.numpy.num != check.pybind11.num:
+ print("NOTE: typenum mismatch for {}: {} != {}".format(
+ check, check.numpy.num, check.pybind11.num))
+
+
@pytest.fixture(scope='function')
def arr():
return np.array([[1, 2, 3], [4, 5, 6]], '=u2')
@@ -135,8 +150,18 @@ def test_make_c_f_array():
assert not m.make_f_array().flags.c_contiguous
+def test_make_empty_shaped_array():
+ m.make_empty_shaped_array()
+
+ # empty shape means numpy scalar, PEP 3118
+ assert m.scalar_int().ndim == 0
+ assert m.scalar_int().shape == ()
+ assert m.scalar_int() == 42
+
+
def test_wrap():
def assert_references(a, b, base=None):
+ from distutils.version import LooseVersion
if base is None:
base = a
assert a is not b
@@ -147,7 +172,10 @@ def test_wrap():
assert a.flags.f_contiguous == b.flags.f_contiguous
assert a.flags.writeable == b.flags.writeable
assert a.flags.aligned == b.flags.aligned
- assert a.flags.updateifcopy == b.flags.updateifcopy
+ if LooseVersion(np.__version__) >= LooseVersion("1.14.0"):
+ assert a.flags.writebackifcopy == b.flags.writebackifcopy
+ else:
+ assert a.flags.updateifcopy == b.flags.updateifcopy
assert np.all(a == b)
assert not b.flags.owndata
assert b.base is base
@@ -282,17 +310,17 @@ def test_overload_resolution(msg):
1. (arg0: numpy.ndarray[int32]) -> str
2. (arg0: numpy.ndarray[float64]) -> str
- Invoked with:"""
+ Invoked with: """
with pytest.raises(TypeError) as excinfo:
m.overloaded3(np.array([1], dtype='uintc'))
- assert msg(excinfo.value) == expected_exc + " array([1], dtype=uint32)"
+ assert msg(excinfo.value) == expected_exc + repr(np.array([1], dtype='uint32'))
with pytest.raises(TypeError) as excinfo:
m.overloaded3(np.array([1], dtype='float32'))
- assert msg(excinfo.value) == expected_exc + " array([ 1.], dtype=float32)"
+ assert msg(excinfo.value) == expected_exc + repr(np.array([1.], dtype='float32'))
with pytest.raises(TypeError) as excinfo:
m.overloaded3(np.array([1], dtype='complex'))
- assert msg(excinfo.value) == expected_exc + " array([ 1.+0.j])"
+ assert msg(excinfo.value) == expected_exc + repr(np.array([1. + 0.j]))
# Exact matches:
assert m.overloaded4(np.array([1], dtype='double')) == 'double'
@@ -400,3 +428,20 @@ def test_array_create_and_resize(msg):
a = m.create_and_resize(2)
assert(a.size == 4)
assert(np.all(a == 42.))
+
+
+@pytest.unsupported_on_py2
+def test_index_using_ellipsis():
+ a = m.index_using_ellipsis(np.zeros((5, 6, 7)))
+ assert a.shape == (6,)
+
+
+@pytest.unsupported_on_pypy
+def test_dtype_refcount_leak():
+ from sys import getrefcount
+ dtype = np.dtype(np.float_)
+ a = np.array([1], dtype=dtype)
+ before = getrefcount(dtype)
+ m.ndim(a)
+ after = getrefcount(dtype)
+ assert after == before
diff --git a/ext/pybind11/tests/test_numpy_dtypes.cpp b/ext/pybind11/tests/test_numpy_dtypes.cpp
index ddec851f6..467e0253f 100644
--- a/ext/pybind11/tests/test_numpy_dtypes.cpp
+++ b/ext/pybind11/tests/test_numpy_dtypes.cpp
@@ -29,6 +29,13 @@ std::ostream& operator<<(std::ostream& os, const SimpleStruct& v) {
return os << "s:" << v.bool_ << "," << v.uint_ << "," << v.float_ << "," << v.ldbl_;
}
+struct SimpleStructReordered {
+ bool bool_;
+ float float_;
+ uint32_t uint_;
+ long double ldbl_;
+};
+
PYBIND11_PACKED(struct PackedStruct {
bool bool_;
uint32_t uint_;
@@ -244,6 +251,9 @@ py::list test_dtype_ctors() {
return list;
}
+struct A {};
+struct B {};
+
TEST_SUBMODULE(numpy_dtypes, m) {
try { py::module::import("numpy"); }
catch (...) { return; }
@@ -252,6 +262,7 @@ TEST_SUBMODULE(numpy_dtypes, m) {
py::class_<SimpleStruct>(m, "SimpleStruct");
PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_);
+ PYBIND11_NUMPY_DTYPE(SimpleStructReordered, bool_, uint_, float_, ldbl_);
PYBIND11_NUMPY_DTYPE(PackedStruct, bool_, uint_, float_, ldbl_);
PYBIND11_NUMPY_DTYPE(NestedStruct, a, b);
PYBIND11_NUMPY_DTYPE(PartialStruct, bool_, uint_, float_, ldbl_);
@@ -271,6 +282,15 @@ TEST_SUBMODULE(numpy_dtypes, m) {
// struct NotPOD { std::string v; NotPOD() : v("hi") {}; };
// PYBIND11_NUMPY_DTYPE(NotPOD, v);
+ // Check that dtypes can be registered programmatically, both from
+ // initializer lists of field descriptors and from other containers.
+ py::detail::npy_format_descriptor<A>::register_dtype(
+ {}
+ );
+ py::detail::npy_format_descriptor<B>::register_dtype(
+ std::vector<py::detail::field_descriptor>{}
+ );
+
// test_recarray, test_scalar_conversion
m.def("create_rec_simple", &create_recarray<SimpleStruct>);
m.def("create_rec_packed", &create_recarray<PackedStruct>);
@@ -448,4 +468,7 @@ TEST_SUBMODULE(numpy_dtypes, m) {
// test_register_dtype
m.def("register_dtype", []() { PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_); });
+
+ // test_str_leak
+ m.def("dtype_wrapper", [](py::object d) { return py::dtype::from_args(std::move(d)); });
}
diff --git a/ext/pybind11/tests/test_numpy_dtypes.py b/ext/pybind11/tests/test_numpy_dtypes.py
index 5f9a95404..2e6388517 100644
--- a/ext/pybind11/tests/test_numpy_dtypes.py
+++ b/ext/pybind11/tests/test_numpy_dtypes.py
@@ -103,7 +103,7 @@ def test_dtype(simple_dtype):
partial_nested_fmt(),
"[('a', 'S3'), ('b', 'S3')]",
("{{'names':['a','b','c','d'], " +
- "'formats':[('S4', (3,)),('<i4', (2,)),('u1', (3,)),('<f4', (4, 2))], " +
+ "'formats':[('S4', (3,)),('" + e + "i4', (2,)),('u1', (3,)),('" + e + "f4', (4, 2))], " +
"'offsets':[0,12,20,24], 'itemsize':56}}").format(e=e),
"[('e1', '" + e + "i8'), ('e2', 'u1')]",
"[('x', 'i1'), ('y', '" + e + "u8')]",
@@ -215,7 +215,7 @@ def test_array_array():
arr = m.create_array_array(3)
assert str(arr.dtype) == (
"{{'names':['a','b','c','d'], " +
- "'formats':[('S4', (3,)),('<i4', (2,)),('u1', (3,)),('{e}f4', (4, 2))], " +
+ "'formats':[('S4', (3,)),('" + e + "i4', (2,)),('u1', (3,)),('{e}f4', (4, 2))], " +
"'offsets':[0,12,20,24], 'itemsize':56}}").format(e=e)
assert m.print_array_array(arr) == [
"a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1}," +
@@ -293,6 +293,18 @@ def test_register_dtype():
assert 'dtype is already registered' in str(excinfo.value)
-@pytest.requires_numpy
+@pytest.unsupported_on_pypy
+def test_str_leak():
+ from sys import getrefcount
+ fmt = "f4"
+ pytest.gc_collect()
+ start = getrefcount(fmt)
+ d = m.dtype_wrapper(fmt)
+ assert d is np.dtype("f4")
+ del d
+ pytest.gc_collect()
+ assert getrefcount(fmt) == start
+
+
def test_compare_buffer_info():
assert all(m.compare_buffer_info())
diff --git a/ext/pybind11/tests/test_opaque_types.cpp b/ext/pybind11/tests/test_opaque_types.cpp
index 5e83df0f6..0d20d9a01 100644
--- a/ext/pybind11/tests/test_opaque_types.cpp
+++ b/ext/pybind11/tests/test_opaque_types.cpp
@@ -11,10 +11,14 @@
#include <pybind11/stl.h>
#include <vector>
-using StringList = std::vector<std::string>;
+// IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures
+//
+// This also deliberately doesn't use the below StringList type alias to test
+// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
+// bit is just the default `std::vector` allocator).
+PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
-/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */
-PYBIND11_MAKE_OPAQUE(StringList);
+using StringList = std::vector<std::string, std::allocator<std::string>>;
TEST_SUBMODULE(opaque_types, m) {
// test_string_list
diff --git a/ext/pybind11/tests/test_opaque_types.py b/ext/pybind11/tests/test_opaque_types.py
index 2d3aef5d1..6b3802fdb 100644
--- a/ext/pybind11/tests/test_opaque_types.py
+++ b/ext/pybind11/tests/test_opaque_types.py
@@ -4,21 +4,21 @@ from pybind11_tests import ConstructorStats, UserType
def test_string_list():
- l = m.StringList()
- l.push_back("Element 1")
- l.push_back("Element 2")
- assert m.print_opaque_list(l) == "Opaque list: [Element 1, Element 2]"
- assert l.back() == "Element 2"
+ lst = m.StringList()
+ lst.push_back("Element 1")
+ lst.push_back("Element 2")
+ assert m.print_opaque_list(lst) == "Opaque list: [Element 1, Element 2]"
+ assert lst.back() == "Element 2"
- for i, k in enumerate(l, start=1):
+ for i, k in enumerate(lst, start=1):
assert k == "Element {}".format(i)
- l.pop_back()
- assert m.print_opaque_list(l) == "Opaque list: [Element 1]"
+ lst.pop_back()
+ assert m.print_opaque_list(lst) == "Opaque list: [Element 1]"
cvp = m.ClassWithSTLVecProperty()
assert m.print_opaque_list(cvp.stringList) == "Opaque list: []"
- cvp.stringList = l
+ cvp.stringList = lst
cvp.stringList.push_back("Element 3")
assert m.print_opaque_list(cvp.stringList) == "Opaque list: [Element 1, Element 3]"
diff --git a/ext/pybind11/tests/test_operator_overloading.cpp b/ext/pybind11/tests/test_operator_overloading.cpp
index 4ad34d104..7b111704b 100644
--- a/ext/pybind11/tests/test_operator_overloading.cpp
+++ b/ext/pybind11/tests/test_operator_overloading.cpp
@@ -23,6 +23,7 @@ public:
std::string toString() const { return "[" + std::to_string(x) + ", " + std::to_string(y) + "]"; }
+ Vector2 operator-() const { return Vector2(-x, -y); }
Vector2 operator+(const Vector2 &v) const { return Vector2(x + v.x, y + v.y); }
Vector2 operator-(const Vector2 &v) const { return Vector2(x - v.x, y - v.y); }
Vector2 operator-(float value) const { return Vector2(x - value, y - value); }
@@ -62,6 +63,25 @@ namespace std {
};
}
+// MSVC warns about unknown pragmas, and warnings are errors.
+#ifndef _MSC_VER
+ #pragma GCC diagnostic push
+ // clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to
+ // `-Wall`, which is used here for overloading (e.g. `py::self += py::self `).
+ // Here, we suppress the warning using `#pragma diagnostic`.
+ // Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46
+ // TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`).
+ #if (__APPLE__) && (__clang__)
+ #if (__clang_major__ >= 10) && (__clang_minor__ >= 0) && (__clang_patchlevel__ >= 1)
+ #pragma GCC diagnostic ignored "-Wself-assign-overloaded"
+ #endif
+ #elif (__clang__)
+ #if (__clang_major__ >= 7)
+ #pragma GCC diagnostic ignored "-Wself-assign-overloaded"
+ #endif
+ #endif
+#endif
+
TEST_SUBMODULE(operators, m) {
// test_operator_overloading
@@ -85,6 +105,7 @@ TEST_SUBMODULE(operators, m) {
.def(float() - py::self)
.def(float() * py::self)
.def(float() / py::self)
+ .def(-py::self)
.def("__str__", &Vector2::toString)
.def(hash(py::self))
;
@@ -144,3 +165,7 @@ TEST_SUBMODULE(operators, m) {
.def_readwrite("b", &NestC::b);
m.def("get_NestC", [](const NestC &c) { return c.value; });
}
+
+#ifndef _MSC_VER
+ #pragma GCC diagnostic pop
+#endif
diff --git a/ext/pybind11/tests/test_operator_overloading.py b/ext/pybind11/tests/test_operator_overloading.py
index 0d80e5ed3..bd36ac2a5 100644
--- a/ext/pybind11/tests/test_operator_overloading.py
+++ b/ext/pybind11/tests/test_operator_overloading.py
@@ -9,6 +9,8 @@ def test_operator_overloading():
assert str(v1) == "[1.000000, 2.000000]"
assert str(v2) == "[3.000000, -1.000000]"
+ assert str(-v2) == "[-3.000000, 1.000000]"
+
assert str(v1 + v2) == "[4.000000, 1.000000]"
assert str(v1 - v2) == "[-2.000000, 3.000000]"
assert str(v1 - 8) == "[-7.000000, -6.000000]"
@@ -44,13 +46,13 @@ def test_operator_overloading():
del v2
assert cstats.alive() == 0
assert cstats.values() == ['[1.000000, 2.000000]', '[3.000000, -1.000000]',
- '[4.000000, 1.000000]', '[-2.000000, 3.000000]',
- '[-7.000000, -6.000000]', '[9.000000, 10.000000]',
- '[8.000000, 16.000000]', '[0.125000, 0.250000]',
- '[7.000000, 6.000000]', '[9.000000, 10.000000]',
- '[8.000000, 16.000000]', '[8.000000, 4.000000]',
- '[3.000000, -2.000000]', '[3.000000, -0.500000]',
- '[6.000000, -2.000000]']
+ '[-3.000000, 1.000000]', '[4.000000, 1.000000]',
+ '[-2.000000, 3.000000]', '[-7.000000, -6.000000]',
+ '[9.000000, 10.000000]', '[8.000000, 16.000000]',
+ '[0.125000, 0.250000]', '[7.000000, 6.000000]',
+ '[9.000000, 10.000000]', '[8.000000, 16.000000]',
+ '[8.000000, 4.000000]', '[3.000000, -2.000000]',
+ '[3.000000, -0.500000]', '[6.000000, -2.000000]']
assert cstats.default_constructions == 0
assert cstats.copy_constructions == 0
assert cstats.move_constructions >= 10
@@ -98,7 +100,7 @@ def test_nested():
del c
pytest.gc_collect()
- del a # Should't delete while abase is still alive
+ del a # Shouldn't delete while abase is still alive
pytest.gc_collect()
assert abase.value == 42
diff --git a/ext/pybind11/tests/test_pickling.py b/ext/pybind11/tests/test_pickling.py
index 707d34786..5ae05aaa0 100644
--- a/ext/pybind11/tests/test_pickling.py
+++ b/ext/pybind11/tests/test_pickling.py
@@ -34,3 +34,9 @@ def test_roundtrip_with_dict(cls_name):
assert p2.value == p.value
assert p2.extra == p.extra
assert p2.dynamic == p.dynamic
+
+
+def test_enum_pickle():
+ from pybind11_tests import enums as e
+ data = pickle.dumps(e.EOne, 2)
+ assert e.EOne == pickle.loads(data)
diff --git a/ext/pybind11/tests/test_pytypes.cpp b/ext/pybind11/tests/test_pytypes.cpp
index a962f0ccc..244e1db0d 100644
--- a/ext/pybind11/tests/test_pytypes.cpp
+++ b/ext/pybind11/tests/test_pytypes.cpp
@@ -17,6 +17,8 @@ TEST_SUBMODULE(pytypes, m) {
list.append("value");
py::print("Entry at position 0:", list[0]);
list[0] = py::str("overwritten");
+ list.insert(0, "inserted-0");
+ list.insert(2, "inserted-2");
return list;
});
m.def("print_list", [](py::list list) {
@@ -37,6 +39,12 @@ TEST_SUBMODULE(pytypes, m) {
for (auto item : set)
py::print("key:", item);
});
+ m.def("set_contains", [](py::set set, py::object key) {
+ return set.contains(key);
+ });
+ m.def("set_contains", [](py::set set, const char* key) {
+ return set.contains(key);
+ });
// test_dict
m.def("get_dict", []() { return py::dict("key"_a="value"); });
@@ -49,6 +57,12 @@ TEST_SUBMODULE(pytypes, m) {
auto d2 = py::dict("z"_a=3, **d1);
return d2;
});
+ m.def("dict_contains", [](py::dict dict, py::object val) {
+ return dict.contains(val);
+ });
+ m.def("dict_contains", [](py::dict dict, const char* val) {
+ return dict.contains(val);
+ });
// test_str
m.def("str_from_string", []() { return py::str(std::string("baz")); });
@@ -269,4 +283,28 @@ TEST_SUBMODULE(pytypes, m) {
m.def("print_failure", []() { py::print(42, UnregisteredType()); });
m.def("hash_function", [](py::object obj) { return py::hash(obj); });
+
+ m.def("test_number_protocol", [](py::object a, py::object b) {
+ py::list l;
+ l.append(a.equal(b));
+ l.append(a.not_equal(b));
+ l.append(a < b);
+ l.append(a <= b);
+ l.append(a > b);
+ l.append(a >= b);
+ l.append(a + b);
+ l.append(a - b);
+ l.append(a * b);
+ l.append(a / b);
+ l.append(a | b);
+ l.append(a & b);
+ l.append(a ^ b);
+ l.append(a >> b);
+ l.append(a << b);
+ return l;
+ });
+
+ m.def("test_list_slicing", [](py::list a) {
+ return a[py::slice(0, -1, 2)];
+ });
}
diff --git a/ext/pybind11/tests/test_pytypes.py b/ext/pybind11/tests/test_pytypes.py
index 94c90a909..0e8d6c33a 100644
--- a/ext/pybind11/tests/test_pytypes.py
+++ b/ext/pybind11/tests/test_pytypes.py
@@ -1,3 +1,4 @@
+from __future__ import division
import pytest
import sys
@@ -7,15 +8,17 @@ from pybind11_tests import debug_enabled
def test_list(capture, doc):
with capture:
- l = m.get_list()
- assert l == ["overwritten"]
+ lst = m.get_list()
+ assert lst == ["inserted-0", "overwritten", "inserted-2"]
- l.append("value2")
- m.print_list(l)
+ lst.append("value2")
+ m.print_list(lst)
assert capture.unordered == """
Entry at position 0: value
- list item 0: overwritten
- list item 1: value2
+ list item 0: inserted-0
+ list item 1: overwritten
+ list item 2: inserted-2
+ list item 3: value2
"""
assert doc(m.get_list) == "get_list() -> list"
@@ -36,6 +39,10 @@ def test_set(capture, doc):
key: key4
"""
+ assert not m.set_contains(set([]), 42)
+ assert m.set_contains({42}, 42)
+ assert m.set_contains({"foo"}, "foo")
+
assert doc(m.get_list) == "get_list() -> list"
assert doc(m.print_list) == "print_list(arg0: list) -> None"
@@ -52,6 +59,10 @@ def test_dict(capture, doc):
key: key2, value=value2
"""
+ assert not m.dict_contains({}, 42)
+ assert m.dict_contains({42: None}, 42)
+ assert m.dict_contains({"foo": None}, "foo")
+
assert doc(m.get_dict) == "get_dict() -> dict"
assert doc(m.print_dict) == "print_dict(arg0: dict) -> None"
@@ -238,3 +249,15 @@ def test_hash():
assert m.hash_function(Hashable(42)) == 42
with pytest.raises(TypeError):
m.hash_function(Unhashable())
+
+
+def test_number_protocol():
+ for a, b in [(1, 1), (3, 5)]:
+ li = [a == b, a != b, a < b, a <= b, a > b, a >= b, a + b,
+ a - b, a * b, a / b, a | b, a & b, a ^ b, a >> b, a << b]
+ assert m.test_number_protocol(a, b) == li
+
+
+def test_list_slicing():
+ li = list(range(100))
+ assert li[::2] == m.test_list_slicing(li)
diff --git a/ext/pybind11/tests/test_sequences_and_iterators.cpp b/ext/pybind11/tests/test_sequences_and_iterators.cpp
index a45521256..87ccf99d6 100644
--- a/ext/pybind11/tests/test_sequences_and_iterators.cpp
+++ b/ext/pybind11/tests/test_sequences_and_iterators.cpp
@@ -71,6 +71,25 @@ py::list test_random_access_iterator(PythonType x) {
}
TEST_SUBMODULE(sequences_and_iterators, m) {
+ // test_sliceable
+ class Sliceable{
+ public:
+ Sliceable(int n): size(n) {}
+ int start,stop,step;
+ int size;
+ };
+ py::class_<Sliceable>(m,"Sliceable")
+ .def(py::init<int>())
+ .def("__getitem__",[](const Sliceable &s, py::slice slice) {
+ ssize_t start, stop, step, slicelength;
+ if (!slice.compute(s.size, &start, &stop, &step, &slicelength))
+ throw py::error_already_set();
+ int istart = static_cast<int>(start);
+ int istop = static_cast<int>(stop);
+ int istep = static_cast<int>(step);
+ return std::make_tuple(istart,istop,istep);
+ })
+ ;
// test_sequence
class Sequence {
diff --git a/ext/pybind11/tests/test_sequences_and_iterators.py b/ext/pybind11/tests/test_sequences_and_iterators.py
index 640ca07bd..6bd160640 100644
--- a/ext/pybind11/tests/test_sequences_and_iterators.py
+++ b/ext/pybind11/tests/test_sequences_and_iterators.py
@@ -33,6 +33,19 @@ def test_generalized_iterators():
next(it)
+def test_sliceable():
+ sliceable = m.Sliceable(100)
+ assert sliceable[::] == (0, 100, 1)
+ assert sliceable[10::] == (10, 100, 1)
+ assert sliceable[:10:] == (0, 10, 1)
+ assert sliceable[::10] == (0, 100, 10)
+ assert sliceable[-10::] == (90, 100, 1)
+ assert sliceable[:-10:] == (0, 90, 1)
+ assert sliceable[::-10] == (99, -1, -10)
+ assert sliceable[50:60:1] == (50, 60, 1)
+ assert sliceable[50:60:-1] == (50, 60, -1)
+
+
def test_sequence():
cstats = ConstructorStats.get(m.Sequence)
@@ -131,9 +144,9 @@ def test_python_iterator_in_cpp():
m.iterator_to_list(iter(bad_next_call, None))
assert str(excinfo.value) == "py::iterator::advance() should propagate errors"
- l = [1, None, 0, None]
- assert m.count_none(l) == 2
- assert m.find_none(l) is True
+ lst = [1, None, 0, None]
+ assert m.count_none(lst) == 2
+ assert m.find_none(lst) is True
assert m.count_nonzeros({"a": 0, "b": 1, "c": 2}) == 2
r = range(5)
diff --git a/ext/pybind11/tests/test_smart_ptr.cpp b/ext/pybind11/tests/test_smart_ptr.cpp
index dccb1e9be..87c9be8c2 100644
--- a/ext/pybind11/tests/test_smart_ptr.cpp
+++ b/ext/pybind11/tests/test_smart_ptr.cpp
@@ -19,7 +19,7 @@
// ref<T> is a wrapper for 'Object' which uses intrusive reference counting
// It is always possible to construct a ref<T> from an Object* pointer without
-// possible incosistencies, hence the 'true' argument at the end.
+// possible inconsistencies, hence the 'true' argument at the end.
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
// Make pybind11 aware of the non-standard getter member function
namespace pybind11 { namespace detail {
@@ -55,6 +55,35 @@ public:
};
PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
+// Simple custom holder that works like shared_ptr and has operator& overload
+// To obtain address of an instance of this holder pybind should use std::addressof
+// Attempt to get address via operator& may leads to segmentation fault
+template <typename T>
+class shared_ptr_with_addressof_operator {
+ std::shared_ptr<T> impl;
+public:
+ shared_ptr_with_addressof_operator( ) = default;
+ shared_ptr_with_addressof_operator(T* p) : impl(p) { }
+ T* get() const { return impl.get(); }
+ T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
+};
+PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>);
+
+// Simple custom holder that works like unique_ptr and has operator& overload
+// To obtain address of an instance of this holder pybind should use std::addressof
+// Attempt to get address via operator& may leads to segmentation fault
+template <typename T>
+class unique_ptr_with_addressof_operator {
+ std::unique_ptr<T> impl;
+public:
+ unique_ptr_with_addressof_operator() = default;
+ unique_ptr_with_addressof_operator(T* p) : impl(p) { }
+ T* get() const { return impl.get(); }
+ T* release_ptr() { return impl.release(); }
+ T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); }
+};
+PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>);
+
TEST_SUBMODULE(smart_ptr, m) {
@@ -98,6 +127,7 @@ TEST_SUBMODULE(smart_ptr, m) {
// Object managed by a std::shared_ptr<>
class MyObject2 {
public:
+ MyObject2(const MyObject2 &) = default;
MyObject2(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
virtual ~MyObject2() { print_destroyed(this); }
@@ -116,6 +146,7 @@ TEST_SUBMODULE(smart_ptr, m) {
// Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<>
class MyObject3 : public std::enable_shared_from_this<MyObject3> {
public:
+ MyObject3(const MyObject3 &) = default;
MyObject3(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; }
virtual ~MyObject3() { print_destroyed(this); }
@@ -155,6 +186,32 @@ TEST_SUBMODULE(smart_ptr, m) {
.def(py::init<int>())
.def_readwrite("value", &MyObject4::value);
+ // test_unique_deleter
+ // Object with std::unique_ptr<T, D> where D is not matching the base class
+ // Object with a protected destructor
+ class MyObject4a {
+ public:
+ MyObject4a(int i) {
+ value = i;
+ print_created(this);
+ };
+ int value;
+ protected:
+ virtual ~MyObject4a() { print_destroyed(this); }
+ };
+ py::class_<MyObject4a, std::unique_ptr<MyObject4a, py::nodelete>>(m, "MyObject4a")
+ .def(py::init<int>())
+ .def_readwrite("value", &MyObject4a::value);
+
+ // Object derived but with public destructor and no Deleter in default holder
+ class MyObject4b : public MyObject4a {
+ public:
+ MyObject4b(int i) : MyObject4a(i) { print_created(this); }
+ ~MyObject4b() { print_destroyed(this); }
+ };
+ py::class_<MyObject4b, MyObject4a>(m, "MyObject4b")
+ .def(py::init<int>());
+
// test_large_holder
class MyObject5 { // managed by huge_unique_ptr
public:
@@ -219,6 +276,8 @@ TEST_SUBMODULE(smart_ptr, m) {
// Issue #865: shared_from_this doesn't work with virtual inheritance
struct SharedFromThisVBase : std::enable_shared_from_this<SharedFromThisVBase> {
+ SharedFromThisVBase() = default;
+ SharedFromThisVBase(const SharedFromThisVBase &) = default;
virtual ~SharedFromThisVBase() = default;
};
struct SharedFromThisVirt : virtual SharedFromThisVBase {};
@@ -234,6 +293,41 @@ TEST_SUBMODULE(smart_ptr, m) {
py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder")
.def_static("make", []() { return custom_unique_ptr<C>(new C); });
+ // test_holder_with_addressof_operator
+ struct TypeForHolderWithAddressOf {
+ TypeForHolderWithAddressOf() { print_created(this); }
+ TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); }
+ TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) { print_move_created(this); }
+ ~TypeForHolderWithAddressOf() { print_destroyed(this); }
+ std::string toString() const {
+ return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]";
+ }
+ int value = 42;
+ };
+ using HolderWithAddressOf = shared_ptr_with_addressof_operator<TypeForHolderWithAddressOf>;
+ py::class_<TypeForHolderWithAddressOf, HolderWithAddressOf>(m, "TypeForHolderWithAddressOf")
+ .def_static("make", []() { return HolderWithAddressOf(new TypeForHolderWithAddressOf); })
+ .def("get", [](const HolderWithAddressOf &self) { return self.get(); })
+ .def("print_object_1", [](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); })
+ .def("print_object_2", [](HolderWithAddressOf obj) { py::print(obj.get()->toString()); })
+ .def("print_object_3", [](const HolderWithAddressOf &obj) { py::print(obj.get()->toString()); })
+ .def("print_object_4", [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); });
+
+ // test_move_only_holder_with_addressof_operator
+ struct TypeForMoveOnlyHolderWithAddressOf {
+ TypeForMoveOnlyHolderWithAddressOf(int value) : value{value} { print_created(this); }
+ ~TypeForMoveOnlyHolderWithAddressOf() { print_destroyed(this); }
+ std::string toString() const {
+ return "MoveOnlyHolderWithAddressOf[" + std::to_string(value) + "]";
+ }
+ int value;
+ };
+ using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>;
+ py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(m, "TypeForMoveOnlyHolderWithAddressOf")
+ .def_static("make", []() { return MoveOnlyHolderWithAddressOf(new TypeForMoveOnlyHolderWithAddressOf(0)); })
+ .def_readwrite("value", &TypeForMoveOnlyHolderWithAddressOf::value)
+ .def("print_object", [](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); });
+
// test_smart_ptr_from_default
struct HeldByDefaultHolder { };
py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder")
@@ -242,7 +336,9 @@ TEST_SUBMODULE(smart_ptr, m) {
// test_shared_ptr_gc
// #187: issue involving std::shared_ptr<> return value policy & garbage collection
- struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ };
+ struct ElementBase {
+ virtual ~ElementBase() { } /* Force creation of virtual table */
+ };
py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase");
struct ElementA : ElementBase {
diff --git a/ext/pybind11/tests/test_smart_ptr.py b/ext/pybind11/tests/test_smart_ptr.py
index 4dfe0036f..c6627043b 100644
--- a/ext/pybind11/tests/test_smart_ptr.py
+++ b/ext/pybind11/tests/test_smart_ptr.py
@@ -115,6 +115,27 @@ def test_unique_nodelete():
assert cstats.alive() == 1 # Leak, but that's intentional
+def test_unique_nodelete4a():
+ o = m.MyObject4a(23)
+ assert o.value == 23
+ cstats = ConstructorStats.get(m.MyObject4a)
+ assert cstats.alive() == 1
+ del o
+ assert cstats.alive() == 1 # Leak, but that's intentional
+
+
+def test_unique_deleter():
+ o = m.MyObject4b(23)
+ assert o.value == 23
+ cstats4a = ConstructorStats.get(m.MyObject4a)
+ assert cstats4a.alive() == 2 # Two because of previous test
+ cstats4b = ConstructorStats.get(m.MyObject4b)
+ assert cstats4b.alive() == 1
+ del o
+ assert cstats4a.alive() == 1 # Should now only be one leftover from previous test
+ assert cstats4b.alive() == 0 # Should be deleted
+
+
def test_large_holder():
o = m.MyObject5(5)
assert o.value == 5
@@ -203,11 +224,56 @@ def test_move_only_holder():
assert stats.alive() == 0
+def test_holder_with_addressof_operator():
+ # this test must not throw exception from c++
+ a = m.TypeForHolderWithAddressOf.make()
+ a.print_object_1()
+ a.print_object_2()
+ a.print_object_3()
+ a.print_object_4()
+
+ stats = ConstructorStats.get(m.TypeForHolderWithAddressOf)
+ assert stats.alive() == 1
+
+ np = m.TypeForHolderWithAddressOf.make()
+ assert stats.alive() == 2
+ del a
+ assert stats.alive() == 1
+ del np
+ assert stats.alive() == 0
+
+ b = m.TypeForHolderWithAddressOf.make()
+ c = b
+ assert b.get() is c.get()
+ assert stats.alive() == 1
+
+ del b
+ assert stats.alive() == 1
+
+ del c
+ assert stats.alive() == 0
+
+
+def test_move_only_holder_with_addressof_operator():
+ a = m.TypeForMoveOnlyHolderWithAddressOf.make()
+ a.print_object()
+
+ stats = ConstructorStats.get(m.TypeForMoveOnlyHolderWithAddressOf)
+ assert stats.alive() == 1
+
+ a.value = 42
+ assert a.value == 42
+
+ del a
+ assert stats.alive() == 0
+
+
def test_smart_ptr_from_default():
instance = m.HeldByDefaultHolder()
with pytest.raises(RuntimeError) as excinfo:
m.HeldByDefaultHolder.load_shared_ptr(instance)
- assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo)
+ assert "Unable to load a custom holder type from a " \
+ "default-holder instance" in str(excinfo.value)
def test_shared_ptr_gc():
diff --git a/ext/pybind11/tests/test_stl.cpp b/ext/pybind11/tests/test_stl.cpp
index 7d53e9c18..207c9fb2b 100644
--- a/ext/pybind11/tests/test_stl.cpp
+++ b/ext/pybind11/tests/test_stl.cpp
@@ -8,8 +8,12 @@
*/
#include "pybind11_tests.h"
+#include "constructor_stats.h"
#include <pybind11/stl.h>
+#include <vector>
+#include <string>
+
// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
#if PYBIND11_HAS_VARIANT
using std::variant;
@@ -32,6 +36,8 @@ struct visit_helper<boost::variant> {
}} // namespace pybind11::detail
#endif
+PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
+
/// Issue #528: templated constructor
struct TplCtorClass {
template <typename T> TplCtorClass(const T &) { }
@@ -57,6 +63,10 @@ TEST_SUBMODULE(stl, m) {
static std::vector<RValueCaster> lvv{2};
m.def("cast_ptr_vector", []() { return &lvv; });
+ // test_deque
+ m.def("cast_deque", []() { return std::deque<int>{1}; });
+ m.def("load_deque", [](const std::deque<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; });
@@ -235,4 +245,40 @@ TEST_SUBMODULE(stl, m) {
// test_stl_pass_by_pointer
m.def("stl_pass_by_pointer", [](std::vector<int>* v) { return *v; }, "v"_a=nullptr);
+
+ // #1258: pybind11/stl.h converts string to vector<string>
+ m.def("func_with_string_or_vector_string_arg_overload", [](std::vector<std::string>) { return 1; });
+ m.def("func_with_string_or_vector_string_arg_overload", [](std::list<std::string>) { return 2; });
+ m.def("func_with_string_or_vector_string_arg_overload", [](std::string) { return 3; });
+
+ class Placeholder {
+ public:
+ Placeholder() { print_created(this); }
+ Placeholder(const Placeholder &) = delete;
+ ~Placeholder() { print_destroyed(this); }
+ };
+ py::class_<Placeholder>(m, "Placeholder");
+
+ /// test_stl_vector_ownership
+ m.def("test_stl_ownership",
+ []() {
+ std::vector<Placeholder *> result;
+ result.push_back(new Placeholder());
+ return result;
+ },
+ py::return_value_policy::take_ownership);
+
+ m.def("array_cast_sequence", [](std::array<int, 3> x) { return x; });
+
+ /// test_issue_1561
+ struct Issue1561Inner { std::string data; };
+ struct Issue1561Outer { std::vector<Issue1561Inner> list; };
+
+ py::class_<Issue1561Inner>(m, "Issue1561Inner")
+ .def(py::init<std::string>())
+ .def_readwrite("data", &Issue1561Inner::data);
+
+ py::class_<Issue1561Outer>(m, "Issue1561Outer")
+ .def(py::init<>())
+ .def_readwrite("list", &Issue1561Outer::list);
}
diff --git a/ext/pybind11/tests/test_stl.py b/ext/pybind11/tests/test_stl.py
index db8515e7a..2335cb9fd 100644
--- a/ext/pybind11/tests/test_stl.py
+++ b/ext/pybind11/tests/test_stl.py
@@ -2,15 +2,16 @@ import pytest
from pybind11_tests import stl as m
from pybind11_tests import UserType
+from pybind11_tests import ConstructorStats
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))
+ lst = m.cast_vector()
+ assert lst == [1]
+ lst.append(2)
+ assert m.load_vector(lst)
+ assert m.load_vector(tuple(lst))
assert m.cast_bool_vector() == [True, False]
assert m.load_bool_vector([True, False])
@@ -22,11 +23,20 @@ def test_vector(doc):
assert m.cast_ptr_vector() == ["lvalue", "lvalue"]
+def test_deque(doc):
+ """std::deque <-> list"""
+ lst = m.cast_deque()
+ assert lst == [1]
+ lst.append(2)
+ assert m.load_deque(lst)
+ assert m.load_deque(tuple(lst))
+
+
def test_array(doc):
"""std::array <-> list"""
- l = m.cast_array()
- assert l == [1, 2]
- assert m.load_array(l)
+ lst = m.cast_array()
+ assert lst == [1, 2]
+ assert m.load_array(lst)
assert doc(m.cast_array) == "cast_array() -> List[int[2]]"
assert doc(m.load_array) == "load_array(arg0: List[int[2]]) -> bool"
@@ -34,9 +44,9 @@ def test_array(doc):
def test_valarray(doc):
"""std::valarray <-> list"""
- l = m.cast_valarray()
- assert l == [1, 4, 9]
- assert m.load_valarray(l)
+ lst = m.cast_valarray()
+ assert lst == [1, 4, 9]
+ assert m.load_valarray(lst)
assert doc(m.cast_valarray) == "cast_valarray() -> List[int]"
assert doc(m.load_valarray) == "load_valarray(arg0: List[int]) -> bool"
@@ -46,7 +56,9 @@ def test_map(doc):
"""std::map <-> dict"""
d = m.cast_map()
assert d == {"key": "value"}
+ assert "key" in d
d["key2"] = "value2"
+ assert "key2" in d
assert m.load_map(d)
assert doc(m.cast_map) == "cast_map() -> Dict[str, str]"
@@ -164,7 +176,7 @@ def test_stl_pass_by_pointer(msg):
m.stl_pass_by_pointer() # default value is `nullptr`
assert msg(excinfo.value) == """
stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported:
- 1. (v: List[int]=None) -> List[int]
+ 1. (v: List[int] = None) -> List[int]
Invoked with:
""" # noqa: E501 line too long
@@ -173,7 +185,7 @@ def test_stl_pass_by_pointer(msg):
m.stl_pass_by_pointer(None)
assert msg(excinfo.value) == """
stl_pass_by_pointer(): incompatible function arguments. The following argument types are supported:
- 1. (v: List[int]=None) -> List[int]
+ 1. (v: List[int] = None) -> List[int]
Invoked with: None
""" # noqa: E501 line too long
@@ -198,3 +210,32 @@ def test_missing_header_message():
with pytest.raises(TypeError) as excinfo:
cm.missing_header_return()
assert expected_message in str(excinfo.value)
+
+
+def test_function_with_string_and_vector_string_arg():
+ """Check if a string is NOT implicitly converted to a list, which was the
+ behavior before fix of issue #1258"""
+ assert m.func_with_string_or_vector_string_arg_overload(('A', 'B', )) == 2
+ assert m.func_with_string_or_vector_string_arg_overload(['A', 'B']) == 2
+ assert m.func_with_string_or_vector_string_arg_overload('A') == 3
+
+
+def test_stl_ownership():
+ cstats = ConstructorStats.get(m.Placeholder)
+ assert cstats.alive() == 0
+ r = m.test_stl_ownership()
+ assert len(r) == 1
+ del r
+ assert cstats.alive() == 0
+
+
+def test_array_cast_sequence():
+ assert m.array_cast_sequence((1, 2, 3)) == [1, 2, 3]
+
+
+def test_issue_1561():
+ """ check fix for issue #1561 """
+ bar = m.Issue1561Outer()
+ bar.list = [m.Issue1561Inner('bar')]
+ bar.list
+ assert bar.list[0].data == 'bar'
diff --git a/ext/pybind11/tests/test_stl_binders.py b/ext/pybind11/tests/test_stl_binders.py
index bf1aa674c..6d5a15983 100644
--- a/ext/pybind11/tests/test_stl_binders.py
+++ b/ext/pybind11/tests/test_stl_binders.py
@@ -11,6 +11,10 @@ def test_vector_int():
assert len(v_int) == 2
assert bool(v_int) is True
+ # test construction from a generator
+ v_int1 = m.VectorInt(x for x in range(5))
+ assert v_int1 == m.VectorInt([0, 1, 2, 3, 4])
+
v_int2 = m.VectorInt([0, 0])
assert v_int == v_int2
v_int2[1] = 1
@@ -33,6 +37,32 @@ def test_vector_int():
del v_int2[0]
assert v_int2 == m.VectorInt([0, 99, 2, 3])
+ v_int2.extend(m.VectorInt([4, 5]))
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5])
+
+ v_int2.extend([6, 7])
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7])
+
+ # test error handling, and that the vector is unchanged
+ with pytest.raises(RuntimeError):
+ v_int2.extend([8, 'a'])
+
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7])
+
+ # test extending from a generator
+ v_int2.extend(x for x in range(5))
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4])
+
+ # test negative indexing
+ assert v_int2[-1] == 4
+
+ # insert with negative index
+ v_int2.insert(-1, 88)
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88, 4])
+
+ # delete negative index
+ del v_int2[-1]
+ assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88])
# related to the PyPy's buffer protocol.
@pytest.unsupported_on_pypy
@@ -181,3 +211,25 @@ def test_noncopyable_containers():
vsum += v.value
assert vsum == 150
+
+
+def test_map_delitem():
+ mm = m.MapStringDouble()
+ mm['a'] = 1
+ mm['b'] = 2.5
+
+ assert list(mm) == ['a', 'b']
+ assert list(mm.items()) == [('a', 1), ('b', 2.5)]
+ del mm['a']
+ assert list(mm) == ['b']
+ assert list(mm.items()) == [('b', 2.5)]
+
+ um = m.UnorderedMapStringDouble()
+ um['ua'] = 1.1
+ um['ub'] = 2.6
+
+ assert sorted(list(um)) == ['ua', 'ub']
+ assert sorted(list(um.items())) == [('ua', 1.1), ('ub', 2.6)]
+ del um['ua']
+ assert sorted(list(um)) == ['ub']
+ assert sorted(list(um.items())) == [('ub', 2.6)]
diff --git a/ext/pybind11/tests/test_tagbased_polymorphic.cpp b/ext/pybind11/tests/test_tagbased_polymorphic.cpp
new file mode 100644
index 000000000..272e460c9
--- /dev/null
+++ b/ext/pybind11/tests/test_tagbased_polymorphic.cpp
@@ -0,0 +1,136 @@
+/*
+ tests/test_tagbased_polymorphic.cpp -- test of polymorphic_type_hook
+
+ Copyright (c) 2018 Hudson River Trading LLC <opensource@hudson-trading.com>
+
+ 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>
+
+struct Animal
+{
+ enum class Kind {
+ Unknown = 0,
+ Dog = 100, Labrador, Chihuahua, LastDog = 199,
+ Cat = 200, Panther, LastCat = 299
+ };
+ static const std::type_info* type_of_kind(Kind kind);
+ static std::string name_of_kind(Kind kind);
+
+ const Kind kind;
+ const std::string name;
+
+ protected:
+ Animal(const std::string& _name, Kind _kind)
+ : kind(_kind), name(_name)
+ {}
+};
+
+struct Dog : Animal
+{
+ Dog(const std::string& _name, Kind _kind = Kind::Dog) : Animal(_name, _kind) {}
+ std::string bark() const { return name_of_kind(kind) + " " + name + " goes " + sound; }
+ std::string sound = "WOOF!";
+};
+
+struct Labrador : Dog
+{
+ Labrador(const std::string& _name, int _excitement = 9001)
+ : Dog(_name, Kind::Labrador), excitement(_excitement) {}
+ int excitement;
+};
+
+struct Chihuahua : Dog
+{
+ Chihuahua(const std::string& _name) : Dog(_name, Kind::Chihuahua) { sound = "iyiyiyiyiyi"; }
+ std::string bark() const { return Dog::bark() + " and runs in circles"; }
+};
+
+struct Cat : Animal
+{
+ Cat(const std::string& _name, Kind _kind = Kind::Cat) : Animal(_name, _kind) {}
+ std::string purr() const { return "mrowr"; }
+};
+
+struct Panther : Cat
+{
+ Panther(const std::string& _name) : Cat(_name, Kind::Panther) {}
+ std::string purr() const { return "mrrrRRRRRR"; }
+};
+
+std::vector<std::unique_ptr<Animal>> create_zoo()
+{
+ std::vector<std::unique_ptr<Animal>> ret;
+ ret.emplace_back(new Labrador("Fido", 15000));
+
+ // simulate some new type of Dog that the Python bindings
+ // haven't been updated for; it should still be considered
+ // a Dog, not just an Animal.
+ ret.emplace_back(new Dog("Ginger", Dog::Kind(150)));
+
+ ret.emplace_back(new Chihuahua("Hertzl"));
+ ret.emplace_back(new Cat("Tiger", Cat::Kind::Cat));
+ ret.emplace_back(new Panther("Leo"));
+ return ret;
+}
+
+const std::type_info* Animal::type_of_kind(Kind kind)
+{
+ switch (kind) {
+ case Kind::Unknown: break;
+
+ case Kind::Dog: break;
+ case Kind::Labrador: return &typeid(Labrador);
+ case Kind::Chihuahua: return &typeid(Chihuahua);
+ case Kind::LastDog: break;
+
+ case Kind::Cat: break;
+ case Kind::Panther: return &typeid(Panther);
+ case Kind::LastCat: break;
+ }
+
+ if (kind >= Kind::Dog && kind <= Kind::LastDog) return &typeid(Dog);
+ if (kind >= Kind::Cat && kind <= Kind::LastCat) return &typeid(Cat);
+ return nullptr;
+}
+
+std::string Animal::name_of_kind(Kind kind)
+{
+ std::string raw_name = type_of_kind(kind)->name();
+ py::detail::clean_type_id(raw_name);
+ return raw_name;
+}
+
+namespace pybind11 {
+ template <typename itype>
+ struct polymorphic_type_hook<itype, detail::enable_if_t<std::is_base_of<Animal, itype>::value>>
+ {
+ static const void *get(const itype *src, const std::type_info*& type)
+ { type = src ? Animal::type_of_kind(src->kind) : nullptr; return src; }
+ };
+}
+
+TEST_SUBMODULE(tagbased_polymorphic, m) {
+ py::class_<Animal>(m, "Animal")
+ .def_readonly("name", &Animal::name);
+ py::class_<Dog, Animal>(m, "Dog")
+ .def(py::init<std::string>())
+ .def_readwrite("sound", &Dog::sound)
+ .def("bark", &Dog::bark);
+ py::class_<Labrador, Dog>(m, "Labrador")
+ .def(py::init<std::string, int>(), "name"_a, "excitement"_a = 9001)
+ .def_readwrite("excitement", &Labrador::excitement);
+ py::class_<Chihuahua, Dog>(m, "Chihuahua")
+ .def(py::init<std::string>())
+ .def("bark", &Chihuahua::bark);
+ py::class_<Cat, Animal>(m, "Cat")
+ .def(py::init<std::string>())
+ .def("purr", &Cat::purr);
+ py::class_<Panther, Cat>(m, "Panther")
+ .def(py::init<std::string>())
+ .def("purr", &Panther::purr);
+ m.def("create_zoo", &create_zoo);
+};
diff --git a/ext/pybind11/tests/test_tagbased_polymorphic.py b/ext/pybind11/tests/test_tagbased_polymorphic.py
new file mode 100644
index 000000000..2574d7de7
--- /dev/null
+++ b/ext/pybind11/tests/test_tagbased_polymorphic.py
@@ -0,0 +1,20 @@
+from pybind11_tests import tagbased_polymorphic as m
+
+
+def test_downcast():
+ zoo = m.create_zoo()
+ assert [type(animal) for animal in zoo] == [
+ m.Labrador, m.Dog, m.Chihuahua, m.Cat, m.Panther
+ ]
+ assert [animal.name for animal in zoo] == [
+ "Fido", "Ginger", "Hertzl", "Tiger", "Leo"
+ ]
+ zoo[1].sound = "woooooo"
+ assert [dog.bark() for dog in zoo[:3]] == [
+ "Labrador Fido goes WOOF!",
+ "Dog Ginger goes woooooo",
+ "Chihuahua Hertzl goes iyiyiyiyiyi and runs in circles"
+ ]
+ assert [cat.purr() for cat in zoo[3:]] == ["mrowr", "mrrrRRRRRR"]
+ zoo[0].excitement -= 1000
+ assert zoo[0].excitement == 14000
diff --git a/ext/pybind11/tests/test_union.cpp b/ext/pybind11/tests/test_union.cpp
new file mode 100644
index 000000000..7b98ea216
--- /dev/null
+++ b/ext/pybind11/tests/test_union.cpp
@@ -0,0 +1,22 @@
+/*
+ tests/test_class.cpp -- test py::class_ definitions and basic functionality
+
+ Copyright (c) 2019 Roland Dreier <roland.dreier@gmail.com>
+
+ 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(union_, m) {
+ union TestUnion {
+ int value_int;
+ unsigned value_uint;
+ };
+
+ py::class_<TestUnion>(m, "TestUnion")
+ .def(py::init<>())
+ .def_readonly("as_int", &TestUnion::value_int)
+ .def_readwrite("as_uint", &TestUnion::value_uint);
+}
diff --git a/ext/pybind11/tests/test_union.py b/ext/pybind11/tests/test_union.py
new file mode 100644
index 000000000..e1866e701
--- /dev/null
+++ b/ext/pybind11/tests/test_union.py
@@ -0,0 +1,8 @@
+from pybind11_tests import union_ as m
+
+
+def test_union():
+ instance = m.TestUnion()
+
+ instance.as_uint = 10
+ assert instance.as_int == 10
diff --git a/ext/pybind11/tests/test_virtual_functions.cpp b/ext/pybind11/tests/test_virtual_functions.cpp
index 953b390b8..ccf018d99 100644
--- a/ext/pybind11/tests/test_virtual_functions.cpp
+++ b/ext/pybind11/tests/test_virtual_functions.cpp
@@ -10,6 +10,7 @@
#include "pybind11_tests.h"
#include "constructor_stats.h"
#include <pybind11/functional.h>
+#include <thread>
/* This is an example class that we'll want to be able to extend from Python */
class ExampleVirt {
@@ -17,7 +18,7 @@ public:
ExampleVirt(int state) : state(state) { print_created(this, state); }
ExampleVirt(const ExampleVirt &e) : state(e.state) { print_copy_created(this); }
ExampleVirt(ExampleVirt &&e) : state(e.state) { print_move_created(this); e.state = 0; }
- ~ExampleVirt() { print_destroyed(this); }
+ virtual ~ExampleVirt() { print_destroyed(this); }
virtual int run(int value) {
py::print("Original implementation of "
@@ -128,6 +129,7 @@ private:
class NCVirt {
public:
+ virtual ~NCVirt() { }
virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); }
virtual Movable get_movable(int a, int b) = 0;
@@ -157,6 +159,28 @@ struct DispatchIssue : Base {
}
};
+static void test_gil() {
+ {
+ py::gil_scoped_acquire lock;
+ py::print("1st lock acquired");
+
+ }
+
+ {
+ py::gil_scoped_acquire lock;
+ py::print("2nd lock acquired");
+ }
+
+}
+
+static void test_gil_from_thread() {
+ py::gil_scoped_release release;
+
+ std::thread t(test_gil);
+ t.join();
+}
+
+
// Forward declaration (so that we can put the main tests here; the inherited virtual approaches are
// rather long).
void initialize_inherited_virtuals(py::module &m);
@@ -207,7 +231,9 @@ TEST_SUBMODULE(virtual_functions, m) {
void f() override {
py::print("PyA.f()");
- PYBIND11_OVERLOAD(void, A, f);
+ // This convolution just gives a `void`, but tests that PYBIND11_TYPE() works to protect
+ // a type containing a ,
+ PYBIND11_OVERLOAD(PYBIND11_TYPE(typename std::enable_if<true, void>::type), A, f);
}
};
@@ -249,7 +275,7 @@ TEST_SUBMODULE(virtual_functions, m) {
m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
// test_override_ref
- // #392/397: overridding reference-returning functions
+ // #392/397: overriding reference-returning functions
class OverrideTest {
public:
struct A { std::string value = "hi"; };
@@ -414,7 +440,6 @@ public:
};
*/
-
void initialize_inherited_virtuals(py::module &m) {
// test_inherited_virtuals
@@ -447,4 +472,8 @@ void initialize_inherited_virtuals(py::module &m) {
py::class_<D_Tpl, C_Tpl, PyB_Tpl<D_Tpl>>(m, "D_Tpl")
.def(py::init<>());
+
+ // Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
+ m.def("test_gil", &test_gil);
+ m.def("test_gil_from_thread", &test_gil_from_thread);
};
diff --git a/ext/pybind11/tests/test_virtual_functions.py b/ext/pybind11/tests/test_virtual_functions.py
index b91ebfa3e..5ce9abd35 100644
--- a/ext/pybind11/tests/test_virtual_functions.py
+++ b/ext/pybind11/tests/test_virtual_functions.py
@@ -227,7 +227,7 @@ def test_dispatch_issue(msg):
def test_override_ref():
- """#392/397: overridding reference-returning functions"""
+ """#392/397: overriding reference-returning functions"""
o = m.OverrideTest("asdf")
# Not allowed (see associated .cpp comment)
@@ -369,3 +369,9 @@ def test_inherited_virtuals():
assert obj.unlucky_number() == -7
assert obj.lucky_number() == -1.375
assert obj.say_everything() == "BT -7"
+
+
+def test_issue_1454():
+ # Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
+ m.test_gil()
+ m.test_gil_from_thread()