Upgrade python/pybind11 to v2.5.0 am: 7c3ac77d79 am: 3396051161 am: 797ea7b848 am: da4609218b
Change-Id: Icd8e7e5a6ccab738a7c49a9d71cdcaa347f7e1ba
diff --git a/.travis.yml b/.travis.yml
index 28f35f9..d81cd8c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,8 @@
- PY_CMD=python3
- $PY_CMD -m pip install --user --upgrade pip wheel setuptools
install:
- - $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe flake8 pep8-naming pytest
+ # breathe 4.14 doesn't work with bit fields. See https://github.com/michaeljones/breathe/issues/462
+ - $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe==4.13.1 flake8 pep8-naming pytest
- curl -fsSL https://sourceforge.net/projects/doxygen/files/rel-1.8.15/doxygen-1.8.15.linux.bin.tar.gz/download | tar xz
- export PATH="$PWD/doxygen-1.8.15/bin:$PATH"
script:
@@ -32,8 +33,7 @@
- |
# Barebones build
cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) .
- make pytest -j 2
- make cpptest -j 2
+ make pytest -j 2 && make cpptest -j 2
# The following are regular test configurations, including optional dependencies.
# With regard to each other they differ in Python version, C++ standard and compiler.
- os: linux
@@ -131,15 +131,14 @@
- |
# Barebones build
cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) .
- make pytest -j 2
- make cpptest -j 2
+ make pytest -j 2 && make cpptest -j 2
- os: osx
name: Python 2.7, c++14, AppleClang 7.3, CMake test
osx_image: xcode7.3
env: PYTHON=2.7 CPP=14 CLANG CMAKE=1
- os: osx
name: Python 3.7, c++14, AppleClang 9, Debug build
- osx_image: xcode9
+ osx_image: xcode9.4
env: PYTHON=3.7 CPP=14 CLANG DEBUG=1
# Test a PyPy 2.7 build
- os: linux
@@ -220,7 +219,7 @@
PY_CMD=python$PYTHON
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
if [ "$PY" = "3" ]; then
- brew update && brew upgrade python
+ brew update && brew unlink python@2 && brew upgrade python
else
curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user
fi
diff --git a/METADATA b/METADATA
index 53e2881..bbcbe08 100644
--- a/METADATA
+++ b/METADATA
@@ -1,19 +1,19 @@
name: "pybind11"
-description:
- "pybind11 is a lightweight header-only library that exposes C++ types in "
- "Python and vice versa, mainly to create Python bindings of existing C++ "
- "code."
+description: "pybind11 is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code."
third_party {
url {
type: HOMEPAGE
value: "https://pybind11.readthedocs.io/en/stable/"
}
url {
- type: GIT
+ type: GIT
value: "https://github.com/pybind/pybind11.git"
}
- version: "v2.4.3"
- last_upgrade_date { year: 2019 month: 11 day: 4 }
+ version: "v2.5.0"
license_type: NOTICE
+ last_upgrade_date {
+ year: 2020
+ month: 3
+ day: 31
+ }
}
-
diff --git a/docs/advanced/exceptions.rst b/docs/advanced/exceptions.rst
index 75ac24a..75ad7f7 100644
--- a/docs/advanced/exceptions.rst
+++ b/docs/advanced/exceptions.rst
@@ -28,6 +28,8 @@
+--------------------------------------+--------------------------------------+
| :class:`std::range_error` | ``ValueError`` |
+--------------------------------------+--------------------------------------+
+| :class:`std::overflow_error` | ``OverflowError`` |
++--------------------------------------+--------------------------------------+
| :class:`pybind11::stop_iteration` | ``StopIteration`` (used to implement |
| | custom iterators) |
+--------------------------------------+--------------------------------------+
diff --git a/docs/basics.rst b/docs/basics.rst
index 447250e..7bf4d42 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -164,7 +164,7 @@
Keyword arguments
=================
-With a simple modification code, it is possible to inform Python about the
+With a simple code modification, it is possible to inform Python about the
names of the arguments ("i" and "j" in this case).
.. code-block:: cpp
diff --git a/docs/changelog.rst b/docs/changelog.rst
index d65c2d8..2def2b0 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -6,6 +6,51 @@
Starting with version 1.8.0, pybind11 releases use a `semantic versioning
<http://semver.org>`_ policy.
+v2.5.0 (Mar 31, 2020)
+-----------------------------------------------------
+
+* Use C++17 fold expressions in type casters, if available. This can
+ improve performance during overload resolution when functions have
+ multiple arguments.
+ `#2043 <https://github.com/pybind/pybind11/pull/2043>`_.
+
+* Changed include directory resolution in ``pybind11/__init__.py``
+ and installation in ``setup.py``. This fixes a number of open issues
+ where pybind11 headers could not be found in certain environments.
+ `#1995 <https://github.com/pybind/pybind11/pull/1995>`_.
+
+* C++20 ``char8_t`` and ``u8string`` support. `#2026
+ <https://github.com/pybind/pybind11/pull/2026>`_.
+
+* CMake: search for Python 3.9. `bb9c91
+ <https://github.com/pybind/pybind11/commit/bb9c91>`_.
+
+* Fixes for MSYS-based build environments.
+ `#2087 <https://github.com/pybind/pybind11/pull/2087>`_,
+ `#2053 <https://github.com/pybind/pybind11/pull/2053>`_.
+
+* STL bindings for ``std::vector<...>::clear``. `#2074
+ <https://github.com/pybind/pybind11/pull/2074>`_.
+
+* Read-only flag for ``py::buffer``. `#1466
+ <https://github.com/pybind/pybind11/pull/1466>`_.
+
+* Exception handling during module initialization.
+ `bf2b031 <https://github.com/pybind/pybind11/commit/bf2b031>`_.
+
+* Support linking against a CPython debug build.
+ `#2025 <https://github.com/pybind/pybind11/pull/2025>`_.
+
+* Fixed issues involving the availability and use of aligned ``new`` and
+ ``delete``. `#1988 <https://github.com/pybind/pybind11/pull/1988>`_,
+ `759221 <https://github.com/pybind/pybind11/commit/759221>`_.
+
+* Fixed a resource leak upon interpreter shutdown.
+ `#2020 <https://github.com/pybind/pybind11/pull/2020>`_.
+
+* Fixed error handling in the boolean caster.
+ `#1976 <https://github.com/pybind/pybind11/pull/1976>`_.
+
v2.4.3 (Oct 15, 2019)
-----------------------------------------------------
diff --git a/docs/conf.py b/docs/conf.py
index c438546..fa6332d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -61,9 +61,9 @@
# built documents.
#
# The short X.Y version.
-version = '2.4'
+version = '2.5'
# The full version, including alpha/beta/rc tags.
-release = '2.4.3'
+release = '2.5.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/faq.rst b/docs/faq.rst
index 93ccf10..4d491fb 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -248,6 +248,33 @@
structures with incompatible ABIs, and so on. pybind11 is very careful not
to make these types of mistakes.
+How can I properly handle Ctrl-C in long-running functions?
+===========================================================
+
+Ctrl-C is received by the Python interpreter, and holds it until the GIL
+is released, so a long-running function won't be interrupted.
+
+To interrupt from inside your function, you can use the ``PyErr_CheckSignals()``
+function, that will tell if a signal has been raised on the Python side. This
+function merely checks a flag, so its impact is negligible. When a signal has
+been received, you must either explicitly interrupt execution by throwing
+``py::error_already_set`` (which will propagate the existing
+``KeyboardInterrupt``), or clear the error (which you usually will not want):
+
+.. code-block:: cpp
+
+ PYBIND11_MODULE(example, m)
+ {
+ m.def("long running_func", []()
+ {
+ for (;;) {
+ if (PyErr_CheckSignals() != 0)
+ throw py::error_already_set();
+ // Long running iteration
+ }
+ });
+ }
+
Inconsistent detection of Python version in CMake and pybind11
==============================================================
diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h
index 9f072fa..1f4115a 100644
--- a/include/pybind11/buffer_info.h
+++ b/include/pybind11/buffer_info.h
@@ -21,14 +21,15 @@
std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format()
ssize_t ndim = 0; // Number of dimensions
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
- std::vector<ssize_t> strides; // Number of entries between adjacent entries (for each per dimension)
+ std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
+ bool readonly = false; // flag to indicate if the underlying storage may be written to
buffer_info() { }
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
- detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
+ detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
- shape(std::move(shape_in)), strides(std::move(strides_in)) {
+ shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
for (size_t i = 0; i < (size_t) ndim; ++i)
@@ -36,19 +37,23 @@
}
template <typename T>
- buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
- : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in)) { }
+ buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
+ : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { }
- buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size)
- : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
+ buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false)
+ : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { }
template <typename T>
- buffer_info(T *ptr, ssize_t size)
- : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size) { }
+ buffer_info(T *ptr, ssize_t size, bool readonly=false)
+ : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { }
+
+ template <typename T>
+ buffer_info(const T *ptr, ssize_t size, bool readonly=true)
+ : buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { }
explicit buffer_info(Py_buffer *view, bool ownview = true)
: buffer_info(view->buf, view->itemsize, view->format, view->ndim,
- {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) {
+ {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}, view->readonly) {
this->view = view;
this->ownview = ownview;
}
@@ -70,6 +75,7 @@
strides = std::move(rhs.strides);
std::swap(view, rhs.view);
std::swap(ownview, rhs.ownview);
+ readonly = rhs.readonly;
return *this;
}
@@ -81,8 +87,8 @@
struct private_ctr_tag { };
buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
- detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in)
- : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { }
+ detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly)
+ : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
Py_buffer *view = nullptr;
bool ownview = false;
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 605acb3..a0b4d1b 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -32,6 +32,10 @@
#include <string_view>
#endif
+#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
+# define PYBIND11_HAS_U8STRING
+#endif
+
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
NAMESPACE_BEGIN(detail)
@@ -533,9 +537,17 @@
case return_value_policy::copy:
if (copy_constructor)
valueptr = copy_constructor(src);
- else
- throw cast_error("return_value_policy = copy, but the "
- "object is non-copyable!");
+ else {
+#if defined(NDEBUG)
+ throw cast_error("return_value_policy = copy, but type is "
+ "non-copyable! (compile in debug mode for details)");
+#else
+ std::string type_name(tinfo->cpptype->name());
+ detail::clean_type_id(type_name);
+ throw cast_error("return_value_policy = copy, but type " +
+ type_name + " is non-copyable!");
+#endif
+ }
wrapper->owned = true;
break;
@@ -544,9 +556,18 @@
valueptr = move_constructor(src);
else if (copy_constructor)
valueptr = copy_constructor(src);
- else
- throw cast_error("return_value_policy = move, but the "
- "object is neither movable nor copyable!");
+ else {
+#if defined(NDEBUG)
+ throw cast_error("return_value_policy = move, but type is neither "
+ "movable nor copyable! "
+ "(compile in debug mode for details)");
+#else
+ std::string type_name(tinfo->cpptype->name());
+ detail::clean_type_id(type_name);
+ throw cast_error("return_value_policy = move, but type " +
+ type_name + " is neither movable nor copyable!");
+#endif
+ }
wrapper->owned = true;
break;
@@ -574,10 +595,10 @@
if (type->operator_new) {
vptr = type->operator_new(type->type_size);
} else {
- #if defined(PYBIND11_CPP17)
+ #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
vptr = ::operator new(type->type_size,
- (std::align_val_t) type->type_align);
+ std::align_val_t(type->type_align));
else
#endif
vptr = ::operator new(type->type_size);
@@ -780,12 +801,20 @@
negation<std::is_same<Container, typename Container::value_type>>
>::value>> : is_copy_constructible<typename Container::value_type> {};
-#if !defined(PYBIND11_CPP17)
-// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the
-// two types aren't themselves copy constructible).
+// Likewise for std::pair
+// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
+// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T2>>
: all_of<is_copy_constructible<T1>, is_copy_constructible<T2>> {};
-#endif
+
+// The same problems arise with std::is_copy_assignable, so we use the same workaround.
+template <typename T, typename SFINAE = void> struct is_copy_assignable : std::is_copy_assignable<T> {};
+template <typename Container> struct is_copy_assignable<Container, enable_if_t<all_of<
+ std::is_copy_assignable<Container>,
+ std::is_same<typename Container::value_type &, typename Container::reference>
+ >::value>> : is_copy_assignable<typename Container::value_type> {};
+template <typename T1, typename T2> struct is_copy_assignable<std::pair<T1, T2>>
+ : all_of<is_copy_assignable<T1>, is_copy_assignable<T2>> {};
NAMESPACE_END(detail)
@@ -963,6 +992,9 @@
template <typename CharT> using is_std_char_type = any_of<
std::is_same<CharT, char>, /* std::string */
+#if defined(PYBIND11_HAS_U8STRING)
+ std::is_same<CharT, char8_t>, /* std::u8string */
+#endif
std::is_same<CharT, char16_t>, /* std::u16string */
std::is_same<CharT, char32_t>, /* std::u32string */
std::is_same<CharT, wchar_t> /* std::wstring */
@@ -1147,6 +1179,8 @@
if (res == 0 || res == 1) {
value = (bool) res;
return true;
+ } else {
+ PyErr_Clear();
}
}
return false;
@@ -1164,6 +1198,9 @@
// Simplify life by being able to assume standard char sizes (the standard only guarantees
// minimums, but Python requires exact sizes)
static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1, "Unsupported char size != 1");
+#if defined(PYBIND11_HAS_U8STRING)
+ static_assert(!std::is_same<CharT, char8_t>::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1");
+#endif
static_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2");
static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4");
// wchar_t can be either 16 bits (Windows) or 32 (everywhere else)
@@ -1182,7 +1219,7 @@
#if PY_MAJOR_VERSION >= 3
return load_bytes(load_src);
#else
- if (sizeof(CharT) == 1) {
+ if (std::is_same<CharT, char>::value) {
return load_bytes(load_src);
}
@@ -1242,7 +1279,7 @@
// without any encoding/decoding attempt). For other C++ char sizes this is a no-op.
// which supports loading a unicode from a str, doesn't take this path.
template <typename C = CharT>
- bool load_bytes(enable_if_t<sizeof(C) == 1, handle> src) {
+ bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src) {
if (PYBIND11_BYTES_CHECK(src.ptr())) {
// We were passed a Python 3 raw bytes; accept it into a std::string or char*
// without any encoding attempt.
@@ -1257,7 +1294,7 @@
}
template <typename C = CharT>
- bool load_bytes(enable_if_t<sizeof(C) != 1, handle>) { return false; }
+ bool load_bytes(enable_if_t<!std::is_same<C, char>::value, handle>) { return false; }
};
template <typename CharT, class Traits, class Allocator>
@@ -1395,9 +1432,14 @@
template <size_t... Is>
bool load_impl(const sequence &seq, bool convert, index_sequence<Is...>) {
+#ifdef __cpp_fold_expressions
+ if ((... || !std::get<Is>(subcasters).load(seq[Is], convert)))
+ return false;
+#else
for (bool r : {std::get<Is>(subcasters).load(seq[Is], convert)...})
if (!r)
return false;
+#endif
return true;
}
@@ -1924,14 +1966,19 @@
template <size_t... Is>
bool load_impl_sequence(function_call &call, index_sequence<Is...>) {
+#ifdef __cpp_fold_expressions
+ if ((... || !std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is])))
+ return false;
+#else
for (bool r : {std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is])...})
if (!r)
return false;
+#endif
return true;
}
template <typename Return, typename Func, size_t... Is, typename Guard>
- Return call_impl(Func &&f, index_sequence<Is...>, Guard &&) {
+ Return call_impl(Func &&f, index_sequence<Is...>, Guard &&) && {
return std::forward<Func>(f)(cast_op<Args>(std::move(std::get<Is>(argcasters)))...);
}
diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h
index 230ae81..edfa7de 100644
--- a/include/pybind11/detail/class.h
+++ b/include/pybind11/detail/class.h
@@ -491,6 +491,13 @@
view->len = view->itemsize;
for (auto s : info->shape)
view->len *= s;
+ view->readonly = info->readonly;
+ if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
+ if (view)
+ view->obj = nullptr;
+ PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
+ return -1;
+ }
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
view->format = const_cast<char *>(info->format.c_str());
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h
index 6da5470..e53f502 100644
--- a/include/pybind11/detail/common.h
+++ b/include/pybind11/detail/common.h
@@ -93,8 +93,8 @@
#endif
#define PYBIND11_VERSION_MAJOR 2
-#define PYBIND11_VERSION_MINOR 4
-#define PYBIND11_VERSION_PATCH 3
+#define PYBIND11_VERSION_MINOR 5
+#define PYBIND11_VERSION_PATCH 0
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER)
@@ -103,7 +103,7 @@
# endif
# pragma warning(push)
# pragma warning(disable: 4510 4610 4512 4005)
-# if defined(_DEBUG)
+# if defined(_DEBUG) && !defined(Py_DEBUG)
# define PYBIND11_DEBUG_MARKER
# undef _DEBUG
# endif
@@ -113,6 +113,9 @@
#include <frameobject.h>
#include <pythread.h>
+/* Python #defines overrides on all sorts of core functions, which
+ tends to weak havok in C++ codebases that expect these to work
+ like regular functions (potentially with several overloads) */
#if defined(isalnum)
# undef isalnum
# undef isalpha
@@ -123,6 +126,10 @@
# undef toupper
#endif
+#if defined(copysign)
+# undef copysign
+#endif
+
#if defined(_MSC_VER)
# if defined(PYBIND11_DEBUG_MARKER)
# define _DEBUG
@@ -211,6 +218,8 @@
#define PYBIND11_STRINGIFY(x) #x
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
#define PYBIND11_CONCAT(first, second) first##second
+#define PYBIND11_ENSURE_INTERNALS_READY \
+ pybind11::detail::get_internals();
#define PYBIND11_CHECK_PYTHON_VERSION \
{ \
@@ -257,6 +266,7 @@
static PyObject *pybind11_init(); \
PYBIND11_PLUGIN_IMPL(name) { \
PYBIND11_CHECK_PYTHON_VERSION \
+ PYBIND11_ENSURE_INTERNALS_READY \
try { \
return pybind11_init(); \
} PYBIND11_CATCH_INIT_EXCEPTIONS \
@@ -284,6 +294,7 @@
static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \
PYBIND11_PLUGIN_IMPL(name) { \
PYBIND11_CHECK_PYTHON_VERSION \
+ PYBIND11_ENSURE_INTERNALS_READY \
auto m = pybind11::module(PYBIND11_TOSTRING(name)); \
try { \
PYBIND11_CONCAT(pybind11_init_, name)(m); \
@@ -674,6 +685,7 @@
PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
+PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError)
PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h
index 067780c..6224dfb 100644
--- a/include/pybind11/detail/internals.h
+++ b/include/pybind11/detail/internals.h
@@ -25,6 +25,7 @@
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
+# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
#else
// Usually an int but a long on Cygwin64 with Python 3.x
# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
@@ -43,6 +44,7 @@
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
PyThread_set_key_value((key), (value))
# endif
+# define PYBIND11_TLS_FREE(key) (void)key
#endif
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
@@ -108,6 +110,16 @@
#if defined(WITH_THREAD)
PYBIND11_TLS_KEY_INIT(tstate);
PyInterpreterState *istate = nullptr;
+ ~internals() {
+ // This destructor is called *after* Py_Finalize() in finalize_interpreter().
+ // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called.
+ // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing.
+ // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
+ // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither
+ // of those have anything to do with CPython internals.
+ // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
+ PYBIND11_TLS_FREE(tstate);
+ }
#endif
};
@@ -138,7 +150,7 @@
};
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
-#define PYBIND11_INTERNALS_VERSION 3
+#define PYBIND11_INTERNALS_VERSION 4
/// On MSVC, debug and release builds are not ABI-compatible!
#if defined(_MSC_VER) && defined(_DEBUG)
@@ -211,6 +223,7 @@
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
+ } catch (const std::overflow_error &e) { PyErr_SetString(PyExc_OverflowError, e.what()); return;
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return;
} catch (...) {
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h
index 7265588..f814c78 100644
--- a/include/pybind11/embed.h
+++ b/include/pybind11/embed.h
@@ -18,11 +18,13 @@
#if PY_MAJOR_VERSION >= 3
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
+ extern "C" PyObject *pybind11_init_impl_##name(); \
extern "C" PyObject *pybind11_init_impl_##name() { \
return pybind11_init_wrapper_##name(); \
}
#else
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
+ extern "C" void pybind11_init_impl_##name(); \
extern "C" void pybind11_init_impl_##name() { \
pybind11_init_wrapper_##name(); \
}
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index c623705..d95d61f 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -1003,14 +1003,21 @@
inline void call_operator_delete(void *p, size_t s, size_t a) {
(void)s; (void)a;
-#if defined(PYBIND11_CPP17)
- if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
- ::operator delete(p, s, std::align_val_t(a));
- else
+ #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
+ if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+ #ifdef __cpp_sized_deallocation
+ ::operator delete(p, s, std::align_val_t(a));
+ #else
+ ::operator delete(p, std::align_val_t(a));
+ #endif
+ return;
+ }
+ #endif
+ #ifdef __cpp_sized_deallocation
::operator delete(p, s);
-#else
- ::operator delete(p);
-#endif
+ #else
+ ::operator delete(p);
+ #endif
}
NAMESPACE_END(detail)
diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h
index 96eab96..4003d69 100644
--- a/include/pybind11/pytypes.h
+++ b/include/pybind11/pytypes.h
@@ -1345,7 +1345,7 @@
buf.strides = py_strides.data();
buf.shape = py_shape.data();
buf.suboffsets = nullptr;
- buf.readonly = false;
+ buf.readonly = info.readonly;
buf.internal = nullptr;
m_ptr = PyMemoryView_FromBuffer(&buf);
diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h
index d3adaed..da233ec 100644
--- a/include/pybind11/stl_bind.h
+++ b/include/pybind11/stl_bind.h
@@ -136,6 +136,13 @@
return v.release();
}));
+ cl.def("clear",
+ [](Vector &v) {
+ v.clear();
+ },
+ "Clear the contents"
+ );
+
cl.def("extend",
[](Vector &v, const Vector &src) {
v.insert(v.end(), src.begin(), src.end());
@@ -512,7 +519,7 @@
// Map assignment when copy-assignable: just copy the value
template <typename Map, typename Class_>
-void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
+void map_assignment(enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type;
@@ -528,7 +535,7 @@
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
template<typename Map, typename Class_>
void map_assignment(enable_if_t<
- !std::is_copy_assignable<typename Map::mapped_type>::value &&
+ !is_copy_assignable<typename Map::mapped_type>::value &&
is_copy_constructible<typename Map::mapped_type>::value,
Class_> &cl) {
using KeyType = typename Map::key_type;
diff --git a/pybind11/__init__.py b/pybind11/__init__.py
index c625e8c..4b1de3e 100644
--- a/pybind11/__init__.py
+++ b/pybind11/__init__.py
@@ -2,35 +2,11 @@
def get_include(user=False):
- from distutils.dist import Distribution
import os
- import sys
-
- # Are we running in a virtual environment?
- virtualenv = hasattr(sys, 'real_prefix') or \
- sys.prefix != getattr(sys, "base_prefix", sys.prefix)
-
- # Are we running in a conda environment?
- conda = os.path.exists(os.path.join(sys.prefix, 'conda-meta'))
-
- if virtualenv:
- return os.path.join(sys.prefix, 'include', 'site',
- 'python' + sys.version[:3])
- elif conda:
- if os.name == 'nt':
- return os.path.join(sys.prefix, 'Library', 'include')
- else:
- return os.path.join(sys.prefix, 'include')
+ d = os.path.dirname(__file__)
+ if os.path.exists(os.path.join(d, "include")):
+ # Package is installed
+ return os.path.join(d, "include")
else:
- dist = Distribution({'name': 'pybind11'})
- dist.parse_config_files()
-
- dist_cobj = dist.get_command_obj('install', create=True)
-
- # Search for packages in user's home directory?
- if user:
- dist_cobj.user = user
- dist_cobj.prefix = ""
- dist_cobj.finalize_options()
-
- return os.path.dirname(dist_cobj.install_headers)
+ # Package is from a source directory
+ return os.path.join(os.path.dirname(d), "include")
diff --git a/pybind11/__main__.py b/pybind11/__main__.py
index 9ef8378..89b263a 100644
--- a/pybind11/__main__.py
+++ b/pybind11/__main__.py
@@ -10,8 +10,7 @@
def print_includes():
dirs = [sysconfig.get_path('include'),
sysconfig.get_path('platinclude'),
- get_include(),
- get_include(True)]
+ get_include()]
# Make unique but preserve order
unique_dirs = []
diff --git a/pybind11/_version.py b/pybind11/_version.py
index 2709cc5..8d5aa5c 100644
--- a/pybind11/_version.py
+++ b/pybind11/_version.py
@@ -1,2 +1,2 @@
-version_info = (2, 4, 3)
+version_info = (2, 5, 0)
__version__ = '.'.join(map(str, version_info))
diff --git a/setup.py b/setup.py
index f677f2a..473ea1e 100644
--- a/setup.py
+++ b/setup.py
@@ -4,40 +4,43 @@
from setuptools import setup
from distutils.command.install_headers import install_headers
+from distutils.command.build_py import build_py
from pybind11 import __version__
import os
+package_data = [
+ 'include/pybind11/detail/class.h',
+ 'include/pybind11/detail/common.h',
+ 'include/pybind11/detail/descr.h',
+ 'include/pybind11/detail/init.h',
+ 'include/pybind11/detail/internals.h',
+ 'include/pybind11/detail/typeid.h',
+ 'include/pybind11/attr.h',
+ 'include/pybind11/buffer_info.h',
+ 'include/pybind11/cast.h',
+ 'include/pybind11/chrono.h',
+ 'include/pybind11/common.h',
+ 'include/pybind11/complex.h',
+ 'include/pybind11/eigen.h',
+ 'include/pybind11/embed.h',
+ 'include/pybind11/eval.h',
+ 'include/pybind11/functional.h',
+ 'include/pybind11/iostream.h',
+ 'include/pybind11/numpy.h',
+ 'include/pybind11/operators.h',
+ 'include/pybind11/options.h',
+ 'include/pybind11/pybind11.h',
+ 'include/pybind11/pytypes.h',
+ 'include/pybind11/stl.h',
+ 'include/pybind11/stl_bind.h',
+]
+
# Prevent installation of pybind11 headers by setting
# PYBIND11_USE_CMAKE.
if os.environ.get('PYBIND11_USE_CMAKE'):
headers = []
else:
- headers = [
- 'include/pybind11/detail/class.h',
- 'include/pybind11/detail/common.h',
- 'include/pybind11/detail/descr.h',
- 'include/pybind11/detail/init.h',
- 'include/pybind11/detail/internals.h',
- 'include/pybind11/detail/typeid.h',
- 'include/pybind11/attr.h',
- 'include/pybind11/buffer_info.h',
- 'include/pybind11/cast.h',
- 'include/pybind11/chrono.h',
- 'include/pybind11/common.h',
- 'include/pybind11/complex.h',
- 'include/pybind11/eigen.h',
- 'include/pybind11/embed.h',
- 'include/pybind11/eval.h',
- 'include/pybind11/functional.h',
- 'include/pybind11/iostream.h',
- 'include/pybind11/numpy.h',
- 'include/pybind11/operators.h',
- 'include/pybind11/options.h',
- 'include/pybind11/pybind11.h',
- 'include/pybind11/pytypes.h',
- 'include/pybind11/stl.h',
- 'include/pybind11/stl_bind.h',
- ]
+ headers = package_data
class InstallHeaders(install_headers):
@@ -55,6 +58,16 @@
self.outfiles.append(out)
+# Install the headers inside the package as well
+class BuildPy(build_py):
+ def build_package_data(self):
+ build_py.build_package_data(self)
+ for header in package_data:
+ target = os.path.join(self.build_lib, 'pybind11', header)
+ self.mkpath(os.path.dirname(target))
+ self.copy_file(header, target, preserve_mode=False)
+
+
setup(
name='pybind11',
version=__version__,
@@ -66,7 +79,8 @@
packages=['pybind11'],
license='BSD',
headers=headers,
- cmdclass=dict(install_headers=InstallHeaders),
+ zip_safe=False,
+ cmdclass=dict(install_headers=InstallHeaders, build_py=BuildPy),
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
diff --git a/tests/constructor_stats.h b/tests/constructor_stats.h
index f026e70..431e5ac 100644
--- a/tests/constructor_stats.h
+++ b/tests/constructor_stats.h
@@ -180,7 +180,7 @@
}
}
}
- catch (const 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/tests/test_buffers.cpp b/tests/test_buffers.cpp
index 433dfee..1bc67ff 100644
--- a/tests/test_buffers.cpp
+++ b/tests/test_buffers.cpp
@@ -166,4 +166,30 @@
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
.def_buffer(&DerivedBuffer::get_buffer_info);
+ struct BufferReadOnly {
+ const uint8_t value = 0;
+ BufferReadOnly(uint8_t value): value(value) {}
+
+ py::buffer_info get_buffer_info() {
+ return py::buffer_info(&value, 1);
+ }
+ };
+ py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
+ .def(py::init<uint8_t>())
+ .def_buffer(&BufferReadOnly::get_buffer_info);
+
+ struct BufferReadOnlySelect {
+ uint8_t value = 0;
+ bool readonly = false;
+
+ py::buffer_info get_buffer_info() {
+ return py::buffer_info(&value, 1, readonly);
+ }
+ };
+ py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
+ .def(py::init<>())
+ .def_readwrite("value", &BufferReadOnlySelect::value)
+ .def_readwrite("readonly", &BufferReadOnlySelect::readonly)
+ .def_buffer(&BufferReadOnlySelect::get_buffer_info);
+
}
diff --git a/tests/test_buffers.py b/tests/test_buffers.py
index f006552..bf7aaed 100644
--- a/tests/test_buffers.py
+++ b/tests/test_buffers.py
@@ -1,8 +1,14 @@
+import io
import struct
+import sys
+
import pytest
+
from pybind11_tests import buffers as m
from pybind11_tests import ConstructorStats
+PY3 = sys.version_info[0] >= 3
+
pytestmark = pytest.requires_numpy
with pytest.suppress(ImportError):
@@ -85,3 +91,28 @@
buf.value = 0x12345678
value = struct.unpack('i', bytearray(buf))[0]
assert value == 0x12345678
+
+
+@pytest.unsupported_on_pypy
+def test_readonly_buffer():
+ buf = m.BufferReadOnly(0x64)
+ view = memoryview(buf)
+ assert view[0] == 0x64 if PY3 else b'd'
+ assert view.readonly
+
+
+@pytest.unsupported_on_pypy
+def test_selective_readonly_buffer():
+ buf = m.BufferReadOnlySelect()
+
+ memoryview(buf)[0] = 0x64 if PY3 else b'd'
+ assert buf.value == 0x64
+
+ io.BytesIO(b'A').readinto(buf)
+ assert buf.value == ord(b'A')
+
+ buf.readonly = True
+ with pytest.raises(TypeError):
+ memoryview(buf)[0] = 0 if PY3 else b'\0'
+ with pytest.raises(TypeError):
+ io.BytesIO(b'1').readinto(buf)
diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp
index e026127..acb2446 100644
--- a/tests/test_builtin_casters.cpp
+++ b/tests/test_builtin_casters.cpp
@@ -30,7 +30,7 @@
else { wstr.push_back((wchar_t) mathbfA32); } // π, utf32
wstr.push_back(0x7a); // z
- m.def("good_utf8_string", []() { return std::string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8β½ π π
+ m.def("good_utf8_string", []() { return std::string((const char*)u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8β½ π π
m.def("good_utf16_string", [=]() { return std::u16string({ b16, ib16, cake16_1, cake16_2, mathbfA16_1, mathbfA16_2, z16 }); }); // bβ½ππz
m.def("good_utf32_string", [=]() { return std::u32string({ a32, mathbfA32, cake32, ib32, z32 }); }); // aππβ½z
m.def("good_wchar_string", [=]() { return wstr; }); // aβ½πz
@@ -60,6 +60,18 @@
m.def("strlen", [](char *s) { return strlen(s); });
m.def("string_length", [](std::string s) { return s.length(); });
+#ifdef PYBIND11_HAS_U8STRING
+ m.attr("has_u8string") = true;
+ m.def("good_utf8_u8string", []() { return std::u8string(u8"Say utf8\u203d \U0001f382 \U0001d400"); }); // Say utf8β½ π π
+ m.def("bad_utf8_u8string", []() { return std::u8string((const char8_t*)"abc\xd0" "def"); });
+
+ m.def("u8_char8_Z", []() -> char8_t { return u8'Z'; });
+
+ // test_single_char_arguments
+ m.def("ord_char8", [](char8_t c) -> int { return static_cast<unsigned char>(c); });
+ m.def("ord_char8_lv", [](char8_t &c) -> int { return static_cast<unsigned char>(c); });
+#endif
+
// test_string_view
#ifdef PYBIND11_HAS_STRING_VIEW
m.attr("has_string_view") = true;
@@ -69,9 +81,15 @@
m.def("string_view_chars", [](std::string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; });
m.def("string_view16_chars", [](std::u16string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
m.def("string_view32_chars", [](std::u32string_view s) { py::list l; for (auto c : s) l.append((int) c); return l; });
- m.def("string_view_return", []() { return std::string_view(u8"utf8 secret \U0001f382"); });
+ m.def("string_view_return", []() { return std::string_view((const char*)u8"utf8 secret \U0001f382"); });
m.def("string_view16_return", []() { return std::u16string_view(u"utf16 secret \U0001f382"); });
m.def("string_view32_return", []() { return std::u32string_view(U"utf32 secret \U0001f382"); });
+
+# ifdef PYBIND11_HAS_U8STRING
+ m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
+ m.def("string_view8_chars", [](std::u8string_view s) { py::list l; for (auto c : s) l.append((std::uint8_t) c); return l; });
+ m.def("string_view8_return", []() { return std::u8string_view(u8"utf8 secret \U0001f382"); });
+# endif
#endif
// test_integer_casting
diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py
index 73cc465..9142258 100644
--- a/tests/test_builtin_casters.py
+++ b/tests/test_builtin_casters.py
@@ -15,6 +15,8 @@
assert m.good_utf16_string() == u"bβ½ππz"
assert m.good_utf32_string() == u"aππβ½z"
assert m.good_wchar_string() == u"aβΈπz"
+ if hasattr(m, "has_u8string"):
+ assert m.good_utf8_u8string() == u"Say utf8β½ π π"
with pytest.raises(UnicodeDecodeError):
m.bad_utf8_string()
@@ -29,12 +31,17 @@
if hasattr(m, "bad_wchar_string"):
with pytest.raises(UnicodeDecodeError):
m.bad_wchar_string()
+ if hasattr(m, "has_u8string"):
+ with pytest.raises(UnicodeDecodeError):
+ m.bad_utf8_u8string()
assert m.u8_Z() == 'Z'
assert m.u8_eacute() == u'é'
assert m.u16_ibang() == u'β½'
assert m.u32_mathbfA() == u'π'
assert m.wchar_heart() == u'♥'
+ if hasattr(m, "has_u8string"):
+ assert m.u8_char8_Z() == 'Z'
def test_single_char_arguments():
@@ -92,6 +99,17 @@
assert m.ord_wchar(u'aa')
assert str(excinfo.value) == toolong_message
+ if hasattr(m, "has_u8string"):
+ assert m.ord_char8(u'a') == 0x61 # simple ASCII
+ assert m.ord_char8_lv(u'b') == 0x62
+ assert m.ord_char8(u'é') == 0xE9 # requires 2 bytes in utf-8, but can be stuffed in a char
+ with pytest.raises(ValueError) as excinfo:
+ assert m.ord_char8(u'Δ') == 0x100 # requires 2 bytes, doesn't fit in a char
+ assert str(excinfo.value) == toobig_message(0x100)
+ with pytest.raises(ValueError) as excinfo:
+ assert m.ord_char8(u'ab')
+ assert str(excinfo.value) == toolong_message
+
def test_bytes_to_string():
"""Tests the ability to pass bytes to C++ string-accepting functions. Note that this is
@@ -116,10 +134,15 @@
assert m.string_view_chars("Hi π") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82]
assert m.string_view16_chars("Hi π") == [72, 105, 32, 0xd83c, 0xdf82]
assert m.string_view32_chars("Hi π") == [72, 105, 32, 127874]
+ if hasattr(m, "has_u8string"):
+ assert m.string_view8_chars("Hi") == [72, 105]
+ assert m.string_view8_chars("Hi π") == [72, 105, 32, 0xf0, 0x9f, 0x8e, 0x82]
assert m.string_view_return() == "utf8 secret π"
assert m.string_view16_return() == "utf16 secret π"
assert m.string_view32_return() == "utf32 secret π"
+ if hasattr(m, "has_u8string"):
+ assert m.string_view8_return() == "utf8 secret π"
with capture:
m.string_view_print("Hi")
@@ -132,6 +155,14 @@
utf16 π 8
utf32 π 7
"""
+ if hasattr(m, "has_u8string"):
+ with capture:
+ m.string_view8_print("Hi")
+ m.string_view8_print("utf8 π")
+ assert capture == """
+ Hi 2
+ utf8 π 9
+ """
with capture:
m.string_view_print("Hi, ascii")
@@ -144,6 +175,14 @@
Hi, utf16 π 12
Hi, utf32 π 11
"""
+ if hasattr(m, "has_u8string"):
+ with capture:
+ m.string_view8_print("Hi, ascii")
+ m.string_view8_print("Hi, utf8 π")
+ assert capture == """
+ Hi, ascii 9
+ Hi, utf8 π 13
+ """
def test_integer_casting():
@@ -318,11 +357,15 @@
import numpy as np
convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert
+ def cant_convert(v):
+ pytest.raises(TypeError, convert, v)
+
# np.bool_ is not considered implicit
assert convert(np.bool_(True)) is True
assert convert(np.bool_(False)) is False
assert noconvert(np.bool_(True)) is True
assert noconvert(np.bool_(False)) is False
+ cant_convert(np.zeros(2, dtype='int'))
def test_int_long():
diff --git a/tests/test_copy_move.py b/tests/test_copy_move.py
index aff2d99..0e671d9 100644
--- a/tests/test_copy_move.py
+++ b/tests/test_copy_move.py
@@ -5,13 +5,13 @@
def test_lacking_copy_ctor():
with pytest.raises(RuntimeError) as excinfo:
m.lacking_copy_ctor.get_one()
- assert "the object is non-copyable!" in str(excinfo.value)
+ assert "is non-copyable!" in str(excinfo.value)
def test_lacking_move_ctor():
with pytest.raises(RuntimeError) as excinfo:
m.lacking_move_ctor.get_one()
- assert "the object is neither movable nor copyable!" in str(excinfo.value)
+ assert "is neither movable nor copyable!" in str(excinfo.value)
def test_move_and_copy_casts():
@@ -98,7 +98,7 @@
with pytest.raises(RuntimeError) as excinfo:
m.private_op_new_value()
- assert "the object is neither movable nor copyable" in str(excinfo.value)
+ assert "is neither movable nor copyable" in str(excinfo.value)
assert m.private_op_new_reference().value == 1
diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp
index d301390..56cd9bc 100644
--- a/tests/test_exceptions.cpp
+++ b/tests/test_exceptions.cpp
@@ -116,6 +116,7 @@
m.def("throws5", []() { throw MyException5("this is a helper-defined translated exception"); });
m.def("throws5_1", []() { throw MyException5_1("MyException5 subclass"); });
m.def("throws_logic_error", []() { throw std::logic_error("this error should fall through to the standard handler"); });
+ m.def("throws_overflow_error", []() {throw std::overflow_error(""); });
m.def("exception_matches", []() {
py::dict foo;
try {
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
index 6edff9f..ac2b360 100644
--- a/tests/test_exceptions.py
+++ b/tests/test_exceptions.py
@@ -79,6 +79,10 @@
m.throws_logic_error()
assert msg(excinfo.value) == "this error should fall through to the standard handler"
+ # OverFlow error translation.
+ with pytest.raises(OverflowError) as excinfo:
+ m.throws_overflow_error()
+
# Can we handle a helper-declared exception?
with pytest.raises(m.MyException5) as excinfo:
m.throws5()
diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp
index a88b589..8688874 100644
--- a/tests/test_stl_binders.cpp
+++ b/tests/test_stl_binders.cpp
@@ -54,6 +54,14 @@
return m;
}
+template <class NestMap> NestMap *times_hundred(int n) {
+ auto m = new NestMap();
+ for (int i = 1; i <= n; i++)
+ for (int j = 1; j <= n; j++)
+ (*m)[i].emplace(int(j*10), E_nc(100*j));
+ return m;
+}
+
TEST_SUBMODULE(stl_binders, m) {
// test_vector_int
py::bind_vector<std::vector<unsigned int>>(m, "VectorInt", py::buffer_protocol());
@@ -85,6 +93,20 @@
m.def("get_mnc", ×_ten<std::map<int, E_nc>>, py::return_value_policy::reference);
py::bind_map<std::unordered_map<int, E_nc>>(m, "UmapENC");
m.def("get_umnc", ×_ten<std::unordered_map<int, E_nc>>, py::return_value_policy::reference);
+ // Issue #1885: binding nested std::map<X, Container<E>> with E non-copyable
+ py::bind_map<std::map<int, std::vector<E_nc>>>(m, "MapVecENC");
+ m.def("get_nvnc", [](int n)
+ {
+ auto m = new std::map<int, std::vector<E_nc>>();
+ for (int i = 1; i <= n; i++)
+ for (int j = 1; j <= n; j++)
+ (*m)[i].emplace_back(j);
+ return m;
+ }, py::return_value_policy::reference);
+ py::bind_map<std::map<int, std::map<int, E_nc>>>(m, "MapMapENC");
+ m.def("get_nmnc", ×_hundred<std::map<int, std::map<int, E_nc>>>, py::return_value_policy::reference);
+ py::bind_map<std::unordered_map<int, std::unordered_map<int, E_nc>>>(m, "UmapUmapENC");
+ m.def("get_numnc", ×_hundred<std::unordered_map<int, std::unordered_map<int, E_nc>>>, py::return_value_policy::reference);
// test_vector_buffer
py::bind_vector<std::vector<unsigned char>>(m, "VectorUChar", py::buffer_protocol());
diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py
index 6d5a159..c7b7e85 100644
--- a/tests/test_stl_binders.py
+++ b/tests/test_stl_binders.py
@@ -64,6 +64,9 @@
del v_int2[-1]
assert v_int2 == m.VectorInt([0, 99, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 88])
+ v_int2.clear()
+ assert len(v_int2) == 0
+
# related to the PyPy's buffer protocol.
@pytest.unsupported_on_pypy
def test_vector_buffer():
@@ -212,6 +215,44 @@
assert vsum == 150
+ # nested std::map<std::vector>
+ nvnc = m.get_nvnc(5)
+ for i in range(1, 6):
+ for j in range(0, 5):
+ assert nvnc[i][j].value == j + 1
+
+ for k, v in nvnc.items():
+ for i, j in enumerate(v, start=1):
+ assert j.value == i
+
+ # nested std::map<std::map>
+ nmnc = m.get_nmnc(5)
+ for i in range(1, 6):
+ for j in range(10, 60, 10):
+ assert nmnc[i][j].value == 10 * j
+
+ vsum = 0
+ for k_o, v_o in nmnc.items():
+ for k_i, v_i in v_o.items():
+ assert v_i.value == 10 * k_i
+ vsum += v_i.value
+
+ assert vsum == 7500
+
+ # nested std::unordered_map<std::unordered_map>
+ numnc = m.get_numnc(5)
+ for i in range(1, 6):
+ for j in range(10, 60, 10):
+ assert numnc[i][j].value == 10 * j
+
+ vsum = 0
+ for k_o, v_o in numnc.items():
+ for k_i, v_i in v_o.items():
+ assert v_i.value == 10 * k_i
+ vsum += v_i.value
+
+ assert vsum == 7500
+
def test_map_delitem():
mm = m.MapStringDouble()
diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake
index e660c5f..9ea6036 100644
--- a/tools/FindPythonLibsNew.cmake
+++ b/tools/FindPythonLibsNew.cmake
@@ -140,11 +140,11 @@
list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH)
# Make sure all directory separators are '/'
-string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX})
-string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR})
-string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES})
+string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}")
+string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}")
-if(CMAKE_HOST_WIN32 AND NOT (MSYS OR MINGW))
+if(CMAKE_HOST_WIN32 AND NOT (MINGW AND DEFINED ENV{MSYSTEM}))
set(PYTHON_LIBRARY
"${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake
index c7156c0..508e474 100644
--- a/tools/pybind11Tools.cmake
+++ b/tools/pybind11Tools.cmake
@@ -12,7 +12,7 @@
set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules")
endif()
-set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4)
+set(Python_ADDITIONAL_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4)
find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED)
include(CheckCXXCompilerFlag)