From 431fc0e19810a73d8278b71d8f991b319b5377d8 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sat, 17 Feb 2018 11:07:37 -0400 Subject: [PATCH 0001/1206] Fix numpy dtypes test on big-endian architectures This fixes the test code on big-endian architectures: the array support (PR #832) had hard-coded the little-endian '<' but we need to use '>' on big-endian architectures. --- tests/test_numpy_dtypes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index ae5a7020..2e638851 100644 --- a/tests/test_numpy_dtypes.py +++ b/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,)),(' Date: Sun, 28 Jan 2018 10:16:27 -0500 Subject: [PATCH 0002/1206] Minor typo --- docs/advanced/functions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index e3acff06..4d28f06d 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -438,7 +438,7 @@ To explicitly enable or disable this behaviour, using the py::class_(m, "Cat").def(py::init<>()); m.def("bark", [](Dog *dog) -> std::string { if (dog) return "woof!"; /* Called with a Dog instance */ - else return "(no dog)"; /* Called with None, d == nullptr */ + else return "(no dog)"; /* Called with None, dog == nullptr */ }, py::arg("dog").none(true)); m.def("meow", [](Cat *cat) -> std::string { // Can't be called with None argument From 13c08072dc98ba679251ab8b17cf7e959d924fea Mon Sep 17 00:00:00 2001 From: "luz.paz" Date: Wed, 21 Feb 2018 13:28:55 -0500 Subject: [PATCH 0003/1206] Typo --- tests/test_eigen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp index 22141df0..aba088d7 100644 --- a/tests/test_eigen.cpp +++ b/tests/test_eigen.cpp @@ -124,7 +124,7 @@ TEST_SUBMODULE(eigen, m) { // This one accepts a matrix of any stride: m.def("add_any", [](py::EigenDRef 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(get_cm()); }); m.def("get_rm_ref", []() { return Eigen::Ref(get_rm()); }); // The same references, but non-mutable (numpy maps into eigen variables, but is !writeable) From 1ddfacbad1c8dbbbcf6527e16bfae0a93749a2fb Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sat, 10 Mar 2018 13:30:29 -0400 Subject: [PATCH 0004/1206] Fix for Python3 via brew Apparently with homebrew the correct package for python3 is now just `python`; python 2 was relegated to 'python@2', and `python3` is an alias for `python` (which needs to be upgraded rather than installed). --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d1ebdb0b..abbd79ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,7 +138,7 @@ before_install: PY_CMD=python$PYTHON if [ "$TRAVIS_OS_NAME" = "osx" ]; then if [ "$PY" = "3" ]; then - brew update && brew install python$PY; + brew update && brew upgrade python else curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user fi From ab003dbdd994de6b1de5a20e348939576408917c Mon Sep 17 00:00:00 2001 From: Marc Schlaich Date: Tue, 6 Feb 2018 16:07:27 +0100 Subject: [PATCH 0005/1206] Correct VS version in FAQ --- docs/faq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index bfe83036..67b15129 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -228,14 +228,14 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids potential serious issues when loading multiple modules and is required for proper pybind operation. See the previous FAQ entry for more details. -Working with ancient Visual Studio 2009 builds on Windows +Working with ancient Visual Studio 2008 builds on Windows ========================================================= The official Windows distributions of Python are compiled using truly ancient versions of Visual Studio that lack good C++11 support. Some users implicitly assume that it would be impossible to load a plugin built with Visual Studio 2015 into a Python distribution that was compiled using Visual -Studio 2009. However, no such issue exists: it's perfectly legitimate to +Studio 2008. However, no such issue exists: it's perfectly legitimate to interface DLLs that are built with different compilers and/or C libraries. Common gotchas to watch out for involve not ``free()``-ing memory region that that were ``malloc()``-ed in another shared library, using data From e88656ab45ae75df7dcb1fcdd2c89805b52e4665 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Tue, 27 Feb 2018 22:33:41 -0400 Subject: [PATCH 0006/1206] Improve macro type handling for types with commas - PYBIND11_MAKE_OPAQUE now takes ... rather than a single argument and expands it with __VA_ARGS__; this lets templated, comma-containing types get through correctly. - Adds a new macro PYBIND11_TYPE() that lets you pass the type into a macro as a single argument, such as: PYBIND11_OVERLOAD(PYBIND11_TYPE(R<1,2>), PYBIND11_TYPE(C<3,4>), func) Unfortunately this only works for one macro call: to forward the argument on to the next macro call (without the processor breaking it up again) requires also adding the PYBIND11_TYPE(...) to type macro arguments in the PYBIND11_OVERLOAD_... macro chain. - updated the documentation with these two changes, and use them at a couple places in the test suite to test that they work. --- docs/advanced/cast/stl.rst | 3 --- docs/advanced/misc.rst | 33 +++++++++++++++++++++++++------- include/pybind11/cast.h | 8 ++++++-- include/pybind11/pybind11.h | 8 ++++---- tests/test_opaque_types.cpp | 10 +++++++--- tests/test_virtual_functions.cpp | 4 +++- 6 files changed, 46 insertions(+), 20 deletions(-) diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst index 3f30c029..468bd2c6 100644 --- a/docs/advanced/cast/stl.rst +++ b/docs/advanced/cast/stl.rst @@ -175,9 +175,6 @@ in Python, and to define a set of available operations, e.g.: }, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ // .... -Please take a look at the :ref:`macro_notes` before using the -``PYBIND11_MAKE_OPAQUE`` macro. - .. seealso:: The file :file:`tests/test_opaque_types.cpp` contains a complete diff --git a/docs/advanced/misc.rst b/docs/advanced/misc.rst index 5faf11f0..be4699d3 100644 --- a/docs/advanced/misc.rst +++ b/docs/advanced/misc.rst @@ -7,13 +7,32 @@ General notes regarding convenience macros ========================================== pybind11 provides a few convenience macros such as -:func:`PYBIND11_MAKE_OPAQUE` and :func:`PYBIND11_DECLARE_HOLDER_TYPE`, and -``PYBIND11_OVERLOAD_*``. Since these are "just" macros that are evaluated -in the preprocessor (which has no concept of types), they *will* get confused -by commas in a template argument such as ``PYBIND11_OVERLOAD(MyReturnValue, myFunc)``. In this case, the preprocessor assumes that the comma indicates -the beginning of the next parameter. Use a ``typedef`` to bind the template to -another name and use it in the macro to avoid this problem. +:func:`PYBIND11_DECLARE_HOLDER_TYPE` and ``PYBIND11_OVERLOAD_*``. Since these +are "just" macros that are evaluated in the preprocessor (which has no concept +of types), they *will* get confused by commas in a template argument; for +example, consider: + +.. code-block:: cpp + + PYBIND11_OVERLOAD(MyReturnType, Class, func) + +The limitation of the C preprocessor interprets this as five arguments (with new +arguments beginning after each comma) rather than three. To get around this, +there are two alternatives: you can use a type alias, or you can wrap the type +using the ``PYBIND11_TYPE`` macro: + +.. code-block:: cpp + + // Version 1: using a type alias + using ReturnType = MyReturnType; + using ClassType = Class; + PYBIND11_OVERLOAD(ReturnType, ClassType, func); + + // Version 2: using the PYBIND11_TYPE macro: + PYBIND11_OVERLOAD(PYBIND11_TYPE(MyReturnType), + PYBIND11_TYPE(Class), func) + +The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds. .. _gil: diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 3ec6f728..8a680ed2 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -2054,9 +2054,13 @@ object object_api::call(Args &&...args) const { NAMESPACE_END(detail) -#define PYBIND11_MAKE_OPAQUE(Type) \ +#define PYBIND11_MAKE_OPAQUE(...) \ namespace pybind11 { namespace detail { \ - template<> class type_caster : public type_caster_base { }; \ + template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ }} +/// Lets you pass a type containing a `,` through a macro parameter without needing a separate +/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` +#define PYBIND11_TYPE(...) __VA_ARGS__ + NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 977045d6..9eff45dd 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1958,18 +1958,18 @@ template function get_overload(const T *this_ptr, const char *name) { } #define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ return cname::fn(__VA_ARGS__) #define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ - PYBIND11_OVERLOAD_INT(ret_type, cname, name, __VA_ARGS__) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\""); #define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) + PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) #define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ - PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, #fn, fn, __VA_ARGS__) + PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/test_opaque_types.cpp b/tests/test_opaque_types.cpp index 5e83df0f..0d20d9a0 100644 --- a/tests/test_opaque_types.cpp +++ b/tests/test_opaque_types.cpp @@ -11,10 +11,14 @@ #include #include -using StringList = std::vector; +// 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>); -/* IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures */ -PYBIND11_MAKE_OPAQUE(StringList); +using StringList = std::vector>; TEST_SUBMODULE(opaque_types, m) { // test_string_list diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index 336f5e45..a69ba153 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -207,7 +207,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::type), A, f); } }; From 9f41c8eade51f88331758e561a7c12cea0042463 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Tue, 27 Feb 2018 23:43:16 -0400 Subject: [PATCH 0007/1206] Fix class name in overload failure message --- include/pybind11/pybind11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9eff45dd..a50de836 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1963,7 +1963,7 @@ template function get_overload(const T *this_ptr, const char *name) { #define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ - pybind11::pybind11_fail("Tried to call pure virtual function \"" #cname "::" name "\""); + pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); #define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) From 6d0b4708c6eb1be31b943419a73aec00095a2d64 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sat, 10 Mar 2018 11:42:24 -0400 Subject: [PATCH 0008/1206] Reimplement version check and combine init macros This reimplements the version check to avoid sscanf (which has reportedly started throwing warnings under MSVC, even when used perfectly safely -- #1314). It also extracts the mostly duplicated parts of PYBIND11_MODULE/PYBIND11_PLUGIN into separate macros. --- include/pybind11/detail/common.h | 67 ++++++++++++++------------------ 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 4b2b0849..a291ecf6 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -211,6 +211,31 @@ extern "C" { #define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) #define PYBIND11_CONCAT(first, second) first##second +#define PYBIND11_CHECK_PYTHON_VERSION \ + { \ + const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ + "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ + const char *runtime_ver = Py_GetVersion(); \ + size_t len = std::strlen(compiled_ver); \ + if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ + || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for Python %s, " \ + "but the interpreter version is incompatible: %s.", \ + compiled_ver, runtime_ver); \ + return nullptr; \ + } \ + } + +#define PYBIND11_CATCH_INIT_EXCEPTIONS \ + catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + /** \rst ***Deprecated in favor of PYBIND11_MODULE*** @@ -230,27 +255,10 @@ extern "C" { PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ static PyObject *pybind11_init(); \ PYBIND11_PLUGIN_IMPL(name) { \ - int major, minor; \ - if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ - PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ - return nullptr; \ - } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for " \ - "version %i.%i, while the interpreter is running " \ - "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ - major, minor); \ - return nullptr; \ - } \ + PYBIND11_CHECK_PYTHON_VERSION \ try { \ return pybind11_init(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ } \ PyObject *pybind11_init() @@ -274,29 +282,12 @@ extern "C" { #define PYBIND11_MODULE(name, variable) \ static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ PYBIND11_PLUGIN_IMPL(name) { \ - int major, minor; \ - if (sscanf(Py_GetVersion(), "%i.%i", &major, &minor) != 2) { \ - PyErr_SetString(PyExc_ImportError, "Can't parse Python version."); \ - return nullptr; \ - } else if (major != PY_MAJOR_VERSION || minor != PY_MINOR_VERSION) { \ - PyErr_Format(PyExc_ImportError, \ - "Python version mismatch: module was compiled for " \ - "version %i.%i, while the interpreter is running " \ - "version %i.%i.", PY_MAJOR_VERSION, PY_MINOR_VERSION, \ - major, minor); \ - return nullptr; \ - } \ + PYBIND11_CHECK_PYTHON_VERSION \ auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ try { \ PYBIND11_CONCAT(pybind11_init_, name)(m); \ return m.ptr(); \ - } catch (pybind11::error_already_set &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ } \ void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) From 41a4fd8ae941406d6635df43f0e84021fc2a528f Mon Sep 17 00:00:00 2001 From: Patrik Huber Date: Tue, 3 Apr 2018 00:08:30 +0100 Subject: [PATCH 0009/1206] Fix missing word typo I think that there's the word "for" missing for that sentence to be correct. Please double-check that the sentence means what it's supposed to mean. :-) --- docs/advanced/misc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/misc.rst b/docs/advanced/misc.rst index be4699d3..5b38ec75 100644 --- a/docs/advanced/misc.rst +++ b/docs/advanced/misc.rst @@ -156,7 +156,7 @@ Naturally, both methods will fail when there are cyclic dependencies. Note that pybind11 code compiled with hidden-by-default symbol visibility (e.g. via the command line flag ``-fvisibility=hidden`` on GCC/Clang), which is -required proper pybind11 functionality, can interfere with the ability to +required for proper pybind11 functionality, can interfere with the ability to access types defined in another extension module. Working around this requires manually exporting types that are accessed by multiple extension modules; pybind11 provides a macro to do just this: From 6c62d2797cc8e50a58604ea3120dc4d8323db53b Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 3 Apr 2018 11:27:04 +0200 Subject: [PATCH 0010/1206] Fix for conda failures on Windows --- .appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.appveyor.yml b/.appveyor.yml index 2d37fc7a..f82ab83a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -48,6 +48,7 @@ install: if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } $env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH" $env:PYTHONHOME = "C:\Miniconda$env:CONDA" + conda update -y -n base conda conda install -y -q pytest numpy scipy } - ps: | From 6862cb9b3564215805f5a4015c89914282b3509c Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 5 Apr 2018 11:04:15 -0300 Subject: [PATCH 0011/1206] Add workaround for clang 3.3/3.4 As reported in #1349, clang before 3.5 can segfault on a function-local variable referenced inside a lambda. This moves the function-local static into a separate function that the lambda can invoke to avoid the issue. Fixes #1349 --- include/pybind11/pybind11.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index a50de836..c7751eb2 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1650,6 +1650,7 @@ void register_exception_translator(ExceptionTranslator&& translator) { template class exception : public object { public: + exception() = default; exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { std::string full_name = scope.attr("__name__").cast() + std::string(".") + name; @@ -1666,6 +1667,14 @@ class exception : public object { } }; +NAMESPACE_BEGIN(detail) +// Returns a reference to a function-local static exception object used in the simple +// register_exception approach below. (It would be simpler to have the static local variable +// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). +template +exception &get_exception_object() { static exception ex; return ex; } +NAMESPACE_END(detail) + /** * Registers a Python exception in `m` of the given `name` and installs an exception translator to * translate the C++ exception to the created Python exception using the exceptions what() method. @@ -1676,13 +1685,15 @@ template exception ®ister_exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { - static exception ex(scope, name, base); + auto &ex = detail::get_exception_object(); + if (!ex) ex = exception(scope, name, base); + register_exception_translator([](std::exception_ptr p) { if (!p) return; try { std::rethrow_exception(p); } catch (const CppException &e) { - ex(e.what()); + detail::get_exception_object()(e.what()); } }); return ex; From 289e5d9cc2a4545d832d3c7fb50066476bce3c1d Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Mon, 2 Apr 2018 23:26:48 +0200 Subject: [PATCH 0012/1206] Implement an enum_ property "name" The property returns the enum_ value as a string. For example: >>> import module >>> module.enum.VALUE enum.VALUE >>> str(module.enum.VALUE) 'enum.VALUE' >>> module.enum.VALUE.name 'VALUE' This is actually the equivalent of Boost.Python "name" property. --- docs/classes.rst | 18 ++++++++++++++++++ include/pybind11/pybind11.h | 8 ++++++++ tests/test_enum.py | 13 +++++++++++++ 3 files changed, 39 insertions(+) diff --git a/docs/classes.rst b/docs/classes.rst index 890257d5..75a8fb2c 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -488,6 +488,24 @@ The entries defined by the enumeration type are exposed in the ``__members__`` p >>> Pet.Kind.__members__ {'Dog': Kind.Dog, 'Cat': Kind.Cat} +The ``name`` property returns the name of the enum value as a unicode string. + +.. note:: + + It is also possible to use ``str(enum)``, however these accomplish different + goals. The following shows how these two approaches differ. + + .. code-block:: pycon + + >>> p = Pet( "Lucy", Pet.Cat ) + >>> pet_type = p.type + >>> pet_type + Pet.Cat + >>> str(pet_type) + 'Pet.Cat' + >>> pet_type.name + 'Cat' + .. note:: When the special tag ``py::arithmetic()`` is specified to the ``enum_`` diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c7751eb2..6947d440 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1364,6 +1364,7 @@ detail::initimpl::pickle_factory pickle(GetState &&g, SetSta template class enum_ : public class_ { public: using class_::def; + using class_::def_property_readonly; using class_::def_property_readonly_static; using Scalar = typename std::underlying_type::type; @@ -1381,6 +1382,13 @@ template class enum_ : public class_ { } return pybind11::str("{}.???").format(name); }); + def_property_readonly("name", [m_entries_ptr](Type value) -> pybind11::str { + for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { + if (pybind11::cast(kv.second[int_(0)]) == value) + return pybind11::str(kv.first); + } + return pybind11::str("???"); + }); def_property_readonly_static("__doc__", [m_entries_ptr](handle self) { std::string docstring; const char *tp_doc = ((PyTypeObject *) self.ptr())->tp_doc; diff --git a/tests/test_enum.py b/tests/test_enum.py index d3f5b4d1..c2c272a2 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -6,6 +6,19 @@ 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} From 8fbb5594fdf02eea6024d7b0c5eef3891d7366ab Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 11 Mar 2018 16:15:56 -0700 Subject: [PATCH 0013/1206] Clarify error_already_set documentation. --- docs/advanced/exceptions.rst | 78 ++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/advanced/exceptions.rst b/docs/advanced/exceptions.rst index 3122c372..629af4aa 100644 --- a/docs/advanced/exceptions.rst +++ b/docs/advanced/exceptions.rst @@ -11,45 +11,45 @@ exceptions: .. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}| -+--------------------------------------+------------------------------+ -| C++ exception type | Python exception type | -+======================================+==============================+ -| :class:`std::exception` | ``RuntimeError`` | -+--------------------------------------+------------------------------+ -| :class:`std::bad_alloc` | ``MemoryError`` | -+--------------------------------------+------------------------------+ -| :class:`std::domain_error` | ``ValueError`` | -+--------------------------------------+------------------------------+ -| :class:`std::invalid_argument` | ``ValueError`` | -+--------------------------------------+------------------------------+ -| :class:`std::length_error` | ``ValueError`` | -+--------------------------------------+------------------------------+ -| :class:`std::out_of_range` | ``ValueError`` | -+--------------------------------------+------------------------------+ -| :class:`std::range_error` | ``ValueError`` | -+--------------------------------------+------------------------------+ -| :class:`pybind11::stop_iteration` | ``StopIteration`` (used to | -| | implement custom iterators) | -+--------------------------------------+------------------------------+ -| :class:`pybind11::index_error` | ``IndexError`` (used to | -| | indicate out of bounds | -| | accesses in ``__getitem__``, | -| | ``__setitem__``, etc.) | -+--------------------------------------+------------------------------+ -| :class:`pybind11::value_error` | ``ValueError`` (used to | -| | indicate wrong value passed | -| | in ``container.remove(...)`` | -+--------------------------------------+------------------------------+ -| :class:`pybind11::key_error` | ``KeyError`` (used to | -| | indicate out of bounds | -| | accesses in ``__getitem__``, | -| | ``__setitem__`` in dict-like | -| | objects, etc.) | -+--------------------------------------+------------------------------+ -| :class:`pybind11::error_already_set` | Indicates that the Python | -| | exception flag has already | -| | been initialized | -+--------------------------------------+------------------------------+ ++--------------------------------------+--------------------------------------+ +| C++ exception type | Python exception type | ++======================================+======================================+ +| :class:`std::exception` | ``RuntimeError`` | ++--------------------------------------+--------------------------------------+ +| :class:`std::bad_alloc` | ``MemoryError`` | ++--------------------------------------+--------------------------------------+ +| :class:`std::domain_error` | ``ValueError`` | ++--------------------------------------+--------------------------------------+ +| :class:`std::invalid_argument` | ``ValueError`` | ++--------------------------------------+--------------------------------------+ +| :class:`std::length_error` | ``ValueError`` | ++--------------------------------------+--------------------------------------+ +| :class:`std::out_of_range` | ``ValueError`` | ++--------------------------------------+--------------------------------------+ +| :class:`std::range_error` | ``ValueError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::stop_iteration` | ``StopIteration`` (used to implement | +| | custom iterators) | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::index_error` | ``IndexError`` (used to indicate out | +| | of bounds access in ``__getitem__``, | +| | ``__setitem__``, etc.) | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::value_error` | ``ValueError`` (used to indicate | +| | wrong value passed in | +| | ``container.remove(...)``) | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::key_error` | ``KeyError`` (used to indicate out | +| | of bounds access in ``__getitem__``, | +| | ``__setitem__`` in dict-like | +| | objects, etc.) | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::error_already_set` | Indicates that the Python exception | +| | flag has already been set via Python | +| | API calls from C++ code; this C++ | +| | exception is used to propagate such | +| | a Python exception back to Python. | ++--------------------------------------+--------------------------------------+ When a Python function invoked from C++ throws an exception, it is converted into a C++ exception of type :class:`error_already_set` whose string payload From fd9bc8f54d1d9869e086923ad776ff4eda1ae6f5 Mon Sep 17 00:00:00 2001 From: oremanj Date: Fri, 13 Apr 2018 20:13:10 -0400 Subject: [PATCH 0014/1206] Add basic support for tag-based static polymorphism (#1326) * Add basic support for tag-based static polymorphism Sometimes it is possible to look at a C++ object and know what its dynamic type is, even if it doesn't use C++ polymorphism, because instances of the object and its subclasses conform to some other mechanism for being self-describing; for example, perhaps there's an enumerated "tag" or "kind" member in the base class that's always set to an indication of the correct type. This might be done for performance reasons, or to permit most-derived types to be trivially copyable. One of the most widely-known examples is in LLVM: https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html This PR permits pybind11 to be informed of such conventions via a new specializable detail::polymorphic_type_hook<> template, which generalizes the previous logic for determining the runtime type of an object based on C++ RTTI. Implementors provide a way to map from a base class object to a const std::type_info* for the dynamic type; pybind11 then uses this to ensure that casting a Base* to Python creates a Python object that knows it's wrapping the appropriate sort of Derived. There are a number of restrictions with this tag-based static polymorphism support compared to pybind11's existing support for built-in C++ polymorphism: - there is no support for this-pointer adjustment, so only single inheritance is permitted - there is no way to make C++ code call new Python-provided subclasses - when binding C++ classes that redefine a method in a subclass, the .def() must be repeated in the binding for Python to know about the update But these are not much of an issue in practice in many cases, the impact on the complexity of pybind11's innards is minimal and localized, and the support for automatic downcasting improves usability a great deal. --- docs/advanced/classes.rst | 83 +++++++++++++++++ docs/classes.rst | 10 +- include/pybind11/cast.h | 72 +++++++++++---- tests/CMakeLists.txt | 1 + tests/test_tagbased_polymorphic.cpp | 136 ++++++++++++++++++++++++++++ tests/test_tagbased_polymorphic.py | 20 ++++ 6 files changed, 297 insertions(+), 25 deletions(-) create mode 100644 tests/test_tagbased_polymorphic.cpp create mode 100644 tests/test_tagbased_polymorphic.py diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 93deeec6..7709d282 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -999,3 +999,86 @@ described trampoline: requires a more explicit function binding in the form of ``.def("foo", static_cast(&Publicist::foo));`` where ``int (A::*)() const`` is the type of ``A::foo``. + +Custom automatic downcasters +============================ + +As explained in :ref:`inheritance`, pybind11 comes with built-in +understanding of the dynamic type of polymorphic objects in C++; that +is, returning a Pet to Python produces a Python object that knows it's +wrapping a Dog, if Pet has virtual methods and pybind11 knows about +Dog and this Pet is in fact a Dog. Sometimes, you might want to +provide this automatic downcasting behavior when creating bindings for +a class hierarchy that does not use standard C++ polymorphism, such as +LLVM [#f4]_. As long as there's some way to determine at runtime +whether a downcast is safe, you can proceed by specializing the +``pybind11::polymorphic_type_hook`` template: + +.. code-block:: cpp + + enum class PetKind { Cat, Dog, Zebra }; + struct Pet { // Not polymorphic: has no virtual methods + const PetKind kind; + int age = 0; + protected: + Pet(PetKind _kind) : kind(_kind) {} + }; + struct Dog : Pet { + Dog() : Pet(PetKind::Dog) {} + std::string sound = "woof!"; + std::string bark() const { return sound; } + }; + + namespace pybind11 { + template<> struct polymorphic_type_hook { + static const void *get(const Pet *src, const std::type_info*& type) { + // note that src may be nullptr + if (src && src->kind == PetKind::Dog) { + type = &typeid(Dog); + return static_cast(src); + } + return src; + } + }; + } // namespace pybind11 + +When pybind11 wants to convert a C++ pointer of type ``Base*`` to a +Python object, it calls ``polymorphic_type_hook::get()`` to +determine if a downcast is possible. The ``get()`` function should use +whatever runtime information is available to determine if its ``src`` +parameter is in fact an instance of some class ``Derived`` that +inherits from ``Base``. If it finds such a ``Derived``, it sets ``type += &typeid(Derived)`` and returns a pointer to the ``Derived`` object +that contains ``src``. Otherwise, it just returns ``src``, leaving +``type`` at its default value of nullptr. If you set ``type`` to a +type that pybind11 doesn't know about, no downcasting will occur, and +the original ``src`` pointer will be used with its static type +``Base*``. + +It is critical that the returned pointer and ``type`` argument of +``get()`` agree with each other: if ``type`` is set to something +non-null, the returned pointer must point to the start of an object +whose type is ``type``. If the hierarchy being exposed uses only +single inheritance, a simple ``return src;`` will achieve this just +fine, but in the general case, you must cast ``src`` to the +appropriate derived-class pointer (e.g. using +``static_cast(src)``) before allowing it to be returned as a +``void*``. + +.. [#f4] https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html + +.. note:: + + pybind11's standard support for downcasting objects whose types + have virtual methods is implemented using + ``polymorphic_type_hook`` too, using the standard C++ ability to + determine the most-derived type of a polymorphic object using + ``typeid()`` and to cast a base pointer to that most-derived type + (even if you don't know what it is) using ``dynamic_cast``. + +.. seealso:: + + The file :file:`tests/test_tagbased_polymorphic.cpp` contains a + more complete example, including a demonstration of how to provide + automatic downcasting for an entire class hierarchy without + writing one get() function for each class. diff --git a/docs/classes.rst b/docs/classes.rst index 75a8fb2c..1deec9b5 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -228,8 +228,8 @@ just brings them on par. .. _inheritance: -Inheritance and automatic upcasting -=================================== +Inheritance and automatic downcasting +===================================== Suppose now that the example consists of two data structures with an inheritance relationship: @@ -298,7 +298,7 @@ inheritance relationship. This is reflected in Python: >>> p = example.pet_store() >>> type(p) # `Dog` instance behind `Pet` pointer - Pet # no pointer upcasting for regular non-polymorphic types + Pet # no pointer downcasting for regular non-polymorphic types >>> p.bark() AttributeError: 'Pet' object has no attribute 'bark' @@ -330,11 +330,11 @@ will automatically recognize this: >>> p = example.pet_store2() >>> type(p) - PolymorphicDog # automatically upcast + PolymorphicDog # automatically downcast >>> p.bark() u'woof!' -Given a pointer to a polymorphic base, pybind11 performs automatic upcasting +Given a pointer to a polymorphic base, pybind11 performs automatic downcasting to the actual derived type. Note that this goes beyond the usual situation in C++: we don't just get access to the virtual functions of the base, we get the concrete derived type including functions and attributes that the base type may diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 8a680ed2..efcdc5ba 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -774,9 +774,45 @@ template struct is_copy_constructible, is_copy_constructible> {}; #endif +NAMESPACE_END(detail) + +// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed +// to by `src` actually is an instance of some class derived from `itype`. +// If so, it sets `tinfo` to point to the std::type_info representing that derived +// type, and returns a pointer to the start of the most-derived object of that type +// (in which `src` is a subobject; this will be the same address as `src` in most +// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` +// and leaves `tinfo` at its default value of nullptr. +// +// The default polymorphic_type_hook just returns src. A specialization for polymorphic +// types determines the runtime type of the passed object and adjusts the this-pointer +// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear +// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is +// registered with pybind11, and this Animal is in fact a Dog). +// +// You may specialize polymorphic_type_hook yourself for types that want to appear +// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern +// in performance-sensitive applications, used most notably in LLVM.) +template +struct polymorphic_type_hook +{ + static const void *get(const itype *src, const std::type_info*&) { return src; } +}; +template +struct polymorphic_type_hook::value>> +{ + static const void *get(const itype *src, const std::type_info*& type) { + type = src ? &typeid(*src) : nullptr; + return dynamic_cast(src); + } +}; + +NAMESPACE_BEGIN(detail) + /// Generic type caster for objects stored on the heap template class type_caster_base : public type_caster_generic { using itype = intrinsic_t; + public: static constexpr auto name = _(); @@ -793,32 +829,28 @@ template class type_caster_base : public type_caster_generic { return cast(&src, return_value_policy::move, parent); } - // Returns a (pointer, type_info) pair taking care of necessary RTTI type lookup for a - // polymorphic type. If the instance isn't derived, returns the non-RTTI base version. - template ::value, int> = 0> + // Returns a (pointer, type_info) pair taking care of necessary type lookup for a + // polymorphic type (using RTTI by default, but can be overridden by specializing + // polymorphic_type_hook). If the instance isn't derived, returns the base version. static std::pair src_and_type(const itype *src) { - const void *vsrc = src; auto &cast_type = typeid(itype); const std::type_info *instance_type = nullptr; - if (vsrc) { - instance_type = &typeid(*src); - if (!same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type; if it is a pybind11-registered type, we - // can get the correct derived pointer (which may be != base pointer) by a - // dynamic_cast to most derived type: - if (auto *tpi = get_type_info(*instance_type)) - return {dynamic_cast(src), const_cast(tpi)}; - } + const void *vsrc = polymorphic_type_hook::get(src, instance_type); + if (instance_type && !same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type. If the derived type is registered + // with pybind11, we want to make the full derived object available. + // In the typical case where itype is polymorphic, we get the correct + // derived pointer (which may be != base pointer) by a dynamic_cast to + // most derived type. If itype is not polymorphic, we won't get here + // except via a user-provided specialization of polymorphic_type_hook, + // and the user has promised that no this-pointer adjustment is + // required in that case, so it's OK to use static_cast. + if (const auto *tpi = get_type_info(*instance_type)) + return {vsrc, tpi}; } // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so // don't do a cast - return type_caster_generic::src_and_type(vsrc, cast_type, instance_type); - } - - // Non-polymorphic type, so no dynamic casting; just call the generic version directly - template ::value, int> = 0> - static std::pair src_and_type(const itype *src) { - return type_caster_generic::src_and_type(src, typeid(itype)); + return type_caster_generic::src_and_type(src, cast_type, instance_type); } static handle cast(const itype *src, return_value_policy policy, handle parent) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8f2f300e..b5e0b526 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -57,6 +57,7 @@ set(PYBIND11_TEST_FILES test_smart_ptr.cpp test_stl.cpp test_stl_binders.cpp + test_tagbased_polymorphic.cpp test_virtual_functions.cpp ) diff --git a/tests/test_tagbased_polymorphic.cpp b/tests/test_tagbased_polymorphic.cpp new file mode 100644 index 00000000..272e460c --- /dev/null +++ b/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 + + 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 + +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> create_zoo() +{ + std::vector> 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 + struct polymorphic_type_hook::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_(m, "Animal") + .def_readonly("name", &Animal::name); + py::class_(m, "Dog") + .def(py::init()) + .def_readwrite("sound", &Dog::sound) + .def("bark", &Dog::bark); + py::class_(m, "Labrador") + .def(py::init(), "name"_a, "excitement"_a = 9001) + .def_readwrite("excitement", &Labrador::excitement); + py::class_(m, "Chihuahua") + .def(py::init()) + .def("bark", &Chihuahua::bark); + py::class_(m, "Cat") + .def(py::init()) + .def("purr", &Cat::purr); + py::class_(m, "Panther") + .def(py::init()) + .def("purr", &Panther::purr); + m.def("create_zoo", &create_zoo); +}; diff --git a/tests/test_tagbased_polymorphic.py b/tests/test_tagbased_polymorphic.py new file mode 100644 index 00000000..2574d7de --- /dev/null +++ b/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 From 060936fed2ad0bc14251a8703b6139eb7ea62a25 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 16 Apr 2018 10:27:21 +0200 Subject: [PATCH 0015/1206] Detect pybind11 header path without depending on pip internals (fixes #1174) (#1190) --- pybind11/__init__.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/pybind11/__init__.py b/pybind11/__init__.py index a765692f..5782ffea 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -1,11 +1,28 @@ from ._version import version_info, __version__ # noqa: F401 imported but unused -def get_include(*args, **kwargs): +def get_include(user=False): + from distutils.dist import Distribution import os - try: - from pip import locations - return os.path.dirname( - locations.distutils_scheme('pybind11', *args, **kwargs)['headers']) - except ImportError: - return 'include' + import sys + + # Are we running in a virtual environment? + virtualenv = hasattr(sys, 'real_prefix') or \ + sys.prefix != getattr(sys, "base_prefix", sys.prefix) + + if virtualenv: + return os.path.join(sys.prefix, 'include', 'site', + 'python' + sys.version[:3]) + 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) From ffd56ebec0b1940e8d8d49b33cdc541f4e04c0a9 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 22 Apr 2018 14:04:12 +0200 Subject: [PATCH 0016/1206] Fix pip issues on AppVeyor CI (#1369) --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f82ab83a..5e969400 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,8 +41,8 @@ install: if ($env:PYTHON) { if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" - pip install --disable-pip-version-check --user --upgrade pip wheel - pip install pytest numpy + python -m pip install --upgrade pip wheel + python -m pip install pytest numpy --no-warn-script-location } elseif ($env:CONDA) { if ($env:CONDA -eq "27") { $env:CONDA = "" } if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } From ed670055832f6669fbb7849878ed2cb99be4889c Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 22 Apr 2018 14:04:31 +0200 Subject: [PATCH 0017/1206] Minor fix for MSVC warning CS4459 (#1374) When using pybind11 to bind enums on MSVC and warnings (/W4) enabled, the following warning pollutes builds. This fix renames one of the occurrences. pybind11\include\pybind11\pybind11.h(1398): warning C4459: declaration of 'self' hides global declaration pybind11\include\pybind11\operators.h(41): note: see declaration of 'pybind11::detail::self' --- include/pybind11/pybind11.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 6947d440..a04d5365 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1389,9 +1389,9 @@ template class enum_ : public class_ { } return pybind11::str("???"); }); - def_property_readonly_static("__doc__", [m_entries_ptr](handle self) { + def_property_readonly_static("__doc__", [m_entries_ptr](handle self_) { std::string docstring; - const char *tp_doc = ((PyTypeObject *) self.ptr())->tp_doc; + const char *tp_doc = ((PyTypeObject *) self_.ptr())->tp_doc; if (tp_doc) docstring += std::string(tp_doc) + "\n\n"; docstring += "Members:"; @@ -1404,7 +1404,7 @@ template class enum_ : public class_ { } return docstring; }); - def_property_readonly_static("__members__", [m_entries_ptr](handle /* self */) { + def_property_readonly_static("__members__", [m_entries_ptr](handle /* self_ */) { dict m; for (const auto &kv : reinterpret_borrow(m_entries_ptr)) m[kv.first] = kv.second[int_(0)]; From 307ea6b7fdc77ae6d42ac71fef74ff6924498e58 Mon Sep 17 00:00:00 2001 From: David Caron Date: Tue, 24 Apr 2018 10:16:18 -0400 Subject: [PATCH 0018/1206] Typo --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index 67b15129..dc2b9761 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -116,7 +116,7 @@ following example: .. code-block:: cpp - void init_ex1(py::module &m) { + void init_ex2(py::module &m) { m.def("sub", [](int a, int b) { return a - b; }); } From bdbe8d0bde6aeb5360007cc57a453c18a33ec17b Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Sun, 29 Apr 2018 07:48:25 -0400 Subject: [PATCH 0019/1206] Enforces intel icpc >= 2017, fixes #1121 (#1363) --- README.md | 2 +- docs/changelog.rst | 4 ++++ docs/intro.rst | 2 +- include/pybind11/detail/common.h | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 04568ace..dbebf0a8 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ In addition to the core functionality, pybind11 provides some extra goodies: 1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer) 2. GCC 4.8 or newer 3. Microsoft Visual Studio 2015 Update 3 or newer -4. Intel C++ compiler 16 or newer (15 with a [workaround](https://github.com/pybind/pybind11/issues/276)) +4. Intel C++ compiler 17 or newer (16 with pybind11 v2.0 and 15 with pybind11 v2.0 and a [workaround](https://github.com/pybind/pybind11/issues/276)) 5. Cygwin/GCC (tested on 2.5.1) ## About diff --git a/docs/changelog.rst b/docs/changelog.rst index 2397056e..2d9b452c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -21,6 +21,10 @@ v2.3.0 (Not yet released) * The ``value()`` method of ``py::enum_`` now accepts an optional docstring that will be shown in the documentation of the associated enumeration. +* Intel compilers have needed to be >= 17.0 since v2.1. Now the check + is explicit and a compile-time error is raised if the compiler does + not meet the requirements. + v2.2.2 (February 7, 2018) ----------------------------------------------------- diff --git a/docs/intro.rst b/docs/intro.rst index 3e9420ec..118cb504 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -91,4 +91,4 @@ Supported compilers 1. Clang/LLVM (any non-ancient version with C++11 support) 2. GCC 4.8 or newer 3. Microsoft Visual Studio 2015 or newer -4. Intel C++ compiler v15 or newer +4. Intel C++ compiler v17 or newer (v16 with pybind11 v2.0 and v15 with pybind11 v2.0 and a `workaround `_ ) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index a291ecf6..3c672289 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -46,8 +46,8 @@ // Compiler version assertions #if defined(__INTEL_COMPILER) -# if __INTEL_COMPILER < 1500 -# error pybind11 requires Intel C++ compiler v15 or newer +# if __INTEL_COMPILER < 1700 +# error pybind11 requires Intel C++ compiler v17 or newer # endif #elif defined(__clang__) && !defined(__apple_build_version__) # if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) From f5f66189620b7575f3124c66e3b3c8d0d03caf25 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 29 Apr 2018 15:47:03 +0200 Subject: [PATCH 0020/1206] updated changelog for v2.2.3 --- docs/changelog.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 2d9b452c..c559b47b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -21,9 +21,24 @@ v2.3.0 (Not yet released) * The ``value()`` method of ``py::enum_`` now accepts an optional docstring that will be shown in the documentation of the associated enumeration. -* Intel compilers have needed to be >= 17.0 since v2.1. Now the check - is explicit and a compile-time error is raised if the compiler does - not meet the requirements. +v2.2.3 (April 29, 2018) +----------------------------------------------------- + +* The pybind11 header location detection was replaced by a new implementation + that no longer depends on ``pip`` internals (the recently released ``pip`` + 10 has restricted access to this API). + `#1190 `_. + +* Small adjustment to an implementation detail to work around a compiler segmentation fault in Clang 3.3/3.4. + `#1350 `_. + +* The minimal supported version of the Intel compiler was >= 17.0 since + pybind11 v2.1. This check is now explicit, and a compile-time error is raised + if the compiler meet the requirement. + `#1363 `_. + +* Fixed an endianness-related fault in the test suite. + `#1287 `_. v2.2.2 (February 7, 2018) ----------------------------------------------------- From a7ff616dfbdc947c6fca7cbd82e9bb73ffaccd09 Mon Sep 17 00:00:00 2001 From: Tom de Geus Date: Fri, 4 May 2018 17:04:45 +0200 Subject: [PATCH 0021/1206] Simplified example allowing more robust usage, fixed minor spelling issues --- docs/advanced/classes.rst | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 7709d282..8e4485ee 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -46,11 +46,10 @@ Normally, the binding code for these classes would look as follows: .. code-block:: cpp PYBIND11_MODULE(example, m) { - py::class_ animal(m, "Animal"); - animal + py::class_(m, "Animal"); .def("go", &Animal::go); - py::class_(m, "Dog", animal) + py::class_(m, "Dog") .def(py::init<>()); m.def("call_go", &call_go); @@ -93,15 +92,14 @@ function have different names, e.g. ``operator()`` vs ``__call__``. The binding code also needs a few minor adaptations (highlighted): .. code-block:: cpp - :emphasize-lines: 2,4,5 + :emphasize-lines: 2,3 PYBIND11_MODULE(example, m) { - py::class_ animal(m, "Animal"); - animal + py::class_(m, "Animal"); .def(py::init<>()) .def("go", &Animal::go); - py::class_(m, "Dog", animal) + py::class_(m, "Dog") .def(py::init<>()); m.def("call_go", &call_go); @@ -116,11 +114,11 @@ define a constructor as usual. Bindings should be made against the actual class, not the trampoline helper class. .. code-block:: cpp + :emphasize-lines: 3 - py::class_ animal(m, "Animal"); - animal - .def(py::init<>()) - .def("go", &PyAnimal::go); /* <--- THIS IS WRONG, use &Animal::go */ + py::class_(m, "Animal"); + .def(py::init<>()) + .def("go", &PyAnimal::go); /* <--- THIS IS WRONG, use &Animal::go */ Note, however, that the above is sufficient for allowing python classes to extend ``Animal``, but not ``Dog``: see :ref:`virtual_and_inheritance` for the @@ -157,7 +155,7 @@ Here is an example: class Dachschund(Dog): def __init__(self, name): - Dog.__init__(self) # Without this, undefind behavior may occur if the C++ portions are referenced. + Dog.__init__(self) # Without this, undefined behavior may occur if the C++ portions are referenced. self.name = name def bark(self): return "yap!" @@ -760,7 +758,7 @@ document)---pybind11 will automatically find out which is which. The only requirement is that the first template argument is the type to be declared. It is also permitted to inherit multiply from exported C++ classes in Python, -as well as inheriting from multiple Python and/or pybind-exported classes. +as well as inheriting from multiple Python and/or pybind11-exported classes. There is one caveat regarding the implementation of this feature: @@ -781,7 +779,7 @@ are listed. Module-local class bindings =========================== -When creating a binding for a class, pybind by default makes that binding +When creating a binding for a class, pybind11 by default makes that binding "global" across modules. What this means is that a type defined in one module can be returned from any module resulting in the same Python type. For example, this allows the following: From 4b874616b2eda574c9e92b6e9b858c38108595f5 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Sun, 6 May 2018 13:54:10 +0000 Subject: [PATCH 0022/1206] Misc. typos (#1384) Found via `codespell` --- docs/advanced/cast/eigen.rst | 2 +- docs/changelog.rst | 6 +++--- include/pybind11/cast.h | 2 +- tests/test_smart_ptr.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/advanced/cast/eigen.rst b/docs/advanced/cast/eigen.rst index 9c7cbd22..7cbeac00 100644 --- a/docs/advanced/cast/eigen.rst +++ b/docs/advanced/cast/eigen.rst @@ -275,7 +275,7 @@ Vectors versus column/row matrices Eigen and numpy have fundamentally different notions of a vector. In Eigen, a vector is simply a matrix with the number of columns or rows set to 1 at compile time (for a column vector or row vector, respectively). Numpy, in -contast, has comparable 2-dimensional 1xN and Nx1 arrays, but *also* has +contrast, has comparable 2-dimensional 1xN and Nx1 arrays, but *also* has 1-dimensional arrays of size N. When passing a 2-dimensional 1xN or Nx1 array to Eigen, the Eigen type must diff --git a/docs/changelog.rst b/docs/changelog.rst index c559b47b..c6ef76b5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -597,7 +597,7 @@ Happy Christmas! being (notably dynamic attributes in custom types). `#527 `_. -* Significant work on the documentation -- in particular, the monolitic +* Significant work on the documentation -- in particular, the monolithic ``advanced.rst`` file was restructured into a easier to read hierarchical organization. `#448 `_. @@ -665,8 +665,8 @@ Happy Christmas! `_. - 3. This version of pybind11 uses a redesigned mechnism for instantiating - trempoline classes that are used to override virtual methods from within + 3. This version of pybind11 uses a redesigned mechanism for instantiating + trampoline classes that are used to override virtual methods from within Python. This led to the following user-visible syntax change: instead of .. code-block:: cpp diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index efcdc5ba..b65d961f 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1204,7 +1204,7 @@ template struct string_caster { #else // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a - // non-const char * arguments, which is also a nuissance, so bypass the whole thing by just + // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just // passing the encoding as a string value, which works properly: return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); #endif diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 5f298506..61066f76 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -19,7 +19,7 @@ // ref is a wrapper for 'Object' which uses intrusive reference counting // It is always possible to construct a ref 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, true); // Make pybind11 aware of the non-standard getter member function namespace pybind11 { namespace detail { From 5ef1af138dc3ef94c05274ae554e70d64cd589bf Mon Sep 17 00:00:00 2001 From: Naotoshi Seo Date: Sun, 6 May 2018 13:59:25 +0000 Subject: [PATCH 0023/1206] Fix SEGV to create empty shaped numpy array (#1371) Fix a segfault when creating a 0-dimension, c-strides array. --- include/pybind11/numpy.h | 5 +++-- tests/test_numpy_array.cpp | 3 +++ tests/test_numpy_array.py | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index c7cbb9fd..0d92af2d 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -758,8 +758,9 @@ class array : public buffer { static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { auto ndim = shape.size(); std::vector strides(ndim, itemsize); - for (size_t i = ndim - 1; i > 0; --i) - strides[i - 1] = strides[i] * shape[i]; + if (ndim > 0) + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; return strides; } diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 2046c0e0..79a157e6 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -102,6 +102,9 @@ TEST_SUBMODULE(numpy_array, sm) { sm.def("make_f_array", [] { return py::array_t({ 2, 2 }, { 4, 8 }); }); sm.def("make_c_array", [] { return py::array_t({ 2, 2 }, { 8, 4 }); }); + // test_empty_shaped_array + sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); }); + // test_wrap sm.def("wrap", [](py::array a) { return py::array( diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 50848989..1e83135b 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -135,6 +135,10 @@ 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() + + def test_wrap(): def assert_references(a, b, base=None): from distutils.version import LooseVersion From ce9d6e2c0d02019c957ad48dad86a06d54103565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Becker?= Date: Mon, 7 May 2018 15:18:08 +0200 Subject: [PATCH 0024/1206] Fixed typo in classes.rst (#1388) Fixed typos (erroneous `;`) in `classes.rst`. --- docs/advanced/classes.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 8e4485ee..c7dbc676 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -46,7 +46,7 @@ Normally, the binding code for these classes would look as follows: .. code-block:: cpp PYBIND11_MODULE(example, m) { - py::class_(m, "Animal"); + py::class_(m, "Animal") .def("go", &Animal::go); py::class_(m, "Dog") @@ -95,7 +95,7 @@ The binding code also needs a few minor adaptations (highlighted): :emphasize-lines: 2,3 PYBIND11_MODULE(example, m) { - py::class_(m, "Animal"); + py::class_(m, "Animal") .def(py::init<>()) .def("go", &Animal::go); From e763f046899ce09bf8a586ea75a7d9a9d27e70e4 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 18 May 2018 12:48:32 -0300 Subject: [PATCH 0025/1206] Base class destructor should be virtual Fixes #1401 --- tests/test_virtual_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index a69ba153..6ffdf33a 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -17,7 +17,7 @@ class ExampleVirt { 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 " From 55dc131944c764ba7e30085b971a9d70531114b3 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 14 Nov 2017 19:52:11 -0800 Subject: [PATCH 0026/1206] Clarify docs for functions taking bytes and not str. --- .gitignore | 1 + docs/advanced/cast/strings.rst | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c444c17e..979fd443 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ MANIFEST *.py[co] *.egg-info *~ +.*.swp .DS_Store /dist /build diff --git a/docs/advanced/cast/strings.rst b/docs/advanced/cast/strings.rst index 2cdbade3..e25701ec 100644 --- a/docs/advanced/cast/strings.rst +++ b/docs/advanced/cast/strings.rst @@ -58,7 +58,9 @@ Passing bytes to C++ -------------------- A Python ``bytes`` object will be passed to C++ functions that accept -``std::string`` or ``char*`` *without* conversion. +``std::string`` or ``char*`` *without* conversion. On Python 3, in order to +make a function *only* accept ``bytes`` (and not ``str``), declare it as taking +a ``py::bytes`` argument. Returning C++ strings to Python From 58e551cc737d77575b05de198da49479b81222a1 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 16 Apr 2018 19:08:45 -0700 Subject: [PATCH 0027/1206] Properly report exceptions thrown during module initialization. If an exception is thrown during module initialization, the error_already_set destructor will try to call `get_internals()` *after* setting Python's error indicator, resulting in a `SystemError: ... returned with an error set`. Fix that by temporarily stashing away the error indicator in the destructor. --- include/pybind11/pybind11.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index a04d5365..9c7f3688 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1894,6 +1894,7 @@ class gil_scoped_release { }; error_already_set::~error_already_set() { if (type) { + error_scope scope; gil_scoped_acquire gil; type.release().dec_ref(); value.release().dec_ref(); From 4b84bad7efc0100fc6cd2382507bb1a38ee1ea3b Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 21 Jun 2018 16:31:46 +0200 Subject: [PATCH 0028/1206] Fix Travis GCC 7 Python 3.6.6 (#1436) Add missing python3 distutils on Debian "buster". --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index abbd79ef..4591e006 100644 --- a/.travis.yml +++ b/.travis.yml @@ -108,7 +108,7 @@ before_install: export CXX=g++-$GCC CC=gcc-$GCC fi if [ "$GCC" = "6" ]; then DOCKER=${ARCH:+$ARCH/}debian:stretch - elif [ "$GCC" = "7" ]; then DOCKER=debian:buster EXTRA_PACKAGES+=" catch" DOWNLOAD_CATCH=OFF + elif [ "$GCC" = "7" ]; then DOCKER=debian:buster EXTRA_PACKAGES+=" catch python3-distutils" DOWNLOAD_CATCH=OFF fi elif [ "$TRAVIS_OS_NAME" = "osx" ]; then export CXX=clang++ CC=clang; From 9b02856293fb66543d135ac3ad441326dd7bc066 Mon Sep 17 00:00:00 2001 From: Maciek Starzyk Date: Sat, 2 Jun 2018 20:21:19 +0200 Subject: [PATCH 0029/1206] Update PyPI URLs --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 5b03bb2c..f677f2af 100644 --- a/setup.py +++ b/setup.py @@ -61,8 +61,8 @@ def run(self): description='Seamless operability between C++11 and Python', author='Wenzel Jakob', author_email='wenzel.jakob@epfl.ch', - url='https://github.com/pybind11/pybind11', - download_url='https://github.com/pybind11/pybind11/tarball/v' + __version__, + url='https://github.com/pybind/pybind11', + download_url='https://github.com/pybind/pybind11/tarball/v' + __version__, packages=['pybind11'], license='BSD', headers=headers, From e3cb2a674a3a8131717c2eb89f813698b4546fdb Mon Sep 17 00:00:00 2001 From: Khachajantc Michael Date: Wed, 20 Jun 2018 18:33:50 +0300 Subject: [PATCH 0030/1206] Use std::addressof to obtain holder address instead of operator& --- include/pybind11/cast.h | 4 +-- include/pybind11/pybind11.h | 10 +++--- tests/test_smart_ptr.cpp | 64 +++++++++++++++++++++++++++++++++++++ tests/test_smart_ptr.py | 44 +++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index b65d961f..ce4dcd38 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1423,7 +1423,7 @@ struct copyable_holder_caster : public type_caster_base { explicit operator type*() { return this->value; } explicit operator type&() { return *(this->value); } - explicit operator holder_type*() { return &holder; } + explicit operator holder_type*() { return std::addressof(holder); } // Workaround for Intel compiler bug // see pybind11 issue 94 @@ -1493,7 +1493,7 @@ struct move_only_holder_caster { static handle cast(holder_type &&src, return_value_policy, handle) { auto *ptr = holder_helper::get(src); - return type_caster_base::cast_holder(ptr, &src); + return type_caster_base::cast_holder(ptr, std::addressof(src)); } static constexpr auto name = type_caster_base::name; }; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9c7f3688..504e0a9b 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1271,25 +1271,25 @@ class class_ : public detail::generic_type { auto sh = std::dynamic_pointer_cast( v_h.value_ptr()->shared_from_this()); if (sh) { - new (&v_h.holder()) holder_type(std::move(sh)); + new (std::addressof(v_h.holder())) holder_type(std::move(sh)); v_h.set_holder_constructed(); } } catch (const std::bad_weak_ptr &) {} if (!v_h.holder_constructed() && inst->owned) { - new (&v_h.holder()) holder_type(v_h.value_ptr()); + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); v_h.set_holder_constructed(); } } static void init_holder_from_existing(const detail::value_and_holder &v_h, const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { - new (&v_h.holder()) holder_type(*reinterpret_cast(holder_ptr)); + new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); } static void init_holder_from_existing(const detail::value_and_holder &v_h, const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { - new (&v_h.holder()) holder_type(std::move(*const_cast(holder_ptr))); + new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); } /// Initialize holder object, variant 2: try to construct from existing holder object, if possible @@ -1299,7 +1299,7 @@ class class_ : public detail::generic_type { init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); v_h.set_holder_constructed(); } else if (inst->owned || detail::always_construct_holder::value) { - new (&v_h.holder()) holder_type(v_h.value_ptr()); + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); v_h.set_holder_constructed(); } } diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 61066f76..098b1829 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -55,6 +55,35 @@ class custom_unique_ptr { }; PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr); +// 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 +class shared_ptr_with_addressof_operator { + std::shared_ptr 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); + +// 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 +class unique_ptr_with_addressof_operator { + std::unique_ptr 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); + TEST_SUBMODULE(smart_ptr, m) { @@ -238,6 +267,41 @@ TEST_SUBMODULE(smart_ptr, m) { py::class_>(m, "TypeWithMoveOnlyHolder") .def_static("make", []() { return custom_unique_ptr(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; + py::class_(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; + py::class_(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_(m, "HeldByDefaultHolder") diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 4dfe0036..60f48b39 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -203,6 +203,50 @@ 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: From 97b20e537abaca2a0235b3272cbb39340e1f4a43 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Sun, 24 Jun 2018 15:22:41 +0200 Subject: [PATCH 0031/1206] CMake: Remember Python Version (#1434) It is useful not only to remember the python libs and includes but also the interpreter version in cache. If users call pybind11 throught `add_subdirectories` they will otherwise have no access to the selected interpreter version. The interpreter version is useful for downstream projects, e.g. to select default `lib/pythonX.Y/site-packages/` install paths. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4280ba74..85ecd902 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,8 @@ set(PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} CACHE INTERNAL "") set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "") set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "") set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "") +set(PYTHON_VERSION_MAJOR ${PYTHON_VERSION_MAJOR} CACHE INTERNAL "") +set(PYTHON_VERSION_MINOR ${PYTHON_VERSION_MINOR} CACHE INTERNAL "") # NB: when adding a header don't forget to also add it to setup.py set(PYBIND11_HEADERS From baf6b99004bc61aee73bf199bb28669fd2f3a7cf Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 24 Jun 2018 15:38:09 +0200 Subject: [PATCH 0032/1206] Silence GCC8's -Wcast-function-type. (#1396) * Silence GCC8's -Wcast-function-type. See https://bugs.python.org/issue33012 and PRs linked therein. --- include/pybind11/pybind11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 504e0a9b..cba219c0 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -306,7 +306,7 @@ class cpp_function : public function { rec->def = new PyMethodDef(); std::memset(rec->def, 0, sizeof(PyMethodDef)); rec->def->ml_name = rec->name; - rec->def->ml_meth = reinterpret_cast(*dispatcher); + rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; capsule rec_capsule(rec, [](void *ptr) { From 534b756cb3244aca959359c07d44e4fed3498ba8 Mon Sep 17 00:00:00 2001 From: Thomas Hrabe <3632672+thomashrabe@users.noreply.github.com> Date: Sun, 24 Jun 2018 06:41:27 -0700 Subject: [PATCH 0033/1206] Minor documentation clarification in numpy.rst (#1356) --- docs/advanced/pycpp/numpy.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index 18eff80b..71917ce6 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -261,7 +261,7 @@ simply using ``vectorize``). namespace py = pybind11; py::array_t add_arrays(py::array_t input1, py::array_t input2) { - auto buf1 = input1.request(), buf2 = input2.request(); + py::buffer_info buf1 = input1.request(), buf2 = input2.request(); if (buf1.ndim != 1 || buf2.ndim != 1) throw std::runtime_error("Number of dimensions must be one"); @@ -272,7 +272,7 @@ simply using ``vectorize``). /* No pointer is passed, so NumPy will allocate the buffer */ auto result = py::array_t(buf1.size); - auto buf3 = result.request(); + py::buffer_info buf3 = result.request(); double *ptr1 = (double *) buf1.ptr, *ptr2 = (double *) buf2.ptr, From 221fb1e11e79b50308ec5afc55ea1337b828fa77 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 17 Jul 2018 15:48:51 +0200 Subject: [PATCH 0034/1206] Untangle cast logic to not implicitly require castability (#1442) The current code requires implicitly that integral types are cast-able to floating point. In case of strongly-typed integrals (e.g. as explained at http://www.ilikebigbits.com/blog/2014/5/6/type-safe-identifiers-in-c) this is not always the case. This commit uses SFINAE to move the numeric conversions into separate `cast()` implementations to avoid the issue. --- include/pybind11/cast.h | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index ce4dcd38..2d33d81b 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -17,6 +17,7 @@ #include #include #include +#include #if defined(PYBIND11_CPP17) # if defined(__has_include) @@ -1009,21 +1010,34 @@ struct type_caster::value && !is_std_char_t return true; } - static handle cast(T src, return_value_policy /* policy */, handle /* parent */) { - if (std::is_floating_point::value) { - return PyFloat_FromDouble((double) src); - } else if (sizeof(T) <= sizeof(ssize_t)) { - // This returns a long automatically if needed - if (std::is_signed::value) - return PYBIND11_LONG_FROM_SIGNED(src); - else - return PYBIND11_LONG_FROM_UNSIGNED(src); - } else { - if (std::is_signed::value) - return PyLong_FromLongLong((long long) src); - else - return PyLong_FromUnsignedLongLong((unsigned long long) src); - } + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); } PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); From b30734ee9faf90eba7bb18962cf1d08baeb96863 Mon Sep 17 00:00:00 2001 From: Boris Dalstein Date: Sun, 1 Jul 2018 17:47:22 +0200 Subject: [PATCH 0035/1206] Fix typo in doc: build-in -> built-in --- docs/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference.rst b/docs/reference.rst index e41141bd..3f748497 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -80,7 +80,7 @@ Redirecting C++ streams .. doxygenfunction:: add_ostream_redirect -Python build-in functions +Python built-in functions ========================= .. doxygengroup:: python_builtins From b4719a60d32821fa47aaaba7c1035cb447bab386 Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Tue, 17 Jul 2018 16:55:52 +0200 Subject: [PATCH 0036/1206] Switching deprecated Thread Local Storage (TLS) usage in Python 3.7 to Thread Specific Storage (TSS) (#1454) * Switching deprecated Thread Local Storage (TLS) usage in Python 3.7 to Thread Specific Storage (TSS) * Changing Python version from 3.6 to 3.7 for Travis CI, to match brew's version of Python 3 * Introducing PYBIND11_ macros to switch between TLS and TSS API --- .travis.yml | 2 +- include/pybind11/detail/internals.h | 36 +++++++++++++++++++++++++---- include/pybind11/pybind11.h | 20 ++++------------ 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4591e006..928f517f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,7 +68,7 @@ matrix: env: PYTHON=2.7 CPP=14 CLANG CMAKE=1 - os: osx osx_image: xcode9 - env: PYTHON=3.6 CPP=14 CLANG DEBUG=1 + env: PYTHON=3.7 CPP=14 CLANG DEBUG=1 # Test a PyPy 2.7 build - os: linux env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8 diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index e39f3869..e6f851ab 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -18,6 +18,25 @@ inline PyTypeObject *make_static_property_type(); inline PyTypeObject *make_default_metaclass(); inline PyObject *make_object_base_type(PyTypeObject *metaclass); +// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new +// Thread Specific Storage (TSS) API. +#if PY_VERSION_HEX >= 0x03070000 + #define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr + #define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) + #define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) + #define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) +#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 + #define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) + #if PY_MAJOR_VERSION < 3 + #define PYBIND11_TLS_REPLACE_VALUE(key, value) do { PyThread_delete_key_value((key)); PyThread_set_key_value((key), (value)); } while (false) + #else + #define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value)) + #endif + #define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr) +#endif + // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly // other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module // even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under @@ -79,7 +98,7 @@ struct internals { PyTypeObject *default_metaclass; PyObject *instance_base; #if defined(WITH_THREAD) - decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x + PYBIND11_TLS_KEY_INIT(tstate); PyInterpreterState *istate = nullptr; #endif }; @@ -111,7 +130,7 @@ struct type_info { }; /// Tracks the `internals` and `type_info` ABI version independent of the main library version -#define PYBIND11_INTERNALS_VERSION 1 +#define PYBIND11_INTERNALS_VERSION 2 #if defined(WITH_THREAD) # define PYBIND11_INTERNALS_KIND "" @@ -166,8 +185,17 @@ PYBIND11_NOINLINE inline internals &get_internals() { #if defined(WITH_THREAD) PyEval_InitThreads(); PyThreadState *tstate = PyThreadState_Get(); - internals_ptr->tstate = PyThread_create_key(); - PyThread_set_key_value(internals_ptr->tstate, tstate); + #if PY_VERSION_HEX >= 0x03070000 + internals_ptr->tstate = PyThread_tss_alloc(); + if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) + pybind11_fail("get_internals: could not successfully initialize the TSS key!"); + PyThread_tss_set(internals_ptr->tstate, tstate); + #else + internals_ptr->tstate = PyThread_create_key(); + if (internals_ptr->tstate == -1) + pybind11_fail("get_internals: could not successfully initialize the TLS key!"); + PyThread_set_key_value(internals_ptr->tstate, tstate); + #endif internals_ptr->istate = tstate->interp; #endif builtins[id] = capsule(internals_pp); diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index cba219c0..5ddc601e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1774,7 +1774,7 @@ class gil_scoped_acquire { public: PYBIND11_NOINLINE gil_scoped_acquire() { auto const &internals = detail::get_internals(); - tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate); + tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); if (!tstate) { tstate = PyThreadState_New(internals.istate); @@ -1783,10 +1783,7 @@ class gil_scoped_acquire { pybind11_fail("scoped_acquire: could not create thread state!"); #endif tstate->gilstate_counter = 0; - #if PY_MAJOR_VERSION < 3 - PyThread_delete_key_value(internals.tstate); - #endif - PyThread_set_key_value(internals.tstate, tstate); + PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); } else { release = detail::get_thread_state_unchecked() != tstate; } @@ -1825,7 +1822,7 @@ class gil_scoped_acquire { #endif PyThreadState_Clear(tstate); PyThreadState_DeleteCurrent(); - PyThread_delete_key_value(detail::get_internals().tstate); + PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); release = false; } } @@ -1850,11 +1847,7 @@ class gil_scoped_release { tstate = PyEval_SaveThread(); if (disassoc) { auto key = internals.tstate; - #if PY_MAJOR_VERSION < 3 - PyThread_delete_key_value(key); - #else - PyThread_set_key_value(key, nullptr); - #endif + PYBIND11_TLS_DELETE_VALUE(key); } } ~gil_scoped_release() { @@ -1863,10 +1856,7 @@ class gil_scoped_release { PyEval_RestoreThread(tstate); if (disassoc) { auto key = detail::get_internals().tstate; - #if PY_MAJOR_VERSION < 3 - PyThread_delete_key_value(key); - #endif - PyThread_set_key_value(key, tstate); + PYBIND11_TLS_REPLACE_VALUE(key, tstate); } } private: From cbd16a8247b65c6eaab0369a54a25f51c5c0bd45 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 17 Jul 2018 16:56:26 +0200 Subject: [PATCH 0037/1206] stl.h: propagate return value policies to type-specific casters (#1455) * stl.h: propagate return value policies to type-specific casters Return value policies for containers like those handled in in 'stl.h' are currently broken. The problem is that detail::return_value_policy_override::policy() always returns 'move' when given a non-pointer/reference type, e.g. 'std::vector<...>'. This is sensible behavior for custom types that are exposed via 'py::class_<>', but it does not make sense for types that are handled by other type casters (STL containers, Eigen matrices, etc.). This commit changes the behavior so that detail::return_value_policy_override only becomes active when the type caster derives from type_caster_generic. Furthermore, the override logic is called recursively in STL type casters to enable key/value-specific behavior. --- include/pybind11/cast.h | 8 ++++++-- include/pybind11/eigen.h | 8 -------- include/pybind11/pybind11.h | 2 +- include/pybind11/stl.h | 10 ++++++++-- tests/test_stl.cpp | 18 ++++++++++++++++++ tests/test_stl.py | 10 ++++++++++ 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 2d33d81b..4084d42a 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1606,9 +1606,13 @@ template using cast_is_temporary_value_reference = bool_constant // When a value returned from a C++ function is being cast back to Python, we almost always want to // force `policy = move`, regardless of the return value policy the function/method was declared -// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by -// specializing this struct. +// with. template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +template struct return_value_policy_override>::value, void>> { static return_value_policy policy(return_value_policy p) { return !std::is_lvalue_reference::value && !std::is_pointer::value ? return_value_policy::move : p; diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 531d0706..d963d965 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -353,14 +353,6 @@ struct type_caster::value>> { Type value; }; -// Eigen Ref/Map classes have slightly different policy requirements, meaning we don't want to force -// `move` when a Ref/Map rvalue is returned; we treat Ref<> sort of like a pointer (we care about -// the underlying data, not the outer shell). -template -struct return_value_policy_override::value>> { - static return_value_policy policy(return_value_policy p) { return p; } -}; - // Base class for casting reference/map/block/etc. objects back to python. template struct eigen_map_caster { private: diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 5ddc601e..e986d007 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -145,7 +145,7 @@ class cpp_function : public function { capture *cap = const_cast(reinterpret_cast(data)); /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ - const auto policy = return_value_policy_override::policy(call.func.policy); + return_value_policy policy = return_value_policy_override::policy(call.func.policy); /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ using Guard = extract_guard_t; diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index eb4c812c..faa7087e 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -83,6 +83,7 @@ template struct set_caster { template static handle cast(T &&src, return_value_policy policy, handle parent) { + policy = return_value_policy_override::policy(policy); pybind11::set s; for (auto &&value : src) { auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); @@ -118,9 +119,11 @@ template struct map_caster { template static handle cast(T &&src, return_value_policy policy, handle parent) { dict d; + return_value_policy policy_key = return_value_policy_override::policy(policy); + return_value_policy policy_value = return_value_policy_override::policy(policy); for (auto &&kv : src) { - auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy, parent)); - auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy, parent)); + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); if (!key || !value) return handle(); d[key] = value; @@ -158,6 +161,7 @@ template struct list_caster { public: template static handle cast(T &&src, return_value_policy policy, handle parent) { + policy = return_value_policy_override::policy(policy); list l(src.size()); size_t index = 0; for (auto &&value : src) { @@ -252,6 +256,7 @@ template struct optional_caster { static handle cast(T_ &&src, return_value_policy policy, handle parent) { if (!src) return none().inc_ref(); + policy = return_value_policy_override::policy(policy); return value_conv::cast(*std::forward(src), policy, parent); } @@ -356,6 +361,7 @@ struct variant_caster> { template struct type_caster> : variant_caster> { }; #endif + NAMESPACE_END(detail) inline std::ostream &operator<<(std::ostream &os, const handle &obj) { diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 7d53e9c1..cd0985d0 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -8,6 +8,7 @@ */ #include "pybind11_tests.h" +#include "constructor_stats.h" #include // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 @@ -235,4 +236,21 @@ TEST_SUBMODULE(stl, m) { // test_stl_pass_by_pointer m.def("stl_pass_by_pointer", [](std::vector* v) { return *v; }, "v"_a=nullptr); + + class Placeholder { + public: + Placeholder() { print_created(this); } + Placeholder(const Placeholder &) = delete; + ~Placeholder() { print_destroyed(this); } + }; + py::class_(m, "Placeholder"); + + /// test_stl_vector_ownership + m.def("test_stl_ownership", + []() { + std::vector result; + result.push_back(new Placeholder()); + return result; + }, + py::return_value_policy::take_ownership); } diff --git a/tests/test_stl.py b/tests/test_stl.py index 422b02c2..2c5e995f 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -2,6 +2,7 @@ from pybind11_tests import stl as m from pybind11_tests import UserType +from pybind11_tests import ConstructorStats def test_vector(doc): @@ -198,3 +199,12 @@ def test_missing_header_message(): with pytest.raises(TypeError) as excinfo: cm.missing_header_return() assert expected_message in str(excinfo.value) + + +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 From f7bc18f528bb35cd06c93d0a58c17e6eea3fa68c Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 19 Jul 2018 15:03:26 -0300 Subject: [PATCH 0038/1206] Fix compatibility with catch v2 Catch v2 changed the `run(...)` signature to take a `char *argv[]`, arguing partly that technically a `char *argv[]` type is the correct `main()` signature rather than `const char *argv[]`. Dropping the `const` here doesn't appear to cause any problems with catch v1 (tested against both the cmake-downloaded 1.9.3 and Debian's 1.12.1 package) so we can follow suit. --- tests/test_embed/catch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_embed/catch.cpp b/tests/test_embed/catch.cpp index 236409db..dd137385 100644 --- a/tests/test_embed/catch.cpp +++ b/tests/test_embed/catch.cpp @@ -14,7 +14,7 @@ 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); From d4b37a284aa37f1d78f136fc854e41a54d5f67f2 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 28 Aug 2018 00:23:59 +0200 Subject: [PATCH 0039/1206] added py::ellipsis() method for slicing of multidimensional NumPy arrays This PR adds a new py::ellipsis() method which can be used in conjunction with NumPy's generalized slicing support. For instance, the following is now valid (where "a" is a NumPy array): py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; --- docs/advanced/pycpp/numpy.rst | 20 ++++++++++++++++++++ include/pybind11/pytypes.h | 11 +++++++++++ tests/test_numpy_array.cpp | 6 ++++++ tests/test_numpy_array.py | 6 ++++++ 4 files changed, 43 insertions(+) diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index 71917ce6..458f99e9 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -364,3 +364,23 @@ uses of ``py::array``: The file :file:`tests/test_numpy_array.cpp` contains additional examples demonstrating the use of this feature. + +Ellipsis +======== + +Python 3 provides a convenient ``...`` ellipsis notation that is often used to +slice multidimensional arrays. For instance, the following snippet extracts the +middle dimensions of a tensor with the first and last index set to zero. + +.. code-block:: python + + a = # a NumPy array + b = a[0, ..., 0] + +The function ``py::ellipsis()`` function can be used to perform the same +operation on the C++ side: + +.. code-block:: cpp + + py::array a = /* A NumPy array */; + py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index bcee8b5b..976abf86 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -693,6 +693,9 @@ inline bool PyIterable_Check(PyObject *obj) { } inline bool PyNone_Check(PyObject *o) { return o == Py_None; } +#if PY_MAJOR_VERSION >= 3 +inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } +#endif inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } @@ -967,6 +970,14 @@ class none : public object { none() : object(Py_None, borrowed_t{}) { } }; +#if PY_MAJOR_VERSION >= 3 +class ellipsis : public object { +public: + PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) + ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } +}; +#endif + class bool_ : public object { public: PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 79a157e6..57025949 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -295,4 +295,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/tests/test_numpy_array.py b/tests/test_numpy_array.py index 1e83135b..8ac0e66f 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -408,3 +408,9 @@ 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,) From 885b5b905a58247b5038cc02cf12e38b1573f5f7 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 28 Aug 2018 23:04:47 +0200 Subject: [PATCH 0040/1206] Eigen test suite: don't create a np.matrix --- tests/test_eigen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 64fb2e5a..45f64ca9 100644 --- a/tests/test_eigen.py +++ b/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(): From 7bb1da969a2747c24e6bd29dad6d44c4c5796141 Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Wed, 15 Aug 2018 17:13:36 +0200 Subject: [PATCH 0041/1206] fix copy-paste error: non-const -> const --- include/pybind11/numpy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 0d92af2d..bdc3a5dd 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1593,7 +1593,7 @@ Helper vectorize(Return (Class::*f)(Args...)) { return Helper(std::mem_fn(f)); } -// Vectorize a class method (non-const): +// Vectorize a class method (const): template ())), Return, const Class *, Args...>> Helper vectorize(Return (Class::*f)(Args...) const) { From 3789b4f9fd4c126c919f933ba0b8605683da4ac2 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 29 Aug 2018 00:07:35 +0200 Subject: [PATCH 0042/1206] Update C++ macros for C++17 and MSVC Z mode (#1347) --- include/pybind11/detail/common.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 3c672289..c150cce8 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -27,15 +27,16 @@ # endif #endif -#if !defined(_MSC_VER) && !defined(__INTEL_COMPILER) +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) # if __cplusplus >= 201402L # define PYBIND11_CPP14 -# if __cplusplus > 201402L /* Temporary: should be updated to >= the final C++17 value once known */ +# if __cplusplus >= 201703L # define PYBIND11_CPP17 # endif # endif -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && __cplusplus == 199711L // MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer # if _MSVC_LANG >= 201402L # define PYBIND11_CPP14 # if _MSVC_LANG > 201402L && _MSC_VER >= 1910 From 2cbafb057f0031ac382f62c797a10bad4ea777b8 Mon Sep 17 00:00:00 2001 From: Justin Bassett Date: Wed, 29 Aug 2018 02:48:30 -0700 Subject: [PATCH 0043/1206] fix detail::pythonbuf::overflow()'s return value to return not_eof(c) (#1479) --- include/pybind11/iostream.h | 2 +- tests/test_iostream.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/pybind11/iostream.h b/include/pybind11/iostream.h index a9c27aac..3caf5563 100644 --- a/include/pybind11/iostream.h +++ b/include/pybind11/iostream.h @@ -34,7 +34,7 @@ class pythonbuf : public std::streambuf { *pptr() = traits_type::to_char_type(c); pbump(1); } - return sync() ? traits_type::not_eof(c) : traits_type::eof(); + return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); } int sync() { diff --git a/tests/test_iostream.py b/tests/test_iostream.py index 3364849a..167b8875 100644 --- a/tests/test_iostream.py +++ b/tests/test_iostream.py @@ -53,6 +53,16 @@ def test_captured(capsys): assert stdout == '' 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!" From e0f3a766e951ebbf2aadb31f41eb490de5bb607e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 29 Aug 2018 12:10:48 +0200 Subject: [PATCH 0044/1206] Fixed flake8 error in test_iostream.py --- tests/test_iostream.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_iostream.py b/tests/test_iostream.py index 167b8875..27095b27 100644 --- a/tests/test_iostream.py +++ b/tests/test_iostream.py @@ -53,6 +53,7 @@ def test_captured(capsys): assert stdout == '' 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!" From 3a94561c4d05abf9f3bdadb1e68ce812705f9a70 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 29 Aug 2018 13:18:43 +0200 Subject: [PATCH 0045/1206] Debug Builds: -DPy_DEBUG (#1438) builds against a python debug library were unreliable and could lead to symbol errors during linking. Setting the `Py_DEBUG` define is necessary when linking against a debug build: https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib https://docs.python.org/2/c-api/intro.html#debugging-builds https://docs.python.org/3.6/c-api/intro.html#debugging-builds --- tools/pybind11Tools.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index a7c471a0..52a70c23 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -135,6 +135,13 @@ function(pybind11_add_module target_name) PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config PRIVATE ${PYTHON_INCLUDE_DIRS}) + # Python debug libraries expose slightly different objects + # https://docs.python.org/3.6/c-api/intro.html#debugging-builds + # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib + if(PYTHON_IS_DEBUG) + target_compile_definitions(${target_name} PRIVATE Py_DEBUG) + endif() + # The prefix and extension are provided by FindPythonLibsNew.cmake set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") From 435dbdd114d135712a8a3b68eb9e640756ffe73b Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 29 Aug 2018 13:20:11 +0200 Subject: [PATCH 0046/1206] add_module: allow include as SYSTEM (#1416) pybind11 headers passed via the `pybind11_add_module` CMake function can now be included as `SYSTEM` includes (`-isystem`). This allows to set stricter (or experimental) warnings in calling projects that might throw otherwise in headers a user of pybind11 can not influence. --- docs/compiling.rst | 6 +++++- tools/pybind11Tools.cmake | 10 +++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/compiling.rst b/docs/compiling.rst index b5d6ce94..bbd7a2cc 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -59,7 +59,7 @@ function with the following signature: .. code-block:: cmake pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] - [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) + [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) This function behaves very much like CMake's builtin ``add_library`` (in fact, it's a wrapper function around that command). It will add a library target @@ -86,6 +86,10 @@ latter optimizations are never applied in ``Debug`` mode. If ``NO_EXTRAS`` is given, they will always be disabled, even in ``Release`` mode. However, this will result in code bloat and is generally not recommended. +By default, pybind11 and Python headers will be included with ``-I``. In order +to include pybind11 as system library, e.g. to avoid warnings in downstream +code with warn-levels outside of pybind11's scope, set the option ``SYSTEM``. + As stated above, LTO is enabled by default. Some newer compilers also support different flavors of LTO such as `ThinLTO`_. Setting ``THIN_LTO`` will cause the function to prefer this flavor if available. The function falls back to diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 52a70c23..6515f276 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -110,10 +110,10 @@ endfunction() # Build a Python extension module: # pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] -# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) +# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) # function(pybind11_add_module target_name) - set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS THIN_LTO) + set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) if(ARG_MODULE AND ARG_SHARED) @@ -130,7 +130,11 @@ function(pybind11_add_module target_name) add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) - target_include_directories(${target_name} + if(ARG_SYSTEM) + set(inc_isystem SYSTEM) + endif() + + target_include_directories(${target_name} ${inc_isystem} PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config PRIVATE ${PYTHON_INCLUDE_DIRS}) From 77374a7e5f48b64ee8b51de0a7d3783177a67716 Mon Sep 17 00:00:00 2001 From: Michael Goulding Date: Sat, 8 Sep 2018 07:25:11 -0700 Subject: [PATCH 0047/1206] VS 15.8.0 Preview 4.0 has a bug with alias templates (#1462) * VS 15.8.0 Preview 4.0 has a bug with alias templates --- include/pybind11/detail/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index c150cce8..5ff74856 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -477,7 +477,7 @@ template struct void_t_impl { using type = void; }; template using void_t = typename void_t_impl::type; /// Compile-time all/any/none of that check the boolean value of all template types -#ifdef __cpp_fold_expressions +#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) template using all_of = bool_constant<(Ts::value && ...)>; template using any_of = bool_constant<(Ts::value || ...)>; #elif !defined(_MSC_VER) From 44e39e0de7ad011de9862a66666632401d66cdf3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 11 Sep 2018 09:32:45 +0200 Subject: [PATCH 0048/1206] fix regression reported by @cyfdecyf in #1454 (#1517) --- include/pybind11/detail/internals.h | 32 ++++++++++++++++++----------- tests/test_virtual_functions.cpp | 28 ++++++++++++++++++++++++- tests/test_virtual_functions.py | 6 ++++++ 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index e6f851ab..78d4afed 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -21,20 +21,28 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); // The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new // Thread Specific Storage (TSS) API. #if PY_VERSION_HEX >= 0x03070000 - #define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr - #define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) - #define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) - #define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) +# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr +# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) +# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) #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 - #define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) - #if PY_MAJOR_VERSION < 3 - #define PYBIND11_TLS_REPLACE_VALUE(key, value) do { PyThread_delete_key_value((key)); PyThread_set_key_value((key), (value)); } while (false) - #else - #define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value)) - #endif - #define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr) +# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 +# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) +# if PY_MAJOR_VERSION < 3 +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_delete_key_value(key) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + do { \ + PyThread_delete_key_value((key)); \ + PyThread_set_key_value((key), (value)); \ + } while (false) +# else +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_set_key_value((key), nullptr) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + PyThread_set_key_value((key), (value)) +# endif #endif // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index 6ffdf33a..c9a561c0 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -10,6 +10,7 @@ #include "pybind11_tests.h" #include "constructor_stats.h" #include +#include /* This is an example class that we'll want to be able to extend from Python */ class ExampleVirt { @@ -157,6 +158,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); @@ -416,7 +439,6 @@ template class PyD_Tpl : public PyC_Tpl { }; */ - void initialize_inherited_virtuals(py::module &m) { // test_inherited_virtuals @@ -449,4 +471,8 @@ void initialize_inherited_virtuals(py::module &m) { py::class_>(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/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index 2a92476f..5ce9abd3 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -369,3 +369,9 @@ def lucky_number(self): 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() From 01839dce8de5b9ecd7085e8708e05b98810032bf Mon Sep 17 00:00:00 2001 From: Jeff VanOss Date: Tue, 11 Sep 2018 04:39:17 -0400 Subject: [PATCH 0049/1206] remove duplicate feature from list (#1476) --- README.md | 1 - docs/intro.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index dbebf0a8..135f9e1e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,6 @@ pybind11 can map the following core C++ features to Python - Custom operators - Single and multiple inheritance - STL data structures -- Iterators and ranges - Smart pointers with reference counting like ``std::shared_ptr`` - Internal references with correct reference counting - C++ classes with virtual (and pure virtual) methods can be extended in Python diff --git a/docs/intro.rst b/docs/intro.rst index 118cb504..10e1799a 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -41,7 +41,6 @@ The following core C++ features can be mapped to Python - Custom operators - Single and multiple inheritance - STL data structures -- Iterators and ranges - Smart pointers with reference counting like ``std::shared_ptr`` - Internal references with correct reference counting - C++ classes with virtual (and pure virtual) methods can be extended in Python From 35c82c725097f2d58208f233c4b42a1e2face560 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 11 Sep 2018 10:08:48 +0200 Subject: [PATCH 0050/1206] changelog for version 2.2.4 & features targeted for 2.3.0 --- docs/changelog.rst | 69 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c6ef76b5..9162048a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -15,11 +15,76 @@ v2.3.0 (Not yet released) for non-MSVC compilers). `#934 `_. +* Add basic support for tag-based static polymorphism, where classes + provide a method to returns the desired type of an instance. + `#1326 `_. + * Added support for write only properties. `#1144 `_. -* The ``value()`` method of ``py::enum_`` now accepts an optional docstring - that will be shown in the documentation of the associated enumeration. +* A number of improvements related to enumerations: + + 1. The ``enum_`` implementation was rewritten from scratch to reduce + code bloat. Rather than instantiating a full implementation for each + enumeration, most code is now contained in a generic base class. + `#1511 `_. + + 2. The ``value()`` method of ``py::enum_`` now accepts an optional + docstring that will be shown in the documentation of the associated + enumeration. `#1160 `_. + + 3. check for already existing enum value and throw an error if present. + `#1453 `_. + +* added ``py::ellipsis()`` method for slicing of multidimensional NumPy arrays + `#1502 `_. + +* ``pybind11_add_module()``: allow including Python as a ``SYSTEM`` include path. + `#1416 `_. + +v2.2.4 (September 11, 2018) +----------------------------------------------------- + +* Use new Python 3.7 Thread Specific Storage (TSS) implementation if available. + `#1454 `_, + `#1517 `_. + +* Fixes for newer MSVC versions and C++17 mode. + `#1347 `_, + `#1462 `_. + +* Propagate return value policies to type-specific casters + when casting STL containers. + `#1455 `_. + +* Allow ostream-redirection of more than 1024 characters. + `#1479 `_. + +* Set ``Py_DEBUG`` define when compiling against a debug Python build. + `#1438 `_. + +* Untangle integer logic in number type caster to work for custom + types that may only be castable to a restricted set of builtin types. + `#1442 `_. + +* CMake build system: Remember Python version in cache file. + `#1434 `_. + +* Fix for custom smart pointers: use ``std::addressof`` to obtain holder + address instead of ``operator&``. + `#1435 `_. + +* Properly report exceptions thrown during module initialization. + `#1362 `_. + +* Fixed a segmentation fault when creating empty-shaped NumPy array. + `#1371 `_. + +* The version of Intel C++ compiler must be >= 2017, and this is now checked by + the header files. `#1363 `_. + +* A few minor typo fixes and improvements to the test suite, and + patches that silence compiler warnings. v2.2.3 (April 29, 2018) ----------------------------------------------------- From 5c8746ff135abb390bf95944be593e895a586a50 Mon Sep 17 00:00:00 2001 From: Krzysztof Fornalczyk Date: Tue, 11 Sep 2018 10:59:56 +0200 Subject: [PATCH 0051/1206] check for already existing enum value added; added test (#1453) * check for already existing enum value added; added test * added enum value name to exception message * test for defining enum with multiple identical names moved to test_enum.cpp/py --- include/pybind11/pybind11.h | 5 ++++- tests/test_enum.cpp | 14 ++++++++++++++ tests/test_enum.py | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index e986d007..8e40c9f8 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1463,7 +1463,10 @@ template class enum_ : public class_ { enum_& value(char const* name, Type value, const char *doc = nullptr) { auto v = pybind11::cast(value, return_value_policy::copy); this->attr(name) = v; - m_entries[pybind11::str(name)] = std::make_pair(v, doc); + auto name_converted = pybind11::str(name); + if (m_entries.contains(name_converted)) + throw value_error("Enum error - element with name: " + std::string(name) + " already exists"); + m_entries[name_converted] = std::make_pair(v, doc); return *this; } diff --git a/tests/test_enum.cpp b/tests/test_enum.cpp index 4cd14a96..498a00e1 100644 --- a/tests/test_enum.cpp +++ b/tests/test_enum.cpp @@ -68,4 +68,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_(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/tests/test_enum.py b/tests/test_enum.py index c2c272a2..a031d957 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -148,3 +148,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) == "Enum error - element with name: ONE already exists" From 067100201fbeefc4e4bcc041c7e3a36a28e4c7f7 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 1 Sep 2018 01:09:16 +0200 Subject: [PATCH 0052/1206] object_api: support the number protocol This commit revamps the object_api class so that it maps most C++ operators to their Python analogs. This makes it possible to, e.g. perform arithmetic using a py::int_ or py::array. --- include/pybind11/pytypes.h | 82 ++++++++++++++++++++++++++++++++++++++ tests/test_pytypes.cpp | 20 ++++++++++ tests/test_pytypes.py | 8 ++++ 3 files changed, 110 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 976abf86..ab804b10 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -114,6 +114,35 @@ class object_api : public pyobject_tag { bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } /// Equivalent to ``obj is None`` in Python. bool is_none() const { return derived().ptr() == Py_None; } + /// Equivalent to obj == other in Python + bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } + bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } + bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } + bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } + bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } + bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } + + object operator-() const; + object operator~() const; + object operator+(object_api const &other) const; + object operator+=(object_api const &other) const; + object operator-(object_api const &other) const; + object operator-=(object_api const &other) const; + object operator*(object_api const &other) const; + object operator*=(object_api const &other) const; + object operator/(object_api const &other) const; + object operator/=(object_api const &other) const; + object operator|(object_api const &other) const; + object operator|=(object_api const &other) const; + object operator&(object_api const &other) const; + object operator&=(object_api const &other) const; + object operator^(object_api const &other) const; + object operator^=(object_api const &other) const; + object operator<<(object_api const &other) const; + object operator<<=(object_api const &other) const; + object operator>>(object_api const &other) const; + object operator>>=(object_api const &other) const; + PYBIND11_DEPRECATED("Use py::str(obj) instead") pybind11::str str() const; @@ -124,6 +153,9 @@ class object_api : public pyobject_tag { int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } /// Return a handle to the Python type object underlying the instance handle get_type() const; + +private: + bool rich_compare(object_api const &other, int value) const; }; NAMESPACE_END(detail) @@ -1342,5 +1374,55 @@ str_attr_accessor object_api::doc() const { return attr("__doc__"); } template handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } +template +bool object_api::rich_compare(object_api const &other, int value) const { + int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); + if (rv == -1) + throw error_already_set(); + return rv == 1; +} + +#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ + template object object_api::op() const { \ + object result = reinterpret_steal(fn(derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ + template \ + object object_api::op(object_api const &other) const { \ + object result = reinterpret_steal( \ + fn(derived().ptr(), other.derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) +PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) +PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) +PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) +PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) +PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) +PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) +PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) +PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) +PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) +PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) +PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) +PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) +PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) +PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) +PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) + +#undef PYBIND11_MATH_OPERATOR_UNARY +#undef PYBIND11_MATH_OPERATOR_BINARY + NAMESPACE_END(detail) NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index a962f0cc..af7391d8 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -269,4 +269,24 @@ 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; + }); } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 992e7fc8..66119090 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -1,3 +1,4 @@ +from __future__ import division import pytest import sys @@ -238,3 +239,10 @@ class Unhashable(object): 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 From b4b2292488dc59892ed0ec1c1fe17c4ba663f5ef Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 1 Sep 2018 01:19:16 +0200 Subject: [PATCH 0053/1206] relax operator[] for tuples, lists, and sequences object_api::operator[] has a powerful overload for py::handle that can accept slices, tuples (for NumPy), etc. Lists, sequences, and tuples provide their own specialized operator[], which unfortunately disables this functionality. This is accidental, and the purpose of this commit is to re-enable the more general behavior. This commit is tangentially related to the previous one in that it makes py::handle/py::object et al. behave more like their Python counterparts. --- include/pybind11/pytypes.h | 3 +++ tests/test_pytypes.cpp | 4 ++++ tests/test_pytypes.py | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index ab804b10..fa5ed7cb 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1183,6 +1183,7 @@ class tuple : public object { } size_t size() const { return (size_t) PyTuple_Size(m_ptr); } detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::tuple_iterator begin() const { return {*this, 0}; } detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } }; @@ -1220,6 +1221,7 @@ class sequence : public object { PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) size_t size() const { return (size_t) PySequence_Size(m_ptr); } detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::sequence_iterator begin() const { return {*this, 0}; } detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } }; @@ -1232,6 +1234,7 @@ class list : public object { } size_t size() const { return (size_t) PyList_Size(m_ptr); } detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::list_iterator begin() const { return {*this, 0}; } detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } template void append(T &&val) const { diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index af7391d8..e6c955ff 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -289,4 +289,8 @@ TEST_SUBMODULE(pytypes, m) { l.append(a << b); return l; }); + + m.def("test_list_slicing", [](py::list a) { + return a[py::slice(0, -1, 2)]; + }); } diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 66119090..0116d4ef 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -246,3 +246,8 @@ def test_number_protocol(): 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) From f4245181ae40e1922a76e17e06f659dc8b75cee2 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 1 Sep 2018 01:20:24 +0200 Subject: [PATCH 0054/1206] enum_: move most functionality to a non-template implementation This commit addresses an inefficiency in how enums are created in pybind11. Most of the enum_<> implementation is completely generic -- however, being a template class, it ended up instantiating vast amounts of essentially identical code in larger projects with many enums. This commit introduces a generic non-templated helper class that is compatible with any kind of enumeration. enum_ then becomes a thin wrapper around this new class. The new enum_<> API is designed to be 100% compatible with the old one. --- docs/changelog.rst | 5 + include/pybind11/pybind11.h | 230 +++++++++++++++++++++++------------- tests/test_enum.py | 2 +- tests/test_pickling.py | 6 + 4 files changed, 159 insertions(+), 84 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9162048a..eb25578c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -22,6 +22,11 @@ v2.3.0 (Not yet released) * Added support for write only properties. `#1144 `_. +* Python type wrappers (``py::handle``, ``py::object``, etc.) + now support map Python's number protocol onto C++ arithmetic + operators such as ``operator+``, ``operator/=``, etc. + `#1511 `_. + * A number of improvements related to enumerations: 1. The ``enum_`` implementation was rewritten from scratch to reduce diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 8e40c9f8..26a03347 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1360,6 +1360,146 @@ detail::initimpl::pickle_factory pickle(GetState &&g, SetSta return {std::forward(g), std::forward(s)}; } +NAMESPACE_BEGIN(detail) +struct enum_base { + enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } + + PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { + m_base.attr("__entries") = dict(); + auto property = handle((PyObject *) &PyProperty_Type); + auto static_property = handle((PyObject *) get_internals().static_property_type); + + m_base.attr("__repr__") = cpp_function( + [](handle arg) -> str { + handle type = arg.get_type(); + object type_name = type.attr("__name__"); + dict entries = type.attr("__entries"); + for (const auto &kv : entries) { + object other = kv.second[int_(0)]; + if (other.equal(arg)) + return pybind11::str("{}.{}").format(type_name, kv.first); + } + return pybind11::str("{}.???").format(type_name); + }, is_method(m_base) + ); + + m_base.attr("name") = property(cpp_function( + [](handle arg) -> str { + dict entries = arg.get_type().attr("__entries"); + for (const auto &kv : entries) { + if (handle(kv.second[int_(0)]).equal(arg)) + return pybind11::str(kv.first); + } + return "???"; + }, is_method(m_base) + )); + + m_base.attr("__doc__") = static_property(cpp_function( + [](handle arg) -> std::string { + std::string docstring; + dict entries = arg.attr("__entries"); + if (((PyTypeObject *) arg.ptr())->tp_doc) + docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; + docstring += "Members:"; + for (const auto &kv : entries) { + auto key = std::string(pybind11::str(kv.first)); + auto comment = kv.second[int_(1)]; + docstring += "\n\n " + key; + if (!comment.is_none()) + docstring += " : " + (std::string) pybind11::str(comment); + } + return docstring; + } + ), none(), none(), ""); + + m_base.attr("__members__") = static_property(cpp_function( + [](handle arg) -> dict { + dict entries = arg.attr("__entries"), m; + for (const auto &kv : entries) + m[kv.first] = kv.second[int_(0)]; + return m; + }), none(), none(), "" + ); + + #define PYBIND11_ENUM_OP_STRICT(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a, object b) { \ + if (!a.get_type().is(b.get_type())) \ + throw type_error("Expected an enumeration of matching type!"); \ + return expr; \ + }, \ + is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b_) { \ + int_ a(a_), b(b_); \ + return expr; \ + }, \ + is_method(m_base)) + + if (is_convertible) { + PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b)); + PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b)); + + if (is_arithmetic) { + PYBIND11_ENUM_OP_CONV("__lt__", a < b); + PYBIND11_ENUM_OP_CONV("__gt__", a > b); + PYBIND11_ENUM_OP_CONV("__le__", a <= b); + PYBIND11_ENUM_OP_CONV("__ge__", a >= b); + PYBIND11_ENUM_OP_CONV("__and__", a & b); + PYBIND11_ENUM_OP_CONV("__rand__", a & b); + PYBIND11_ENUM_OP_CONV("__or__", a | b); + PYBIND11_ENUM_OP_CONV("__ror__", a | b); + PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); + PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); + } + } else { + PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b))); + PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b))); + + if (is_arithmetic) { + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b)); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b)); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b)); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b)); + } + } + + #undef PYBIND11_ENUM_OP_CONV + #undef PYBIND11_ENUM_OP_STRICT + + object getstate = cpp_function( + [](object arg) { return int_(arg); }, is_method(m_base)); + + m_base.attr("__getstate__") = getstate; + m_base.attr("__hash__") = getstate; + } + + PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { + dict entries = m_base.attr("__entries"); + str name(name_); + if (entries.contains(name)) { + std::string type_name = (std::string) str(m_base.attr("__name__")); + throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); + } + + entries[name] = std::make_pair(value, doc); + m_base.attr(name) = value; + } + + PYBIND11_NOINLINE void export_values() { + dict entries = m_base.attr("__entries"); + for (const auto &kv : entries) + m_parent.attr(kv.first) = kv.second[int_(0)]; + } + + handle m_base; + handle m_parent; +}; + +NAMESPACE_END(detail) + /// Binds C++ enumerations and enumeration classes to Python template class enum_ : public class_ { public: @@ -1370,109 +1510,33 @@ template class enum_ : public class_ { template enum_(const handle &scope, const char *name, const Extra&... extra) - : class_(scope, name, extra...), m_entries(), m_parent(scope) { - + : class_(scope, name, extra...), m_base(*this, scope) { constexpr bool is_arithmetic = detail::any_of...>::value; + constexpr bool is_convertible = std::is_convertible::value; + m_base.init(is_arithmetic, is_convertible); - auto m_entries_ptr = m_entries.inc_ref().ptr(); - def("__repr__", [name, m_entries_ptr](Type value) -> pybind11::str { - for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { - if (pybind11::cast(kv.second[int_(0)]) == value) - return pybind11::str("{}.{}").format(name, kv.first); - } - return pybind11::str("{}.???").format(name); - }); - def_property_readonly("name", [m_entries_ptr](Type value) -> pybind11::str { - for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { - if (pybind11::cast(kv.second[int_(0)]) == value) - return pybind11::str(kv.first); - } - return pybind11::str("???"); - }); - def_property_readonly_static("__doc__", [m_entries_ptr](handle self_) { - std::string docstring; - const char *tp_doc = ((PyTypeObject *) self_.ptr())->tp_doc; - if (tp_doc) - docstring += std::string(tp_doc) + "\n\n"; - docstring += "Members:"; - for (const auto &kv : reinterpret_borrow(m_entries_ptr)) { - auto key = std::string(pybind11::str(kv.first)); - auto comment = kv.second[int_(1)]; - docstring += "\n\n " + key; - if (!comment.is_none()) - docstring += " : " + (std::string) pybind11::str(comment); - } - return docstring; - }); - def_property_readonly_static("__members__", [m_entries_ptr](handle /* self_ */) { - dict m; - for (const auto &kv : reinterpret_borrow(m_entries_ptr)) - m[kv.first] = kv.second[int_(0)]; - return m; - }, return_value_policy::copy); def(init([](Scalar i) { return static_cast(i); })); def("__int__", [](Type value) { return (Scalar) value; }); #if PY_MAJOR_VERSION < 3 def("__long__", [](Type value) { return (Scalar) value; }); #endif - def("__eq__", [](const Type &value, Type *value2) { return value2 && value == *value2; }); - def("__ne__", [](const Type &value, Type *value2) { return !value2 || value != *value2; }); - if (is_arithmetic) { - def("__lt__", [](const Type &value, Type *value2) { return value2 && value < *value2; }); - def("__gt__", [](const Type &value, Type *value2) { return value2 && value > *value2; }); - def("__le__", [](const Type &value, Type *value2) { return value2 && value <= *value2; }); - def("__ge__", [](const Type &value, Type *value2) { return value2 && value >= *value2; }); - } - if (std::is_convertible::value) { - // Don't provide comparison with the underlying type if the enum isn't convertible, - // i.e. if Type is a scoped enum, mirroring the C++ behaviour. (NB: we explicitly - // convert Type to Scalar below anyway because this needs to compile). - def("__eq__", [](const Type &value, Scalar value2) { return (Scalar) value == value2; }); - def("__ne__", [](const Type &value, Scalar value2) { return (Scalar) value != value2; }); - if (is_arithmetic) { - def("__lt__", [](const Type &value, Scalar value2) { return (Scalar) value < value2; }); - def("__gt__", [](const Type &value, Scalar value2) { return (Scalar) value > value2; }); - def("__le__", [](const Type &value, Scalar value2) { return (Scalar) value <= value2; }); - def("__ge__", [](const Type &value, Scalar value2) { return (Scalar) value >= value2; }); - def("__invert__", [](const Type &value) { return ~((Scalar) value); }); - def("__and__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); - def("__or__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); - def("__xor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); - def("__rand__", [](const Type &value, Scalar value2) { return (Scalar) value & value2; }); - def("__ror__", [](const Type &value, Scalar value2) { return (Scalar) value | value2; }); - def("__rxor__", [](const Type &value, Scalar value2) { return (Scalar) value ^ value2; }); - def("__and__", [](const Type &value, const Type &value2) { return (Scalar) value & (Scalar) value2; }); - def("__or__", [](const Type &value, const Type &value2) { return (Scalar) value | (Scalar) value2; }); - def("__xor__", [](const Type &value, const Type &value2) { return (Scalar) value ^ (Scalar) value2; }); - } - } - def("__hash__", [](const Type &value) { return (Scalar) value; }); - // Pickling and unpickling -- needed for use with the 'multiprocessing' module - def(pickle([](const Type &value) { return pybind11::make_tuple((Scalar) value); }, - [](tuple t) { return static_cast(t[0].cast()); })); + def("__setstate__", [](Type &value, Scalar arg) { value = static_cast(arg); }); } /// Export enumeration entries into the parent scope enum_& export_values() { - for (const auto &kv : m_entries) - m_parent.attr(kv.first) = kv.second[int_(0)]; + m_base.export_values(); return *this; } /// Add an enumeration entry enum_& value(char const* name, Type value, const char *doc = nullptr) { - auto v = pybind11::cast(value, return_value_policy::copy); - this->attr(name) = v; - auto name_converted = pybind11::str(name); - if (m_entries.contains(name_converted)) - throw value_error("Enum error - element with name: " + std::string(name) + " already exists"); - m_entries[name_converted] = std::make_pair(v, doc); + m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); return *this; } private: - dict m_entries; - handle m_parent; + detail::enum_base m_base; }; NAMESPACE_BEGIN(detail) diff --git a/tests/test_enum.py b/tests/test_enum.py index a031d957..b1a5089e 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -153,4 +153,4 @@ def test_enum_to_int(): def test_duplicate_enum_name(): with pytest.raises(ValueError) as excinfo: m.register_bad_enum() - assert str(excinfo.value) == "Enum error - element with name: ONE already exists" + assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!' diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 707d3478..5ae05aaa 100644 --- a/tests/test_pickling.py +++ b/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) From ef13fb2e1c89f3e67f83eb860e8f6f37954a8cb1 Mon Sep 17 00:00:00 2001 From: Semen Yesylevskyy Date: Wed, 12 Sep 2018 01:20:56 +0300 Subject: [PATCH 0055/1206] =?UTF-8?q?Info=20about=20inconsistent=20detecti?= =?UTF-8?q?on=20of=20Python=20version=20between=20pybind11=20=E2=80=A6=20(?= =?UTF-8?q?#1093)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Info about inconsistent detection of Python version between pybind11 and CMake in FAQ --- docs/faq.rst | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index dc2b9761..85ba29d3 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -242,6 +242,39 @@ that that were ``malloc()``-ed in another shared library, using data structures with incompatible ABIs, and so on. pybind11 is very careful not to make these types of mistakes. +Inconsistent detection of Python version in CMake and pybind11 +============================================================== + +The functions ``find_package(PythonInterp)`` and ``find_package(PythonLibs)`` provided by CMake +for Python version detection are not used by pybind11 due to unreliability and limitations that make +them unsuitable for pybind11's needs. Instead pybind provides its own, more reliable Python detection +CMake code. Conflicts can arise, however, when using pybind11 in a project that *also* uses the CMake +Python detection in a system with several Python versions installed. + +This difference may cause inconsistencies and errors if *both* mechanisms are used in the same project. Consider the following +Cmake code executed in a system with Python 2.7 and 3.x installed: + +.. code-block:: cmake + + find_package(PythonInterp) + find_package(PythonLibs) + find_package(pybind11) + +It will detect Python 2.7 and pybind11 will pick it as well. + +In contrast this code: + +.. code-block:: cmake + + find_package(pybind11) + find_package(PythonInterp) + find_package(PythonLibs) + +will detect Python 3.x for pybind11 and may crash on ``find_package(PythonLibs)`` afterwards. + +It is advised to avoid using ``find_package(PythonInterp)`` and ``find_package(PythonLibs)`` from CMake and rely +on pybind11 in detecting Python version. If this is not possible CMake machinery should be called *before* including pybind11. + How to cite this project? ========================= @@ -256,4 +289,3 @@ discourse: note = {https://github.com/pybind/pybind11}, title = {pybind11 -- Seamless operability between C++11 and Python} } - From c8e9f3ccad8cde7bf5e1b33bf822ec7364e8ff0d Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 14 Sep 2018 12:07:47 +0200 Subject: [PATCH 0056/1206] quench __setstate__ warnings (fixes #1522) --- include/pybind11/pybind11.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 26a03347..84577e97 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1503,9 +1503,11 @@ NAMESPACE_END(detail) /// Binds C++ enumerations and enumeration classes to Python template class enum_ : public class_ { public: - using class_::def; - using class_::def_property_readonly; - using class_::def_property_readonly_static; + using Base = class_; + using Base::def; + using Base::attr; + using Base::def_property_readonly; + using Base::def_property_readonly_static; using Scalar = typename std::underlying_type::type; template @@ -1520,7 +1522,10 @@ template class enum_ : public class_ { #if PY_MAJOR_VERSION < 3 def("__long__", [](Type value) { return (Scalar) value; }); #endif - def("__setstate__", [](Type &value, Scalar arg) { value = static_cast(arg); }); + cpp_function setstate( + [](Type &value, Scalar arg) { value = static_cast(arg); }, + is_method(*this)); + attr("__setstate__") = setstate; } /// Export enumeration entries into the parent scope From 9343e68b4611926fb9bae4c01a61c83841b1a0a8 Mon Sep 17 00:00:00 2001 From: "Davis E. King" Date: Fri, 14 Sep 2018 08:28:54 -0400 Subject: [PATCH 0057/1206] Fix cmake scripts so projects using CUDA .cu files build correctly. (#1441) --- tools/pybind11Tools.cmake | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 6515f276..ba17d7f2 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -156,6 +156,7 @@ function(pybind11_add_module target_name) # namespace; also turning it on for a pybind module compilation here avoids # potential warnings or issues from having mixed hidden/non-hidden types. set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") if(WIN32 OR CYGWIN) # Link against the Python shared library on Windows @@ -208,6 +209,15 @@ function(pybind11_add_module target_name) if(MSVC) # /MP enables multithreaded builds (relevant when there are many files), /bigobj is # needed for bigger binding projects due to the limit to 64k addressable sections - target_compile_options(${target_name} PRIVATE /MP /bigobj) + set(msvc_extra_options /MP /bigobj) + if(CMAKE_VERSION VERSION_LESS 3.11) + target_compile_options(${target_name} PRIVATE ${msvc_extra_options}) + else() + # Only set these options for C++ files. This is important so that, for + # instance, projects that include other types of source files like CUDA + # .cu files don't get these options propagated to nvcc since that would + # cause the build to fail. + target_compile_options(${target_name} PRIVATE $<$:${msvc_extra_options}>) + endif() endif() endfunction() From e7761e338313ae649b8a694f1d6dee93b3b72a73 Mon Sep 17 00:00:00 2001 From: oremanj Date: Tue, 25 Sep 2018 14:55:18 -0700 Subject: [PATCH 0058/1206] Fix potential crash when calling an overloaded function (#1327) * Fix potential crash when calling an overloaded function The crash would occur if: - dispatcher() uses two-pass logic (because the target is overloaded and some arguments support conversions) - the first pass (with conversions disabled) doesn't find any matching overload - the second pass does find a matching overload, but its return value can't be converted to Python The code for formatting the error message assumed `it` still pointed to the selected overload, but during the second-pass loop `it` was nullptr. Fix by setting `it` correctly if a second-pass call returns a nullptr `handle`. Add a new test that segfaults without this fix. * Make overload iteration const-correct so we don't have to iterate again on second-pass error * Change test_error_after_conversions dependencies to local classes/variables --- include/pybind11/attr.h | 2 +- include/pybind11/cast.h | 2 +- include/pybind11/pybind11.h | 17 +++++++++++------ tests/test_class.cpp | 13 +++++++++++++ tests/test_class.py | 7 +++++++ 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index dce875a6..985bc579 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -278,7 +278,7 @@ struct type_record { } }; -inline function_call::function_call(function_record &f, handle p) : +inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { args.reserve(f.nargs); args_convert.reserve(f.nargs); diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 4084d42a..5529768b 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1843,7 +1843,7 @@ struct function_record; /// Internal data associated with a single function call struct function_call { - function_call(function_record &f, handle p); // Implementation in attr.h + function_call(const function_record &f, handle p); // Implementation in attr.h /// The function data: const function_record &func; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 84577e97..14ea8dc3 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -420,8 +420,8 @@ class cpp_function : public function { using namespace detail; /* Iterator over the list of potentially admissible overloads */ - function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), - *it = overloads; + const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; /* Need to know how many arguments + keyword arguments there are to pick the right overload */ const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); @@ -477,7 +477,7 @@ class cpp_function : public function { result other than PYBIND11_TRY_NEXT_OVERLOAD. */ - function_record &func = *it; + const function_record &func = *it; size_t pos_args = func.nargs; // Number of positional arguments that we need if (func.has_args) --pos_args; // (but don't count py::args if (func.has_kwargs) --pos_args; // or py::kwargs) @@ -509,7 +509,7 @@ class cpp_function : public function { // 1. Copy any position arguments given. bool bad_arg = false; for (; args_copied < args_to_copy; ++args_copied) { - argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { bad_arg = true; break; @@ -650,8 +650,13 @@ class cpp_function : public function { result = PYBIND11_TRY_NEXT_OVERLOAD; } - if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { + // The error reporting logic below expects 'it' to be valid, as it would be + // if we'd encountered this failure in the first-pass loop. + if (!result) + it = &call.func; break; + } } } } catch (error_already_set &e) { @@ -703,7 +708,7 @@ class cpp_function : public function { " arguments. The following argument types are supported:\n"; int ctr = 0; - for (function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { msg += " "+ std::to_string(++ctr) + ". "; bool wrote_sig = false; diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 9265e2e3..9ed1f50b 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -341,6 +341,19 @@ TEST_SUBMODULE(class_, m) { "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_(m, "StringWrapper").def(py::init()); + py::implicitly_convertible(); } template class BreaksBase { public: virtual ~BreaksBase() = default; }; diff --git a/tests/test_class.py b/tests/test_class.py index 8cf4757c..4a488ab6 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -266,3 +266,10 @@ def test_reentrant_implicit_conversion_failure(msg): 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!") From 73634b6db743e5b5b6fc5c64db020e4434cd41f0 Mon Sep 17 00:00:00 2001 From: Rune Paamand Date: Thu, 27 Sep 2018 16:26:42 +0200 Subject: [PATCH 0059/1206] Update iostream.h: Changed a local varname 'self' to 'self_' (#1535) * Update iostream.h: Changed a local varname 'self' to 'self_' Avoiding conflicts in namespace pybind11::self. https://github.com/pybind/pybind11/issues/1531 --- include/pybind11/iostream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/iostream.h b/include/pybind11/iostream.h index 3caf5563..182e8eef 100644 --- a/include/pybind11/iostream.h +++ b/include/pybind11/iostream.h @@ -194,7 +194,7 @@ inline class_ add_ostream_redirect(module m, std::strin return class_(m, name.c_str(), module_local()) .def(init(), arg("stdout")=true, arg("stderr")=true) .def("__enter__", &detail::OstreamRedirect::enter) - .def("__exit__", [](detail::OstreamRedirect &self, args) { self.exit(); }); + .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); } NAMESPACE_END(PYBIND11_NAMESPACE) From 177713fa4ea49c5594b5568279757f22354fa000 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Tue, 2 Oct 2018 12:11:37 -0300 Subject: [PATCH 0060/1206] Fix gcc-8 compilation warning --- tests/constructor_stats.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/constructor_stats.h b/tests/constructor_stats.h index babded03..f026e70f 100644 --- a/tests/constructor_stats.h +++ b/tests/constructor_stats.h @@ -180,7 +180,7 @@ class ConstructorStats { } } } - 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 From 0f404a5d4d84e883360ec8c7ff11e6c25b33ca85 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 10 Oct 2018 21:28:53 +0200 Subject: [PATCH 0061/1206] Allow recursive checkout without https (#1563) --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 5191885e..d063a8e8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "tools/clang"] path = tools/clang - url = https://github.com/wjakob/clang-cindex-python3 + url = ../../wjakob/clang-cindex-python3 From e76dff7751847df560bc0aea79abae814d39b475 Mon Sep 17 00:00:00 2001 From: Allan Leal Date: Thu, 11 Oct 2018 10:28:12 +0200 Subject: [PATCH 0062/1206] Fix for Issue #1258 (#1298) * Fix for Issue #1258 list_caster::load method will now check for a Python string and prevent its automatic conversion to a list. This should fix the issue "pybind11/stl.h converts string to vector #1258" (https://github.com/pybind/pybind11/issues/1258) * Added tests for fix of issue #1258 * Changelog: stl string auto-conversion --- docs/changelog.rst | 3 +++ include/pybind11/stl.h | 2 +- tests/test_stl.cpp | 10 ++++++++++ tests/test_stl.py | 8 ++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index eb25578c..606be413 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -47,6 +47,9 @@ v2.3.0 (Not yet released) * ``pybind11_add_module()``: allow including Python as a ``SYSTEM`` include path. `#1416 `_. +* ``pybind11/stl.h`` does not convert strings to ``vector`` anymore. + `#1258 `_. + v2.2.4 (September 11, 2018) ----------------------------------------------------- diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index faa7087e..659505d8 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -138,7 +138,7 @@ template struct list_caster { using value_conv = make_caster; bool load(handle src, bool convert) { - if (!isinstance(src)) + if (!isinstance(src) || isinstance(src)) return false; auto s = reinterpret_borrow(src); value.clear(); diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index cd0985d0..8736ea86 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -11,6 +11,9 @@ #include "constructor_stats.h" #include +#include +#include + // Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 #if PYBIND11_HAS_VARIANT using std::variant; @@ -33,6 +36,8 @@ struct visit_helper { }} // namespace pybind11::detail #endif +PYBIND11_MAKE_OPAQUE(std::vector>); + /// Issue #528: templated constructor struct TplCtorClass { template TplCtorClass(const T &) { } @@ -237,6 +242,11 @@ TEST_SUBMODULE(stl, m) { // test_stl_pass_by_pointer m.def("stl_pass_by_pointer", [](std::vector* v) { return *v; }, "v"_a=nullptr); + // #1258: pybind11/stl.h converts string to vector + m.def("func_with_string_or_vector_string_arg_overload", [](std::vector) { return 1; }); + m.def("func_with_string_or_vector_string_arg_overload", [](std::list) { return 2; }); + m.def("func_with_string_or_vector_string_arg_overload", [](std::string) { return 3; }); + class Placeholder { public: Placeholder() { print_created(this); } diff --git a/tests/test_stl.py b/tests/test_stl.py index 2c5e995f..b78f86a2 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -201,6 +201,14 @@ def test_missing_header_message(): 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 From 111b25b260fd687df9524d850c9d91927a661623 Mon Sep 17 00:00:00 2001 From: cdyson37 Date: Sun, 14 Oct 2018 19:14:04 +0100 Subject: [PATCH 0063/1206] Mention flake8 and check-style.sh in CONTRIBUTING (#1567) --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 375735f6..01596d94 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,8 @@ adhere to the following rules to make the process as smooth as possible: do add value by themselves. * Add tests for any new functionality and run the test suite (``make pytest``) to ensure that no existing features break. +* Please run ``flake8`` and ``tools/check-style.sh`` to check your code matches + the project style. (Note that ``check-style.sh`` requires ``gawk``.) * This project has a strong focus on providing general solutions using a minimal amount of code, thus small pull requests are greatly preferred. From 1377fbf73c02ba3ceca61536726d8145253d9cf4 Mon Sep 17 00:00:00 2001 From: Ryota Suzuki Date: Wed, 24 Oct 2018 18:18:04 +0900 Subject: [PATCH 0064/1206] Fix unintentional escaping of character on Windows (#1574) (#1575) --- tools/FindPythonLibsNew.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index b29b287d..919a384e 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -100,6 +100,9 @@ if(NOT _PYTHON_SUCCESS MATCHES 0) endif() # Convert the process output into a list +if(WIN32) + string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) +endif() string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) From 54eb8193e512f35edca99753155c3a804ed5c176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarc=C3=ADsio=20Fischer?= Date: Wed, 24 Oct 2018 06:18:58 -0300 Subject: [PATCH 0065/1206] Fix scoped enums comparison for equal/not equal cases (#1339) (#1571) --- include/pybind11/pybind11.h | 18 ++++++++++-------- tests/test_enum.py | 19 +++++++++++++++---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 14ea8dc3..f326bd72 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1426,11 +1426,11 @@ struct enum_base { }), none(), none(), "" ); - #define PYBIND11_ENUM_OP_STRICT(op, expr) \ + #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ m_base.attr(op) = cpp_function( \ [](object a, object b) { \ if (!a.get_type().is(b.get_type())) \ - throw type_error("Expected an enumeration of matching type!"); \ + strict_behavior; \ return expr; \ }, \ is_method(m_base)) @@ -1460,14 +1460,16 @@ struct enum_base { PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); } } else { - PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b))); - PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b))); + PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); + PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); if (is_arithmetic) { - PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b)); - PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b)); - PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b)); - PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b)); + #define THROW throw type_error("Expected an enumeration of matching type!"); + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), THROW); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), THROW); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), THROW); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), THROW); + #undef THROW } } diff --git a/tests/test_enum.py b/tests/test_enum.py index b1a5089e..d0989adc 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -47,10 +47,12 @@ def test_unscoped_enum(): EOne : Docstring for EOne''' - # no TypeError exception for unscoped enum ==/!= int comparisons + # Unscoped enums will accept ==/!= int comparisons y = m.UnscopedEnum.ETwo assert y == 2 + assert 2 == y assert y != 3 + assert 3 != y assert int(m.UnscopedEnum.ETwo) == 2 assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo" @@ -75,11 +77,20 @@ def test_scoped_enum(): 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 + # Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions) with pytest.raises(TypeError): - assert z == 2 + z > 3 with pytest.raises(TypeError): - assert z != 3 + z < 3 + with pytest.raises(TypeError): + z >= 3 + with pytest.raises(TypeError): + z <= 3 # order assert m.ScopedEnum.Two < m.ScopedEnum.Three From c9b8933e9aa8914aa56c0195cbaaf023302fe1c9 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 24 Oct 2018 13:25:25 +0200 Subject: [PATCH 0066/1206] flake8 fix --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 369788b2..9bbbd03f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,4 +7,4 @@ show_source = True exclude = .git, __pycache__, build, dist, docs, tools, venv ignore = # required for pretty matrix formatting: multiple spaces after `,` and `[` - E201, E241 + E201, E241, W504 From 06d021b6e46905ffb84c3646b35739f9c5e70aad Mon Sep 17 00:00:00 2001 From: Rune Paamand Date: Thu, 25 Oct 2018 01:35:33 +0200 Subject: [PATCH 0067/1206] Issue #1532: Incompatible config options, /MP vs /Gm for MSVC in DEBUG (#1533) * Issue #1532: Incompatible config options, /MP vs /Gm for MSVC in DEBUG --- tools/pybind11Tools.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index ba17d7f2..ab8bf627 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -209,15 +209,15 @@ function(pybind11_add_module target_name) if(MSVC) # /MP enables multithreaded builds (relevant when there are many files), /bigobj is # needed for bigger binding projects due to the limit to 64k addressable sections - set(msvc_extra_options /MP /bigobj) + target_compile_options(${target_name} PRIVATE /bigobj) if(CMAKE_VERSION VERSION_LESS 3.11) - target_compile_options(${target_name} PRIVATE ${msvc_extra_options}) + target_compile_options(${target_name} PRIVATE $<$>:/MP>) else() # Only set these options for C++ files. This is important so that, for # instance, projects that include other types of source files like CUDA # .cu files don't get these options propagated to nvcc since that would # cause the build to fail. - target_compile_options(${target_name} PRIVATE $<$:${msvc_extra_options}>) + target_compile_options(${target_name} PRIVATE $<$>:$<$:/MP>>) endif() endif() endfunction() From 741576dd115bd9a2457f7a1e854bb4d0b0f8ec43 Mon Sep 17 00:00:00 2001 From: Josh Kelley Date: Wed, 31 Oct 2018 21:10:11 -0400 Subject: [PATCH 0068/1206] Update documentation for initialize_interpreter (#1584) Add a detailed link to Python 3 documentation. Add a caveat about the program terminating if initializing the interpreter fails. --- include/pybind11/embed.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 9abc61c3..72655885 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -90,8 +90,14 @@ NAMESPACE_END(detail) Initialize the Python interpreter. No other pybind11 or CPython API functions can be called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The optional parameter can be used to skip the registration of signal handlers (see the - Python documentation for details). Calling this function again after the interpreter + `Python documentation`_ for details). Calling this function again after the interpreter has already been initialized is a fatal error. + + If initializing the Python interpreter fails, then the program is terminated. (This + is controlled by the CPython runtime and is an exception to pybind11's normal behavior + of throwing exceptions on errors.) + + .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx \endrst */ inline void initialize_interpreter(bool init_signal_handlers = true) { if (Py_IsInitialized()) From 978d439e929397e625fab26566cdd3ef66b79610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wawrzyniec=20Urba=C5=84czyk?= Date: Sat, 3 Nov 2018 13:20:08 +0100 Subject: [PATCH 0069/1206] Add PYBIND11_ prefix to the THROW macro to prevent name collisions. (#1578) --- include/pybind11/pybind11.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index f326bd72..99a1e0a7 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1464,12 +1464,12 @@ struct enum_base { PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); if (is_arithmetic) { - #define THROW throw type_error("Expected an enumeration of matching type!"); - PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), THROW); - PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), THROW); - PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), THROW); - PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), THROW); - #undef THROW + #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); + #undef PYBIND11_THROW } } From 9f73060cc7759b2375d71243a93dadf1ad996077 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 9 Nov 2018 12:32:48 +0100 Subject: [PATCH 0070/1206] std::array<> caster: support arbitrary sequences (#1602) This PR brings the std::array<> caster in sync with the other STL type casters: to accept an arbitrary sequence as input (rather than a list, which is too restrictive). --- include/pybind11/stl.h | 4 ++-- tests/test_stl.cpp | 2 ++ tests/test_stl.py | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 659505d8..50828a01 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -199,9 +199,9 @@ template s public: bool load(handle src, bool convert) { - if (!isinstance(src)) + if (!isinstance(src)) return false; - auto l = reinterpret_borrow(src); + auto l = reinterpret_borrow(src); if (!require_size(l.size())) return false; size_t ctr = 0; diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 8736ea86..0bb6433d 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -263,4 +263,6 @@ TEST_SUBMODULE(stl, m) { return result; }, py::return_value_policy::take_ownership); + + m.def("array_cast_sequence", [](std::array x) { return x; }); } diff --git a/tests/test_stl.py b/tests/test_stl.py index b78f86a2..9e58223c 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -216,3 +216,7 @@ def 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] From adc2cdd5c4e201f583bba0b74758ba418e57be52 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 9 Nov 2018 20:12:46 +0100 Subject: [PATCH 0071/1206] fixed regression in STL type caster RVPs (fixes #1561) (#1603) --- .appveyor.yml | 1 + include/pybind11/cast.h | 5 +++-- include/pybind11/stl.h | 14 ++++++++++---- tests/test_stl.cpp | 12 ++++++++++++ tests/test_stl.py | 8 ++++++++ 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 5e969400..3d49d56b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,6 +3,7 @@ image: - Visual Studio 2017 - Visual Studio 2015 test: off +skip_branch_with_pr: true build: parallel: true platform: diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 5529768b..a21d6bae 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1614,8 +1614,9 @@ template struct return_value_policy_ov template struct return_value_policy_override>::value, void>> { static return_value_policy policy(return_value_policy p) { - return !std::is_lvalue_reference::value && !std::is_pointer::value - ? return_value_policy::move : p; + return !std::is_lvalue_reference::value && + !std::is_pointer::value + ? return_value_policy::move : p; } }; diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 50828a01..2d29a968 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -83,7 +83,8 @@ template struct set_caster { template static handle cast(T &&src, return_value_policy policy, handle parent) { - policy = return_value_policy_override::policy(policy); + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); pybind11::set s; for (auto &&value : src) { auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); @@ -119,8 +120,12 @@ template struct map_caster { template static handle cast(T &&src, return_value_policy policy, handle parent) { dict d; - return_value_policy policy_key = return_value_policy_override::policy(policy); - return_value_policy policy_value = return_value_policy_override::policy(policy); + return_value_policy policy_key = policy; + return_value_policy policy_value = policy; + if (!std::is_lvalue_reference::value) { + policy_key = return_value_policy_override::policy(policy_key); + policy_value = return_value_policy_override::policy(policy_value); + } for (auto &&kv : src) { auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); @@ -161,7 +166,8 @@ template struct list_caster { public: template static handle cast(T &&src, return_value_policy policy, handle parent) { - policy = return_value_policy_override::policy(policy); + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); list l(src.size()); size_t index = 0; for (auto &&value : src) { diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 0bb6433d..37368852 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -265,4 +265,16 @@ TEST_SUBMODULE(stl, m) { py::return_value_policy::take_ownership); m.def("array_cast_sequence", [](std::array x) { return x; }); + + /// test_issue_1561 + struct Issue1561Inner { std::string data; }; + struct Issue1561Outer { std::vector list; }; + + py::class_(m, "Issue1561Inner") + .def(py::init()) + .def_readwrite("data", &Issue1561Inner::data); + + py::class_(m, "Issue1561Outer") + .def(py::init<>()) + .def_readwrite("list", &Issue1561Outer::list); } diff --git a/tests/test_stl.py b/tests/test_stl.py index 9e58223c..ba71ca31 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -220,3 +220,11 @@ def test_stl_ownership(): 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' From e2eca4f8f86685507764ea25dc96a404026a50f2 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 9 Nov 2018 20:14:53 +0100 Subject: [PATCH 0072/1206] Support C++17 aligned new statement (#1582) * Support C++17 aligned new statement This patch makes pybind11 aware of nonstandard alignment requirements in bound types and passes on this information to C++17 aligned 'new' operator. Pre-C++17, the behavior is unchanged. --- include/pybind11/attr.h | 5 ++++- include/pybind11/cast.h | 12 +++++++++++- include/pybind11/detail/internals.h | 4 ++-- include/pybind11/pybind11.h | 23 +++++++++++++++++++---- tests/test_class.cpp | 13 +++++++++++++ tests/test_class.py | 6 ++++++ 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 985bc579..8732cfe1 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -214,11 +214,14 @@ struct type_record { /// How large is the underlying C++ type? size_t type_size = 0; + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + /// How large is the type's holder? size_t holder_size = 0; /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = ::operator new; + void *(*operator_new)(size_t) = nullptr; /// Function pointer to class_<..>::init_instance void (*init_instance)(instance *, const void *) = nullptr; diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index a21d6bae..2dec23cb 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -571,7 +571,17 @@ class type_caster_generic { // Lazy allocation for unallocated values: if (vptr == nullptr) { auto *type = v_h.type ? v_h.type : typeinfo; - vptr = type->operator_new(type->type_size); + if (type->operator_new) { + vptr = type->operator_new(type->type_size); + } else { + #if defined(PYBIND11_CPP17) + if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + vptr = ::operator new(type->type_size, + (std::align_val_t) type->type_align); + else + #endif + vptr = ::operator new(type->type_size); + } } value = vptr; } diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 78d4afed..ad344157 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -116,7 +116,7 @@ struct internals { struct type_info { PyTypeObject *type; const std::type_info *cpptype; - size_t type_size, holder_size_in_ptrs; + size_t type_size, type_align, holder_size_in_ptrs; void *(*operator_new)(size_t); void (*init_instance)(instance *, const void *); void (*dealloc)(value_and_holder &v_h); @@ -138,7 +138,7 @@ struct type_info { }; /// Tracks the `internals` and `type_info` ABI version independent of the main library version -#define PYBIND11_INTERNALS_VERSION 2 +#define PYBIND11_INTERNALS_VERSION 3 #if defined(WITH_THREAD) # define PYBIND11_INTERNALS_KIND "" diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 99a1e0a7..e45f1a0c 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -896,6 +896,7 @@ class generic_type : public object { tinfo->type = (PyTypeObject *) m_ptr; tinfo->cpptype = rec.type; tinfo->type_size = rec.type_size; + tinfo->type_align = rec.type_align; tinfo->operator_new = rec.operator_new; tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); tinfo->init_instance = rec.init_instance; @@ -987,11 +988,21 @@ template struct has_operator_delete_size::value, int> = 0> -void call_operator_delete(T *p, size_t) { T::operator delete(p); } +void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } template ::value && has_operator_delete_size::value, int> = 0> -void call_operator_delete(T *p, size_t s) { T::operator delete(p, s); } +void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } -inline void call_operator_delete(void *p, size_t) { ::operator delete(p); } +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 + ::operator delete(p, s); +#else + ::operator delete(p); +#endif +} NAMESPACE_END(detail) @@ -1054,6 +1065,7 @@ class class_ : public detail::generic_type { record.name = name; record.type = &typeid(type); record.type_size = sizeof(conditional_t); + record.type_align = alignof(conditional_t&); record.holder_size = sizeof(holder_type); record.init_instance = init_instance; record.dealloc = dealloc; @@ -1329,7 +1341,10 @@ class class_ : public detail::generic_type { v_h.set_holder_constructed(false); } else { - detail::call_operator_delete(v_h.value_ptr(), v_h.type->type_size); + detail::call_operator_delete(v_h.value_ptr(), + v_h.type->type_size, + v_h.type->type_align + ); } v_h.value_ptr() = nullptr; } diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 9ed1f50b..499d0cc5 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -12,6 +12,10 @@ #include "local_bindings.h" #include +#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 v) : vec{std::move(v)} {} @@ -354,6 +358,15 @@ TEST_SUBMODULE(class_, m) { [](StringWrapper) -> NotRegistered { return {}; }); py::class_(m, "StringWrapper").def(py::init()); py::implicitly_convertible(); + + #if defined(PYBIND11_CPP17) + struct alignas(1024) Aligned { + std::uintptr_t ptr() const { return (uintptr_t) this; } + }; + py::class_(m, "Aligned") + .def(py::init<>()) + .def("ptr", &Aligned::ptr); + #endif } template class BreaksBase { public: virtual ~BreaksBase() = default; }; diff --git a/tests/test_class.py b/tests/test_class.py index 4a488ab6..ed63ca85 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -273,3 +273,9 @@ def test_error_after_conversions(): m.test_error_after_conversions("hello") assert str(exc_info.value).startswith( "Unable to convert function return value to a Python type!") + + +def test_aligned(): + if hasattr(m, "Aligned"): + p = m.Aligned().ptr() + assert p % 1024 == 0 From cea42467b02451a6330f6fbe5984844ee6ed2472 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 11 Nov 2018 19:32:09 +0100 Subject: [PATCH 0073/1206] fix py::cast (#1605) Pybind11 provides a cast operator between opaque void* pointers on the C++ side and capsules on the Python side. The py::cast expression was not aware of this possibility and incorrectly triggered a compile-time assertion ("Unable to cast type to reference: value is local to type caster") that is now fixed. --- include/pybind11/cast.h | 3 ++- tests/test_builtin_casters.cpp | 7 +++++++ tests/test_builtin_casters.py | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 2dec23cb..80abb2b9 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1611,7 +1611,8 @@ template using move_never = none_of, move_if_unrefer // everything else returns a reference/pointer to a local variable. template using cast_is_temporary_value_reference = bool_constant< (std::is_reference::value || std::is_pointer::value) && - !std::is_base_of>::value + !std::is_base_of>::value && + !std::is_same, void>::value >; // When a value returned from a C++ function is being cast back to Python, we almost always want to diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index 45081340..e026127f 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -160,4 +160,11 @@ TEST_SUBMODULE(builtin_casters, m) { 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(o) == v; + }); } diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 01d0437b..73cc465f 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -336,3 +336,7 @@ def test_int_long(): 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() From 63c2a972fe566def1d32974545518a8dbf87fe1e Mon Sep 17 00:00:00 2001 From: Trevor Laughlin Date: Sun, 11 Nov 2018 13:36:55 -0500 Subject: [PATCH 0074/1206] Enable unique_ptr holder with mixed Deleters between base and derived types (#1353) * Check default holder -Recognize "std::unique_ptr" as a default holder even if "D" doesn't match between base and derived holders * Add test for unique_ptr change --- include/pybind11/pybind11.h | 2 +- tests/test_smart_ptr.cpp | 26 ++++++++++++++++++++++++++ tests/test_smart_ptr.py | 21 +++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index e45f1a0c..628ff37e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1069,7 +1069,7 @@ class class_ : public detail::generic_type { record.holder_size = sizeof(holder_type); record.init_instance = init_instance; record.dealloc = dealloc; - record.default_holder = std::is_same>::value; + record.default_holder = detail::is_instantiation::value; set_operator_new(&record); diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 098b1829..5f1fd07d 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -186,6 +186,32 @@ TEST_SUBMODULE(smart_ptr, m) { .def(py::init()) .def_readwrite("value", &MyObject4::value); + // test_unique_deleter + // Object with std::unique_ptr 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_>(m, "MyObject4a") + .def(py::init()) + .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_(m, "MyObject4b") + .def(py::init()); + // test_large_holder class MyObject5 { // managed by huge_unique_ptr public: diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 60f48b39..72b1c2a1 100644 --- a/tests/test_smart_ptr.py +++ b/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 becaue 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 From e9d6e879499d56c26e7a55424e499b24a5047c36 Mon Sep 17 00:00:00 2001 From: Karl Haubenwallner Date: Sun, 11 Nov 2018 20:47:26 +0100 Subject: [PATCH 0075/1206] Added a debug flag to the PYBIND11_INTERNALS_VERSION (#1549) --- include/pybind11/detail/internals.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index ad344157..6d7dc5cf 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -140,6 +140,12 @@ struct type_info { /// Tracks the `internals` and `type_info` ABI version independent of the main library version #define PYBIND11_INTERNALS_VERSION 3 +#if defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + #if defined(WITH_THREAD) # define PYBIND11_INTERNALS_KIND "" #else @@ -147,10 +153,10 @@ struct type_info { #endif #define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__" + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" #define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND "__" + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" /// Each module locally stores a pointer to the `internals` data. The data /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. From 8f5b7fce849b64216068c087207343c81c3776ed Mon Sep 17 00:00:00 2001 From: Ahuva Kroizer Date: Tue, 13 Nov 2018 14:25:57 +0200 Subject: [PATCH 0076/1206] FAQ addition (#1606) * Add possible solution to ImportError issue --- docs/faq.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 85ba29d3..99a12cb3 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -4,9 +4,13 @@ Frequently asked questions "ImportError: dynamic module does not define init function" =========================================================== -You are likely using an incompatible version of Python (for instance, the -extension library was compiled against Python 2, while the interpreter is -running on top of some version of Python 3, or vice versa). +1. Make sure that the name specified in PYBIND11_MODULE is identical to the +filename of the extension library (without prefixes such as .so) + +2. If the above did not fix the issue, you are likely using an incompatible +version of Python (for instance, the extension library was compiled against +Python 2, while the interpreter is running on top of some version of Python +3, or vice versa). "Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" ======================================================================== From 17983e74256bb8ec136ed3d3d17e0726c80f1adb Mon Sep 17 00:00:00 2001 From: voxmea Date: Fri, 16 Nov 2018 00:45:19 -0500 Subject: [PATCH 0077/1206] Adds type_caster support for std::deque. (#1609) * Adds std::deque to the types supported by list_caster in stl.h. * Adds a new test_deque test in test_stl.{py,cpp}. * Updates the documentation to include std::deque as a default supported type. --- docs/advanced/cast/stl.rst | 2 +- include/pybind11/stl.h | 4 ++++ tests/test_stl.cpp | 4 ++++ tests/test_stl.py | 9 +++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst index 468bd2c6..e48409f0 100644 --- a/docs/advanced/cast/stl.rst +++ b/docs/advanced/cast/stl.rst @@ -5,7 +5,7 @@ Automatic conversion ==================== When including the additional header file :file:`pybind11/stl.h`, conversions -between ``std::vector<>``/``std::list<>``/``std::array<>``, +between ``std::vector<>``/``std::deque<>``/``std::list<>``/``std::array<>``, ``std::set<>``/``std::unordered_set<>``, and ``std::map<>``/``std::unordered_map<>`` and the Python ``list``, ``set`` and ``dict`` data structures are automatically enabled. The types ``std::pair<>`` diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 2d29a968..32f8d294 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #if defined(_MSC_VER) @@ -185,6 +186,9 @@ template struct list_caster { template struct type_caster> : list_caster, Type> { }; +template struct type_caster> + : list_caster, Type> { }; + template struct type_caster> : list_caster, Type> { }; diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 37368852..207c9fb2 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -63,6 +63,10 @@ TEST_SUBMODULE(stl, m) { static std::vector lvv{2}; m.def("cast_ptr_vector", []() { return &lvv; }); + // test_deque + m.def("cast_deque", []() { return std::deque{1}; }); + m.def("load_deque", [](const std::deque &v) { return v.at(0) == 1 && v.at(1) == 2; }); + // test_array m.def("cast_array", []() { return std::array {{1 , 2}}; }); m.def("load_array", [](const std::array &a) { return a[0] == 1 && a[1] == 2; }); diff --git a/tests/test_stl.py b/tests/test_stl.py index ba71ca31..bf185d57 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -23,6 +23,15 @@ 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""" lst = m.cast_array() From 64205140bdaf02be50d3476bb507e8354a512d04 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 16 Nov 2018 06:46:46 +0100 Subject: [PATCH 0078/1206] added std::deque to overview.rst [ci skip] --- docs/advanced/cast/overview.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/advanced/cast/overview.rst b/docs/advanced/cast/overview.rst index 2ac7d300..b0e32a52 100644 --- a/docs/advanced/cast/overview.rst +++ b/docs/advanced/cast/overview.rst @@ -131,6 +131,8 @@ as arguments and return values, refer to the section on binding :ref:`classes`. +------------------------------------+---------------------------+-------------------------------+ | ``std::vector`` | STL dynamic array | :file:`pybind11/stl.h` | +------------------------------------+---------------------------+-------------------------------+ +| ``std::deque`` | STL double-ended queue | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-------------------------------+ | ``std::valarray`` | STL value array | :file:`pybind11/stl.h` | +------------------------------------+---------------------------+-------------------------------+ | ``std::list`` | STL linked list | :file:`pybind11/stl.h` | From 81da9888c70e0067921395bd722d143907c0f5f7 Mon Sep 17 00:00:00 2001 From: Baljak Date: Tue, 20 Nov 2018 23:22:02 +0100 Subject: [PATCH 0079/1206] Fix Intel C++ compiler warning on Windows (#1608) --- include/pybind11/pybind11.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 628ff37e..c50ba89d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -10,7 +10,17 @@ #pragma once -#if defined(_MSC_VER) +#if defined(__INTEL_COMPILER) +# pragma warning push +# pragma warning disable 68 // integer conversion resulted in a change of sign +# pragma warning disable 186 // pointless comparison of unsigned integer with zero +# pragma warning disable 878 // incompatible exception specifications +# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning disable 1786 // function "strdup" was declared deprecated +# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" +#elif defined(_MSC_VER) # pragma warning(push) # pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant @@ -19,15 +29,6 @@ # pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name # pragma warning(disable: 4702) // warning C4702: unreachable code # pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified -#elif defined(__INTEL_COMPILER) -# pragma warning(push) -# pragma warning(disable: 68) // integer conversion resulted in a change of sign -# pragma warning(disable: 186) // pointless comparison of unsigned integer with zero -# pragma warning(disable: 878) // incompatible exception specifications -# pragma warning(disable: 1334) // the "template" keyword used for syntactic disambiguation may only be used within a template -# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) -# pragma warning(disable: 1875) // offsetof applied to non-POD (Plain Old Data) types is nonstandard -# pragma warning(disable: 2196) // warning #2196: routine is both "inline" and "noinline" #elif defined(__GNUG__) && !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-but-set-parameter" @@ -2077,10 +2078,8 @@ template function get_overload(const T *this_ptr, const char *name) { NAMESPACE_END(PYBIND11_NAMESPACE) -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning(pop) -#elif defined(__INTEL_COMPILER) -/* Leave ignored warnings on */ #elif defined(__GNUG__) && !defined(__clang__) # pragma GCC diagnostic pop #endif From e2b884c33bcde70b2ea562ffa52dd7ebee276d50 Mon Sep 17 00:00:00 2001 From: Borja Zarco Date: Sat, 1 Dec 2018 08:47:40 -0500 Subject: [PATCH 0080/1206] Use `PyGILState_GetThisThreadState` when using gil_scoped_acquire. (#1211) This avoids GIL deadlocking when pybind11 tries to acquire the GIL in a thread that already acquired it using standard Python API (e.g. when running from a Python thread). --- include/pybind11/pybind11.h | 9 +++++ tests/CMakeLists.txt | 1 + tests/test_gil_scoped.cpp | 43 ++++++++++++++++++++ tests/test_gil_scoped.py | 80 +++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 tests/test_gil_scoped.cpp create mode 100644 tests/test_gil_scoped.py diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c50ba89d..7fa0f0e1 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1871,6 +1871,15 @@ class gil_scoped_acquire { auto const &internals = detail::get_internals(); tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); + if (!tstate) { + /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if + calling from a Python thread). Since we use a different key, this ensures + we don't create a new thread state and deadlock in PyEval_AcquireThread + below. Note we don't save this state with internals.tstate, since we don't + create it we would fail to clear it (its reference count should be > 0). */ + tstate = PyGILState_GetThisThreadState(); + } + if (!tstate) { tstate = PyThreadState_New(internals.istate); #if !defined(NDEBUG) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b5e0b526..a31d5b8b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,6 +40,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 diff --git a/tests/test_gil_scoped.cpp b/tests/test_gil_scoped.cpp new file mode 100644 index 00000000..a94b7a2e --- /dev/null +++ b/tests/test_gil_scoped.cpp @@ -0,0 +1,43 @@ +/* + tests/test_gil_scoped.cpp -- acquire and release gil + + Copyright (c) 2017 Borja Zarco (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_tests.h" +#include + + +class VirtClass { +public: + 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_(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 &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(); }); +} diff --git a/tests/test_gil_scoped.py b/tests/test_gil_scoped.py new file mode 100644 index 00000000..5e702431 --- /dev/null +++ b/tests/test_gil_scoped.py @@ -0,0 +1,80 @@ +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 1s (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, 1s should be more than enough. + process.join(timeout=1) + 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 From f4b4e2e957985eb2b906286943c0e568499249ff Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Thu, 3 Jan 2019 12:01:34 +0100 Subject: [PATCH 0081/1206] Use new Doxygen archive URL - fixes Travis --- .travis.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 928f517f..d7f300e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,16 +10,17 @@ matrix: # - Makes sure that everything still works without optional deps (numpy/scipy/eigen) and # also tests the automatic discovery functions in CMake (Python version, C++ standard). - os: linux + dist: xenial # Necessary to run doxygen 1.8.15 env: STYLE DOCS PIP cache: false before_install: - pyenv global $(pyenv whence 2to3) # activate all python versions - PY_CMD=python3 - - $PY_CMD -m pip install --user --upgrade pip wheel + - $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 - - curl -fsSL ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.12.linux.bin.tar.gz | tar xz - - export PATH="$PWD/doxygen-1.8.12/bin:$PATH" + - 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: - tools/check-style.sh - flake8 @@ -32,7 +33,7 @@ matrix: diff -rq $installed ./include/pybind11 - | # Barebones build - cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON + cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) make pytest -j 2 make cpptest -j 2 # The following are regular test configurations, including optional dependencies. From 0ca6867e8e233b64de24d5a01ab854702a1dbf66 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Thu, 3 Jan 2019 12:02:39 +0100 Subject: [PATCH 0082/1206] Avoid Visual Studio 2017 15.9.4 ICE --- include/pybind11/numpy.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index bdc3a5dd..37471d8b 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1466,7 +1466,10 @@ struct vectorize_helper { private: remove_reference_t f; - template using param_n_t = typename pack_element::call_type...>::type; + // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag + // when arg_call_types is manually inlined. + using arg_call_types = std::tuple::call_type...>; + template using param_n_t = typename std::tuple_element::type; // Runs a vectorized function given arguments tuple and three index sequences: // - Index is the full set of 0 ... (N-1) argument indices; From 085a29436a8c472caaaf7157aa644b571079bcaa Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Thu, 3 Jan 2019 19:41:10 +0100 Subject: [PATCH 0083/1206] Increasing timeout in test_gil_scoped.py to get AppVeyor to succeed --- tests/test_gil_scoped.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_gil_scoped.py b/tests/test_gil_scoped.py index 5e702431..2c72fc6d 100644 --- a/tests/test_gil_scoped.py +++ b/tests/test_gil_scoped.py @@ -4,13 +4,13 @@ def _run_in_process(target, *args, **kwargs): - """Runs target in process and returns its exitcode after 1s (None if still alive).""" + """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, 1s should be more than enough. - process.join(timeout=1) + # Do not need to wait much, 10s should be more than enough. + process.join(timeout=10) return process.exitcode finally: if process.is_alive(): From e7ef34f23f194cfa40bdbf967c6d34712261a4ee Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Wed, 23 Jan 2019 14:22:39 +0100 Subject: [PATCH 0084/1206] compatibility with pytest 4.0, fix #1670 Cf. https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace --- tests/conftest.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f4c22826..0b76395c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -185,7 +185,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 +202,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(): From 43a39bc7d8f3b1f4e8b319b77ecab8d34584f4a9 Mon Sep 17 00:00:00 2001 From: Guilhem Saurel Date: Wed, 23 Jan 2019 15:13:59 +0100 Subject: [PATCH 0085/1206] ignore numpy.ufunc size warnings --- tests/pytest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/pytest.ini b/tests/pytest.ini index 1e44f0a0..f209964a 100644 --- a/tests/pytest.ini +++ b/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 From d1f64fa920806801954e4d8d85674db8a7a810b3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 4 Feb 2019 16:18:01 +0100 Subject: [PATCH 0086/1206] AppVeyor: quench pip deprecation warnings for v2.7 --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3d49d56b..0eb6aa5b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,8 +42,8 @@ install: if ($env:PYTHON) { if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" - python -m pip install --upgrade pip wheel - python -m pip install pytest numpy --no-warn-script-location + python -W ignore -m pip install --upgrade pip wheel + python -W ignore -m pip install pytest numpy --no-warn-script-location } elseif ($env:CONDA) { if ($env:CONDA -eq "27") { $env:CONDA = "" } if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } From ccbe68b084806dece5863437a7dc93de20bd9b15 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 4 Feb 2019 16:19:36 +0100 Subject: [PATCH 0087/1206] added binding delattr() -> PyObject_DelAttr analogous to hasattr() --- include/pybind11/pytypes.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index fa5ed7cb..3329fda2 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -388,6 +388,14 @@ inline bool hasattr(handle obj, const char *name) { return PyObject_HasAttrString(obj.ptr(), name) == 1; } +inline void delattr(handle obj, handle name) { + if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } +} + +inline void delattr(handle obj, const char *name) { + if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } +} + inline object getattr(handle obj, handle name) { PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); if (!result) { throw error_already_set(); } @@ -459,7 +467,6 @@ object object_or_cast(T &&o); // Match a PyObject*, which we want to convert directly to handle via its converting constructor inline handle object_or_cast(PyObject *ptr) { return ptr; } - template class accessor : public object_api> { using key_type = typename Policy::key_type; From 25abf7efba0b2990f5a6dfb0a31bc65c0f2f4d17 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 4 Feb 2019 17:09:47 +0100 Subject: [PATCH 0088/1206] flake8 fixes --- setup.cfg | 4 +++- tests/conftest.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 9bbbd03f..002f38d1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,4 +7,6 @@ show_source = True exclude = .git, __pycache__, build, dist, docs, tools, venv ignore = # required for pretty matrix formatting: multiple spaces after `,` and `[` - E201, E241, W504 + E201, E241, W504, + # camelcase 'cPickle' imported as lowercase 'pickle' + N813 diff --git a/tests/conftest.py b/tests/conftest.py index 0b76395c..55d9d0d5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -75,7 +75,7 @@ def __enter__(self): self.capfd.readouterr() return self - def __exit__(self, *_): + def __exit__(self, *args): self.out, self.err = self.capfd.readouterr() def __eq__(self, other): From ae951ca085f6a3a12958467b60ad0162eec7f72f Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 6 Apr 2019 19:09:39 +0200 Subject: [PATCH 0089/1206] CI fixes (#1744) * Fix warning that not including a cmake source or build dir will be a fatal error (it is now on newest CMakes) * Fixes appveyor * Travis uses CMake 3.9 for more than a year now * Travis dropped sudo: false in December * Dropping Sphinx 2 - clang7: Suppress self-assign warnings; fix missing virtual dtors - pypy: - Keep old version (newer stuff breaks) - Pin packages to extra index for speed - travis: - Make docker explicit; remove docker if not needed - Make commands more verbose (for debugging / repro) - Make Ubuntu dist explicit per job - Fix Windows - Add names to travis --- .appveyor.yml | 1 + .travis.yml | 198 ++++++++++++++++++---------- include/pybind11/numpy.h | 4 +- tests/test_gil_scoped.cpp | 1 + tests/test_kwargs_and_defaults.cpp | 4 +- tests/test_operator_overloading.cpp | 23 ++++ tests/test_smart_ptr.cpp | 4 +- tests/test_virtual_functions.cpp | 1 + 8 files changed, 166 insertions(+), 70 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 0eb6aa5b..79a5916c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -62,6 +62,7 @@ build_script: -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_SUPPRESS_REGENERATION=1 + . - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - cmake --build . --config %CONFIG% --target pytest -- /m /v:m /logger:%MSBuildLogger% - cmake --build . --config %CONFIG% --target cpptest -- /m /v:m /logger:%MSBuildLogger% diff --git a/.travis.yml b/.travis.yml index d7f300e6..e1209b60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: cpp -dist: trusty -sudo: false matrix: include: # This config does a few things: @@ -11,14 +9,14 @@ matrix: # also tests the automatic discovery functions in CMake (Python version, C++ standard). - os: linux dist: xenial # Necessary to run doxygen 1.8.15 - env: STYLE DOCS PIP + name: Style, docs, and pip cache: false before_install: - pyenv global $(pyenv whence 2to3) # activate all python versions - 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 + install: # Breathe does not yet support Sphinx 2 + - $PY_CMD -m pip install --user --upgrade "sphinx<2" sphinx_rtd_theme breathe 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: @@ -33,62 +31,119 @@ matrix: diff -rq $installed ./include/pybind11 - | # Barebones build - cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) + cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) . 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 + dist: trusty + name: Python 2.7, c++11, gcc 4.8 env: PYTHON=2.7 CPP=11 GCC=4.8 addons: apt: - packages: [cmake=2.\*, cmake-data=2.\*] + packages: + - cmake=2.\* + - cmake-data=2.\* - os: linux + dist: trusty + name: Python 3.6, c++11, gcc 4.8 env: PYTHON=3.6 CPP=11 GCC=4.8 addons: apt: - sources: [deadsnakes] - packages: [python3.6-dev python3.6-venv, cmake=2.\*, cmake-data=2.\*] - - sudo: true - services: docker + sources: + - deadsnakes + packages: + - python3.6-dev + - python3.6-venv + - cmake=2.\* + - cmake-data=2.\* + - os: linux + dist: trusty env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1 - - sudo: true - services: docker - env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 - - sudo: true + name: Python 2.7, c++14, gcc 4.8, CMake test + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - os: linux + dist: trusty + name: Python 3.5, c++14, gcc 6, Debug build + # N.B. `ensurepip` could be installed transitively by `python3.5-venv`, but + # seems to have apt conflicts (at least for Trusty). Use Docker instead. services: docker + env: DOCKER=debian:stretch PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 + - os: linux + dist: xenial env: PYTHON=3.6 CPP=17 GCC=7 + name: Python 3.6, c++17, gcc 7 + addons: + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - g++-7 + - python3.6-dev + - python3.6-venv - os: linux - env: PYTHON=3.6 CPP=17 CLANG=5.0 + dist: xenial + env: PYTHON=3.6 CPP=17 CLANG=7 + name: Python 3.6, c++17, Clang 7 addons: apt: - sources: [deadsnakes, llvm-toolchain-trusty-5.0, ubuntu-toolchain-r-test] - packages: [python3.6-dev python3.6-venv clang-5.0 llvm-5.0-dev, lld-5.0] + sources: + - deadsnakes + - llvm-toolchain-xenial-7 + packages: + - python3.6-dev + - python3.6-venv + - clang-7 + - libclang-7-dev + - llvm-7-dev + - lld-7 + - libc++-7-dev + - libc++abi-7-dev # Why is this necessary??? - 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 env: PYTHON=3.7 CPP=14 CLANG DEBUG=1 # Test a PyPy 2.7 build - os: linux + dist: trusty env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8 + name: PyPy 5.8, Python 2.7, c++11, gcc 4.8 addons: apt: - packages: [libblas-dev, liblapack-dev, gfortran] + packages: + - libblas-dev + - liblapack-dev + - gfortran # Build in 32-bit mode and tests against the CMake-installed version - - sudo: true + - os: linux + dist: trusty services: docker - env: ARCH=i386 PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 + env: DOCKER=i386/debian:stretch PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 + name: Python 3.4, c++14, gcc 6, 32-bit script: - | - $SCRIPT_RUN_PREFIX sh -c "set -e - cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 - make install - cp -a tests /pybind11-tests - mkdir /build-tests && cd /build-tests - cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON - make pytest -j 2" + # Consolidated 32-bit Docker Build + Install + set -ex + $SCRIPT_RUN_PREFIX sh -c " + set -ex + cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 . + make install + cp -a tests /pybind11-tests + mkdir /build-tests && cd /build-tests + cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON + make pytest -j 2" + set +ex cache: directories: - $HOME/.local/bin @@ -98,6 +153,7 @@ cache: before_install: - | # Configure build variables + set -ex if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ -n "$CLANG" ]; then export CXX=clang++-$CLANG CC=clang-$CLANG @@ -108,18 +164,16 @@ before_install: fi export CXX=g++-$GCC CC=gcc-$GCC fi - if [ "$GCC" = "6" ]; then DOCKER=${ARCH:+$ARCH/}debian:stretch - elif [ "$GCC" = "7" ]; then DOCKER=debian:buster EXTRA_PACKAGES+=" catch python3-distutils" DOWNLOAD_CATCH=OFF - fi elif [ "$TRAVIS_OS_NAME" = "osx" ]; then export CXX=clang++ CC=clang; fi if [ -n "$CPP" ]; then CPP=-std=c++$CPP; fi if [ "${PYTHON:0:1}" = "3" ]; then PY=3; fi if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS+=" -DCMAKE_BUILD_TYPE=Debug"; fi + set +ex - | # Initialize environment - set -e + set -ex if [ -n "$DOCKER" ]; then docker pull $DOCKER @@ -148,13 +202,15 @@ before_install: if [ "$PY" = 3 ] || [ -n "$PYPY" ]; then $PY_CMD -m ensurepip --user fi + $PY_CMD --version $PY_CMD -m pip install --user --upgrade pip wheel fi - set +e + set +ex install: - | # Install dependencies - set -e + set -ex + cmake --version if [ -n "$DOCKER" ]; then if [ -n "$DEBUG" ]; then PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg" @@ -166,32 +222,21 @@ install: libeigen3-dev libboost-dev cmake make ${EXTRA_PACKAGES} && break; done" else - if [ "$CLANG" = "5.0" ]; then - if ! [ -d ~/.local/include/c++/v1 ]; then - # Neither debian nor llvm provide a libc++ 5.0 deb; luckily it's fairly quick - # to build, install (and cache), so do it ourselves: - git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source - git clone https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx -b release_50 - git clone https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi -b release_50 - mkdir llvm-build && cd llvm-build - # Building llvm requires a newer cmake than is provided by the trusty container: - CMAKE_VER=cmake-3.8.0-Linux-x86_64 - curl https://cmake.org/files/v3.8/$CMAKE_VER.tar.gz | tar xz - ./$CMAKE_VER/bin/cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local ../llvm-source - make -j2 install-cxxabi install-cxx - cp -a include/c++/v1/*cxxabi*.h ~/.local/include/c++/v1 - cd .. - fi - export CXXFLAGS="-isystem $HOME/.local/include/c++/v1 -stdlib=libc++" - export LDFLAGS="-L$HOME/.local/lib -fuse-ld=lld-$CLANG" - export LD_LIBRARY_PATH="$HOME/.local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" - if [ "$CPP" = "-std=c++17" ]; then CPP="-std=c++1z"; fi + if [ "$CLANG" = "7" ]; then + export CXXFLAGS="-stdlib=libc++" fi export NPY_NUM_BUILD_JOBS=2 echo "Installing pytest, numpy, scipy..." - ${PYPY:+travis_wait 30} $PY_CMD -m pip install --user --upgrade pytest numpy scipy \ - ${PYPY:+--extra-index-url https://imaginary.ca/trusty-pypi} + local PIP_CMD="" + if [ -n $PYPY ]; then + # For expediency, install only versions that are available on the extra index. + travis_wait 30 \ + $PY_CMD -m pip install --user --upgrade --extra-index-url https://imaginary.ca/trusty-pypi \ + pytest numpy==1.15.4 scipy==1.2.0 + else + $PY_CMD -m pip install --user --upgrade pytest numpy scipy + fi echo "done." mkdir eigen @@ -199,16 +244,37 @@ install: tar --extract -j --directory=eigen --strip-components=1 export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+$CMAKE_INCLUDE_PATH:}$PWD/eigen" fi - set +e + set +ex script: -- $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} - -DPYBIND11_PYTHON_VERSION=$PYTHON - -DPYBIND11_CPP_STANDARD=$CPP - -DPYBIND11_WERROR=${WERROR:-ON} - -DDOWNLOAD_CATCH=${DOWNLOAD_CATCH:-ON} -- $SCRIPT_RUN_PREFIX make pytest -j 2 -- $SCRIPT_RUN_PREFIX make cpptest -j 2 -- if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi +- | + # CMake Configuration + set -ex + $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} \ + -DPYBIND11_PYTHON_VERSION=$PYTHON \ + -DPYBIND11_CPP_STANDARD=$CPP \ + -DPYBIND11_WERROR=${WERROR:-ON} \ + -DDOWNLOAD_CATCH=${DOWNLOAD_CATCH:-ON} \ + . + set +ex +- | + # pytest + set -ex + $SCRIPT_RUN_PREFIX make pytest -j 2 VERBOSE=1 + set +ex +- | + # cpptest + set -ex + $SCRIPT_RUN_PREFIX make cpptest -j 2 + set +ex +- | + # CMake Build Interface + set -ex + if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi + set +ex after_failure: cat tests/test_cmake_build/*.log* after_script: -- if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi +- | + # Cleanup (Docker) + set -ex + if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi + set +ex diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 37471d8b..ca954f59 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1509,7 +1509,7 @@ struct vectorize_helper { if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); else result = array_t(shape); - if (size == 0) return result; + if (size == 0) return std::move(result); /* Call the function */ if (trivial == broadcast_trivial::non_trivial) @@ -1517,7 +1517,7 @@ struct vectorize_helper { else apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); - return result; + return std::move(result); } template diff --git a/tests/test_gil_scoped.cpp b/tests/test_gil_scoped.cpp index a94b7a2e..cb0010ee 100644 --- a/tests/test_gil_scoped.cpp +++ b/tests/test_gil_scoped.cpp @@ -13,6 +13,7 @@ class VirtClass { public: + virtual ~VirtClass() {} virtual void virtual_func() {} virtual void pure_virtual_func() = 0; }; diff --git a/tests/test_kwargs_and_defaults.cpp b/tests/test_kwargs_and_defaults.cpp index 2263b6b7..6563fb9a 100644 --- a/tests/test_kwargs_and_defaults.cpp +++ b/tests/test_kwargs_and_defaults.cpp @@ -34,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); }); diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index 4ad34d10..8ca7d8bc 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -62,6 +62,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 @@ -144,3 +163,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/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 5f1fd07d..87c9be8c 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -336,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_>(m, "ElementBase"); struct ElementA : ElementBase { diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index c9a561c0..ccf018d9 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -129,6 +129,7 @@ class Movable { 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; From 73b840dc3420252181efa46cb609a5afba4e231f Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 7 Apr 2019 10:37:21 +0200 Subject: [PATCH 0090/1206] Fix for issue in latest conda (#1757) --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 79a5916c..8fbb7261 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -49,7 +49,7 @@ install: if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } $env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH" $env:PYTHONHOME = "C:\Miniconda$env:CONDA" - conda update -y -n base conda + conda --version conda install -y -q pytest numpy scipy } - ps: | From 9bb3313162c0b856125e481ceece9d8faa567716 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 7 Apr 2019 10:38:10 +0200 Subject: [PATCH 0091/1206] Fixing warnings about conversions in GCC 7+ (#1753) --- include/pybind11/numpy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index ca954f59..b2a02e02 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -855,14 +855,14 @@ template class array_t : public // Reference to element at a given index template const T& at(Ix... index) const { - if (sizeof...(index) != ndim()) + if ((ssize_t) sizeof...(index) != ndim()) fail_dim_check(sizeof...(index), "index dimension mismatch"); return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); } // Mutable reference to element at a given index template T& mutable_at(Ix... index) { - if (sizeof...(index) != ndim()) + if ((ssize_t) sizeof...(index) != ndim()) fail_dim_check(sizeof...(index), "index dimension mismatch"); return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); } From 35045eeef8969b7b446c64b192502ac1cbf7c451 Mon Sep 17 00:00:00 2001 From: martinRenou Date: Fri, 3 May 2019 14:32:28 +0200 Subject: [PATCH 0092/1206] Add getters for exception type, value and traceback (#1641) --- include/pybind11/pybind11.h | 8 ++++---- include/pybind11/pytypes.h | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 7fa0f0e1..785c1c00 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1987,12 +1987,12 @@ class gil_scoped_release { }; #endif error_already_set::~error_already_set() { - if (type) { + if (m_type) { error_scope scope; gil_scoped_acquire gil; - type.release().dec_ref(); - value.release().dec_ref(); - trace.release().dec_ref(); + m_type.release().dec_ref(); + m_value.release().dec_ref(); + m_trace.release().dec_ref(); } } diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 3329fda2..8c8a4620 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -324,7 +324,7 @@ class error_already_set : public std::runtime_error { /// Constructs a new exception from the current Python error indicator, if any. The current /// Python error indicator will be cleared. error_already_set() : std::runtime_error(detail::error_string()) { - PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); + PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); } error_already_set(const error_already_set &) = default; @@ -335,7 +335,7 @@ class error_already_set : public std::runtime_error { /// Give the currently-held error back to Python, if any. If there is currently a Python error /// already set it is cleared first. After this call, the current object no longer stores the /// error variables (but the `.what()` string is still available). - void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } + void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); } // Does nothing; provided for backwards compatibility. PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") @@ -344,10 +344,14 @@ class error_already_set : public std::runtime_error { /// Check if the currently trapped error type matches the given Python exception class (or a /// subclass thereof). May also be passed a tuple to search for any exception class matches in /// the given tuple. - bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } + bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), m_type.ptr()); } + + const object& type() const { return m_type; } + const object& value() const { return m_value; } + const object& trace() const { return m_trace; } private: - object type, value, trace; + object m_type, m_value, m_trace; }; /** \defgroup python_builtins _ From b3f0b4de663e31e2eeb5fbe76cd31d07cc2dd803 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 12 May 2019 17:27:23 +0000 Subject: [PATCH 0093/1206] new sphinx (#1786) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1209b60..4cc5cf07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ matrix: - pyenv global $(pyenv whence 2to3) # activate all python versions - PY_CMD=python3 - $PY_CMD -m pip install --user --upgrade pip wheel setuptools - install: # Breathe does not yet support Sphinx 2 - - $PY_CMD -m pip install --user --upgrade "sphinx<2" sphinx_rtd_theme breathe flake8 pep8-naming pytest + install: + - $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe 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: From a0b8f70df4f6dfcf522164e10fa99e1c7afb3f47 Mon Sep 17 00:00:00 2001 From: Jamie Snape Date: Sun, 12 May 2019 13:43:44 -0400 Subject: [PATCH 0094/1206] Ensure PythonLibsNew_FOUND is set in FindPythonLibsNew module (#1373) Since the module is named FindPythonLibsNew, PythonLibsNew_FOUND should be set appropriately. --- tools/FindPythonLibsNew.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index 919a384e..7d85af4a 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -64,6 +64,7 @@ endif() if(NOT PYTHONINTERP_FOUND) set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) return() endif() @@ -96,6 +97,7 @@ if(NOT _PYTHON_SUCCESS MATCHES 0) "Python config failure:\n${_PYTHON_ERROR_VALUE}") endif() set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) return() endif() @@ -127,6 +129,7 @@ if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZE "chosen compiler is ${_CMAKE_BITS}-bit") endif() set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) return() endif() @@ -196,3 +199,4 @@ find_package_message(PYTHON "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") set(PYTHONLIBS_FOUND TRUE) +set(PythonLibsNew_FOUND TRUE) From 97784dad3e518ccb415d5db57ff9b933495d9024 Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Sun, 12 May 2019 23:35:49 +0200 Subject: [PATCH 0095/1206] [BUGFIX] Fixing pybind11::error_already_set.matches to also work with exception subclasses (#1715) * Fixing order of arguments in call to PyErr_GivenExceptionMatches in pybind11::error_already_set.matches * Added tests on error_already_set::matches fix for exception base classes --- include/pybind11/pytypes.h | 2 +- tests/test_exceptions.cpp | 30 +++++++++++++++++++++++++++++- tests/test_exceptions.py | 4 +++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 8c8a4620..2de9bcd2 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -344,7 +344,7 @@ class error_already_set : public std::runtime_error { /// Check if the currently trapped error type matches the given Python exception class (or a /// subclass thereof). May also be passed a tuple to search for any exception class matches in /// the given tuple. - bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), m_type.ptr()); } + bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); } const object& type() const { return m_type; } const object& value() const { return m_value; } diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp index cf202143..d3013903 100644 --- a/tests/test_exceptions.cpp +++ b/tests/test_exceptions.cpp @@ -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/tests/test_exceptions.py b/tests/test_exceptions.py index 8d37c09b..6edff9fe 100644 --- a/tests/test_exceptions.py +++ b/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): From c251434011959433e254d1b845543b441fccc9b7 Mon Sep 17 00:00:00 2001 From: nstelzen Date: Mon, 10 Jun 2019 16:35:36 +0200 Subject: [PATCH 0096/1206] Added note in documentation regarding make install (#1801) * Added note regarding make install --- docs/compiling.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/compiling.rst b/docs/compiling.rst index bbd7a2cc..c50c7d8a 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -149,6 +149,18 @@ See the `Config file`_ docstring for details of relevant CMake variables. find_package(pybind11 REQUIRED) pybind11_add_module(example example.cpp) +Note that ``find_package(pybind11)`` will only work correctly if pybind11 +has been correctly installed on the system, e. g. after downloading or cloning +the pybind11 repository : + +.. code-block:: bash + + cd pybind11 + mkdir build + cd build + cmake .. + make install + Once detected, the aforementioned ``pybind11_add_module`` can be employed as before. The function usage and configuration variables are identical no matter if pybind11 is added as a subdirectory or found as an installed package. You From 30c035234830c7fdb36841067e9f9ecb50c016b3 Mon Sep 17 00:00:00 2001 From: Blake Thompson Date: Mon, 10 Jun 2019 14:01:11 -0500 Subject: [PATCH 0097/1206] Added __contains__ to stl bindings for maps (#1767) * Added __contains__ to stl bindings for maps --- include/pybind11/stl_bind.h | 9 +++++++++ tests/test_stl.py | 2 ++ 2 files changed, 11 insertions(+) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 38dd68f6..d6f4c633 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -579,6 +579,15 @@ class_ bind_map(handle scope, const std::string &name, Args&&. return_value_policy::reference_internal // ref + keepalive ); + cl.def("__contains__", + [](Map &m, const KeyType &k) -> bool { + auto it = m.find(k); + if (it == m.end()) + return false; + return true; + } + ); + // Assignment provided only if the type is copyable detail::map_assignment(cl); diff --git a/tests/test_stl.py b/tests/test_stl.py index bf185d57..2335cb9f 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -56,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]" From 979d75de23a759e134639934a980231acf685448 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Mon, 10 Jun 2019 22:03:17 +0300 Subject: [PATCH 0098/1206] doc: Add note about casting from `None` to `T*` (#1760) * doc: Add note about casting from `None` to `T*` * doc: reword 'none-to-pointer' note message * doc: mention opaque types in 'none-to-pointer' note message --- docs/advanced/functions.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index 4d28f06d..3e1a3ff0 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -467,6 +467,15 @@ dog)"``, while attempting to call ``meow(None)`` will raise a ``TypeError``: The default behaviour when the tag is unspecified is to allow ``None``. +.. note:: + + Even when ``.none(true)`` is specified for an argument, ``None`` will be converted to a + ``nullptr`` *only* for custom and :ref:`opaque ` types. Pointers to built-in types + (``double *``, ``int *``, ...) and STL types (``std::vector *``, ...; if ``pybind11/stl.h`` + is included) are copied when converted to C++ (see :doc:`/advanced/cast/overview`) and will + not allow ``None`` as argument. To pass optional argument of these copied types consider + using ``std::optional`` + Overload resolution order ========================= From 4ddf7c402d3f460cda8370a7aff920adeefab06c Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Mon, 10 Jun 2019 12:54:56 -0700 Subject: [PATCH 0099/1206] Add missing includes for better Bazel compatibility (#1255) Bazel has a "strict" build model that requires all C++ header files be compilable on their own, and thus must explicitly #include all headers they require (even if de facto header inclusion order means they'd get them "for free"). This adds a couple of headers that are needed (but missing) by this model. --- include/pybind11/detail/class.h | 1 + include/pybind11/detail/typeid.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 7a5dd013..6c88986f 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -10,6 +10,7 @@ #pragma once #include "../attr.h" +#include "../options.h" NAMESPACE_BEGIN(PYBIND11_NAMESPACE) NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/detail/typeid.h b/include/pybind11/detail/typeid.h index 6f36aab7..9c8a4fc6 100644 --- a/include/pybind11/detail/typeid.h +++ b/include/pybind11/detail/typeid.h @@ -16,6 +16,8 @@ #include #endif +#include "common.h" + NAMESPACE_BEGIN(PYBIND11_NAMESPACE) NAMESPACE_BEGIN(detail) /// Erase all occurrences of a substring From ac6cb91a343c388669308ece8e98940b9a00c28c Mon Sep 17 00:00:00 2001 From: Omar Awile Date: Mon, 10 Jun 2019 21:56:17 +0200 Subject: [PATCH 0100/1206] Fixed small typo (#1633) I think this particular method binding should not be done with `PYBIND11_OVERLOAD_PURE` but instead `PYBIND11_OVERLOAD`. --- docs/advanced/classes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index c7dbc676..fe494591 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -239,7 +239,7 @@ override the ``name()`` method): class PyDog : public Dog { public: using Dog::Dog; // Inherit constructors - std::string go(int n_times) override { PYBIND11_OVERLOAD_PURE(std::string, Dog, go, n_times); } + std::string go(int n_times) override { PYBIND11_OVERLOAD(std::string, Dog, go, n_times); } std::string name() override { PYBIND11_OVERLOAD(std::string, Dog, name, ); } std::string bark() override { PYBIND11_OVERLOAD(std::string, Dog, bark, ); } }; From 21bf16f5b852ea2d92446dcb549b9b3fcdd4c2d1 Mon Sep 17 00:00:00 2001 From: luzpaz Date: Mon, 10 Jun 2019 12:56:38 -0700 Subject: [PATCH 0101/1206] misc. comment typo (#1629) --- tests/test_smart_ptr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 72b1c2a1..0a3bb58e 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -128,7 +128,7 @@ def test_unique_deleter(): o = m.MyObject4b(23) assert o.value == 23 cstats4a = ConstructorStats.get(m.MyObject4a) - assert cstats4a.alive() == 2 # Two becaue of previous test + assert cstats4a.alive() == 2 # Two because of previous test cstats4b = ConstructorStats.get(m.MyObject4b) assert cstats4b.alive() == 1 del o From 09330b94ea1e9893d443b5fb42018cb2bf5ff9aa Mon Sep 17 00:00:00 2001 From: Darius Arnold Date: Mon, 10 Jun 2019 21:57:00 +0200 Subject: [PATCH 0102/1206] Fix typos in documentation (#1635) * Always capitalize Eigen * Fix spelling --- docs/advanced/cast/eigen.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/advanced/cast/eigen.rst b/docs/advanced/cast/eigen.rst index 7cbeac00..59ba08c3 100644 --- a/docs/advanced/cast/eigen.rst +++ b/docs/advanced/cast/eigen.rst @@ -37,7 +37,7 @@ that maps into the source ``numpy.ndarray`` data: this requires both that the data types are the same (e.g. ``dtype='float64'`` and ``MatrixType::Scalar`` is ``double``); and that the storage is layout compatible. The latter limitation is discussed in detail in the section below, and requires careful -consideration: by default, numpy matrices and eigen matrices are *not* storage +consideration: by default, numpy matrices and Eigen matrices are *not* storage compatible. If the numpy matrix cannot be used as is (either because its types differ, e.g. @@ -226,7 +226,7 @@ order. Failing rather than copying =========================== -The default behaviour when binding ``Eigen::Ref`` eigen +The default behaviour when binding ``Eigen::Ref`` Eigen references is to copy matrix values when passed a numpy array that does not conform to the element type of ``MatrixType`` or does not have a compatible stride layout. If you want to explicitly avoid copying in such a case, you @@ -289,13 +289,13 @@ will be passed as such a column vector. If not, but the Eigen type constraints will accept a row vector, it will be passed as a row vector. (The column vector takes precedence when both are supported, for example, when passing a 1D numpy array to a MatrixXd argument). Note that the type need not be -expicitly a vector: it is permitted to pass a 1D numpy array of size 5 to an +explicitly a vector: it is permitted to pass a 1D numpy array of size 5 to an Eigen ``Matrix``: you would end up with a 1x5 Eigen matrix. Passing the same to an ``Eigen::MatrixXd`` would result in a 5x1 Eigen matrix. -When returning an eigen vector to numpy, the conversion is ambiguous: a row +When returning an Eigen vector to numpy, the conversion is ambiguous: a row vector of length 4 could be returned as either a 1D array of length 4, or as a -2D array of size 1x4. When encoutering such a situation, pybind11 compromises +2D array of size 1x4. When encountering such a situation, pybind11 compromises by considering the returned Eigen type: if it is a compile-time vector--that is, the type has either the number of rows or columns set to 1 at compile time--pybind11 converts to a 1D numpy array when returning the value. For From 69dc380c0dcb3ee057165844f270c48a1b98c625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Kreuzberger?= Date: Mon, 10 Jun 2019 22:00:55 +0200 Subject: [PATCH 0103/1206] #1208 Handle forced unwind exception (e.g. during pthread termination) * #1208 Bugfix thread kill wihile running pybind11 module * #1208 Bugfix missing space after catch --- include/pybind11/pybind11.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 785c1c00..3c83b574 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -41,6 +41,11 @@ # endif #endif +#if defined(__GNUG__) && !defined(__clang__) + #include +#endif + + #include "attr.h" #include "options.h" #include "detail/class.h" @@ -663,6 +668,10 @@ class cpp_function : public function { } catch (error_already_set &e) { e.restore(); return nullptr; +#if defined(__GNUG__) && !defined(__clang__) + } catch ( abi::__forced_unwind& ) { + throw; +#endif } catch (...) { /* When an exception is caught, give each registered exception translator a chance to translate it to a Python exception From 9424d5d27731e3c7333e7295b545ee8722054c73 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 10 Jun 2019 22:02:40 +0200 Subject: [PATCH 0104/1206] type_record: Uninit Member (#1658) Fix an uninitialized member in `type_record`. Found with coverity in a downstream project. --- include/pybind11/attr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 8732cfe1..6962d6fc 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -200,7 +200,8 @@ struct function_record { /// Special data structure which (temporarily) holds metadata about a bound class struct type_record { PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { } + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), + default_holder(true), module_local(false) { } /// Handle to the parent scope handle scope; From 492da592c2b3ada67574ab8b60105754cdb4f13d Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Mon, 10 Jun 2019 22:02:58 +0200 Subject: [PATCH 0105/1206] another typo (#1675) --- docs/advanced/classes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index fe494591..99a95c79 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -153,7 +153,7 @@ Here is an example: .. code-block:: python - class Dachschund(Dog): + class Dachshund(Dog): def __init__(self, name): Dog.__init__(self) # Without this, undefined behavior may occur if the C++ portions are referenced. self.name = name From 2b045757b5606eb55758cefb0453a7036f052866 Mon Sep 17 00:00:00 2001 From: Ivor Wanders Date: Mon, 10 Jun 2019 16:12:28 -0400 Subject: [PATCH 0106/1206] Improve documentation related to inheritance. (#1676) * Adds section to the reference. * Adds section to advanced classes page describing how to use `get_overload`. --- docs/advanced/classes.rst | 51 ++++++++++++++++++++++++++++++--- docs/faq.rst | 2 ++ docs/reference.rst | 15 ++++++++++ include/pybind11/pybind11.h | 57 +++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 99a95c79..d1f8849b 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -80,10 +80,10 @@ helper class that is defined as follows: } }; -The macro :func:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual -functions, and :func:`PYBIND11_OVERLOAD` should be used for functions which have -a default implementation. There are also two alternate macros -:func:`PYBIND11_OVERLOAD_PURE_NAME` and :func:`PYBIND11_OVERLOAD_NAME` which +The macro :c:macro:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual +functions, and :c:macro:`PYBIND11_OVERLOAD` should be used for functions which have +a default implementation. There are also two alternate macros +:c:macro:`PYBIND11_OVERLOAD_PURE_NAME` and :c:macro:`PYBIND11_OVERLOAD_NAME` which take a string-valued name argument between the *Parent class* and *Name of the function* slots, which defines the name of function in Python. This is required when the C++ and Python versions of the @@ -325,6 +325,10 @@ can now create a python class that inherits from ``Dog``: Extended trampoline class functionality ======================================= +.. _extended_class_functionality_forced_trampoline + +Forced trampoline class initialisation +-------------------------------------- The trampoline classes described in the previous sections are, by default, only initialized when needed. More specifically, they are initialized when a python class actually inherits from a registered type (instead of merely creating an @@ -352,6 +356,45 @@ ensuring member initialization and (eventual) destruction. See the file :file:`tests/test_virtual_functions.cpp` for complete examples showing both normal and forced trampoline instantiation. +Different method signatures +--------------------------- +The macro's introduced in :ref:`overriding_virtuals` cover most of the standard +use cases when exposing C++ classes to Python. Sometimes it is hard or unwieldy +to create a direct one-on-one mapping between the arguments and method return +type. + +An example would be when the C++ signature contains output arguments using +references (See also :ref:`faq_reference_arguments`). Another way of solving +this is to use the method body of the trampoline class to do conversions to the +input and return of the Python method. + +The main building block to do so is the :func:`get_overload`, this function +allows retrieving a method implemented in Python from within the trampoline's +methods. Consider for example a C++ method which has the signature +``bool myMethod(int32_t& value)``, where the return indicates whether +something should be done with the ``value``. This can be made convenient on the +Python side by allowing the Python function to return ``None`` or an ``int``: + +.. code-block:: cpp + + bool MyClass::myMethod(int32_t& value) + { + pybind11::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + // Try to look up the overloaded method on the Python side. + pybind11::function overload = pybind11::get_overload(this, "myMethod"); + if (overload) { // method is found + auto obj = overload(value); // Call the Python function. + if (py::isinstance(obj)) { // check if it returned a Python integer type + value = obj.cast(); // Cast it and assign it to the value. + return true; // Return true; value should be used. + } else { + return false; // Python returned none, return false. + } + } + return false; // Alternatively return MyClass::myMethod(value); + } + + .. _custom_constructors: Custom constructors diff --git a/docs/faq.rst b/docs/faq.rst index 99a12cb3..93ccf10e 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -39,6 +39,8 @@ multiple versions of Python and it finds the wrong one, delete cmake -DPYTHON_EXECUTABLE:FILEPATH= . +.. _faq_reference_arguments: + Limitations involving reference arguments ========================================= diff --git a/docs/reference.rst b/docs/reference.rst index 3f748497..a9fbe600 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -86,6 +86,21 @@ Python built-in functions .. doxygengroup:: python_builtins :members: +Inheritance +=========== + +See :doc:`/classes` and :doc:`/advanced/classes` for more detail. + +.. doxygendefine:: PYBIND11_OVERLOAD + +.. doxygendefine:: PYBIND11_OVERLOAD_PURE + +.. doxygendefine:: PYBIND11_OVERLOAD_NAME + +.. doxygendefine:: PYBIND11_OVERLOAD_PURE_NAME + +.. doxygenfunction:: get_overload + Exceptions ========== diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 3c83b574..1b784673 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2062,6 +2062,14 @@ inline function get_type_overload(const void *this_ptr, const detail::type_info return overload; } +/** \rst + Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr. + + :this_ptr: The pointer to the object the overload should be retrieved for. This should be the first + non-trampoline class encountered in the inheritance chain. + :name: The name of the overloaded Python method to retrieve. + :return: The Python method by this name from the object or an empty function wrapper. + \endrst */ template function get_overload(const T *this_ptr, const char *name) { auto tinfo = detail::get_type_info(typeid(T)); return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); @@ -2080,17 +2088,66 @@ template function get_overload(const T *this_ptr, const char *name) { } \ } +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn' + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method + name in C is not the same as the method name in Python. For example with `__str__`. + + .. code-block:: cpp + + std::string toString() override { + PYBIND11_OVERLOAD_NAME( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + toString, // Name of function in C++ (name) + "__str__", // Name of method in Python (fn) + ); + } +\endrst */ #define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ return cname::fn(__VA_ARGS__) +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD_NAME`, except that it + throws if no overload can be found. +\endrst */ #define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up the method + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. This macro should be used if the method name in C and in Python are identical. + See :ref:`overriding_virtuals` for more information. + + .. code-block:: cpp + + class PyAnimal : public Animal { + public: + // Inherit the constructors + using Animal::Animal; + + // Trampoline (need one for each virtual function) + std::string go(int n_times) override { + PYBIND11_OVERLOAD_PURE( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + go, // Name of function in C++ (must match Python name) (fn) + n_times // Argument(s) (...) + ); + } + }; +\endrst */ #define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD`, except that it throws + if no overload can be found. +\endrst */ #define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) From f93cd0aa72410e9acea0614bd75a11ebb88a5c32 Mon Sep 17 00:00:00 2001 From: Jeffrey Quesnelle Date: Mon, 10 Jun 2019 16:13:35 -0400 Subject: [PATCH 0107/1206] PYBIND11_TLS_REPLACE_VALUE should use macro argument value in Python 3.7+ (#1683) --- include/pybind11/detail/internals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 6d7dc5cf..f1dd3876 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -23,7 +23,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); #if PY_VERSION_HEX >= 0x03070000 # define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr # define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) -# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (tstate)) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value)) # define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) #else // Usually an int but a long on Cygwin64 with Python 3.x From 1c627c9ec0545640497c18b3a75deb6349b53a58 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 10 Jun 2019 22:18:11 +0200 Subject: [PATCH 0108/1206] pybind11_getbuffer: useless safe nullptr check (#1664) Alternative implementation for #1657: if we know that `obj` is never a `nullptr` [1], we should not `nullptr`-check it *after* already dereferencing it. [1] https://github.com/pybind/pybind11/pull/1657#issuecomment-452090058 --- include/pybind11/detail/class.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 6c88986f..b1916fcd 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -469,7 +469,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla if (tinfo && tinfo->get_buffer) break; } - if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) { + if (view == nullptr || !tinfo || !tinfo->get_buffer) { if (view) view->obj = nullptr; PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); From a2cdd0b91520bd3bd6ce67fceb311b51cbb1ecc7 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 10 Jun 2019 22:19:41 +0200 Subject: [PATCH 0109/1206] dict_readonly: member init (#1661) fix missing member initialization in pytypes: read-only dict. Found with coverity in a downstream project. --- include/pybind11/pytypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 2de9bcd2..81906f43 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -708,7 +708,7 @@ class dict_readonly { private: handle obj; - PyObject *key, *value; + PyObject *key = nullptr, *value = nullptr; ssize_t pos = -1; }; NAMESPACE_END(iterator_policies) From 7a24bcf1f67dd5f76c15ee85f1841a38d477a96d Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 11 Jun 2019 01:57:49 -0700 Subject: [PATCH 0110/1206] Fix malformed reST (#1802) Commit 2b045757b560 ("Improve documentation related to inheritance. (#1676)") left off a ':' from a hyperlink, which breaks the Travis CI build. --- docs/advanced/classes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index d1f8849b..c9a0da5a 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -325,7 +325,7 @@ can now create a python class that inherits from ``Dog``: Extended trampoline class functionality ======================================= -.. _extended_class_functionality_forced_trampoline +.. _extended_class_functionality_forced_trampoline: Forced trampoline class initialisation -------------------------------------- From d23c821b209c370b917b78186c613d9a40174b08 Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Tue, 11 Jun 2019 10:59:57 +0200 Subject: [PATCH 0111/1206] Make static member functions, added with `def_static`, `staticmethod` descriptor instances (#1732) --- include/pybind11/pybind11.h | 2 +- include/pybind11/pytypes.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 1b784673..76e243a9 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1121,7 +1121,7 @@ class class_ : public detail::generic_type { "def_static(...) called with a non-static member function pointer"); cpp_function cf(std::forward(f), name(name_), scope(*this), sibling(getattr(*this, name_, none())), extra...); - attr(cf.name()) = cf; + attr(cf.name()) = staticmethod(cf); return *this; } diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 81906f43..b4f4be92 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -742,6 +742,8 @@ inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } +inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; } + class kwargs_proxy : public handle { public: explicit kwargs_proxy(handle h) : handle(h) { } @@ -1281,6 +1283,11 @@ class function : public object { bool is_cpp_function() const { return (bool) cpp_function(); } }; +class staticmethod : public object { +public: + PYBIND11_OBJECT_CVT(staticmethod, object, detail::PyStaticMethod_Check, PyStaticMethod_New) +}; + class buffer : public object { public: PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) From a175b21e4b3b8f0032d30dbf78f33435f1273cd8 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 17:58:50 -0400 Subject: [PATCH 0112/1206] Avoid decoding already-decoded strings from cindex. Recent versions of clang.cindex include [code][1] that converts the internal byte strings to python str for you. [1]: https://github.com/llvm-mirror/clang/blob/master/bindings/python/clang/cindex.py#L72 --- tools/mkdoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 1fd8ccee..740bc3b3 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -59,7 +59,7 @@ output = [] def d(s): - return s.decode('utf8') + return s if isinstance(s, str) else s.decode('utf8') def sanitize_name(name): From 4612db54ace3215ab5a2d6bfa8169191866c8171 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 20:06:08 -0400 Subject: [PATCH 0113/1206] Try to autodetect the location of the clang standard libraries. On some linuxes, /usr/include belongs to GCC and the standard libraries that work with clang are in /usr/lib/clang/8.0.0 or some variation thereof. This results in errors such as: ``` /../lib64/gcc/x86_64-pc-linux-gnu/8.3.0/../../../../include/c++/8.3.0/bits/cxxabi_init_exception.h:38:10: fatal error: 'stddef.h' file not found ``` during extraction. --- tools/mkdoc.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 740bc3b3..f7048ce7 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -14,6 +14,7 @@ from clang import cindex from clang.cindex import CursorKind from collections import OrderedDict +from glob import glob from threading import Thread, Semaphore from multiprocessing import cpu_count @@ -240,6 +241,20 @@ def run(self): sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0]) parameters.append('-isysroot') parameters.append(sysroot_dir) + elif platform.system() == 'Linux': + # clang doesn't find its own base includes by default on Linux, + # but different distros install them in different paths. + # Try to autodetect, preferring the highest numbered version. + def clang_folder_version(d): + return [int(ver) for ver in re.findall(r'(? Date: Wed, 15 May 2019 20:31:51 -0400 Subject: [PATCH 0114/1206] Allow user to override default values of -x and -std=. --- tools/mkdoc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index f7048ce7..0b0ce3e1 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -225,8 +225,12 @@ def run(self): job_semaphore.release() if __name__ == '__main__': - parameters = ['-x', 'c++', '-std=c++11'] + parameters = [] filenames = [] + if "-x" not in args: + parameters.extend(['-x', 'c++']) + if not any(it.startswith("-std=") for it in args): + parameters.append('-std=c++11') if platform.system() == 'Darwin': dev_path = '/Applications/Xcode.app/Contents/Developer/' From a33212df1f9b35946ed66ddf479af8338f48b495 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 20:37:18 -0400 Subject: [PATCH 0115/1206] Wrap the main functionality of mkdoc in a function. --- tools/mkdoc.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 0b0ce3e1..c690aef7 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -59,6 +59,11 @@ output = [] + +class NoFilenamesError(ValueError): + pass + + def d(s): return s if isinstance(s, str) else s.decode('utf8') @@ -224,7 +229,8 @@ def run(self): finally: job_semaphore.release() -if __name__ == '__main__': + +def mkdoc(args): parameters = [] filenames = [] if "-x" not in args: @@ -260,15 +266,14 @@ def clang_folder_version(d): if clang_include_dir: parameters.extend(['-isystem', clang_include_dir]) - for item in sys.argv[1:]: + for item in args: if item.startswith('-'): parameters.append(item) else: filenames.append(item) if len(filenames) == 0: - print('Syntax: %s [.. a list of header files ..]' % sys.argv[0]) - exit(-1) + raise NoFilenamesError("args parameter did not contain any filenames") print('''/* This file contains docstrings for the Python bindings. @@ -321,3 +326,11 @@ def clang_folder_version(d): #pragma GCC diagnostic pop #endif ''') + + +if __name__ == '__main__': + try: + mkdoc(sys.argv[1:]) + except NoFilenamesError: + print('Syntax: %s [.. a list of header files ..]' % sys.argv[0]) + exit(-1) From ede328a784f657b97968a6228c8d3cae79619eb3 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 20:42:47 -0400 Subject: [PATCH 0116/1206] Allow writing output to file instead of stdout. --- tools/mkdoc.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index c690aef7..0757c80d 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -230,7 +230,7 @@ def run(self): job_semaphore.release() -def mkdoc(args): +def mkdoc(args, out_file=sys.stdout): parameters = [] filenames = [] if "-x" not in args: @@ -298,7 +298,7 @@ def clang_folder_version(d): #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #endif -''') +''', file=out_file) output.clear() for filename in filenames: @@ -319,18 +319,33 @@ def clang_folder_version(d): name_prev = name name_ctr = 1 print('\nstatic const char *%s =%sR"doc(%s)doc";' % - (name, '\n' if '\n' in comment else ' ', comment)) + (name, '\n' if '\n' in comment else ' ', comment), file=out_file) print(''' #if defined(__GNUG__) #pragma GCC diagnostic pop #endif -''') +''', file=out_file) if __name__ == '__main__': + args = sys.argv[1:] + out_path = None + for idx, arg in enumerate(args): + if arg.startswith("-o"): + args.remove(arg) + try: + out_path = arg[2:] or args.pop(idx) + except IndexError: + print("-o flag requires an argument") + exit(-1) + break try: - mkdoc(sys.argv[1:]) + if out_path: + with open(out_path, 'w') as out_file: + mkdoc(args, out_file) + else: + mkdoc(args) except NoFilenamesError: print('Syntax: %s [.. a list of header files ..]' % sys.argv[0]) exit(-1) From a163f8813e90d763e651c0d20239c55630321b73 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 20:50:23 -0400 Subject: [PATCH 0117/1206] Delete partially-written file in the event of an error. --- tools/mkdoc.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 0757c80d..ffe37d28 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -342,8 +342,17 @@ def clang_folder_version(d): break try: if out_path: - with open(out_path, 'w') as out_file: - mkdoc(args, out_file) + try: + with open(out_path, 'w') as out_file: + mkdoc(args, out_file) + except: + # In the event of an error, don't leave a partially-written + # output file. + try: + os.unlink(out_path) + except: + pass + raise else: mkdoc(args) except NoFilenamesError: From 590e7acedf7965461b558228929d6f544aab29fc Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 20:53:35 -0400 Subject: [PATCH 0118/1206] Avoid storing global state. --- tools/mkdoc.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index ffe37d28..a99357eb 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -57,8 +57,6 @@ job_count = cpu_count() job_semaphore = Semaphore(job_count) -output = [] - class NoFilenamesError(ValueError): pass @@ -188,7 +186,7 @@ def process_comment(comment): return result.rstrip().lstrip('\n') -def extract(filename, node, prefix): +def extract(filename, node, prefix, output): if not (node.location.file is None or os.path.samefile(d(node.location.file.name), filename)): return 0 @@ -199,7 +197,7 @@ def extract(filename, node, prefix): sub_prefix += '_' sub_prefix += d(node.spelling) for i in node.get_children(): - extract(filename, i, sub_prefix) + extract(filename, i, sub_prefix, output) if node.kind in PRINT_LIST: comment = d(node.raw_comment) if node.raw_comment is not None else '' comment = process_comment(comment) @@ -208,15 +206,15 @@ def extract(filename, node, prefix): sub_prefix += '_' if len(node.spelling) > 0: name = sanitize_name(sub_prefix + d(node.spelling)) - global output output.append((name, filename, comment)) class ExtractionThread(Thread): - def __init__(self, filename, parameters): + def __init__(self, filename, parameters, output): Thread.__init__(self) self.filename = filename self.parameters = parameters + self.output = output job_semaphore.acquire() def run(self): @@ -225,7 +223,7 @@ def run(self): index = cindex.Index( cindex.conf.lib.clang_createIndex(False, True)) tu = index.parse(self.filename, self.parameters) - extract(self.filename, tu.cursor, '') + extract(self.filename, tu.cursor, '', self.output) finally: job_semaphore.release() @@ -300,9 +298,9 @@ def clang_folder_version(d): #endif ''', file=out_file) - output.clear() + output = [] for filename in filenames: - thr = ExtractionThread(filename, parameters) + thr = ExtractionThread(filename, parameters, output) thr.start() print('Waiting for jobs to finish ..', file=sys.stderr) From 2c8c5c4ed395acc3b39fdbc16a59ffe8b7c7a138 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 21:04:02 -0400 Subject: [PATCH 0119/1206] Split into seperate functions for easier invocation from python. --- tools/mkdoc.py | 62 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index a99357eb..2bf4f035 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -228,7 +228,7 @@ def run(self): job_semaphore.release() -def mkdoc(args, out_file=sys.stdout): +def extract_all(args): parameters = [] filenames = [] if "-x" not in args: @@ -273,6 +273,19 @@ def clang_folder_version(d): if len(filenames) == 0: raise NoFilenamesError("args parameter did not contain any filenames") + output = [] + for filename in filenames: + thr = ExtractionThread(filename, parameters, output) + thr.start() + + print('Waiting for jobs to finish ..', file=sys.stderr) + for i in range(job_count): + job_semaphore.acquire() + + return output + + +def write_header(comments, out_file=sys.stdout): print('''/* This file contains docstrings for the Python bindings. Do not edit! These were automatically extracted by mkdoc.py @@ -298,18 +311,10 @@ def clang_folder_version(d): #endif ''', file=out_file) - output = [] - for filename in filenames: - thr = ExtractionThread(filename, parameters, output) - thr.start() - - print('Waiting for jobs to finish ..', file=sys.stderr) - for i in range(job_count): - job_semaphore.acquire() name_ctr = 1 name_prev = None - for name, _, comment in list(sorted(output, key=lambda x: (x[0], x[1]))): + for name, _, comment in list(sorted(comments, key=lambda x: (x[0], x[1]))): if name == name_prev: name_ctr += 1 name = name + "_%i" % name_ctr @@ -326,8 +331,8 @@ def clang_folder_version(d): ''', file=out_file) -if __name__ == '__main__': - args = sys.argv[1:] +def mkdoc(args): + args = list(args) out_path = None for idx, arg in enumerate(args): if arg.startswith("-o"): @@ -338,21 +343,28 @@ def clang_folder_version(d): print("-o flag requires an argument") exit(-1) break - try: - if out_path: + + comments = extract_all(args) + + if out_path: + try: + with open(out_path, 'w') as out_file: + write_header(comments, out_file) + except: + # In the event of an error, don't leave a partially-written + # output file. try: - with open(out_path, 'w') as out_file: - mkdoc(args, out_file) + os.unlink(out_path) except: - # In the event of an error, don't leave a partially-written - # output file. - try: - os.unlink(out_path) - except: - pass - raise - else: - mkdoc(args) + pass + raise + else: + write_header(comments) + + +if __name__ == '__main__': + try: + mkdoc(sys.argv[1:]) except NoFilenamesError: print('Syntax: %s [.. a list of header files ..]' % sys.argv[0]) exit(-1) From e0b8bbbce965e4d9ca6c364a1289150477411d54 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 15 May 2019 21:14:44 -0400 Subject: [PATCH 0120/1206] Use a file-local constant for non-prefixing nodes. --- tools/mkdoc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) mode change 100644 => 100755 tools/mkdoc.py diff --git a/tools/mkdoc.py b/tools/mkdoc.py old mode 100644 new mode 100755 index 2bf4f035..dd954ddc --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -41,6 +41,10 @@ CursorKind.FIELD_DECL ] +PREFIX_BLACKLIST = [ + CursorKind.TRANSLATION_UNIT +] + CPP_OPERATORS = { '<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array', '+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=': @@ -192,7 +196,7 @@ def extract(filename, node, prefix, output): return 0 if node.kind in RECURSE_LIST: sub_prefix = prefix - if node.kind != CursorKind.TRANSLATION_UNIT: + if node.kind not in PREFIX_BLACKLIST: if len(sub_prefix) > 0: sub_prefix += '_' sub_prefix += d(node.spelling) From 41f29ccd9e5c3bb0a61236212dd448a01ac8e649 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 23 May 2019 08:39:18 -0400 Subject: [PATCH 0121/1206] Parse command-line args in a separate function. --- tools/mkdoc.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index dd954ddc..44164af3 100755 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -232,7 +232,7 @@ def run(self): job_semaphore.release() -def extract_all(args): +def read_args(args): parameters = [] filenames = [] if "-x" not in args: @@ -277,6 +277,11 @@ def clang_folder_version(d): if len(filenames) == 0: raise NoFilenamesError("args parameter did not contain any filenames") + return parameters, filenames + + +def extract_all(args): + parameters, filenames = read_args(args) output = [] for filename in filenames: thr = ExtractionThread(filename, parameters, output) From 000aabb2a715fcea1a9857d244321bdbc2847d82 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Tue, 11 Jun 2019 14:00:05 +0200 Subject: [PATCH 0122/1206] Test: Numpy Scalar Creation (#1530) I found that the numpy array tests already contained an empty-shaped array test, but none with data in it. Following PEP 3118, scalars have an empty shape and ndim 0. This works already and is now also documented/covered by a test. --- tests/test_numpy_array.cpp | 5 +++++ tests/test_numpy_array.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 57025949..cdf0b82d 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -68,6 +68,9 @@ template 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; } @@ -104,6 +107,8 @@ TEST_SUBMODULE(numpy_array, sm) { // 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) { diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 8ac0e66f..8bacb7f6 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -138,6 +138,11 @@ def test_make_c_f_array(): 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 77ef03d5b136891fec8878bf8c6e7442ce75e903 Mon Sep 17 00:00:00 2001 From: Jeff VanOss Date: Tue, 11 Jun 2019 08:25:35 -0400 Subject: [PATCH 0123/1206] compile time check that properties have no py:arg values (#1524) --- include/pybind11/pybind11.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 76e243a9..ca0b1a70 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1266,6 +1266,8 @@ class class_ : public detail::generic_type { /// Uses cpp_function's return_value_policy by default template class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + static_assert( 0 == detail::constexpr_sum(std::is_base_of::value...), + "Argument annotations are not allowed for properties"); auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); auto *rec_active = rec_fget; if (rec_fget) { From 868d94fcb4769ad3a5d826c9f5483376433da8a5 Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Tue, 11 Jun 2019 16:05:00 -0400 Subject: [PATCH 0124/1206] Apply c++ standard flag only to files of CXX language. (#1678) --- tools/pybind11Config.cmake.in | 6 +++++- tools/pybind11Tools.cmake | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 3dd1b2c1..8a7272ff 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -90,7 +90,11 @@ if(NOT TARGET ${PN}::pybind11) set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) endif() - set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") + if(CMAKE_VERSION VERSION_LESS 3.3) + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") + else() + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$:${PYBIND11_CPP_STANDARD}>) + endif() get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES) get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index ab8bf627..e3ec572b 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -185,7 +185,11 @@ function(pybind11_add_module target_name) endif() # Make sure C++11/14 are enabled - target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + if(CMAKE_VERSION VERSION_LESS 3.3) + target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + else() + target_compile_options(${target_name} PUBLIC $<$:${PYBIND11_CPP_STANDARD}>) + endif() if(ARG_NO_EXTRAS) return() From 38f408fccd358e4322238f5aada3b3af22f5f388 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Tue, 11 Jun 2019 22:09:56 +0200 Subject: [PATCH 0125/1206] value_and_holder: uninit members (#1660) fix some uninitialized members in `value_and_holder` for some of the constructurs. Found with coverity in a downstream project. --- include/pybind11/cast.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 80abb2b9..8d0fd5d9 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -204,10 +204,10 @@ PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool t } struct value_and_holder { - instance *inst; - size_t index; - const detail::type_info *type; - void **vh; + instance *inst = nullptr; + size_t index = 0u; + const detail::type_info *type = nullptr; + void **vh = nullptr; // Main constructor for a found value/holder: value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : @@ -216,7 +216,7 @@ struct value_and_holder { {} // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() : inst{nullptr} {} + value_and_holder() {} // Used for past-the-end iterator value_and_holder(size_t index) : index{index} {} @@ -270,8 +270,8 @@ struct values_and_holders { struct iterator { private: - instance *inst; - const type_vec *types; + instance *inst = nullptr; + const type_vec *types = nullptr; value_and_holder curr; friend struct values_and_holders; iterator(instance *inst, const type_vec *tinfo) From 95f750a87d82ad6b996ceae27e68737472ff4809 Mon Sep 17 00:00:00 2001 From: Omar Awile Date: Tue, 11 Jun 2019 22:11:38 +0200 Subject: [PATCH 0126/1206] Add optional buffer size to pythonbuf::d_buffer constructor (#1687) In some cases the user of pythonbuf needs to allocate the internal buffer to a specific size e.g. for performance or to enable synchronous writes to the buffer. By changing `pythonbuf::d_buffer` to be dynamically allocated we can now enable these use-cases while still providing the default behavior of allocating a 1024 byte internal buffer (through a default parameter). --- include/pybind11/iostream.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/pybind11/iostream.h b/include/pybind11/iostream.h index 182e8eef..9119270e 100644 --- a/include/pybind11/iostream.h +++ b/include/pybind11/iostream.h @@ -25,7 +25,8 @@ class pythonbuf : public std::streambuf { private: using traits_type = std::streambuf::traits_type; - char d_buffer[1024]; + const size_t buf_size; + std::unique_ptr d_buffer; object pywrite; object pyflush; @@ -51,10 +52,13 @@ class pythonbuf : public std::streambuf { } public: - pythonbuf(object pyostream) - : pywrite(pyostream.attr("write")), + + pythonbuf(object pyostream, size_t buffer_size = 1024) + : buf_size(buffer_size), + d_buffer(new char[buf_size]), + pywrite(pyostream.attr("write")), pyflush(pyostream.attr("flush")) { - setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); + setp(d_buffer.get(), d_buffer.get() + buf_size - 1); } /// Sync before destroy From 047ce8c452087809f5ab10d7a2ea58c8709a8da3 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 11 Jun 2019 16:17:50 -0400 Subject: [PATCH 0127/1206] Fix iostream when used with nogil (#1368) --- include/pybind11/iostream.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/pybind11/iostream.h b/include/pybind11/iostream.h index 9119270e..72baef8f 100644 --- a/include/pybind11/iostream.h +++ b/include/pybind11/iostream.h @@ -43,8 +43,11 @@ class pythonbuf : public std::streambuf { // This subtraction cannot be negative, so dropping the sign str line(pbase(), static_cast(pptr() - pbase())); - pywrite(line); - pyflush(); + { + gil_scoped_acquire tmp; + pywrite(line); + pyflush(); + } setp(pbase(), epptr()); } From 0071a3feb091ee0a1ede2010922ef825c7d08fff Mon Sep 17 00:00:00 2001 From: Alexander Gagarin Date: Wed, 12 Jun 2019 01:23:08 +0500 Subject: [PATCH 0128/1206] Fix async Python functors invoking from multiple C++ threads (#1587) (#1595) * Fix async Python functors invoking from multiple C++ threads (#1587) Ensure GIL is held during functor destruction. * Add async Python callbacks test that runs in separate Python thread --- include/pybind11/functional.h | 15 +++++++++++++-- tests/test_callbacks.cpp | 19 +++++++++++++++++++ tests/test_callbacks.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 9cdf21f7..7a0988ab 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -54,9 +54,20 @@ struct type_caster> { } } - value = [func](Args... args) -> Return { + // ensure GIL is held during functor destruction + struct func_handle { + function f; + func_handle(function&& f_) : f(std::move(f_)) {} + func_handle(const func_handle&) = default; + ~func_handle() { + gil_scoped_acquire acq; + function kill_f(std::move(f)); + } + }; + + value = [hfunc = func_handle(std::move(func))](Args... args) -> Return { gil_scoped_acquire acq; - object retval(func(std::forward(args)...)); + object retval(hfunc.f(std::forward(args)...)); /* Visual studio 2015 parser issue: need parentheses around this expression */ return (retval.template cast()); }; diff --git a/tests/test_callbacks.cpp b/tests/test_callbacks.cpp index 273eacc3..71b88c44 100644 --- a/tests/test_callbacks.cpp +++ b/tests/test_callbacks.cpp @@ -10,6 +10,7 @@ #include "pybind11_tests.h" #include "constructor_stats.h" #include +#include int dummy_function(int i) { return i + 1; } @@ -146,4 +147,22 @@ TEST_SUBMODULE(callbacks, m) { py::class_(m, "CppBoundMethodTest") .def(py::init<>()) .def("triple", [](CppBoundMethodTest &, int val) { return 3 * val; }); + + // test async Python callbacks + using callback_f = std::function; + 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(i)); + }); } diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 93c42c22..6439c8e7 100644 --- a/tests/test_callbacks.py +++ b/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() From 1aa8dd17451108871ade6b1ea86d6bcea9d3215a Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 11 Jun 2019 13:25:12 -0700 Subject: [PATCH 0129/1206] Fix assertion failure for unions (#1685) (#1709) In def_readonly and def_readwrite, there is an assertion that the member comes from the class or a base class: static_assert(std::is_base_of::value, "..."); However, if C and type are the same type, is_base_of will still only be true if they are the same _non-union_ type. This means we can't define accessors for the members of a union type because of this assertion. Update the assertion to test std::is_same::value || std::is_base_of::value which will allow union types, or members of base classes. Also add a basic unit test for accessing unions. --- include/pybind11/pybind11.h | 4 ++-- tests/CMakeLists.txt | 1 + tests/test_union.cpp | 22 ++++++++++++++++++++++ tests/test_union.py | 8 ++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/test_union.cpp create mode 100644 tests/test_union.py diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index ca0b1a70..f1d91c78 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1185,7 +1185,7 @@ class class_ : public detail::generic_type { template class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { - static_assert(std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + static_assert(std::is_same::value || std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); def_property(name, fget, fset, return_value_policy::reference_internal, extra...); @@ -1194,7 +1194,7 @@ class class_ : public detail::generic_type { template class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { - static_assert(std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + static_assert(std::is_same::value || std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); return *this; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a31d5b8b..42640d0b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -59,6 +59,7 @@ set(PYBIND11_TEST_FILES test_stl.cpp test_stl_binders.cpp test_tagbased_polymorphic.cpp + test_union.cpp test_virtual_functions.cpp ) diff --git a/tests/test_union.cpp b/tests/test_union.cpp new file mode 100644 index 00000000..7b98ea21 --- /dev/null +++ b/tests/test_union.cpp @@ -0,0 +1,22 @@ +/* + tests/test_class.cpp -- test py::class_ definitions and basic functionality + + Copyright (c) 2019 Roland Dreier + + 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_(m, "TestUnion") + .def(py::init<>()) + .def_readonly("as_int", &TestUnion::value_int) + .def_readwrite("as_uint", &TestUnion::value_uint); +} diff --git a/tests/test_union.py b/tests/test_union.py new file mode 100644 index 00000000..e1866e70 --- /dev/null +++ b/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 From 22859bb8fc1a7f106182e142dcb8b5aed6a7a879 Mon Sep 17 00:00:00 2001 From: Chris Rusby Date: Wed, 22 Aug 2018 22:38:27 +0100 Subject: [PATCH 0130/1206] Support more natural syntax for vector extend --- docs/changelog.rst | 5 +++++ include/pybind11/pytypes.h | 15 +++++++++++++++ include/pybind11/stl_bind.h | 24 +++++++++++++++++++++++- tests/test_stl_binders.py | 20 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 606be413..28791c89 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -94,6 +94,11 @@ v2.2.4 (September 11, 2018) * A few minor typo fixes and improvements to the test suite, and patches that silence compiler warnings. +* Vectors now support construction from generators, as well as ``extend()`` from a + list or generator. + `#1496 `_. + + v2.2.3 (April 29, 2018) ----------------------------------------------------- diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index b4f4be92..db7dfec2 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1346,6 +1346,21 @@ inline size_t len(handle h) { return (size_t) result; } +inline size_t len_hint(handle h) { +#if PY_VERSION_HEX >= 0x03040000 + ssize_t result = PyObject_LengthHint(h.ptr(), 0); +#else + ssize_t result = PyObject_Length(h.ptr()); +#endif + if (result < 0) { + // Sometimes a length can't be determined at all (eg generators) + // In which case simply return 0 + PyErr_Clear(); + return 0; + } + return (size_t) result; +} + inline str repr(handle h) { PyObject *str_value = PyObject_Repr(h.ptr()); if (!str_value) throw error_already_set(); diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index d6f4c633..1f872526 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -122,7 +122,7 @@ void vector_modifiers(enable_if_t(new Vector()); - v->reserve(len(it)); + v->reserve(len_hint(it)); for (handle h : it) v->push_back(h.cast()); return v.release(); @@ -136,6 +136,28 @@ void vector_modifiers(enable_if_t()); + } + } catch (const cast_error &) { + v.erase(v.begin() + static_cast(old_size), v.end()); + try { + v.shrink_to_fit(); + } catch (const std::exception &) { + // Do nothing + } + throw; + } + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + cl.def("insert", [](Vector &v, SizeType i, const T &x) { if (i > v.size()) diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 0030924f..52c8ac0c 100644 --- a/tests/test_stl_binders.py +++ b/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,22 @@ 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]) + # related to the PyPy's buffer protocol. @pytest.unsupported_on_pypy From 21c3911bd3eef2d763783ff2122d0fddb9958c01 Mon Sep 17 00:00:00 2001 From: sizmailov Date: Fri, 11 May 2018 21:30:15 +0300 Subject: [PATCH 0131/1206] add signed overload for `py::slice::compute` --- include/pybind11/pytypes.h | 7 +++++++ tests/test_sequences_and_iterators.cpp | 19 +++++++++++++++++++ tests/test_sequences_and_iterators.py | 13 +++++++++++++ 3 files changed, 39 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index db7dfec2..2d573dfa 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1133,6 +1133,13 @@ class slice : public object { (ssize_t *) stop, (ssize_t *) step, (ssize_t *) slicelength) == 0; } + bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, + ssize_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + length, start, + stop, step, + slicelength) == 0; + } }; class capsule : public object { diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index a4552125..87ccf99d 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/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_(m,"Sliceable") + .def(py::init()) + .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(start); + int istop = static_cast(stop); + int istep = static_cast(step); + return std::make_tuple(istart,istop,istep); + }) + ; // test_sequence class Sequence { diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index f6c06209..6bd16064 100644 --- a/tests/test_sequences_and_iterators.py +++ b/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) From e11e71d85cb9ff39b93651530c58f8256fcaa576 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 11 Jun 2019 22:42:30 +0200 Subject: [PATCH 0132/1206] Make compiler flags for -Werror specific to GNU, Clang, or Intel --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 42640d0b..9a701108 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -126,14 +126,14 @@ find_package(Boost 1.56) function(pybind11_enable_warnings target_name) if(MSVC) target_compile_options(${target_name} PRIVATE /W4) - else() + 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() From 51ca6b08327810c0edfc70c0c1a3cc074dc39b5f Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 11 Jun 2019 22:47:37 +0200 Subject: [PATCH 0133/1206] Update docs on std::out_of_range exception mapping (#1254) --- docs/advanced/exceptions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/exceptions.rst b/docs/advanced/exceptions.rst index 629af4aa..75ac24ae 100644 --- a/docs/advanced/exceptions.rst +++ b/docs/advanced/exceptions.rst @@ -24,7 +24,7 @@ exceptions: +--------------------------------------+--------------------------------------+ | :class:`std::length_error` | ``ValueError`` | +--------------------------------------+--------------------------------------+ -| :class:`std::out_of_range` | ``ValueError`` | +| :class:`std::out_of_range` | ``IndexError`` | +--------------------------------------+--------------------------------------+ | :class:`std::range_error` | ``ValueError`` | +--------------------------------------+--------------------------------------+ From cf36e3d9baa7c771115edae1b16353793eecf1ac Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 11 Jun 2019 22:03:10 +0200 Subject: [PATCH 0134/1206] updated changelog --- docs/changelog.rst | 63 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 28791c89..8f0f8ef2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,7 +6,7 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. -v2.3.0 (Not yet released) +v2.3.0 (June 11, 2019) ----------------------------------------------------- * Significantly reduced module binary size (10-20%) when compiled in C++11 mode @@ -19,9 +19,6 @@ v2.3.0 (Not yet released) provide a method to returns the desired type of an instance. `#1326 `_. -* Added support for write only properties. - `#1144 `_. - * Python type wrappers (``py::handle``, ``py::object``, etc.) now support map Python's number protocol onto C++ arithmetic operators such as ``operator+``, ``operator/=``, etc. @@ -41,15 +38,68 @@ v2.3.0 (Not yet released) 3. check for already existing enum value and throw an error if present. `#1453 `_. -* added ``py::ellipsis()`` method for slicing of multidimensional NumPy arrays +* Support for over-aligned type allocation via C++17's aligned ``new`` + statement. `#1582 `_. + +* Added ``py::ellipsis()`` method for slicing of multidimensional NumPy arrays `#1502 `_. +* Numerous Improvements to the ``mkdoc.py`` script for extracting documentation + from C++ header files. + `#1788 `_. + * ``pybind11_add_module()``: allow including Python as a ``SYSTEM`` include path. `#1416 `_. * ``pybind11/stl.h`` does not convert strings to ``vector`` anymore. `#1258 `_. +* Mark static methods as such to fix auto-generated Sphinx documentation. + `#1732 `_. + +* Re-throw forced unwind exceptions (e.g. during pthread termination). + `#1208 `_. + +* Added ``__contains__`` method to the bindings of maps (``std::map``, + ``std::unordered_map``). + `#1767 `_. + +* Improvements to ``gil_scoped_acquire``. + `#1211 `_. + +* Type caster support for ``std::deque``. + `#1609 `_. + +* Support for ``std::unique_ptr`` holders, whose deleters differ between a base and derived + class. `#1353 `_. + +* Construction of STL array/vector-like data structures from + iterators. Added an ``extend()`` operation. + `#1709 `_, + +* CMake build system improvements for projects that include non-C++ + files (e.g. plain C, CUDA) in ``pybind11_add_module`` et al. + `#1678 `_. + +* Fixed asynchronous invocation and deallocation of Python functions + wrapped in ``std::function``. + `#1595 `_. + +* Fixes regarding return value policy propagation in STL type casters. + `#1603 `_. + +* Fixed scoped enum comparisons. + `#1571 `_. + +* Fixed iostream redirection for code that releases the GIL. + `#1368 `_, + +* A number of CI-related fixes. + `#1757 `_, + `#1744 `_, + `#1670 `_. + + v2.2.4 (September 11, 2018) ----------------------------------------------------- @@ -408,6 +458,9 @@ v2.2.0 (August 31, 2017) * Fixed overriding static properties in derived classes. `#784 `_. +* Added support for write only properties. + `#1144 `_. + * Improved deduction of member functions of a derived class when its bases aren't registered with pybind11. `#855 `_. From 64f2a5f8e699736f528b6eb3fa143492b65da93a Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 12 Jun 2019 21:03:40 +0200 Subject: [PATCH 0135/1206] begin work on v2.3.1 --- docs/changelog.rst | 6 ++++++ docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8f0f8ef2..f99d3516 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,12 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. + +v2.3.1 (Not yet released) +----------------------------------------------------- + +* TBA + v2.3.0 (June 11, 2019) ----------------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index 868f9609..d17e4ba3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.3' # The full version, including alpha/beta/rc tags. -release = '2.3.dev0' +release = '2.3.dev1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 5ff74856..300c2b23 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 3 -#define PYBIND11_VERSION_PATCH dev0 +#define PYBIND11_VERSION_PATCH dev1 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index c046ed71..fef541bd 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 3, 'dev0') +version_info = (2, 3, 'dev1') __version__ = '.'.join(map(str, version_info)) From ed39c50458738794a77bd2a43a3564aaead0f191 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 12 Jun 2019 21:10:37 +0200 Subject: [PATCH 0136/1206] README.md: added several folks who've made repeated contributions --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 135f9e1e..35d2d76f 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ In addition to the core functionality, pybind11 provides some extra goodies: This project was created by [Wenzel Jakob](http://rgl.epfl.ch/people/wjakob). Significant features and/or improvements to the code were contributed by Jonas Adler, +Lori A. Burns, Sylvain Corlay, Trent Houliston, Axel Huebl, @@ -117,6 +118,7 @@ Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim Schellart, +Henry Schreiner, Ivan Smirnov, and Patrick Stewart. From b3bf248eec9cad8260753c982e1ae6cb72fff470 Mon Sep 17 00:00:00 2001 From: Alexander Gagarin Date: Thu, 13 Jun 2019 12:17:10 +0500 Subject: [PATCH 0137/1206] Fix casting of time points with non-system-clock duration with VS (#1748) * Fix casting of time points with non-system-clock duration on Windows Add explicit `time_point_cast` to time point with duration of system clock. Fixes Visual Studio compile error. * Add test case for custom time points casting --- include/pybind11/chrono.h | 2 +- tests/test_chrono.cpp | 8 ++++++++ tests/test_chrono.py | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h index 95ada76e..2ace2329 100644 --- a/include/pybind11/chrono.h +++ b/include/pybind11/chrono.h @@ -128,7 +128,7 @@ template class type_caster(src)); // this function uses static memory so it's best to copy it out asap just in case // otherwise other code that is using localtime may break this (not just python code) std::tm localtime = *std::localtime(&tt); diff --git a/tests/test_chrono.cpp b/tests/test_chrono.cpp index 195a93bb..899d08d8 100644 --- a/tests/test_chrono.cpp +++ b/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; + using timestamp = std::chrono::time_point; + // 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 a, std::chrono::duration b) { return a - b; }); + + m.def("test_nano_timepoint", [](timestamp start, timespan delta) -> timestamp { + return start + delta; + }); } diff --git a/tests/test_chrono.py b/tests/test_chrono.py index 2b75bd19..f308de97 100644 --- a/tests/test_chrono.py +++ b/tests/test_chrono.py @@ -99,3 +99,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)) From a1b71df137e015d44f7e31f7b6d4807253fb7871 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 19 Jun 2019 10:48:36 +0200 Subject: [PATCH 0138/1206] fix issue #1804 (warning about redefined macros) --- include/pybind11/pybind11.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index f1d91c78..0cc5dd52 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -41,16 +41,15 @@ # endif #endif -#if defined(__GNUG__) && !defined(__clang__) - #include -#endif - - #include "attr.h" #include "options.h" #include "detail/class.h" #include "detail/init.h" +#if defined(__GNUG__) && !defined(__clang__) +# include +#endif + NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object From 502ffe50a9f22c04637bbc8ec0019488458ba948 Mon Sep 17 00:00:00 2001 From: Ian Bell Date: Sat, 22 Jun 2019 04:07:41 -0600 Subject: [PATCH 0139/1206] Add docs and tests for unary op on class (#1814) --- docs/advanced/classes.rst | 1 + tests/test_operator_overloading.cpp | 2 ++ tests/test_operator_overloading.py | 16 +++++++++------- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index c9a0da5a..ae5907de 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -662,6 +662,7 @@ to Python. .def(py::self *= float()) .def(float() * py::self) .def(py::self * float()) + .def(-py::self) .def("__repr__", &Vector2::toString); } diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index 8ca7d8bc..7b111704 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -23,6 +23,7 @@ class Vector2 { 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); } @@ -104,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)) ; diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index 86827d2b..bd36ac2a 100644 --- a/tests/test_operator_overloading.py +++ b/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 From 8b90b1da625f7e96ce9164db55b97a48e039b730 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 6 Jul 2019 14:52:32 +0200 Subject: [PATCH 0140/1206] error_already_set: acquire GIL one line earlier (fixes #1779) --- include/pybind11/pybind11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 0cc5dd52..68e5757e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1998,8 +1998,8 @@ class gil_scoped_release { }; error_already_set::~error_already_set() { if (m_type) { - error_scope scope; gil_scoped_acquire gil; + error_scope scope; m_type.release().dec_ref(); m_value.release().dec_ref(); m_trace.release().dec_ref(); From 9fd4712121fdbb6202a35be4c788525e6c8ab826 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 6 Jul 2019 16:57:17 +0200 Subject: [PATCH 0141/1206] fix test suite (pytest changes in ExceptionInfo class) --- tests/test_eigen.py | 4 ++-- tests/test_local_bindings.py | 4 ++-- tests/test_methods_and_attributes.py | 16 ++++++++-------- tests/test_smart_ptr.py | 3 ++- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 45f64ca9..55d93517 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -679,10 +679,10 @@ def test_issue1105(): # 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) + 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) + assert "incompatible function arguments" in str(excinfo.value) def test_custom_operator_new(): diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index b3dc3619..b380376e 100644 --- a/tests/test_local_bindings.py +++ b/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/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 86b2c3b4..f1c862be 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -100,32 +100,32 @@ def test_properties(): with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_writeonly # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo) + 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) + 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) + 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 with pytest.raises(AttributeError) as excinfo: dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo) + assert "unreadable attribute" in str(excinfo.value) m.TestProperties.def_writeonly_static = 3 assert m.TestProperties.def_readonly_static == 3 @@ -133,14 +133,14 @@ def test_static_properties(): 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) + 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) + assert "unreadable attribute" in str(excinfo.value) m.TestProperties.def_property_writeonly_static = 5 assert m.TestProperties.def_property_static == 5 @@ -158,7 +158,7 @@ def test_static_properties(): with pytest.raises(AttributeError) as excinfo: dummy = instance.def_property_writeonly_static # noqa: F841 unused var - assert "unreadable attribute" in str(excinfo) + assert "unreadable attribute" in str(excinfo.value) instance.def_property_writeonly_static = 4 assert instance.def_property_static == 4 diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 0a3bb58e..c6627043 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -272,7 +272,8 @@ 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(): From 74d335a53580477bad8b5a1bea2db14b5788ce02 Mon Sep 17 00:00:00 2001 From: Toru Niina Date: Wed, 10 Jul 2019 17:13:56 +0900 Subject: [PATCH 0142/1206] Replace a usage of C++14 language features with C++11 code (#1833) --- include/pybind11/functional.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 7a0988ab..f8bda648 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -65,12 +65,19 @@ struct type_caster> { } }; - value = [hfunc = func_handle(std::move(func))](Args... args) -> Return { - gil_scoped_acquire acq; - object retval(hfunc.f(std::forward(args)...)); - /* Visual studio 2015 parser issue: need parentheses around this expression */ - return (retval.template cast()); + // to emulate 'move initialization capture' in C++11 + struct func_wrapper { + func_handle hfunc; + func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {} + Return operator()(Args... args) const { + gil_scoped_acquire acq; + object retval(hfunc.f(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + } }; + + value = func_wrapper(func_handle(std::move(func))); return true; } From a301c5add866fe91bf3b91df3d8034c124858a5e Mon Sep 17 00:00:00 2001 From: Igor Socec Date: Mon, 15 Jul 2019 12:31:03 +0100 Subject: [PATCH 0143/1206] Dtype field ordering for NumPy 1.14 (#1837) * Test dtype field order in numpy dtype tests When running tests with NumPy 1.14 or later this test exposes the "invalid buffer descriptor" error reported in #1274. * Create dtype_ptr with ordered fields --- include/pybind11/numpy.h | 11 +++++++---- tests/test_numpy_dtypes.cpp | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index b2a02e02..a0441efa 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1066,8 +1066,14 @@ inline PYBIND11_NOINLINE void register_structured_dtype( if (numpy_internals.get_type_info(tinfo, false)) pybind11_fail("NumPy: dtype is already registered"); + // Use ordered fields because order matters as of NumPy 1.14: + // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays + std::vector ordered_fields(std::move(fields)); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + list names, formats, offsets; - for (auto field : *fields) { + for (auto& field : ordered_fields) { if (!field.descr) pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ " + tinfo.name()); @@ -1084,9 +1090,6 @@ inline PYBIND11_NOINLINE void register_structured_dtype( // - https://github.com/numpy/numpy/pull/7798 // Because of this, we won't use numpy's logic to generate buffer format // strings and will just do it ourselves. - std::vector ordered_fields(std::move(fields)); - std::sort(ordered_fields.begin(), ordered_fields.end(), - [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); ssize_t offset = 0; std::ostringstream oss; // mark the structure as unaligned with '^', because numpy and C++ don't diff --git a/tests/test_numpy_dtypes.cpp b/tests/test_numpy_dtypes.cpp index 6e3dc6ba..467e0253 100644 --- a/tests/test_numpy_dtypes.cpp +++ b/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_; @@ -255,6 +262,7 @@ TEST_SUBMODULE(numpy_dtypes, m) { py::class_(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_); From dffe869dba4a305ec1d822aea8b21ff406fb8513 Mon Sep 17 00:00:00 2001 From: Thomas Peters Date: Mon, 15 Jul 2019 10:16:14 -0400 Subject: [PATCH 0144/1206] quiet clang warning by adding default move ctor (#1821) --- include/pybind11/iostream.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/pybind11/iostream.h b/include/pybind11/iostream.h index 72baef8f..c43b7c93 100644 --- a/include/pybind11/iostream.h +++ b/include/pybind11/iostream.h @@ -64,6 +64,8 @@ class pythonbuf : public std::streambuf { setp(d_buffer.get(), d_buffer.get() + buf_size - 1); } + pythonbuf(pythonbuf&&) = default; + /// Sync before destroy ~pythonbuf() { sync(); From b60fd233fa423ff56579513efd90820509efb2cc Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Mon, 15 Jul 2019 15:47:02 +0100 Subject: [PATCH 0145/1206] Make sure `detail::get_internals` acquires the GIL before making Python calls. (#1836) This is only necessary if `get_internals` is called for the first time in a given module when the running thread is in a GIL-released state. Fixes #1364 --- include/pybind11/detail/internals.h | 8 ++++ tests/CMakeLists.txt | 12 +++++ tests/cross_module_gil_utils.cpp | 73 +++++++++++++++++++++++++++++ tests/test_gil_scoped.cpp | 8 ++++ tests/test_gil_scoped.py | 5 ++ 5 files changed, 106 insertions(+) create mode 100644 tests/cross_module_gil_utils.cpp diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index f1dd3876..952f8017 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -171,6 +171,14 @@ PYBIND11_NOINLINE inline internals &get_internals() { if (internals_pp && *internals_pp) return **internals_pp; + // Ensure that the GIL is held since we will need to make Python calls. + // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. + struct gil_scoped_acquire { + gil_scoped_acquire() : state (PyGILState_Ensure()) {} + ~gil_scoped_acquire() { PyGILState_Release(state); } + const PyGILState_STATE state; + } gil; + constexpr auto *id = PYBIND11_INTERNALS_ID; auto builtins = handle(PyEval_GetBuiltins()); if (builtins.contains(id) && isinstance(builtins[id])) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9a701108..fb6776f2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -83,6 +83,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). @@ -150,6 +154,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/tests/cross_module_gil_utils.cpp b/tests/cross_module_gil_utils.cpp new file mode 100644 index 00000000..07db9f6e --- /dev/null +++ b/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 +#include + +// 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(&gil_acquire))); + } + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif +} diff --git a/tests/test_gil_scoped.cpp b/tests/test_gil_scoped.cpp index cb0010ee..76c17fdc 100644 --- a/tests/test_gil_scoped.cpp +++ b/tests/test_gil_scoped.cpp @@ -41,4 +41,12 @@ TEST_SUBMODULE(gil_scoped, m) { [](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( + PyLong_AsVoidPtr(cm.attr("gil_acquire_funcaddr").ptr())); + py::gil_scoped_release gil_release; + gil_acquire(); + }); } diff --git a/tests/test_gil_scoped.py b/tests/test_gil_scoped.py index 2c72fc6d..1548337c 100644 --- a/tests/test_gil_scoped.py +++ b/tests/test_gil_scoped.py @@ -78,3 +78,8 @@ def test_python_to_cpp_to_python_from_process(): 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 From b2c4ff6052c4bb91c087f565cad534be38410655 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 15 Jul 2019 17:29:13 +0200 Subject: [PATCH 0146/1206] renamed local gil_scoped_acquire to gil_scoped_acquire_local to avoid ambiguity --- include/pybind11/detail/internals.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 952f8017..7235121d 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -173,9 +173,9 @@ PYBIND11_NOINLINE inline internals &get_internals() { // Ensure that the GIL is held since we will need to make Python calls. // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. - struct gil_scoped_acquire { - gil_scoped_acquire() : state (PyGILState_Ensure()) {} - ~gil_scoped_acquire() { PyGILState_Release(state); } + struct gil_scoped_acquire_local { + gil_scoped_acquire_local() : state (PyGILState_Ensure()) {} + ~gil_scoped_acquire_local() { PyGILState_Release(state); } const PyGILState_STATE state; } gil; From 9b3fb05326d49bbe2849744d8375dfb2406e8865 Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 18 Jul 2019 01:01:50 -0600 Subject: [PATCH 0147/1206] Allow Windows.h min/max to coexist with pybind11 (#1847) * Protect std::min/max functions from windows.h min/max Removed check for windows min/max --- include/pybind11/cast.h | 6 ++++-- include/pybind11/detail/common.h | 4 ---- include/pybind11/pybind11.h | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 8d0fd5d9..67397c4e 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -995,9 +995,11 @@ struct type_caster::value && !is_std_char_t } bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + + // Protect std::numeric_limits::min/max with parentheses if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && - (py_value < (py_type) std::numeric_limits::min() || - py_value > (py_type) std::numeric_limits::max()))) { + (py_value < (py_type) (std::numeric_limits::min)() || + py_value > (py_type) (std::numeric_limits::max)()))) { bool type_error = py_err && PyErr_ExceptionMatches( #if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) PyExc_SystemError diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 300c2b23..a5b47c95 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -113,10 +113,6 @@ #include #include -#if defined(_WIN32) && (defined(min) || defined(max)) -# error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows -#endif - #if defined(isalnum) # undef isalnum # undef isalpha diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 68e5757e..fbd971cf 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -495,7 +495,7 @@ class cpp_function : public function { function_call call(func, parent); - size_t args_to_copy = std::min(pos_args, n_args_in); + size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses size_t args_copied = 0; // 0. Inject new-style `self` argument From a3f4a0e8ab38f6b67174a162a5f0324e900a9c6b Mon Sep 17 00:00:00 2001 From: Jeremy Maitin-Shepard Date: Thu, 18 Jul 2019 00:02:35 -0700 Subject: [PATCH 0148/1206] Add support for __await__, __aiter__, and __anext__ protocols (#1842) --- include/pybind11/detail/class.h | 3 +++ tests/CMakeLists.txt | 8 ++++++++ tests/conftest.py | 5 +++++ tests/test_async.cpp | 26 ++++++++++++++++++++++++++ tests/test_async.py | 23 +++++++++++++++++++++++ 5 files changed, 65 insertions(+) create mode 100644 tests/test_async.cpp create mode 100644 tests/test_async.py diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index b1916fcd..ffdfefe7 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -586,6 +586,9 @@ inline PyObject* make_new_python_type(const type_record &rec) { type->tp_as_number = &heap_type->as_number; type->tp_as_sequence = &heap_type->as_sequence; type->tp_as_mapping = &heap_type->as_mapping; +#if PY_VERSION_HEX >= 0x03050000 + type->tp_as_async = &heap_type->as_async; +#endif /* Flags */ type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb6776f2..765c47ad 100644 --- a/tests/CMakeLists.txt +++ b/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 @@ -71,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 diff --git a/tests/conftest.py b/tests/conftest.py index 55d9d0d5..57f681c6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,11 @@ _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""" diff --git a/tests/test_async.cpp b/tests/test_async.cpp new file mode 100644 index 00000000..f0ad0d53 --- /dev/null +++ b/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_(m, "DoesNotSupportAsync") + .def(py::init<>()); + struct SupportsAsync {}; + py::class_(m, "SupportsAsync") + .def(py::init<>()) + .def("__await__", [](const SupportsAsync& self) -> py::object { + static_cast(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/tests/test_async.py b/tests/test_async.py new file mode 100644 index 00000000..e1c959d6 --- /dev/null +++ b/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())) From c6b699d9c2d71d09359b6785cc3d7c50fac0c847 Mon Sep 17 00:00:00 2001 From: phil-zxx <32247590+phil-zxx@users.noreply.github.com> Date: Fri, 19 Jul 2019 10:28:48 +0100 Subject: [PATCH 0149/1206] Added ability to convert from datetime.date to system_clock::time_point (#1848) * Added ability to convert from Python datetime.date and datetime.time to C++ system_clock::time_point --- docs/advanced/cast/chrono.rst | 2 +- include/pybind11/chrono.h | 30 +++++++++++++-- tests/test_chrono.py | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/docs/advanced/cast/chrono.rst b/docs/advanced/cast/chrono.rst index 8c6b3d7e..fbd46057 100644 --- a/docs/advanced/cast/chrono.rst +++ b/docs/advanced/cast/chrono.rst @@ -59,7 +59,7 @@ Provided conversions .. rubric:: Python to C++ -- ``datetime.datetime`` → ``std::chrono::system_clock::time_point`` +- ``datetime.datetime`` or ``datetime.date`` or ``datetime.time`` → ``std::chrono::system_clock::time_point`` Date/time objects are converted into system clock timepoints. Any timezone information is ignored and the type is treated as a naive object. diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h index 2ace2329..ea777e69 100644 --- a/include/pybind11/chrono.h +++ b/include/pybind11/chrono.h @@ -106,8 +106,11 @@ template class type_caster class type_caster &src, return_value_policy /* policy */, handle /* parent */) { diff --git a/tests/test_chrono.py b/tests/test_chrono.py index f308de97..55c95440 100644 --- a/tests/test_chrono.py +++ b/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) From e9ca89f453cf81bea9a73b976c6a2fa59a6b1fe2 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Tue, 20 Mar 2018 16:55:29 -0400 Subject: [PATCH 0150/1206] numpy: Add test for explicit dtype checks. At present, int64 + uint64 do not exactly match dtype(...).num --- tests/test_numpy_array.cpp | 81 ++++++++++++++++++++++++++++++++++++++ tests/test_numpy_array.py | 15 +++++++ 2 files changed, 96 insertions(+) diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index cdf0b82d..156a3bfa 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -14,6 +14,67 @@ #include +// Size / dtype checks. +struct DtypeCheck { + py::dtype numpy{}; + py::dtype pybind11{}; +}; + +template +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(); + return check; +} + +std::vector get_concrete_dtype_checks() { + return { + // Normalization + get_dtype_check("int8"), + get_dtype_check("uint8"), + get_dtype_check("int16"), + get_dtype_check("uint16"), + get_dtype_check("int32"), + get_dtype_check("uint32"), + get_dtype_check("int64"), + get_dtype_check("uint64") + }; +} + +struct DtypeSizeCheck { + std::string name{}; + int size_cpp{}; + int size_numpy{}; + // For debugging. + py::dtype dtype{}; +}; + +template +DtypeSizeCheck get_dtype_size_check() { + DtypeSizeCheck check{}; + check.name = py::type_id(); + check.size_cpp = sizeof(T); + check.dtype = py::dtype::of(); + check.size_numpy = check.dtype.attr("itemsize").template cast(); + return check; +} + +std::vector get_platform_dtype_size_checks() { + return { + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + }; +} + +// Arrays. using arr = py::array; using arr_t = py::array_t; static_assert(std::is_same::value, ""); @@ -75,6 +136,26 @@ TEST_SUBMODULE(numpy_array, sm) { try { py::module::import("numpy"); } catch (...) { return; } + // test_dtypes + py::class_(sm, "DtypeCheck") + .def_readonly("numpy", &DtypeCheck::numpy) + .def_readonly("pybind11", &DtypeCheck::pybind11) + .def("__repr__", [](const DtypeCheck& self) { + return py::str("").format( + self.numpy, self.pybind11); + }); + sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks); + + py::class_(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("").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()); }); diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 8bacb7f6..17c87ef7 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -7,6 +7,21 @@ 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') From 4a3464fd88ae8666a670a17b99035beea6b464a8 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Sat, 1 Dec 2018 07:31:44 -0500 Subject: [PATCH 0151/1206] numpy: Provide concrete size aliases Test for dtype checks now succeed without warnings --- include/pybind11/numpy.h | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index a0441efa..e67d3715 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -108,6 +109,18 @@ inline numpy_internals& get_numpy_internals() { return *ptr; } +template struct same_size { + template using as = bool_constant; +}; + +// Lookup a type according to its size, and return a value corresponding to the NumPy typenum. +template +constexpr int platform_lookup(Int... codes) { + using code_index = std::integral_constant::template as, Check...>()>; + static_assert(code_index::value != sizeof...(Check), "Unable to match type on this platform"); + return std::get(std::make_tuple(codes...)); +} + struct npy_api { enum constants { NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, @@ -126,7 +139,23 @@ struct npy_api { NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, NPY_OBJECT_ = 17, - NPY_STRING_, NPY_UNICODE_, NPY_VOID_ + NPY_STRING_, NPY_UNICODE_, NPY_VOID_, + // Platform-dependent normalization + NPY_INT8_ = NPY_BYTE_, + NPY_UINT8_ = NPY_UBYTE_, + NPY_INT16_ = NPY_SHORT_, + NPY_UINT16_ = NPY_USHORT_, + // `npy_common.h` defines the integer aliases. In order, it checks: + // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR + // and assigns the alias to the first matching size, so we should check in this order. + NPY_INT32_ = platform_lookup( + NPY_LONG_, NPY_INT_, NPY_SHORT_), + NPY_UINT32_ = platform_lookup( + NPY_ULONG_, NPY_UINT_, NPY_USHORT_), + NPY_INT64_ = platform_lookup( + NPY_LONG_, NPY_LONGLONG_, NPY_INT_), + NPY_UINT64_ = platform_lookup( + NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), }; typedef struct { @@ -1004,8 +1033,8 @@ struct npy_format_descriptor Date: Sat, 27 Jul 2019 09:35:32 +0000 Subject: [PATCH 0152/1206] numpy: fix refcount leak to dtype singleton (#1860) PyArray_DescrFromType returns a new reference, not borrowed one --- include/pybind11/numpy.h | 2 +- tests/test_numpy_array.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index e67d3715..8b21d3d4 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1044,7 +1044,7 @@ struct npy_format_descriptor(ptr); + return reinterpret_steal(ptr); pybind11_fail("Unsupported buffer format!"); } }; diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 17c87ef7..d0a6324d 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -434,3 +434,14 @@ def test_array_create_and_resize(msg): 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 From 640b8fe6d96b3e1498520c7c57d6b41f55ee3bc6 Mon Sep 17 00:00:00 2001 From: Christoph Kahl <26385576+kanonet@users.noreply.github.com> Date: Tue, 13 Aug 2019 21:41:53 +0200 Subject: [PATCH 0153/1206] fix #1406 add mingw compatibility (#1851) --- tools/FindPythonLibsNew.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index 7d85af4a..e660c5f3 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -144,7 +144,7 @@ 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) +if(CMAKE_HOST_WIN32 AND NOT (MSYS OR MINGW)) set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") From bdf1a2cc34815c2f9ee9a5f3b5b05bfadd28dd35 Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Tue, 13 Aug 2019 21:00:47 +0100 Subject: [PATCH 0154/1206] In internals.h, only look at _DEBUG when compiling with MSVC. (#1855) * In internals.h, only look at _DEBUG when compiling with MSVC. (_DEBUG is a MSVC-specific macro.) --- include/pybind11/detail/internals.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 7235121d..53d695ab 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -140,12 +140,20 @@ struct type_info { /// Tracks the `internals` and `type_info` ABI version independent of the main library version #define PYBIND11_INTERNALS_VERSION 3 -#if defined(_DEBUG) +/// On MSVC, debug and release builds are not ABI-compatible! +#if defined(_MSC_VER) && defined(_DEBUG) # define PYBIND11_BUILD_TYPE "_debug" #else # define PYBIND11_BUILD_TYPE "" #endif +/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. +#if defined(__GXX_ABI_VERSION) +# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) +#else +# define PYBIND11_BUILD_ABI "" +#endif + #if defined(WITH_THREAD) # define PYBIND11_INTERNALS_KIND "" #else @@ -153,10 +161,10 @@ struct type_info { #endif #define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" #define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__" + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" /// Each module locally stores a pointer to the `internals` data. The data /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. From b2fdfd122827c1170c75702c05a4040997cf3bf5 Mon Sep 17 00:00:00 2001 From: Borja Zarco Date: Thu, 15 Aug 2019 07:42:43 -0400 Subject: [PATCH 0155/1206] Avoid use of lambda to work around a clang bug. (#1883) Clang has a bug [1] in x86 Windows that is exposed by the use of lambdas with "unforwardable" prototypes. The error is "error: cannot compile this forwarded non-trivially copyable parameter yet", and the message was introduced in [2] (used to be an assertion). [1] https://llvm.org/bugs/show_bug.cgi?id=28299 [2] https://github.com/microsoft/checkedc-clang/commit/feb1567e07573100ea14f9aea02f81463e791496 --- include/pybind11/detail/internals.h | 58 +++++++++++++++-------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 53d695ab..2369d8fe 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -173,6 +173,34 @@ inline internals **&get_internals_pp() { return internals_pp; } +inline void translate_exception(std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } 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::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } +} + +#if !defined(__GLIBCXX__) +inline void translate_local_exception(std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } +} +#endif + /// Return a reference to the current `internals` data PYBIND11_NOINLINE inline internals &get_internals() { auto **&internals_pp = get_internals_pp(); @@ -198,15 +226,7 @@ PYBIND11_NOINLINE inline internals &get_internals() { // // libstdc++ doesn't require this (types there are identified only by name) #if !defined(__GLIBCXX__) - (*internals_pp)->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } - } - ); + (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); #endif } else { if (!internals_pp) internals_pp = new internals*(); @@ -229,25 +249,7 @@ PYBIND11_NOINLINE inline internals &get_internals() { internals_ptr->istate = tstate->interp; #endif builtins[id] = capsule(internals_pp); - internals_ptr->registered_exception_translators.push_front( - [](std::exception_ptr p) -> void { - try { - if (p) std::rethrow_exception(p); - } catch (error_already_set &e) { e.restore(); return; - } catch (const builtin_exception &e) { e.set_error(); return; - } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; - } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; - } 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::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; - } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); - return; - } - } - ); + internals_ptr->registered_exception_translators.push_front(&translate_exception); internals_ptr->static_property_type = make_static_property_type(); internals_ptr->default_metaclass = make_default_metaclass(); internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); From 5ef13eb680069680c41e89265d4f1105bd501846 Mon Sep 17 00:00:00 2001 From: ali-beep <54114435+ali-beep@users.noreply.github.com> Date: Thu, 15 Aug 2019 13:41:12 -0400 Subject: [PATCH 0156/1206] Add negative indexing support to stl_bind. (#1882) --- include/pybind11/stl_bind.h | 65 ++++++++++++++++++++++++------------- tests/test_stl_binders.py | 10 ++++++ 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 1f872526..d3adaed3 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -115,6 +115,14 @@ void vector_modifiers(enable_if_t= n) + throw index_error(); + return i; + }; + cl.def("append", [](Vector &v, const T &value) { v.push_back(value); }, arg("x"), @@ -159,10 +167,13 @@ void vector_modifiers(enable_if_t v.size()) + [](Vector &v, DiffType i, const T &x) { + // Can't use wrap_i; i == v.size() is OK + if (i < 0) + i += v.size(); + if (i < 0 || (SizeType)i > v.size()) throw index_error(); - v.insert(v.begin() + (DiffType) i, x); + v.insert(v.begin() + i, x); }, arg("i") , arg("x"), "Insert an item at a given position." @@ -180,11 +191,10 @@ void vector_modifiers(enable_if_t= v.size()) - throw index_error(); - T t = v[i]; - v.erase(v.begin() + (DiffType) i); + [wrap_i](Vector &v, DiffType i) { + i = wrap_i(i, v.size()); + T t = v[(SizeType) i]; + v.erase(v.begin() + i); return t; }, arg("i"), @@ -192,10 +202,9 @@ void vector_modifiers(enable_if_t= v.size()) - throw index_error(); - v[i] = t; + [wrap_i](Vector &v, DiffType i, const T &t) { + i = wrap_i(i, v.size()); + v[(SizeType)i] = t; } ); @@ -238,10 +247,9 @@ void vector_modifiers(enable_if_t= v.size()) - throw index_error(); - v.erase(v.begin() + DiffType(i)); + [wrap_i](Vector &v, DiffType i) { + i = wrap_i(i, v.size()); + v.erase(v.begin() + i); }, "Delete the list elements at index ``i``" ); @@ -277,13 +285,21 @@ template void vector_accessor(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; using ItType = typename Vector::iterator; + auto wrap_i = [](DiffType i, SizeType n) { + if (i < 0) + i += n; + if (i < 0 || (SizeType)i >= n) + throw index_error(); + return i; + }; + cl.def("__getitem__", - [](Vector &v, SizeType i) -> T & { - if (i >= v.size()) - throw index_error(); - return v[i]; + [wrap_i](Vector &v, DiffType i) -> T & { + i = wrap_i(i, v.size()); + return v[(SizeType)i]; }, return_value_policy::reference_internal // ref + keepalive ); @@ -303,12 +319,15 @@ template void vector_accessor(enable_if_t::value, Class_> &cl) { using T = typename Vector::value_type; using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; using ItType = typename Vector::iterator; cl.def("__getitem__", - [](const Vector &v, SizeType i) -> T { - if (i >= v.size()) + [](const Vector &v, DiffType i) -> T { + if (i < 0 && (i += v.size()) < 0) + throw index_error(); + if ((SizeType)i >= v.size()) throw index_error(); - return v[i]; + return v[(SizeType)i]; } ); diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 52c8ac0c..6d5a1598 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -53,6 +53,16 @@ def test_vector_int(): 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 From 5b0ea77c62ed77ed9f641f7284865a391dc2acab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 16 Aug 2019 08:52:13 +0200 Subject: [PATCH 0157/1206] Fix -Wmissing-prototypes warning on Clang. (#1863) The -Wmissing-prototypes Clang warning (or -Wmissing-declarations on GCC) is very useful to avoid accidents where a function definition in a source file doesn't match the corresponding declaration in a header file, as it would warn already during compilation and not much later during link time. Unfortunately this means that exported functions defined only in the source file (usually the ones annotated with `extern "C"`) will cause this warning to be emitted too (on Clang, GCC has a slightly different behavior with -Wmissing-declarations and doesn't warn here). This fixes the warning by providing a declaration right before the definition. --- include/pybind11/detail/common.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index a5b47c95..d5bf3186 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -164,7 +164,9 @@ #define PYBIND11_STR_TYPE ::pybind11::str #define PYBIND11_BOOL_ATTR "__bool__" #define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +// Providing a separate declaration to make Clang's -Wmissing-prototypes happy #define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name(); \ extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() #else @@ -188,8 +190,10 @@ #define PYBIND11_STR_TYPE ::pybind11::bytes #define PYBIND11_BOOL_ATTR "__nonzero__" #define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy #define PYBIND11_PLUGIN_IMPL(name) \ static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_EXPORT void init##name(); \ extern "C" PYBIND11_EXPORT void init##name() { \ (void)pybind11_init_wrapper(); \ } \ From 08b0bda4bc68dd3f1c57393ed008177e58fc909d Mon Sep 17 00:00:00 2001 From: Sergei Lebedev <185856+superbobry@users.noreply.github.com> Date: Fri, 16 Aug 2019 12:32:27 -0700 Subject: [PATCH 0158/1206] Added set::contains and generalized dict::contains (#1884) Dynamically resolving __contains__ on each call is wasteful since set has a public PySet_Contains function. --- include/pybind11/pytypes.h | 8 ++++++-- tests/test_pytypes.cpp | 12 ++++++++++++ tests/test_pytypes.py | 8 ++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 2d573dfa..f1dd009c 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1224,8 +1224,9 @@ class dict : public object { detail::dict_iterator begin() const { return {*this, 0}; } detail::dict_iterator end() const { return {}; } void clear() const { PyDict_Clear(ptr()); } - bool contains(handle key) const { return PyDict_Contains(ptr(), key.ptr()) == 1; } - bool contains(const char *key) const { return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1; } + template bool contains(T &&key) const { + return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward(key)).ptr()) == 1; + } private: /// Call the `dict` Python type -- always returns a new reference @@ -1276,6 +1277,9 @@ class set : public object { return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; } void clear() const { PySet_Clear(m_ptr); } + template bool contains(T &&val) const { + return PySet_Contains(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 1; + } }; class function : public object { diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index e6c955ff..a8caca45 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -37,6 +37,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 +55,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")); }); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 0116d4ef..a0364d6a 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -37,6 +37,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" @@ -53,6 +57,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" From 046267c6297173fb59edfae2f1484710b4be5771 Mon Sep 17 00:00:00 2001 From: Sergei Lebedev <185856+superbobry@users.noreply.github.com> Date: Fri, 16 Aug 2019 14:43:08 -0700 Subject: [PATCH 0159/1206] Added .empty() to all collection types (#1887) --- include/pybind11/pytypes.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index f1dd009c..10f72ccf 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1202,6 +1202,7 @@ class tuple : public object { if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); } size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + bool empty() const { return size() == 0; } detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::tuple_iterator begin() const { return {*this, 0}; } @@ -1221,6 +1222,7 @@ class dict : public object { explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } size_t size() const { return (size_t) PyDict_Size(m_ptr); } + bool empty() const { return size() == 0; } detail::dict_iterator begin() const { return {*this, 0}; } detail::dict_iterator end() const { return {}; } void clear() const { PyDict_Clear(ptr()); } @@ -1241,6 +1243,7 @@ class sequence : public object { public: PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) size_t size() const { return (size_t) PySequence_Size(m_ptr); } + bool empty() const { return size() == 0; } detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::sequence_iterator begin() const { return {*this, 0}; } @@ -1254,6 +1257,7 @@ class list : public object { if (!m_ptr) pybind11_fail("Could not allocate list object!"); } size_t size() const { return (size_t) PyList_Size(m_ptr); } + bool empty() const { return size() == 0; } detail::list_accessor operator[](size_t index) const { return {*this, index}; } detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::list_iterator begin() const { return {*this, 0}; } @@ -1273,6 +1277,7 @@ class set : public object { if (!m_ptr) pybind11_fail("Could not allocate set object!"); } size_t size() const { return (size_t) PySet_Size(m_ptr); } + bool empty() const { return size() == 0; } template bool add(T &&val) const { return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; } From 87fa6a4342c5a1e8483eb6dd37bc9ed72eceebf4 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Mon, 19 Aug 2019 12:43:33 +0200 Subject: [PATCH 0160/1206] Detect whether we are running in a Conda environment and adjust get_include() (#1877) --- pybind11/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pybind11/__init__.py b/pybind11/__init__.py index 5782ffea..c625e8c9 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -10,9 +10,17 @@ def get_include(user=False): 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') else: dist = Distribution({'name': 'pybind11'}) dist.parse_config_files() From 04c8f4b56e59704558755865a5f4c2f0a94e5c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 19 Aug 2019 12:48:03 +0200 Subject: [PATCH 0161/1206] Expose BufferError among other pybind11 exceptions. (#1852) --- include/pybind11/detail/common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index d5bf3186..d31be9c9 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -673,6 +673,7 @@ PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) 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 From 19189b4c2c7f205ae16fe9d0df121b47f142f54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andre=20Schmei=C3=9Fer?= Date: Mon, 19 Aug 2019 12:54:33 +0200 Subject: [PATCH 0162/1206] Make `overload_cast_impl` available in C++11 mode. (#1581) * Make `overload_cast_impl` available in C++11 mode. Narrow the scope of the `#if defined(PYBIND11_CPP14)` block around overload_cast to only cover the parts where C++14 is stricly required. Thus, the implementation in `pybind11::details::overload_cast_impl` is still available in C++11 mode. * PR #1581: Modify test to use overload_cast_impl, update docs and change log --- docs/changelog.rst | 5 +++-- docs/classes.rst | 11 +++++++++++ include/pybind11/detail/common.h | 10 +++++----- tests/test_methods_and_attributes.cpp | 18 ++++++++++++------ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f99d3516..9576a8bc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,7 +10,9 @@ Starting with version 1.8.0, pybind11 releases use a `semantic versioning v2.3.1 (Not yet released) ----------------------------------------------------- -* TBA +* ``py::details::overload_cast_impl`` is available in C++11 mode, can be used + like ``overload_cast`` with an additional set of parantheses. + `1581 `_. v2.3.0 (June 11, 2019) ----------------------------------------------------- @@ -105,7 +107,6 @@ v2.3.0 (June 11, 2019) `#1744 `_, `#1670 `_. - v2.2.4 (September 11, 2018) ----------------------------------------------------- diff --git a/docs/classes.rst b/docs/classes.rst index 1deec9b5..a63f6a19 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -422,6 +422,17 @@ on constness, the ``py::const_`` tag should be used: .def("foo_mutable", py::overload_cast(&Widget::foo)) .def("foo_const", py::overload_cast(&Widget::foo, py::const_)); +If you prefer the ``py::overload_cast`` syntax but have a C++11 compatible compiler only, +you can use ``py::detail::overload_cast_impl`` with an additional set of parentheses: + +.. code-block:: cpp + + template + using overload_cast_ = pybind11::detail::overload_cast_impl; + + py::class_(m, "Pet") + .def("set", overload_cast_()(&Pet::set), "Set the pet's age") + .def("set", overload_cast_()(&Pet::set), "Set the pet's name"); .. [#cpp14] A compiler which supports the ``-std=c++14`` flag or Visual Studio 2015 Update 2 and newer. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index d31be9c9..7fb427ab 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -720,10 +720,6 @@ struct error_scope { /// Dummy destructor wrapper that can be used to expose classes with a private destructor struct nodelete { template void operator()(T*) { } }; -// overload_cast requires variable templates: C++14 -#if defined(PYBIND11_CPP14) -#define PYBIND11_OVERLOAD_CAST 1 - NAMESPACE_BEGIN(detail) template struct overload_cast_impl { @@ -743,19 +739,23 @@ struct overload_cast_impl { }; NAMESPACE_END(detail) +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 /// Syntax sugar for resolving overloaded function pointers: /// - regular: static_cast(&Class::func) /// - sweet: overload_cast(&Class::func) template static constexpr detail::overload_cast_impl overload_cast = {}; // MSVC 2015 only accepts this particular initialization syntax for this variable template. +#endif /// Const member function selector for overload_cast /// - regular: static_cast(&Class::func) /// - sweet: overload_cast(&Class::func, const_) static constexpr auto const_ = std::true_type{}; -#else // no overload_cast: providing something that static_assert-fails: +#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails: template struct overload_cast { static_assert(detail::deferred_t::value, "pybind11::overload_cast<...> requires compiling in C++14 mode"); diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index fde152b9..c7b82f13 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -11,6 +11,11 @@ #include "pybind11_tests.h" #include "constructor_stats.h" +#if !defined(PYBIND11_OVERLOAD_CAST) +template +using overload_cast_ = pybind11::detail::overload_cast_impl; +#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(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const", py::overload_cast(&ExampleMandA::overloaded, py::const_)) #else - .def("overloaded", static_cast(&ExampleMandA::overloaded)) - .def("overloaded", static_cast(&ExampleMandA::overloaded)) - .def("overloaded", static_cast(&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_()(&ExampleMandA::overloaded)) + .def("overloaded", overload_cast_()(&ExampleMandA::overloaded)) .def("overloaded", static_cast(&ExampleMandA::overloaded)) .def("overloaded", static_cast(&ExampleMandA::overloaded)) .def("overloaded", static_cast(&ExampleMandA::overloaded)) - .def("overloaded_float", static_cast(&ExampleMandA::overloaded)) - .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) - .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) + .def("overloaded_float", overload_cast_()(&ExampleMandA::overloaded)) + .def("overloaded_const", overload_cast_()(&ExampleMandA::overloaded, py::const_)) + .def("overloaded_const", overload_cast_()(&ExampleMandA::overloaded, py::const_)) .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) From 12e8774bc9aa4603136f2979088619b495850ca2 Mon Sep 17 00:00:00 2001 From: kingofpayne <43875454+kingofpayne@users.noreply.github.com> Date: Mon, 19 Aug 2019 23:00:36 +0200 Subject: [PATCH 0163/1206] Added support for list insertion. (#1888) --- include/pybind11/pytypes.h | 4 ++++ tests/test_pytypes.cpp | 2 ++ tests/test_pytypes.py | 8 +++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 10f72ccf..78a604e1 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1265,6 +1265,10 @@ class list : public object { template void append(T &&val) const { PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); } + template void insert(size_t index, T &&val) const { + PyList_Insert(m_ptr, static_cast(index), + detail::object_or_cast(std::forward(val)).ptr()); + } }; class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index a8caca45..244e1db0 100644 --- a/tests/test_pytypes.cpp +++ b/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) { diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index a0364d6a..0e8d6c33 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -9,14 +9,16 @@ def test_list(capture, doc): with capture: lst = m.get_list() - assert lst == ["overwritten"] + assert lst == ["inserted-0", "overwritten", "inserted-2"] 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" From 8f5a8ab4ac693efc9610aba1e3bb1f5e0d711533 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 23 Aug 2019 17:18:05 +0300 Subject: [PATCH 0164/1206] Don't strip debug symbols in RelWithDebInfo mode (#1892) --- tools/pybind11Tools.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index e3ec572b..c7156c02 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -197,7 +197,7 @@ function(pybind11_add_module target_name) _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) - if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug) + if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) # Strip unnecessary sections of the binary on Linux/Mac OS if(CMAKE_STRIP) if(APPLE) From 5b4751af269afab9a2e40a92b486bd29214f352b Mon Sep 17 00:00:00 2001 From: Stephen Larew Date: Tue, 27 Aug 2019 08:05:47 -0700 Subject: [PATCH 0165/1206] Add const to buffer:request() (#1890) --- include/pybind11/pytypes.h | 2 +- tests/test_buffers.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 78a604e1..96eab966 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1312,7 +1312,7 @@ class buffer : public object { public: PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) - buffer_info request(bool writable = false) { + buffer_info request(bool writable = false) const { int flags = PyBUF_STRIDES | PyBUF_FORMAT; if (writable) flags |= PyBUF_WRITABLE; Py_buffer *view = new Py_buffer(); diff --git a/tests/test_buffers.cpp b/tests/test_buffers.cpp index 5199cf64..433dfeee 100644 --- a/tests/test_buffers.cpp +++ b/tests/test_buffers.cpp @@ -78,7 +78,7 @@ TEST_SUBMODULE(buffers, m) { py::class_(m, "Matrix", py::buffer_protocol()) .def(py::init()) /// 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::format() || info.ndim != 2) throw std::runtime_error("Incompatible buffer format!"); From f6c4c1047a293ea77223c21f84f888e062212d78 Mon Sep 17 00:00:00 2001 From: "Lori A. Burns" Date: Wed, 4 Sep 2019 16:16:21 -0400 Subject: [PATCH 0166/1206] restores __invert__ to arithmetic-enabled enum, fixes #1907 (#1909) --- include/pybind11/pybind11.h | 2 ++ tests/test_enum.py | 1 + 2 files changed, 3 insertions(+) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index fbd971cf..a7fe1898 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1484,6 +1484,8 @@ struct enum_base { PYBIND11_ENUM_OP_CONV("__ror__", a | b); PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); + m_base.attr("__invert__") = cpp_function( + [](object arg) { return ~(int_(arg)); }, is_method(m_base)); } } else { PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); diff --git a/tests/test_enum.py b/tests/test_enum.py index d0989adc..2f119a3a 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -140,6 +140,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 From 09f082940113661256310e3f4811aa7261a9fa05 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Thu, 19 Sep 2019 19:23:27 +0300 Subject: [PATCH 0167/1206] Avoid conversion to `int_` rhs argument of enum eq/ne (#1912) * fix: Avoid conversion to `int_` rhs argument of enum eq/ne * test: compare unscoped enum with strings * suppress comparison to None warning * test unscoped enum arithmetic and comparision with unsupported type --- include/pybind11/pybind11.h | 13 +++++++-- tests/test_enum.cpp | 4 ++- tests/test_enum.py | 58 ++++++++++++++++++++++++++++++------- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index a7fe1898..204aaa43 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1469,9 +1469,17 @@ struct enum_base { }, \ is_method(m_base)) + #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b) { \ + int_ a(a_); \ + return expr; \ + }, \ + is_method(m_base)) + if (is_convertible) { - PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b)); - PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b)); + PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b)); + PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b)); if (is_arithmetic) { PYBIND11_ENUM_OP_CONV("__lt__", a < b); @@ -1501,6 +1509,7 @@ struct enum_base { } } + #undef PYBIND11_ENUM_OP_CONV_LHS #undef PYBIND11_ENUM_OP_CONV #undef PYBIND11_ENUM_OP_STRICT diff --git a/tests/test_enum.cpp b/tests/test_enum.cpp index 498a00e1..31530892 100644 --- a/tests/test_enum.cpp +++ b/tests/test_enum.cpp @@ -13,11 +13,13 @@ TEST_SUBMODULE(enums, m) { // test_unscoped_enum enum UnscopedEnum { EOne = 1, - ETwo + ETwo, + EThree }; py::enum_(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 diff --git a/tests/test_enum.py b/tests/test_enum.py index 2f119a3a..7fe9b618 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -21,7 +21,7 @@ def test_unscoped_enum(): # __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__ = {} @@ -29,23 +29,18 @@ 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} - assert m.UnscopedEnum.__doc__ == \ - '''An unscoped enumeration + for docstring_line in '''An unscoped enumeration Members: EOne : Docstring for EOne - ETwo : Docstring for ETwo''' or m.UnscopedEnum.__doc__ == \ - '''An unscoped enumeration - -Members: - ETwo : Docstring for ETwo - EOne : Docstring for EOne''' + EThree : Docstring for EThree'''.split('\n'): + assert docstring_line in m.UnscopedEnum.__doc__ # Unscoped enums will accept ==/!= int comparisons y = m.UnscopedEnum.ETwo @@ -53,6 +48,38 @@ def test_unscoped_enum(): 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" @@ -71,6 +98,11 @@ 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" @@ -82,6 +114,12 @@ def test_scoped_enum(): 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 From c9f5a464bc8ebe91dee8578b2b4a23d9997ffefe Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 19 Sep 2019 21:07:17 +0200 Subject: [PATCH 0168/1206] pybind11 internals: separate different compilers --- include/pybind11/detail/internals.h | 31 +++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 2369d8fe..067780c2 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -147,6 +147,33 @@ struct type_info { # define PYBIND11_BUILD_TYPE "" #endif +/// Let's assume that different compilers are ABI-incompatible. +#if defined(_MSC_VER) +# define PYBIND11_COMPILER_TYPE "_msvc" +#elif defined(__INTEL_COMPILER) +# define PYBIND11_COMPILER_TYPE "_icc" +#elif defined(__clang__) +# define PYBIND11_COMPILER_TYPE "_clang" +#elif defined(__PGI) +# define PYBIND11_COMPILER_TYPE "_pgi" +#elif defined(__MINGW32__) +# define PYBIND11_COMPILER_TYPE "_mingw" +#elif defined(__CYGWIN__) +# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" +#elif defined(__GNUC__) +# define PYBIND11_COMPILER_TYPE "_gcc" +#else +# define PYBIND11_COMPILER_TYPE "_unknown" +#endif + +#if defined(_LIBCPP_VERSION) +# define PYBIND11_STDLIB "_libcpp" +#elif defined(__GLIBCXX__) || defined(__GLIBCPP__) +# define PYBIND11_STDLIB "_libstdcpp" +#else +# define PYBIND11_STDLIB "" +#endif + /// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. #if defined(__GXX_ABI_VERSION) # define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) @@ -161,10 +188,10 @@ struct type_info { #endif #define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" #define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ - PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" /// Each module locally stores a pointer to the `internals` data. The data /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. From 6ca312b3bcdccdd55321dcf7111a50cad37a6c99 Mon Sep 17 00:00:00 2001 From: Samuel Debionne Date: Thu, 19 Sep 2019 21:23:03 +0200 Subject: [PATCH 0169/1206] Avoid infinite recursion in is_copy_constructible (#1910) --- include/pybind11/cast.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 67397c4e..605acb36 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -775,7 +775,9 @@ template struct is_copy_constructible : std // so, copy constructability depends on whether the value_type is copy constructible. template struct is_copy_constructible, - std::is_same + std::is_same, + // Avoid infinite recursion + negation> >::value>> : is_copy_constructible {}; #if !defined(PYBIND11_CPP17) From 00a0aa992953d6482114a0f539a21bb535a16383 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 19 Sep 2019 23:06:22 +0200 Subject: [PATCH 0170/1206] v2.4.0 release --- docs/changelog.rst | 59 ++++++++++++++++++++++++++++++-- docs/conf.py | 4 +-- include/pybind11/detail/common.h | 4 +-- pybind11/_version.py | 2 +- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9576a8bc..eaf8c253 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,14 +6,69 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. - -v2.3.1 (Not yet released) +v2.4.0 (Sep 19, 2019) ----------------------------------------------------- +* Try harder to keep pybind11-internal data structures separate when there + are potential ABI incompatibilities. Fixes crashes that occurred when loading + multiple pybind11 extensions that were e.g. compiled by GCC (libstdc++) + and Clang (libc++). + `1588 `_ and + `c9f5a `_. + +* Added support for ``__await__``, ``__aiter__``, and ``__anext__`` protocols. + `1842 `_. + +* ``pybind11_add_module()``: don't strip symbols when compiling in + ``RelWithDebInfo`` mode. `1980 + `_. + +* ``enum_``: Reproduce Python behavior when comparing against invalid values + (e.g. ``None``, strings, etc.). Add back support for ``__invert__()``. + `1912 `_, + `1907 `_. + +* List insertion operation for ``py::list``. + Added ``.empty()`` to all collection types. + Added ``py::set::contains()`` and ``py::dict::contains()``. + `1887 `_, + `1884 `_, + `1888 `_. + * ``py::details::overload_cast_impl`` is available in C++11 mode, can be used like ``overload_cast`` with an additional set of parantheses. `1581 `_. +* ``overload_cast_impl`` is now available in C++11. + `1581 `_. + +* Fixed ``get_include()`` on Conda. + `1877 `_. + +* ``stl_bind.h``: negative indexing support. + `1882 `_. + +* Minor CMake fix to add MinGW compatibility. + `1851 `_. + +* GIL-related fixes. + `1836 `_, + `8b90b `_. + +* Other very minor/subtle fixes and improvements. + `1329 `_, + `1910 `_, + `1863 `_, + `1847 `_, + `1890 `_, + `1860 `_, + `1848 `_, + `1821 `_, + `1837 `_, + `1833 `_, + `1748 `_, + `1852 `_. + v2.3.0 (June 11, 2019) ----------------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index d17e4ba3..da9dd192 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -61,9 +61,9 @@ # built documents. # # The short X.Y version. -version = '2.3' +version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.3.dev1' +release = '2.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 7fb427ab..d1c6c2bc 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 3 -#define PYBIND11_VERSION_PATCH dev1 +#define PYBIND11_VERSION_MINOR 4 +#define PYBIND11_VERSION_PATCH 0 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index fef541bd..fdd1ea34 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 3, 'dev1') +version_info = (2, 4, 0) __version__ = '.'.join(map(str, version_info)) From e825205ac6995dad6f548217838a0e7544c98e2c Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 19 Sep 2019 23:18:04 +0200 Subject: [PATCH 0171/1206] begin working on v2.4.1 --- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index da9dd192..fec6bfb6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.0' +release = '2.4.dev1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index d1c6c2bc..678a58c0 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH 0 +#define PYBIND11_VERSION_PATCH dev1 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index fdd1ea34..e11cfe30 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 0) +version_info = (2, 4, 'dev1') __version__ = '.'.join(map(str, version_info)) From 21d0eb460f3d6932cbf9544d636d22080bf386b6 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 20 Sep 2019 09:38:30 +0200 Subject: [PATCH 0172/1206] Fix Python 3.8 test regression --- tests/test_enum.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test_enum.py b/tests/test_enum.py index 7fe9b618..cea834d0 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -192,12 +192,15 @@ def test_binary_operators(): def test_enum_to_int(): - m.test_enum_to_int(m.Flags.Read) - m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) - m.test_enum_to_uint(m.Flags.Read) - 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) + import sys + # Implicit conversion to integers is deprecated in Python >= 3.8 + if sys.version_info < (3, 8): + m.test_enum_to_int(m.Flags.Read) + m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_uint(m.Flags.Read) + 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(): From 5fd187ebe92b3fbd3e467a08c194dc254a1edd74 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 20 Sep 2019 10:49:52 +0200 Subject: [PATCH 0173/1206] minor changelog cleanup [ci skip] --- docs/changelog.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index eaf8c253..534f10e1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -39,9 +39,6 @@ v2.4.0 (Sep 19, 2019) like ``overload_cast`` with an additional set of parantheses. `1581 `_. -* ``overload_cast_impl`` is now available in C++11. - `1581 `_. - * Fixed ``get_include()`` on Conda. `1877 `_. From 31680e6f9c1bbe582bc36d457f8e010121ff16bb Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 20 Sep 2019 11:06:10 +0200 Subject: [PATCH 0174/1206] Implicit conversion from enum to int for Python 3.8 (fix by @sizmailov) --- include/pybind11/pybind11.h | 4 ++++ tests/test_enum.py | 15 ++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 204aaa43..a0e63958 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1566,6 +1566,10 @@ template class enum_ : public class_ { #if PY_MAJOR_VERSION < 3 def("__long__", [](Type value) { return (Scalar) value; }); #endif + #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 8 + def("__index__", [](Type value) { return (Scalar) value; }); + #endif + cpp_function setstate( [](Type &value, Scalar arg) { value = static_cast(arg); }, is_method(*this)); diff --git a/tests/test_enum.py b/tests/test_enum.py index cea834d0..7fe9b618 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -192,15 +192,12 @@ def test_binary_operators(): def test_enum_to_int(): - import sys - # Implicit conversion to integers is deprecated in Python >= 3.8 - if sys.version_info < (3, 8): - m.test_enum_to_int(m.Flags.Read) - m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) - m.test_enum_to_uint(m.Flags.Read) - 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) + m.test_enum_to_int(m.Flags.Read) + m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode) + m.test_enum_to_uint(m.Flags.Read) + 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(): From e44fcc3c15b41f0720c72832c0b9cd07de819781 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 20 Sep 2019 11:10:49 +0200 Subject: [PATCH 0175/1206] v2.4.1 release --- docs/changelog.rst | 56 ++++++++++++++++++-------------- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 534f10e1..25c7808d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,12 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. +v2.4.1 (Sep 20, 2019) +----------------------------------------------------- + +* Fixed a problem involving implicit conversion from enumerations to integers + on Python 3.8. `1780 `_. + v2.4.0 (Sep 19, 2019) ----------------------------------------------------- @@ -13,58 +19,58 @@ v2.4.0 (Sep 19, 2019) are potential ABI incompatibilities. Fixes crashes that occurred when loading multiple pybind11 extensions that were e.g. compiled by GCC (libstdc++) and Clang (libc++). - `1588 `_ and + `#1588 `_ and `c9f5a `_. * Added support for ``__await__``, ``__aiter__``, and ``__anext__`` protocols. - `1842 `_. + `#1842 `_. * ``pybind11_add_module()``: don't strip symbols when compiling in - ``RelWithDebInfo`` mode. `1980 + ``RelWithDebInfo`` mode. `#1980 `_. * ``enum_``: Reproduce Python behavior when comparing against invalid values (e.g. ``None``, strings, etc.). Add back support for ``__invert__()``. - `1912 `_, - `1907 `_. + `#1912 `_, + `#1907 `_. * List insertion operation for ``py::list``. Added ``.empty()`` to all collection types. Added ``py::set::contains()`` and ``py::dict::contains()``. - `1887 `_, - `1884 `_, - `1888 `_. + `#1887 `_, + `#1884 `_, + `#1888 `_. * ``py::details::overload_cast_impl`` is available in C++11 mode, can be used like ``overload_cast`` with an additional set of parantheses. - `1581 `_. + `#1581 `_. * Fixed ``get_include()`` on Conda. - `1877 `_. + `#1877 `_. * ``stl_bind.h``: negative indexing support. - `1882 `_. + `#1882 `_. * Minor CMake fix to add MinGW compatibility. - `1851 `_. + `#1851 `_. * GIL-related fixes. - `1836 `_, + `#1836 `_, `8b90b `_. * Other very minor/subtle fixes and improvements. - `1329 `_, - `1910 `_, - `1863 `_, - `1847 `_, - `1890 `_, - `1860 `_, - `1848 `_, - `1821 `_, - `1837 `_, - `1833 `_, - `1748 `_, - `1852 `_. + `#1329 `_, + `#1910 `_, + `#1863 `_, + `#1847 `_, + `#1890 `_, + `#1860 `_, + `#1848 `_, + `#1821 `_, + `#1837 `_, + `#1833 `_, + `#1748 `_, + `#1852 `_. v2.3.0 (June 11, 2019) ----------------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index fec6bfb6..5e9b9b2a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.dev1' +release = '2.4.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 678a58c0..879fb6ca 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH dev1 +#define PYBIND11_VERSION_PATCH 1 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index e11cfe30..39550aa2 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 'dev1') +version_info = (2, 4, 1) __version__ = '.'.join(map(str, version_info)) From 82cf7935883020b7fd89f0ff9f8b8ad2c1d924f8 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 20 Sep 2019 11:12:22 +0200 Subject: [PATCH 0176/1206] begin working on next version --- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5e9b9b2a..040b896c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.1' +release = '2.4.dev2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 879fb6ca..6ed75b6c 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH 1 +#define PYBIND11_VERSION_PATCH dev2 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index 39550aa2..326c9139 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 1) +version_info = (2, 4, 'dev2') __version__ = '.'.join(map(str, version_info)) From f3109d8419b3ed56cf9795f6343ab08b0c27746a Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 21 Sep 2019 18:09:35 +0200 Subject: [PATCH 0177/1206] future-proof Python version check from commit 31680e6 (@lgritz) --- include/pybind11/pybind11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index a0e63958..c6237056 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1566,7 +1566,7 @@ template class enum_ : public class_ { #if PY_MAJOR_VERSION < 3 def("__long__", [](Type value) { return (Scalar) value; }); #endif - #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 8 + #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8) def("__index__", [](Type value) { return (Scalar) value; }); #endif From 7f5dad7d5fba3fb6fccef966f7cde5ca24509473 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 21 Sep 2019 18:54:10 +0200 Subject: [PATCH 0178/1206] Remove usage of C++14 constructs (fixes #1929) --- include/pybind11/numpy.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 8b21d3d4..ba41a223 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -113,12 +113,12 @@ template struct same_size { template using as = bool_constant; }; +template constexpr int platform_lookup() { return -1; } + // Lookup a type according to its size, and return a value corresponding to the NumPy typenum. -template -constexpr int platform_lookup(Int... codes) { - using code_index = std::integral_constant::template as, Check...>()>; - static_assert(code_index::value != sizeof...(Check), "Unable to match type on this platform"); - return std::get(std::make_tuple(codes...)); +template +constexpr int platform_lookup(int I, Ints... Is) { + return sizeof(Concrete) == sizeof(T) ? I : platform_lookup(Is...); } struct npy_api { From 7ec2ddfc95f65d1e986d359466a6c254aa514ef3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 21 Sep 2019 20:19:58 +0200 Subject: [PATCH 0179/1206] v2.4.2 release --- docs/changelog.rst | 11 ++++++++++- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 25c7808d..1ac83ec3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,11 +6,20 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. +v2.4.2 (Sep 21, 2019) +----------------------------------------------------- + +* Replaced usage of a C++14 only construct. `#1929 + `_. + +* Made an ifdef future-proof for Python >= 4. `f3109d + `_. + v2.4.1 (Sep 20, 2019) ----------------------------------------------------- * Fixed a problem involving implicit conversion from enumerations to integers - on Python 3.8. `1780 `_. + on Python 3.8. `#1780 `_. v2.4.0 (Sep 19, 2019) ----------------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index 040b896c..b071d2c7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.dev2' +release = '2.4.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 6ed75b6c..0a6792eb 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH dev2 +#define PYBIND11_VERSION_PATCH 2 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index 326c9139..492138ea 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 'dev2') +version_info = (2, 4, 2) __version__ = '.'.join(map(str, version_info)) From 2abd7e1eb48ce8549b936a3e19581ce82256a20f Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 21 Sep 2019 20:22:33 +0200 Subject: [PATCH 0180/1206] updated release.rst to remove parts that are now automated --- docs/release.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/release.rst b/docs/release.rst index b31bbe97..9846f971 100644 --- a/docs/release.rst +++ b/docs/release.rst @@ -13,10 +13,6 @@ To release a new version of pybind11: - ``git push --tags``. - ``python setup.py sdist upload``. - ``python setup.py bdist_wheel upload``. -- Update conda-forge (https://github.com/conda-forge/pybind11-feedstock) via PR - - download release package from Github: ``wget https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz`` - - compute checksum: ``shasum -a 256 vX.Y.Z.tar.gz`` - - change version number and checksum in ``recipe/meta.yml`` - Get back to work - Update ``_version.py`` (add 'dev' and increment minor). - Update version in ``docs/conf.py`` From 34c2281e315c51f5270321101dc733c1cf26214f Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 21 Sep 2019 20:23:01 +0200 Subject: [PATCH 0181/1206] begin working on next version --- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b071d2c7..dfb20df4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.2' +release = '2.4.dev3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 0a6792eb..3e454949 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH 2 +#define PYBIND11_VERSION_PATCH dev3 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index 492138ea..85c7f94e 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 2) +version_info = (2, 4, 'dev3') __version__ = '.'.join(map(str, version_info)) From 96be2c154f296da7ca8d5d7159773970622d0c77 Mon Sep 17 00:00:00 2001 From: Boris Dalstein Date: Sun, 6 Oct 2019 23:23:10 +0200 Subject: [PATCH 0182/1206] Fix version mismatch typos in .travis.yml (#1948) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cc5cf07..381148e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ matrix: - os: linux dist: trusty env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1 - name: Python 2.7, c++14, gcc 4.8, CMake test + name: Python 2.7, c++14, gcc 6, CMake test addons: apt: sources: @@ -130,7 +130,7 @@ matrix: dist: trusty services: docker env: DOCKER=i386/debian:stretch PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 - name: Python 3.4, c++14, gcc 6, 32-bit + name: Python 3.5, c++14, gcc 6, 32-bit script: - | # Consolidated 32-bit Docker Build + Install From 6cb584e9de6e8d54f5576c299a308f89bfdcb519 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Tue, 8 Oct 2019 19:25:09 +0300 Subject: [PATCH 0183/1206] Adapt to python3.8 C API change (#1950) * Adapt to python3.8 C API change Do `Py_DECREF(type)` on all python objects on deallocation fix #1946 * Add bare python3.8 build to CI matrix While numpy/scipy wheels are available, run python3.8 test without them --- .travis.yml | 27 +++++++++++++++++++++++++++ include/pybind11/detail/class.h | 6 ++++++ 2 files changed, 33 insertions(+) diff --git a/.travis.yml b/.travis.yml index 381148e0..28f35f99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,6 +106,33 @@ matrix: - lld-7 - libc++-7-dev - libc++abi-7-dev # Why is this necessary??? + - os: linux + dist: xenial + env: PYTHON=3.8 CPP=17 GCC=7 + name: Python 3.8, c++17, gcc 7 (w/o numpy/scipy) # TODO: update build name when the numpy/scipy wheels become available + addons: + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - g++-7 + - python3.8-dev + - python3.8-venv + # Currently there is no numpy/scipy wheels available for python3.8 + # TODO: remove next before_install, install and script clause when the wheels become available + before_install: + - pyenv global $(pyenv whence 2to3) # activate all python versions + - PY_CMD=python3 + - $PY_CMD -m pip install --user --upgrade pip wheel setuptools + install: + - $PY_CMD -m pip install --user --upgrade pytest + script: + - | + # 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 - os: osx name: Python 2.7, c++14, AppleClang 7.3, CMake test osx_image: xcode7.3 diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index ffdfefe7..230ae81a 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -350,6 +350,7 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) { auto type = Py_TYPE(self); type->tp_free(self); +#if PY_VERSION_HEX < 0x03080000 // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called // as part of a derived type's dealloc, in which case we're not allowed to decref // the type here. For cross-module compatibility, we shouldn't compare directly @@ -357,6 +358,11 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) { auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; if (type->tp_dealloc == pybind11_object_type->tp_dealloc) Py_DECREF(type); +#else + // This was not needed before Python 3.8 (Python issue 35810) + // https://github.com/pybind/pybind11/issues/1946 + Py_DECREF(type); +#endif } /** Create the type which can be used as a common base for all classes. This is From 80d452484c5409444b0ec19383faa84bb7a4d351 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 15 Oct 2019 01:57:24 +0200 Subject: [PATCH 0184/1206] v2.4.3 release --- docs/changelog.rst | 6 ++++++ docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 1ac83ec3..d65c2d80 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,12 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. +v2.4.3 (Oct 15, 2019) +----------------------------------------------------- + +* Adapt pybind11 to a C API convention change in Python 3.8. `#1950 + `_. + v2.4.2 (Sep 21, 2019) ----------------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index dfb20df4..c4385462 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.dev3' +release = '2.4.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 3e454949..6da54706 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH dev3 +#define PYBIND11_VERSION_PATCH 3 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index 85c7f94e..2709cc55 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 'dev3') +version_info = (2, 4, 3) __version__ = '.'.join(map(str, version_info)) From dfde1554ea3c9d43a914b868b46d3ddf3e3c6274 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 15 Oct 2019 01:58:43 +0200 Subject: [PATCH 0185/1206] begin working on next version --- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index c4385462..a1e4e005 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.4.3' +release = '2.4.dev4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 6da54706..bb1affce 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 4 -#define PYBIND11_VERSION_PATCH 3 +#define PYBIND11_VERSION_PATCH dev4 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index 2709cc55..5bf3483d 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 3) +version_info = (2, 4, 'dev4') __version__ = '.'.join(map(str, version_info)) From de5a29c0d4ad15271c172cf5dd60c7fd33febb80 Mon Sep 17 00:00:00 2001 From: nicolov Date: Thu, 17 Oct 2019 10:43:33 +0200 Subject: [PATCH 0186/1206] Fix build with -Wmissing-prototypes (#1954) When building with `-Werror,-Wmissing-prototypes`, `clang` complains about missing prototypes for functions defined through macro expansions. This PR adds the missing prototypes. ``` error: no previous prototype for function 'pybind11_init_impl_embedded' [ -Werror,-Wmissing-prototypes] PYBIND11_EMBEDDED_MODULE(embedded, mod) { ^ external/pybind11/include/pybind11/embed.h:61:5: note: expanded from macro 'PYBIND11_EMBEDDED_MODULE' PYBIND11_EMBEDDED_MODULE_IMPL(name) \ ^ external/pybind11/include/pybind11/embed.h:26:23: note: expanded from macro 'PYBIND11_EMBEDDED_MODULE_IMPL' extern "C" void pybind11_init_impl_##name() { \ ^ :380:1: note: expanded from here pybind11_init_impl_embedded ^ 1 error generated. ``` --- include/pybind11/embed.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 72655885..f814c783 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(); \ } From 6c29cbf88de0df177ec730fc4c85e52bdf8d82c0 Mon Sep 17 00:00:00 2001 From: Riccardo Bertossa <33728857+rikigigi@users.noreply.github.com> Date: Fri, 18 Oct 2019 17:55:17 +0200 Subject: [PATCH 0187/1206] misleading comment corrected (strides in buffer_info is bytes and not number of entries) (#1958) --- include/pybind11/buffer_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h index 9f072fa7..b106d2cc 100644 --- a/include/pybind11/buffer_info.h +++ b/include/pybind11/buffer_info.h @@ -21,7 +21,7 @@ struct buffer_info { std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() ssize_t ndim = 0; // Number of dimensions std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of entries between adjacent entries (for each per dimension) + std::vector strides; // Number of bytes between adjacent entries (for each per dimension) buffer_info() { } From 759221f5c56939f59d8f342a41f8e2d2cacbc8cf Mon Sep 17 00:00:00 2001 From: Jeremy Nimmer Date: Fri, 14 Jun 2019 12:12:51 -0400 Subject: [PATCH 0188/1206] Obey __cpp_sized_deallocation and __cpp_aligned_new Don't assume that just because the language version is C++17 that the standard library offers all C++17 features, too. When using clang-6.0 and --std=c++17 on Ubuntu 18.04 with libstdc++, __cpp_sized_deallocation is false. --- include/pybind11/cast.h | 4 ++-- include/pybind11/pybind11.h | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 605acb36..efc10d65 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -574,10 +574,10 @@ class type_caster_generic { if (type->operator_new) { vptr = type->operator_new(type->type_size); } else { - #if defined(PYBIND11_CPP17) + #ifdef __cpp_aligned_new 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); diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c6237056..513ceed5 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1003,14 +1003,21 @@ void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } 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 + #ifdef __cpp_aligned_new + 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) From c27a6e1378e6e3e438ed3542246db470fea35fa7 Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Tue, 22 Oct 2019 16:19:15 +0100 Subject: [PATCH 0189/1206] make builds with python tests and cpp tests fail if either one fails (#1967) --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 28f35f99..2d242b4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,8 +32,7 @@ matrix: - | # 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,8 +130,7 @@ matrix: - | # 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 From bdf6a5e8708abe87d42e0e4c8bef51db0f0957ff Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Wed, 23 Oct 2019 12:19:58 +0100 Subject: [PATCH 0190/1206] Report type names in return value policy-related cast exceptions (#1965) --- include/pybind11/cast.h | 29 +++++++++++++++++++++++------ tests/test_copy_move.py | 6 +++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index efc10d65..e1bbf03a 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -533,9 +533,17 @@ class type_caster_generic { 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 +552,18 @@ class type_caster_generic { 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; diff --git a/tests/test_copy_move.py b/tests/test_copy_move.py index aff2d99f..0e671d96 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 @@ def test_private_op_new(): 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 From a83d69e78ffe58bbd410aa7503384a312e08963b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Gs=C3=A4nger?= <8004308+sgsaenger@users.noreply.github.com> Date: Thu, 31 Oct 2019 12:38:24 +0100 Subject: [PATCH 0191/1206] test pair-copyability on C++17 upwards (#1886) * test pair-copyability on C++17 upwards The stdlib falsely detects containers like M=std::map as copyable, even when one of T and U is not copyable. Therefore we cannot rely on the stdlib dismissing std::pair by itself, even on C++17. * fix is_copy_assignable bind_map used std::is_copy_assignable which suffers from the same problems as std::is_copy_constructible, therefore the same fix has been applied. * created tests for copyability --- include/pybind11/cast.h | 16 ++++++++++++---- include/pybind11/stl_bind.h | 4 ++-- tests/test_stl_binders.cpp | 22 +++++++++++++++++++++ tests/test_stl_binders.py | 38 +++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index e1bbf03a..90407eb9 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -797,12 +797,20 @@ template struct is_copy_constructible> >::value>> : is_copy_constructible {}; -#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 struct is_copy_constructible> : all_of, is_copy_constructible> {}; -#endif + +// The same problems arise with std::is_copy_assignable, so we use the same workaround. +template struct is_copy_assignable : std::is_copy_assignable {}; +template struct is_copy_assignable, + std::is_same + >::value>> : is_copy_assignable {}; +template struct is_copy_assignable> + : all_of, is_copy_assignable> {}; NAMESPACE_END(detail) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index d3adaed3..62bd9081 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -512,7 +512,7 @@ template void map_assignment(const Args & // Map assignment when copy-assignable: just copy the value template -void map_assignment(enable_if_t::value, Class_> &cl) { +void map_assignment(enable_if_t::value, Class_> &cl) { using KeyType = typename Map::key_type; using MappedType = typename Map::mapped_type; @@ -528,7 +528,7 @@ void map_assignment(enable_if_t void map_assignment(enable_if_t< - !std::is_copy_assignable::value && + !is_copy_assignable::value && is_copy_constructible::value, Class_> &cl) { using KeyType = typename Map::key_type; diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp index a88b589e..86888740 100644 --- a/tests/test_stl_binders.cpp +++ b/tests/test_stl_binders.cpp @@ -54,6 +54,14 @@ template Map *times_ten(int n) { return m; } +template 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>(m, "VectorInt", py::buffer_protocol()); @@ -85,6 +93,20 @@ TEST_SUBMODULE(stl_binders, m) { m.def("get_mnc", ×_ten>, py::return_value_policy::reference); py::bind_map>(m, "UmapENC"); m.def("get_umnc", ×_ten>, py::return_value_policy::reference); + // Issue #1885: binding nested std::map> with E non-copyable + py::bind_map>>(m, "MapVecENC"); + m.def("get_nvnc", [](int n) + { + auto m = new std::map>(); + 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>>(m, "MapMapENC"); + m.def("get_nmnc", ×_hundred>>, py::return_value_policy::reference); + py::bind_map>>(m, "UmapUmapENC"); + m.def("get_numnc", ×_hundred>>, py::return_value_policy::reference); // test_vector_buffer py::bind_vector>(m, "VectorUChar", py::buffer_protocol()); diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 6d5a1598..b83a587f 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -212,6 +212,44 @@ def test_noncopyable_containers(): assert vsum == 150 + # nested std::map + 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 + 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 + 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() From a6355b00f84d997a9ddcf209b6464447432be78a Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 31 Oct 2019 04:40:15 -0700 Subject: [PATCH 0192/1206] CMake: Add Python 3.8 to pybind11Tools (#1974) Add Python 3.8 to considered versions in CMake for additional hints. https://cmake.org/cmake/help/v3.2/module/FindPythonLibs.html --- tools/pybind11Tools.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index c7156c02..d0a2bfc8 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -12,7 +12,7 @@ if(NOT PYBIND11_PYTHON_VERSION) 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.8 3.7 3.6 3.5 3.4) find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) include(CheckCXXCompilerFlag) From 6f11347a3077fd859153967bcea4b82a418beb1b Mon Sep 17 00:00:00 2001 From: Matthew Dawkins Date: Thu, 14 Nov 2019 02:53:06 -0500 Subject: [PATCH 0193/1206] Prevent cmake error when prefix empty (#1986) --- tools/FindPythonLibsNew.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index e660c5f3..52c92f1e 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -140,9 +140,9 @@ list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) 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)) set(PYTHON_LIBRARY From b32b762c60c0325fe5b3577de3061ba3ed893605 Mon Sep 17 00:00:00 2001 From: Erick Matsen Date: Wed, 13 Nov 2019 23:53:30 -0800 Subject: [PATCH 0194/1206] Fixing minor typo in basics.rst (#1984) --- docs/basics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basics.rst b/docs/basics.rst index 447250ed..7bf4d426 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -164,7 +164,7 @@ load and execute the example: 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 From 55ff464233a189a085f1f7b1f7969b2f4fbc344b Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Thu, 14 Nov 2019 08:55:34 +0100 Subject: [PATCH 0195/1206] Fixing SystemError when nb_bool/nb_nonzero sets a Python exception in type_caster::load (#1976) --- include/pybind11/cast.h | 2 ++ tests/test_builtin_casters.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 90407eb9..3d4a759c 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1172,6 +1172,8 @@ template <> class type_caster { if (res == 0 || res == 1) { value = (bool) res; return true; + } else { + PyErr_Clear(); } } return false; diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 73cc465f..abbfcec4 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -318,11 +318,15 @@ def test_numpy_bool(): 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(): From deb3cb238a9f299d7c57f810165e90a1b14b3e6f Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Thu, 14 Nov 2019 08:56:58 +0100 Subject: [PATCH 0196/1206] Add exception translation for std::overflow_error. (#1977) --- docs/advanced/exceptions.rst | 2 ++ include/pybind11/detail/internals.h | 1 + tests/test_exceptions.cpp | 1 + tests/test_exceptions.py | 4 ++++ 4 files changed, 8 insertions(+) diff --git a/docs/advanced/exceptions.rst b/docs/advanced/exceptions.rst index 75ac24ae..75ad7f7f 100644 --- a/docs/advanced/exceptions.rst +++ b/docs/advanced/exceptions.rst @@ -28,6 +28,8 @@ exceptions: +--------------------------------------+--------------------------------------+ | :class:`std::range_error` | ``ValueError`` | +--------------------------------------+--------------------------------------+ +| :class:`std::overflow_error` | ``OverflowError`` | ++--------------------------------------+--------------------------------------+ | :class:`pybind11::stop_iteration` | ``StopIteration`` (used to implement | | | custom iterators) | +--------------------------------------+--------------------------------------+ diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 067780c2..87952dab 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -211,6 +211,7 @@ inline void translate_exception(std::exception_ptr p) { } 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/tests/test_exceptions.cpp b/tests/test_exceptions.cpp index d3013903..56cd9bc4 100644 --- a/tests/test_exceptions.cpp +++ b/tests/test_exceptions.cpp @@ -116,6 +116,7 @@ TEST_SUBMODULE(exceptions, m) { 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 6edff9fe..ac2b3603 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -79,6 +79,10 @@ def test_custom(msg): 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() From bd24155b8bf798f9f7022b39acd6d92e52d642d6 Mon Sep 17 00:00:00 2001 From: Francesco Biscani Date: Sat, 16 Nov 2019 01:18:24 +0100 Subject: [PATCH 0197/1206] Aligned allocation fix for clang-cl (#1988) --- include/pybind11/cast.h | 2 +- include/pybind11/pybind11.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 3d4a759c..ad225312 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -591,7 +591,7 @@ class type_caster_generic { if (type->operator_new) { vptr = type->operator_new(type->type_size); } else { - #ifdef __cpp_aligned_new + #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)); diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 513ceed5..d95d61f7 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1003,7 +1003,7 @@ void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } inline void call_operator_delete(void *p, size_t s, size_t a) { (void)s; (void)a; - #ifdef __cpp_aligned_new + #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)); From dc65d661718ed10a9d212f1949813f7a7acf9437 Mon Sep 17 00:00:00 2001 From: Sebastian Koslowski Date: Sun, 24 Nov 2019 08:33:05 +0100 Subject: [PATCH 0198/1206] support for readonly buffers (#863) (#1466) --- include/pybind11/buffer_info.h | 28 +++++++++++++++++----------- include/pybind11/detail/class.h | 7 +++++++ include/pybind11/pytypes.h | 2 +- tests/constructor_stats.h | 2 +- tests/test_buffers.cpp | 26 ++++++++++++++++++++++++++ tests/test_buffers.py | 31 +++++++++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 13 deletions(-) diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h index b106d2cc..1f4115a1 100644 --- a/include/pybind11/buffer_info.h +++ b/include/pybind11/buffer_info.h @@ -22,13 +22,14 @@ struct buffer_info { ssize_t ndim = 0; // Number of dimensions std::vector shape; // Shape of the tensor (1 entry per dimension) std::vector 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 shape_in, detail::any_container strides_in) + detail::any_container shape_in, detail::any_container 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 @@ struct buffer_info { } template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in)) { } + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(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 - buffer_info(T *ptr, ssize_t size) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size) { } + buffer_info(T *ptr, ssize_t size, bool readonly=false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } + + template + buffer_info(const T *ptr, ssize_t size, bool readonly=true) + : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::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 @@ struct buffer_info { 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 buffer_info { struct private_ctr_tag { }; buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { } + detail::any_container &&shape_in, detail::any_container &&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/detail/class.h b/include/pybind11/detail/class.h index 230ae81a..edfa7de6 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -491,6 +491,13 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla 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(info->format.c_str()); if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 96eab966..4003d691 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1345,7 +1345,7 @@ class memoryview : public object { 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/tests/constructor_stats.h b/tests/constructor_stats.h index f026e70f..431e5ace 100644 --- a/tests/constructor_stats.h +++ b/tests/constructor_stats.h @@ -180,7 +180,7 @@ class ConstructorStats { } } } - 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 433dfeee..1bc67ff7 100644 --- a/tests/test_buffers.cpp +++ b/tests/test_buffers.cpp @@ -166,4 +166,30 @@ TEST_SUBMODULE(buffers, m) { .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_(m, "BufferReadOnly", py::buffer_protocol()) + .def(py::init()) + .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_(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 f006552b..bf7aaed7 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 @@ def test_pointer_to_member_fn(): 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) From 0f1d3bfee2b5a7e52643fbaa46268c73ebb0497a Mon Sep 17 00:00:00 2001 From: Charles Brossollet Date: Mon, 25 Nov 2019 10:59:53 +0100 Subject: [PATCH 0199/1206] Add FAQ entry for dealing with long functions interruption (#2000) * Add FAQ entry, with code example, for dealing with long functions interruption --- docs/faq.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/faq.rst b/docs/faq.rst index 93ccf10e..a7cbbfdf 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -248,6 +248,41 @@ that that were ``malloc()``-ed in another shared library, using data 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 can explicitely interrupt execution by throwing an exception +that gets translated to KeyboardInterrupt (see :doc:`advanced/exceptions` +section): + +.. code-block:: cpp + + class interruption_error: public std::exception { + public: + const char* what() const noexcept { + return "Interruption signal caught."; + } + }; + + PYBIND11_MODULE(example, m) + { + m.def("long running_func", []() + { + for (;;) { + if (PyErr_CheckSignals() != 0) + throw interruption_error(); + // Long running iteration + } + }); + py::register_exception(m, "KeyboardInterrupt"); + } + Inconsistent detection of Python version in CMake and pybind11 ============================================================== From baf69345f6f252302ca5378a4568f33be178b8a8 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 25 Nov 2019 09:14:06 -0500 Subject: [PATCH 0200/1206] Minor modifications to interrupt handling FAQ (#2007) --- docs/faq.rst | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index a7cbbfdf..4d491fb8 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -257,30 +257,22 @@ 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 can explicitely interrupt execution by throwing an exception -that gets translated to KeyboardInterrupt (see :doc:`advanced/exceptions` -section): +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 - class interruption_error: public std::exception { - public: - const char* what() const noexcept { - return "Interruption signal caught."; - } - }; - PYBIND11_MODULE(example, m) { m.def("long running_func", []() { for (;;) { if (PyErr_CheckSignals() != 0) - throw interruption_error(); + throw py::error_already_set(); // Long running iteration } }); - py::register_exception(m, "KeyboardInterrupt"); } Inconsistent detection of Python version in CMake and pybind11 From 61e4f1182357cc0f55b567b128011c579e4c7ccd Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 28 Nov 2019 07:42:34 +0100 Subject: [PATCH 0201/1206] numpy.h: minor preprocessor fix suggested by @chaekwan --- include/pybind11/numpy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index ba41a223..c544c544 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -311,7 +311,7 @@ template using remove_all_extents_t = typename array_info::type; template using is_pod_struct = all_of< std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type -#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || (defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI != 0) // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). std::is_trivially_copyable, From a60648223d409c51804be63a097eeaae383e8a7c Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 28 Nov 2019 08:07:32 +0100 Subject: [PATCH 0202/1206] Revert "numpy.h: minor preprocessor fix suggested by @chaekwan" This reverts commit 61e4f1182357cc0f55b567b128011c579e4c7ccd. --- include/pybind11/numpy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index c544c544..ba41a223 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -311,7 +311,7 @@ template using remove_all_extents_t = typename array_info::type; template using is_pod_struct = all_of< std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type -#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || (defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI != 0) +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). std::is_trivially_copyable, From 37352491225358b97ce302273bf2d887a477efb0 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Thu, 28 Nov 2019 01:59:23 -0600 Subject: [PATCH 0203/1206] Install headers using both headers and package_data (#1995) --- pybind11/__init__.py | 36 ++++------------------- pybind11/__main__.py | 3 +- setup.py | 68 ++++++++++++++++++++++++++------------------ 3 files changed, 48 insertions(+), 59 deletions(-) diff --git a/pybind11/__init__.py b/pybind11/__init__.py index c625e8c9..4b1de3ef 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 9ef83780..89b263a8 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/setup.py b/setup.py index f677f2af..473ea1ee 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 @@ def run(self): 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 @@ def run(self): 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', From fe2a06e339ac8d10eb0b23dc8d77afbf08044667 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Wed, 11 Dec 2019 12:04:35 +0100 Subject: [PATCH 0204/1206] Pin breathe to 4.13.1 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2d242b4b..b421d8f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,8 @@ matrix: - 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: From dc9006db4f97d9b73cb919b2db691c0fe56bcdb4 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Wed, 11 Dec 2019 12:05:01 +0100 Subject: [PATCH 0205/1206] Use newer macOS image for python3 testing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b421d8f6..56682556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,7 +138,7 @@ matrix: 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 From 819802da9926535714acf2bffe690b04f90cb12b Mon Sep 17 00:00:00 2001 From: Nils Berg Date: Wed, 11 Dec 2019 15:01:45 +0000 Subject: [PATCH 0206/1206] Fix a memory leak when creating Python3 modules. (#2019) --- include/pybind11/pybind11.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index d95d61f7..8e9c55d1 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -794,11 +794,16 @@ class module : public object { explicit module(const char *name, const char *doc = nullptr) { if (!options::show_user_defined_docstrings()) doc = nullptr; #if PY_MAJOR_VERSION >= 3 - PyModuleDef *def = new PyModuleDef(); + PyModuleDef *def = PyMem_New(PyModuleDef, 1); std::memset(def, 0, sizeof(PyModuleDef)); def->m_name = name; def->m_doc = doc; def->m_size = -1; + def->m_free = [](void* module ) { + if (module != nullptr) { + Py_XDECREF(PyModule_GetDef((PyObject*) module)); + } + }; Py_INCREF(def); m_ptr = PyModule_Create(def); #else From fb910ae92b4a489e9b31f9ec545bde9d09d6a40e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 11 Dec 2019 21:26:46 +0100 Subject: [PATCH 0207/1206] Revert "Fix a memory leak when creating Python3 modules. (#2019)" This reverts commit 819802da9926535714acf2bffe690b04f90cb12b. --- include/pybind11/pybind11.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 8e9c55d1..d95d61f7 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -794,16 +794,11 @@ class module : public object { explicit module(const char *name, const char *doc = nullptr) { if (!options::show_user_defined_docstrings()) doc = nullptr; #if PY_MAJOR_VERSION >= 3 - PyModuleDef *def = PyMem_New(PyModuleDef, 1); + PyModuleDef *def = new PyModuleDef(); std::memset(def, 0, sizeof(PyModuleDef)); def->m_name = name; def->m_doc = doc; def->m_size = -1; - def->m_free = [](void* module ) { - if (module != nullptr) { - Py_XDECREF(PyModule_GetDef((PyObject*) module)); - } - }; Py_INCREF(def); m_ptr = PyModule_Create(def); #else From 1376eb0e518ff2b7b412c84a907dd1cd3f7f2dcd Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Thu, 12 Dec 2019 15:55:54 +0100 Subject: [PATCH 0208/1206] Free tstate on python 3.7+ on finalize_interpreter (#2020) --- include/pybind11/detail/internals.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 87952dab..6224dfb2 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -25,6 +25,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); # 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 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); # 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 @@ struct internals { #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 @@ struct type_info { }; /// 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) From b4e5d582cb656a590b256bcf4a8ffa7c8ce9ba19 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 13 Dec 2019 11:11:33 +0100 Subject: [PATCH 0209/1206] undo #define copysign in pyconfig.h --- include/pybind11/detail/common.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index bb1affce..e2330bbe 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -113,6 +113,9 @@ #include #include +/* 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 From 37d04abdee11403e98bc5c09e386dd49d58a58bd Mon Sep 17 00:00:00 2001 From: JGamache-autodesk <56274617+JGamache-autodesk@users.noreply.github.com> Date: Thu, 19 Dec 2019 06:15:42 -0500 Subject: [PATCH 0210/1206] Fixes #1295: Handle debug interpreter (#2025) If a debug interpreter is detected, we allow linking with pythonXX_d.lib under windows. --- include/pybind11/detail/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index e2330bbe..de10f73b 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -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 From 6e39b765b2333cd191001f22fe57ea218bd6ccf2 Mon Sep 17 00:00:00 2001 From: Vemund Handeland Date: Thu, 19 Dec 2019 12:16:24 +0100 Subject: [PATCH 0211/1206] Add C++20 char8_t/u8string support (#2026) * Fix test build in C++20 * Add C++20 char8_t/u8string support --- include/pybind11/cast.h | 16 +++++++++++--- tests/test_builtin_casters.cpp | 22 +++++++++++++++++-- tests/test_builtin_casters.py | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index ad225312..3af67351 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -32,6 +32,10 @@ #include #endif +#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L +# define PYBIND11_HAS_U8STRING +#endif + NAMESPACE_BEGIN(PYBIND11_NAMESPACE) NAMESPACE_BEGIN(detail) @@ -988,6 +992,9 @@ template class type_caster> { template using is_std_char_type = any_of< std::is_same, /* std::string */ +#if defined(PYBIND11_HAS_U8STRING) + std::is_same, /* std::u8string */ +#endif std::is_same, /* std::u16string */ std::is_same, /* std::u32string */ std::is_same /* std::wstring */ @@ -1191,6 +1198,9 @@ template struct string_caster { // 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::value || sizeof(CharT) == 1, "Unsupported char size != 1"); +#if defined(PYBIND11_HAS_U8STRING) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); +#endif static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) @@ -1209,7 +1219,7 @@ template struct string_caster { #if PY_MAJOR_VERSION >= 3 return load_bytes(load_src); #else - if (sizeof(CharT) == 1) { + if (std::is_same::value) { return load_bytes(load_src); } @@ -1269,7 +1279,7 @@ template struct string_caster { // 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 - bool load_bytes(enable_if_t src) { + bool load_bytes(enable_if_t::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. @@ -1284,7 +1294,7 @@ template struct string_caster { } template - bool load_bytes(enable_if_t) { return false; } + bool load_bytes(enable_if_t::value, handle>) { return false; } }; template diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index e026127f..acb24469 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -30,7 +30,7 @@ TEST_SUBMODULE(builtin_casters, m) { 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 @@ TEST_SUBMODULE(builtin_casters, m) { 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(c); }); + m.def("ord_char8_lv", [](char8_t &c) -> int { return static_cast(c); }); +#endif + // test_string_view #ifdef PYBIND11_HAS_STRING_VIEW m.attr("has_string_view") = true; @@ -69,9 +81,15 @@ TEST_SUBMODULE(builtin_casters, m) { 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 abbfcec4..91422588 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -15,6 +15,8 @@ def test_unicode_conversion(): 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 @@ def test_unicode_conversion(): 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 @@ def toobig_message(r): 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 @@ def test_string_view(capture): 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 @@ def test_string_view(capture): 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 @@ def test_string_view(capture): 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(): From f9f3bd711f266c435fa7cd8198e5696e3fb37eef Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 30 Dec 2019 01:26:32 +0100 Subject: [PATCH 0212/1206] Use C++17 fold expressions when casting tuples and argument lists (#2043) This commit introduces the use of C++17-style fold expressions when casting tuples & the argument lists of functions. This change can improve performance of the resulting bindings: because fold expressions have short-circuiting semantics, pybind11 e.g. won't try to cast the second argument of a function if the first one failed. This is particularly effective when working with functions that have many overloads with long argument lists. --- include/pybind11/cast.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 3af67351..38eb8a88 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1432,9 +1432,14 @@ template class Tuple, typename... Ts> class tuple_caster template bool load_impl(const sequence &seq, bool convert, index_sequence) { +#ifdef __cpp_fold_expressions + if ((... || !std::get(subcasters).load(seq[Is], convert))) + return false; +#else for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) if (!r) return false; +#endif return true; } @@ -1961,9 +1966,14 @@ class argument_loader { template bool load_impl_sequence(function_call &call, index_sequence) { +#ifdef __cpp_fold_expressions + if ((... || !std::get(argcasters).load(call.args[Is], call.args_convert[Is]))) + return false; +#else for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) if (!r) return false; +#endif return true; } From 2fda9d5dd876de2fc0c530e4a375b1f8c52e6758 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 31 Dec 2019 01:26:40 +0100 Subject: [PATCH 0213/1206] Travis CI fix (MacOS, Py3) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 56682556..d81cd8c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -219,7 +219,7 @@ before_install: 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 From 4c206e8c79bcbd3784fd05491b3c139e8fadaa4e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 2 Jan 2020 22:17:20 +0100 Subject: [PATCH 0214/1206] bindings for import_error exception --- include/pybind11/detail/common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index de10f73b..928e0a9a 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -681,6 +681,7 @@ PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) 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 From bf2b031449c8c8156443655a80bdaf41433b2534 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 2 Jan 2020 22:18:01 +0100 Subject: [PATCH 0215/1206] Handle cases where binding code immediately throws py::error_already_set When binding code immediately throws an exception of type py::error_already_set (e.g. via py::module::import that fails), the catch block sets an import error as expected. Unfortunately, following this, the deconstructor of py::error_already_set decides to call py::detail::get_internals() and set up various internal data structures of pybind11, which fails given that the error flag is active. The call stack of this looks as follows: Py_init_mymodule() -> __cxa_decrement_exception_refcount -> error_already_set::~error_already_set() -> gil_scoped_acquire::gil_scoped_acquire() -> detail::get_internals() -> ... -> pybind11::detail::simple_collector() -> uh oh.. The solution is simple: we call detail::get_internals() once before running any binding code to make sure that the internal data structures are ready. --- include/pybind11/detail/common.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 928e0a9a..362421df 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -218,6 +218,8 @@ extern "C" { #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 \ { \ @@ -264,6 +266,7 @@ extern "C" { static PyObject *pybind11_init(); \ PYBIND11_PLUGIN_IMPL(name) { \ PYBIND11_CHECK_PYTHON_VERSION \ + PYBIND11_ENSURE_INTERNALS_READY \ try { \ return pybind11_init(); \ } PYBIND11_CATCH_INIT_EXCEPTIONS \ @@ -291,6 +294,7 @@ extern "C" { 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); \ From 370a2ae2b364cc91792ecf2a343b14fca5d488ad Mon Sep 17 00:00:00 2001 From: Robert Haschke Date: Sun, 5 Jan 2020 15:49:24 +0100 Subject: [PATCH 0216/1206] Declare call_impl() as && (#2057) --- include/pybind11/cast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 38eb8a88..a0b4d1ba 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1978,7 +1978,7 @@ class argument_loader { } template - Return call_impl(Func &&f, index_sequence, Guard &&) { + Return call_impl(Func &&f, index_sequence, Guard &&) && { return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); } From 07e225932235ccb0db5271b0874d00f086f28423 Mon Sep 17 00:00:00 2001 From: Baljak Date: Sun, 5 Jan 2020 15:49:59 +0100 Subject: [PATCH 0217/1206] Fix compilation with MinGW only (#2053) --- tools/FindPythonLibsNew.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index 52c92f1e..c9b95a9a 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -144,7 +144,7 @@ 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 DEFINED ENV{MSYSTEM}) set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") From e97c735fc44bd39b390a321a276f41b3095ea2fe Mon Sep 17 00:00:00 2001 From: fwjavox Date: Fri, 17 Jan 2020 01:16:56 +0100 Subject: [PATCH 0218/1206] stl_bind: add binding for std::vector::clear (#2074) --- include/pybind11/stl_bind.h | 7 +++++++ tests/test_stl_binders.py | 3 +++ 2 files changed, 10 insertions(+) diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 62bd9081..da233eca 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -136,6 +136,13 @@ void vector_modifiers(enable_if_t Date: Wed, 22 Jan 2020 11:49:56 +0100 Subject: [PATCH 0219/1206] Fix the use of MSVC in an MSYS environment (#2087) --- tools/FindPythonLibsNew.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index c9b95a9a..9ea6036e 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -144,7 +144,7 @@ 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 DEFINED ENV{MSYSTEM}) +if(CMAKE_HOST_WIN32 AND NOT (MINGW AND DEFINED ENV{MSYSTEM})) set(PYTHON_LIBRARY "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") From bb9c91cce826122a3d7361d8fb9217831a07c4c6 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 4 Mar 2020 16:17:20 +0100 Subject: [PATCH 0220/1206] pybind11Tools.cmake: search for Python 3.9 --- tools/pybind11Tools.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index d0a2bfc8..508e4742 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -12,7 +12,7 @@ if(NOT PYBIND11_PYTHON_VERSION) set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") endif() -set(Python_ADDITIONAL_VERSIONS 3.8 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) From 3b1dbebabc801c9cf6f0953a4c20b904d444f879 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 31 Mar 2020 12:55:48 +0200 Subject: [PATCH 0221/1206] v2.5.0 release --- docs/changelog.rst | 45 ++++++++++++++++++++++++++++++++ docs/conf.py | 4 +-- include/pybind11/detail/common.h | 4 +-- pybind11/_version.py | 2 +- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index d65c2d80..2def2b07 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,51 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ 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 `_. + +* 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 `_. + +* C++20 ``char8_t`` and ``u8string`` support. `#2026 + `_. + +* CMake: search for Python 3.9. `bb9c91 + `_. + +* Fixes for MSYS-based build environments. + `#2087 `_, + `#2053 `_. + +* STL bindings for ``std::vector<...>::clear``. `#2074 + `_. + +* Read-only flag for ``py::buffer``. `#1466 + `_. + +* Exception handling during module initialization. + `bf2b031 `_. + +* Support linking against a CPython debug build. + `#2025 `_. + +* Fixed issues involving the availability and use of aligned ``new`` and + ``delete``. `#1988 `_, + `759221 `_. + +* Fixed a resource leak upon interpreter shutdown. + `#2020 `_. + +* Fixed error handling in the boolean caster. + `#1976 `_. + v2.4.3 (Oct 15, 2019) ----------------------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index a1e4e005..fa6332de 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.dev4' +release = '2.5.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 362421df..e53f502d 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 dev4 +#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) diff --git a/pybind11/_version.py b/pybind11/_version.py index 5bf3483d..8d5aa5c7 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 4, 'dev4') +version_info = (2, 5, 0) __version__ = '.'.join(map(str, version_info)) From 023487164915c5b62e16a9b4e0b37cb01cd5500a Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 31 Mar 2020 13:09:41 +0200 Subject: [PATCH 0222/1206] begin working on next version --- docs/conf.py | 2 +- include/pybind11/detail/common.h | 2 +- pybind11/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fa6332de..585987ec 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,7 +63,7 @@ # The short X.Y version. version = '2.5' # The full version, including alpha/beta/rc tags. -release = '2.5.0' +release = '2.5.dev1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index e53f502d..dd626793 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -94,7 +94,7 @@ #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 5 -#define PYBIND11_VERSION_PATCH 0 +#define PYBIND11_VERSION_PATCH dev1 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) diff --git a/pybind11/_version.py b/pybind11/_version.py index 8d5aa5c7..d0eb659b 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 5, 0) +version_info = (2, 5, 'dev1') __version__ = '.'.join(map(str, version_info)) From 4697149d19738a674ab59b08492becfcf69a87f8 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 4 Dec 2019 15:03:22 -0800 Subject: [PATCH 0223/1206] Allows users to specialize polymorphic_type_hook with std::enable_if. Currently user specializations of the form template struct polymorphic_type_hook> { ... }; will fail if itype is also polymorphic, because the existing specialization will also be enabled, which leads to 2 equally viable candidates. With this change, user provided specializations have higher priority than the built in specialization for polymorphic types. --- include/pybind11/cast.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index a0b4d1ba..fcfd0a8e 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -835,19 +835,25 @@ NAMESPACE_END(detail) // You may specialize polymorphic_type_hook yourself for types that want to appear // polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern // in performance-sensitive applications, used most notably in LLVM.) +// +// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with +// std::enable_if. User provided specializations will always have higher priority than +// the default implementation and specialization provided in polymorphic_type_hook_base. template -struct polymorphic_type_hook +struct polymorphic_type_hook_base { static const void *get(const itype *src, const std::type_info*&) { return src; } }; template -struct polymorphic_type_hook::value>> +struct polymorphic_type_hook_base::value>> { static const void *get(const itype *src, const std::type_info*& type) { type = src ? &typeid(*src) : nullptr; return dynamic_cast(src); } }; +template +struct polymorphic_type_hook : public polymorphic_type_hook_base {}; NAMESPACE_BEGIN(detail) From f6e543b15ec0aaf39c494936440f91a16f161c9a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 16 Jan 2020 00:20:44 +0000 Subject: [PATCH 0224/1206] Adding a default virtual destructor to Animal type in test_tagbased_polymorphic.cpp. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change, and cast.h as-is in master, test_tagbased_polymorphic.cpp fails to compile with the error message below. With the cast.h change in pull/2016, building and testing succeeds. cd pybind11/build/tests && /usr/bin/c++ -DPYBIND11_TEST_BOOST -DPYBIND11_TEST_EIGEN -Dpybind11_tests_EXPORTS -Ipybind11/include -I/usr/include/python3.7m -isystem /usr/include/eigen3 -Os -DNDEBUG -fPIC -fvisibility=hidden -std=c++2a -flto -fno-fat-lto-objects -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -o CMakeFiles/pybind11_tests.dir/test_tagbased_polymorphic.cpp.o -c pybind11/tests/test_tagbased_polymorphic.cpp In file included from pybind11/include/pybind11/attr.h:13, from pybind11/include/pybind11/pybind11.h:44, from pybind11/tests/pybind11_tests.h:2, from pybind11/tests/test_tagbased_polymorphic.cpp:10: pybind11/include/pybind11/cast.h: In instantiation of ‘static std::pair pybind11::detail::type_caster_base::src_and_type(const itype*) [with type = Animal; pybind11::detail::type_caster_base::itype = Animal]’: pybind11/include/pybind11/cast.h:906:31: required from ‘static pybind11::handle pybind11::detail::type_caster_base::cast_holder(const itype*, const void*) [with type = Animal; pybind11::detail::type_caster_base::itype = Animal]’ pybind11/include/pybind11/cast.h:1566:51: required from ‘static pybind11::handle pybind11::detail::move_only_holder_caster::cast(holder_type&&, pybind11::return_value_policy, pybind11::handle) [with type = Animal; holder_type = std::unique_ptr]’ pybind11/include/pybind11/stl.h:175:69: required from ‘static pybind11::handle pybind11::detail::list_caster::cast(T&&, pybind11::return_value_policy, pybind11::handle) [with T = std::vector >; Type = std::vector >; Value = std::unique_ptr]’ pybind11/include/pybind11/pybind11.h:159:43: required from ‘void pybind11::cpp_function::initialize(Func&&, Return (*)(Args ...), const Extra& ...) [with Func = std::vector > (*&)(); Return = std::vector >; Args = {}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling}]’ pybind11/include/pybind11/pybind11.h:64:9: required from ‘pybind11::cpp_function::cpp_function(Return (*)(Args ...), const Extra& ...) [with Return = std::vector >; Args = {}; Extra = {pybind11::name, pybind11::scope, pybind11::sibling}]’ pybind11/include/pybind11/pybind11.h:819:22: required from ‘pybind11::module& pybind11::module::def(const char*, Func&&, const Extra& ...) [with Func = std::vector > (*)(); Extra = {}]’ pybind11/tests/test_tagbased_polymorphic.cpp:141:36: required from here pybind11/include/pybind11/cast.h:880:61: error: ambiguous template instantiation for ‘struct pybind11::polymorphic_type_hook’ const void *vsrc = polymorphic_type_hook::get(src, instance_type); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ pybind11/include/pybind11/cast.h:844:8: note: candidates are: ‘template struct pybind11::polymorphic_type_hook::value, void>::type> [with itype = Animal]’ struct polymorphic_type_hook::value>> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ pybind11/tests/test_tagbased_polymorphic.cpp:115:12: note: ‘template struct pybind11::polymorphic_type_hook::value, void>::type> [with itype = Animal]’ struct polymorphic_type_hook::value>> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from pybind11/include/pybind11/attr.h:13, from pybind11/include/pybind11/pybind11.h:44, from pybind11/tests/pybind11_tests.h:2, from pybind11/tests/test_tagbased_polymorphic.cpp:10: pybind11/include/pybind11/cast.h:880:61: error: incomplete type ‘pybind11::polymorphic_type_hook’ used in nested name specifier const void *vsrc = polymorphic_type_hook::get(src, instance_type); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ --- tests/test_tagbased_polymorphic.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_tagbased_polymorphic.cpp b/tests/test_tagbased_polymorphic.cpp index 272e460c..dcc00512 100644 --- a/tests/test_tagbased_polymorphic.cpp +++ b/tests/test_tagbased_polymorphic.cpp @@ -12,6 +12,12 @@ struct Animal { + // Make this type also a "standard" polymorphic type, to confirm that + // specializing polymorphic_type_hook using enable_if_t still works + // (https://github.com/pybind/pybind11/pull/2016/). + virtual ~Animal() = default; + + // Enum for tag-based polymorphism. enum class Kind { Unknown = 0, Dog = 100, Labrador, Chihuahua, LastDog = 199, From d730fbc0d54a3c3a7655cabe0d4f091f122b4998 Mon Sep 17 00:00:00 2001 From: Chuck Atkins Date: Tue, 12 Jun 2018 13:18:48 -0400 Subject: [PATCH 0225/1206] Utilize CMake's language standards abstraction when possible --- tools/pybind11Tools.cmake | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 508e4742..8d85dd4e 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -18,24 +18,40 @@ find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) include(CheckCXXCompilerFlag) include(CMakeParseArguments) +# Use the language standards abstraction if CMake supports it with the current compiler +if(NOT CMAKE_VERSION VERSION_LESS 3.1) + if(NOT CMAKE_CXX_STANDARD) + if(CMAKE_CXX14_STANDARD_COMPILE_OPTION) + set(CMAKE_CXX_STANDARD 14) + elseif(CMAKE_CXX11_STANDARD_COMPILE_OPTION) + set(CMAKE_CXX_STANDARD 11) + endif() + endif() + if(CMAKE_CXX_STANDARD) + set(CMAKE_CXX_EXTENSIONS OFF) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + endif() +endif() + +# Fall back to heuristics if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) - if(NOT MSVC) + if(MSVC) + set(PYBIND11_CPP_STANDARD /std:c++14) + else() check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) - - if (HAS_CPP14_FLAG) + if(HAS_CPP14_FLAG) set(PYBIND11_CPP_STANDARD -std=c++14) else() check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) - if (HAS_CPP11_FLAG) + if(HAS_CPP11_FLAG) set(PYBIND11_CPP_STANDARD -std=c++11) - else() - message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") endif() endif() - elseif(MSVC) - set(PYBIND11_CPP_STANDARD /std:c++14) endif() + if(NOT PYBIND11_CPP_STANDARD) + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) endif() From 6ebfc4b2b09036eec6a2caeb55d2801ae2df361f Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Sat, 25 Apr 2020 18:10:53 -0700 Subject: [PATCH 0226/1206] Document CMAKE_CXX_STANDARD This variable is a CMake community standard to set the C++ standard of a build. Document it in favor of the previous variable, which stays as a legacy flag for existing projects. https://cmake.org/cmake/help/v3.17/variable/CMAKE_CXX_STANDARD.html --- docs/compiling.rst | 12 ++++-------- tools/pybind11Config.cmake.in | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/compiling.rst b/docs/compiling.rst index c50c7d8a..ea8d1ec1 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -105,18 +105,14 @@ on the target compiler, falling back to C++11 if C++14 support is not available. Note, however, that this default is subject to change: future pybind11 releases are expected to migrate to newer C++ standards as they become available. To override this, the standard flag can be given explicitly in -``PYBIND11_CPP_STANDARD``: +`CMAKE_CXX_STANDARD `_: .. code-block:: cmake # Use just one of these: - # GCC/clang: - set(PYBIND11_CPP_STANDARD -std=c++11) - set(PYBIND11_CPP_STANDARD -std=c++14) - set(PYBIND11_CPP_STANDARD -std=c++1z) # Experimental C++17 support - # MSVC: - set(PYBIND11_CPP_STANDARD /std:c++14) - set(PYBIND11_CPP_STANDARD /std:c++latest) # Enables some MSVC C++17 features + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD 17) # Experimental C++17 support add_subdirectory(pybind11) # or find_package(pybind11) diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 8a7272ff..58426887 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -28,8 +28,8 @@ # # Python headers, libraries (as needed by platform), and the C++ standard # are attached to the target. Set PythonLibsNew variables to influence -# python detection and PYBIND11_CPP_STANDARD (-std=c++11 or -std=c++14) to -# influence standard setting. :: +# python detection and CMAKE_CXX_STANDARD (11 or 14) to influence standard +# setting. :: # # find_package(pybind11 CONFIG REQUIRED) # message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") From 5088364b967d3cf57e99d7d352b8a52230788b0c Mon Sep 17 00:00:00 2001 From: David Stone Date: Thu, 23 Apr 2020 12:47:24 -0600 Subject: [PATCH 0227/1206] Declare `operator==` and `operator!=` member functions const. --- include/pybind11/cast.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index fcfd0a8e..0edc3368 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -288,8 +288,8 @@ struct values_and_holders { // Past-the-end iterator: iterator(size_t end) : curr(end) {} public: - bool operator==(const iterator &other) { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) { return curr.index != other.curr.index; } + bool operator==(const iterator &other) const { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) const { return curr.index != other.curr.index; } iterator &operator++() { if (!inst->simple_layout) curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; From 9ed8b44033e64f2990bf123b5057e92b8142edae Mon Sep 17 00:00:00 2001 From: Orell Garten <10799869+orgarten@users.noreply.github.com> Date: Tue, 21 Apr 2020 15:02:55 +0200 Subject: [PATCH 0228/1206] Change __init__(self) to __new__(cls) __init__(self) cannot return values. According to https://stackoverflow.com/questions/2491819/how-to-return-a-value-from-init-in-python __new__(cls) should be used, which works. --- docs/advanced/cast/custom.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index e4f99ac5..f8035943 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -23,7 +23,7 @@ The following Python snippet demonstrates the intended usage from the Python sid .. code-block:: python class A: - def __int__(self): + def __new__(cls): return 123 from example import print From 00c462d1490d95cc9fd8ee5c22376909651319bc Mon Sep 17 00:00:00 2001 From: MRocholl Date: Wed, 15 Apr 2020 15:00:03 +0200 Subject: [PATCH 0229/1206] find library path to libclang.so via glob command in /usr/lib/llvm-* and set it --- tools/mkdoc.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 44164af3..2b429ae1 100755 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -254,6 +254,13 @@ def read_args(args): parameters.append('-isysroot') parameters.append(sysroot_dir) elif platform.system() == 'Linux': + # cython.util.find_library does not find `libclang` for all clang + # versions and distributions. LLVM switched to a monolithical setup + # that includes everything under /usr/lib/llvm{version_number}/ + # We therefore glob for the library and select the highest version + library_path = sorted(glob("/usr/lib/llvm-*/lib/"), reversed=True)[0] + cindex.Config.set_library_path(library_path) + # clang doesn't find its own base includes by default on Linux, # but different distros install them in different paths. # Try to autodetect, preferring the highest numbered version. From 9358e30d2afd580acacf969e58cfc7230cd563a7 Mon Sep 17 00:00:00 2001 From: MRocholl Date: Wed, 15 Apr 2020 15:35:38 +0200 Subject: [PATCH 0230/1206] change set_path to set_file --- tools/mkdoc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 2b429ae1..c9ee7268 100755 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -258,8 +258,8 @@ def read_args(args): # versions and distributions. LLVM switched to a monolithical setup # that includes everything under /usr/lib/llvm{version_number}/ # We therefore glob for the library and select the highest version - library_path = sorted(glob("/usr/lib/llvm-*/lib/"), reversed=True)[0] - cindex.Config.set_library_path(library_path) + library_file = sorted(glob("/usr/lib/llvm-*/lib/libclang.so"), reversed=True)[0] + cindex.Config.set_library_file(library_file) # clang doesn't find its own base includes by default on Linux, # but different distros install them in different paths. From b14aeb7cfae1a412f17ab3cb87350d5cd2ad788f Mon Sep 17 00:00:00 2001 From: MRocholl Date: Wed, 15 Apr 2020 15:37:41 +0200 Subject: [PATCH 0231/1206] fix typo in sorted function call argument reverse --- tools/mkdoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mkdoc.py b/tools/mkdoc.py index c9ee7268..c85e09d4 100755 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -258,7 +258,7 @@ def read_args(args): # versions and distributions. LLVM switched to a monolithical setup # that includes everything under /usr/lib/llvm{version_number}/ # We therefore glob for the library and select the highest version - library_file = sorted(glob("/usr/lib/llvm-*/lib/libclang.so"), reversed=True)[0] + library_file = sorted(glob("/usr/lib/llvm-*/lib/libclang.so"), reverse=True)[0] cindex.Config.set_library_file(library_file) # clang doesn't find its own base includes by default on Linux, From 0dfffcf257f01ddc72488a0408d227591e5f7e67 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 5 Apr 2020 02:34:00 -0400 Subject: [PATCH 0232/1206] Add is_final to disallow inheritance from Python - Not currently supported on PyPy --- docs/advanced/classes.rst | 26 ++++++++++++++++++++++++++ include/pybind11/attr.h | 13 ++++++++++++- include/pybind11/detail/class.h | 4 +++- tests/test_class.cpp | 8 ++++++++ tests/test_class.py | 18 ++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index ae5907de..20760b70 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -1042,6 +1042,32 @@ described trampoline: ``.def("foo", static_cast(&Publicist::foo));`` where ``int (A::*)() const`` is the type of ``A::foo``. +Binding final classes +===================== + +Some classes may not be appropriate to inherit from. In C++11, classes can +use the ``final`` specifier to ensure that a class cannot be inherited from. +The ``py::is_final`` attribute can be used to ensure that Python classes +cannot inherit from a specified type. The underlying C++ type does not need +to be declared final. + +.. code-block:: cpp + + class IsFinal final {}; + + py::class_(m, "IsFinal", py::is_final()); + +When you try to inherit from such a class in Python, you will now get this +error: + +.. code-block:: pycon + + >>> class PyFinalChild(IsFinal): + ... pass + TypeError: type 'IsFinal' is not an acceptable base type + +.. note:: This attribute is currently ignored on PyPy + Custom automatic downcasters ============================ diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 6962d6fc..744284a8 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -23,6 +23,9 @@ struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; /// Annotation for operators struct is_operator { }; +/// Annotation for classes that cannot be subclassed +struct is_final { }; + /// Annotation for parent scope struct scope { handle value; scope(const handle &s) : value(s) { } }; @@ -201,7 +204,7 @@ struct function_record { struct type_record { PYBIND11_NOINLINE type_record() : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - default_holder(true), module_local(false) { } + default_holder(true), module_local(false), is_final(false) { } /// Handle to the parent scope handle scope; @@ -254,6 +257,9 @@ struct type_record { /// Is the class definition local to the module shared object? bool module_local : 1; + /// Is the class inheritable from python classes? + bool is_final : 1; + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { auto base_info = detail::get_type_info(base, false); if (!base_info) { @@ -416,6 +422,11 @@ struct process_attribute : process_attribute_default static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } }; +template <> +struct process_attribute : process_attribute_default { + static void init(const is_final &, type_record *r) { r->is_final = true; } +}; + template <> struct process_attribute : process_attribute_default { static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index edfa7de6..a05edeb4 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -604,10 +604,12 @@ inline PyObject* make_new_python_type(const type_record &rec) { #endif /* Flags */ - type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; #if PY_MAJOR_VERSION < 3 type->tp_flags |= Py_TPFLAGS_CHECKTYPES; #endif + if (!rec.is_final) + type->tp_flags |= Py_TPFLAGS_BASETYPE; if (rec.dynamic_attr) enable_dynamic_attributes(heap_type); diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 499d0cc5..128bc39e 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -367,6 +367,14 @@ TEST_SUBMODULE(class_, m) { .def(py::init<>()) .def("ptr", &Aligned::ptr); #endif + + // test_final + struct IsFinal final {}; + py::class_(m, "IsFinal", py::is_final()); + + // test_non_final_final + struct IsNonFinalFinal {}; + py::class_(m, "IsNonFinalFinal", py::is_final()); } template class BreaksBase { public: virtual ~BreaksBase() = default; }; diff --git a/tests/test_class.py b/tests/test_class.py index ed63ca85..e58fef69 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -279,3 +279,21 @@ def test_aligned(): if hasattr(m, "Aligned"): p = m.Aligned().ptr() assert p % 1024 == 0 + + +# https://bitbucket.org/pypy/pypy/issues/2742 +@pytest.unsupported_on_pypy +def test_final(): + with pytest.raises(TypeError) as exc_info: + class PyFinalChild(m.IsFinal): + pass + assert str(exc_info.value).endswith("is not an acceptable base type") + + +# https://bitbucket.org/pypy/pypy/issues/2742 +@pytest.unsupported_on_pypy +def test_non_final_final(): + with pytest.raises(TypeError) as exc_info: + class PyNonFinalFinalChild(m.IsNonFinalFinal): + pass + assert str(exc_info.value).endswith("is not an acceptable base type") From 03f9e4a8ec10eaab2181da7ec7d629f0dce74c30 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 22 Mar 2020 12:59:37 +0800 Subject: [PATCH 0233/1206] Fix compilation with clang-cl --- include/pybind11/numpy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index ba41a223..01daf3ac 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1218,7 +1218,7 @@ template struct npy_format_descriptor { #define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT #define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) #define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) -#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround +#if defined(_MSC_VER) && !defined(__clang__) // MSVC is not as eager to expand macros, hence this workaround #define PYBIND11_MAP_LIST_NEXT1(test, next) \ PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) #else @@ -1240,7 +1240,7 @@ template struct npy_format_descriptor { (::std::vector<::pybind11::detail::field_descriptor> \ {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #define PYBIND11_MAP2_LIST_NEXT1(test, next) \ PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) #else From be0d804523c01809292f93f0bbf6adbfcf5cd0fa Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sat, 23 Dec 2017 18:56:07 -0400 Subject: [PATCH 0234/1206] Support keyword-only arguments This adds support for a `py::args_kw_only()` annotation that can be specified between `py::arg` annotations to indicate that any following arguments are keyword-only. This allows you to write: m.def("f", [](int a, int b) { /* ... */ }, py::arg("a"), py::args_kw_only(), py::arg("b")); and have it work like Python 3's: def f(a, *, b): # ... with respect to how `a` and `b` arguments are accepted (that is, `a` can be positional or by keyword; `b` can only be specified by keyword). --- docs/advanced/functions.rst | 28 ++++++++++++++++++++++ include/pybind11/attr.h | 30 ++++++++++++++++++++--- include/pybind11/cast.h | 5 ++++ include/pybind11/pybind11.h | 25 +++++++++++++------- tests/test_kwargs_and_defaults.cpp | 24 +++++++++++++++++++ tests/test_kwargs_and_defaults.py | 38 ++++++++++++++++++++++++++++++ 6 files changed, 139 insertions(+), 11 deletions(-) diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index 3e1a3ff0..c7790533 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -362,6 +362,34 @@ like so: py::class_("MyClass") .def("myFunction", py::arg("arg") = (SomeType *) nullptr); +Keyword-only arguments +====================== + +Python 3 introduced keyword-only arguments by specifying an unnamed ``*`` +argument in a function definition: + +.. code-block:: python + + def f(a, *, b): # a can be positional or via keyword; b must be via keyword + pass + + f(a=1, b=2) # good + f(b=2, a=1) # good + f(1, b=2) # good + f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given + +Pybind11 provides a ``py::args_kw_only`` object that allows you to implement +the same behaviour by specifying the object between positional and keyword-only +argument annotations when registering the function: + +.. code-block:: cpp + + m.def("f", [](int a, int b) { /* ... */ }, + py::arg("a"), py::args_kw_only(), py::arg("b")); + +Note that, as in Python, you cannot combine this with a ``py::args`` argument. +This feature does *not* require Python 3 to work. + .. _nonconverting_arguments: Non-converting arguments diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 744284a8..70325510 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -137,7 +137,8 @@ struct argument_record { struct function_record { function_record() : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } + is_operator(false), is_method(false), + has_args(false), has_kwargs(false), has_kw_only_args(false) { } /// Function name char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ @@ -175,18 +176,24 @@ struct function_record { /// True if this is an operator (__add__), etc. bool is_operator : 1; + /// True if this is a method + bool is_method : 1; + /// True if the function has a '*args' argument bool has_args : 1; /// True if the function has a '**kwargs' argument bool has_kwargs : 1; - /// True if this is a method - bool is_method : 1; + /// True once a 'py::args_kw_only' is encountered (any following args are keyword-only) + bool has_kw_only_args : 1; /// Number of arguments (including py::args and/or py::kwargs, if present) std::uint16_t nargs; + /// Number of trailing arguments (counted in `nargs`) that are keyword-only + std::uint16_t nargs_kwonly = 0; + /// Python method object PyMethodDef *def = nullptr; @@ -359,12 +366,20 @@ template <> struct process_attribute : process_attribu static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } }; +inline void process_kwonly_arg(const arg &a, function_record *r) { + if (!a.name || strlen(a.name) == 0) + pybind11_fail("arg(): cannot specify an unnamed argument after an args_kw_only() annotation"); + ++r->nargs_kwonly; +} + /// Process a keyword argument attribute (*without* a default value) template <> struct process_attribute : process_attribute_default { static void init(const arg &a, function_record *r) { if (r->is_method && r->args.empty()) r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + + if (r->has_kw_only_args) process_kwonly_arg(a, r); } }; @@ -396,6 +411,15 @@ template <> struct process_attribute : process_attribute_default { #endif } r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + + if (r->has_kw_only_args) process_kwonly_arg(a, r); + } +}; + +/// Process a keyword-only-arguments-follow pseudo argument +template <> struct process_attribute : process_attribute_default { + static void init(const args_kw_only &, function_record *r) { + r->has_kw_only_args = true; } }; diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 0edc3368..2ea1c635 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1887,6 +1887,11 @@ struct arg_v : arg { #endif }; +/// \ingroup annotations +/// Annotation indicating that all following arguments are keyword-only; the is the equivalent of an +/// unnamed '*' argument (in Python 3) +struct args_kw_only {}; + template arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index d95d61f7..ff586033 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -168,6 +168,14 @@ class cpp_function : public function { /* Process any user-provided function attributes */ process_attributes::init(extra..., rec); + { + constexpr bool has_kw_only_args = any_of...>::value, + has_args = any_of...>::value, + has_arg_annotations = any_of...>::value; + static_assert(has_arg_annotations || !has_kw_only_args, "py::args_kw_only requires the use of argument annotations"); + static_assert(!(has_args && has_kw_only_args), "py::args_kw_only cannot be combined with a py::args argument"); + } + /* Generate a readable signature describing the function's arguments and return value types */ static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); @@ -483,15 +491,16 @@ class cpp_function : public function { */ const function_record &func = *it; - size_t pos_args = func.nargs; // Number of positional arguments that we need - if (func.has_args) --pos_args; // (but don't count py::args - if (func.has_kwargs) --pos_args; // or py::kwargs) + size_t num_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --num_args; // (but don't count py::args + if (func.has_kwargs) --num_args; // or py::kwargs) + size_t pos_args = num_args - func.nargs_kwonly; if (!func.has_args && n_args_in > pos_args) - continue; // Too many arguments for this overload + continue; // Too many positional arguments for this overload if (n_args_in < pos_args && func.args.size() < pos_args) - continue; // Not enough arguments given, and not enough defaults to fill in the blanks + continue; // Not enough positional arguments given, and not enough defaults to fill in the blanks function_call call(func, parent); @@ -535,10 +544,10 @@ class cpp_function : public function { dict kwargs = reinterpret_borrow(kwargs_in); // 2. Check kwargs and, failing that, defaults that may help complete the list - if (args_copied < pos_args) { + if (args_copied < num_args) { bool copied_kwargs = false; - for (; args_copied < pos_args; ++args_copied) { + for (; args_copied < num_args; ++args_copied) { const auto &arg = func.args[args_copied]; handle value; @@ -564,7 +573,7 @@ class cpp_function : public function { break; } - if (args_copied < pos_args) + if (args_copied < num_args) continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments } diff --git a/tests/test_kwargs_and_defaults.cpp b/tests/test_kwargs_and_defaults.cpp index 6563fb9a..6c497140 100644 --- a/tests/test_kwargs_and_defaults.cpp +++ b/tests/test_kwargs_and_defaults.cpp @@ -94,6 +94,30 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { // m.def("bad_args6", [](py::args, py::args) {}); // m.def("bad_args7", [](py::kwargs, py::kwargs) {}); + // test_keyword_only_args + m.def("kwonly_all", [](int i, int j) { return py::make_tuple(i, j); }, + py::args_kw_only(), py::arg("i"), py::arg("j")); + m.def("kwonly_some", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg(), py::args_kw_only(), py::arg("j"), py::arg("k")); + m.def("kwonly_with_defaults", [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, + py::arg() = 3, "j"_a = 4, py::args_kw_only(), "k"_a = 5, "z"_a); + m.def("kwonly_mixed", [](int i, int j) { return py::make_tuple(i, j); }, + "i"_a, py::args_kw_only(), "j"_a); + m.def("kwonly_plus_more", [](int i, int j, int k, py::kwargs kwargs) { + return py::make_tuple(i, j, k, kwargs); }, + py::arg() /* positional */, py::arg("j") = -1 /* both */, py::args_kw_only(), py::arg("k") /* kw-only */); + + m.def("register_invalid_kwonly", [](py::module m) { + m.def("bad_kwonly", [](int i, int j) { return py::make_tuple(i, j); }, + py::args_kw_only(), py::arg() /* invalid unnamed argument */, "j"_a); + }); + + // These should fail to compile: + // argument annotations are required when using args_kw_only +// m.def("bad_kwonly1", [](int) {}, py::args_kw_only()); + // can't specify both `py::args_kw_only` and a `py::args` argument +// m.def("bad_kwonly2", [](int i, py::args) {}, py::args_kw_only(), "i"_a); + // test_function_signatures (along with most of the above) struct KWClass { void foo(int, float) {} }; py::class_(m, "KWClass") diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index 27a05a02..fa90140f 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -107,6 +107,44 @@ def test_mixed_args_and_kwargs(msg): """ # noqa: E501 line too long +def test_keyword_only_args(msg): + assert m.kwonly_all(i=1, j=2) == (1, 2) + assert m.kwonly_all(j=1, i=2) == (2, 1) + + with pytest.raises(TypeError) as excinfo: + assert m.kwonly_all(i=1) == (1,) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(TypeError) as excinfo: + assert m.kwonly_all(1, 2) == (1, 2) + assert "incompatible function arguments" in str(excinfo.value) + + assert m.kwonly_some(1, k=3, j=2) == (1, 2, 3) + + assert m.kwonly_with_defaults(z=8) == (3, 4, 5, 8) + assert m.kwonly_with_defaults(2, z=8) == (2, 4, 5, 8) + assert m.kwonly_with_defaults(2, j=7, k=8, z=9) == (2, 7, 8, 9) + assert m.kwonly_with_defaults(2, 7, z=9, k=8) == (2, 7, 8, 9) + + assert m.kwonly_mixed(1, j=2) == (1, 2) + assert m.kwonly_mixed(j=2, i=3) == (3, 2) + assert m.kwonly_mixed(i=2, j=3) == (2, 3) + + assert m.kwonly_plus_more(4, 5, k=6, extra=7) == (4, 5, 6, {'extra': 7}) + assert m.kwonly_plus_more(3, k=5, j=4, extra=6) == (3, 4, 5, {'extra': 6}) + assert m.kwonly_plus_more(2, k=3, extra=4) == (2, -1, 3, {'extra': 4}) + + with pytest.raises(TypeError) as excinfo: + assert m.kwonly_mixed(i=1) == (1,) + assert "incompatible function arguments" in str(excinfo.value) + + with pytest.raises(RuntimeError) as excinfo: + m.register_invalid_kwonly(m) + assert msg(excinfo.value) == """ + arg(): cannot specify an unnamed argument after an args_kw_only() annotation + """ + + def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" From a86ac538f59db7af596ee192f42b9f39ec8039ed Mon Sep 17 00:00:00 2001 From: Sebastian Koslowski Date: Tue, 14 Apr 2020 13:04:25 +0200 Subject: [PATCH 0235/1206] rename args_kw_only to kwonly --- docs/advanced/functions.rst | 4 ++-- include/pybind11/attr.h | 18 +++++++++--------- include/pybind11/cast.h | 2 +- include/pybind11/pybind11.h | 6 +++--- tests/test_kwargs_and_defaults.cpp | 20 ++++++++++---------- tests/test_kwargs_and_defaults.py | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index c7790533..984b046e 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -378,14 +378,14 @@ argument in a function definition: f(1, b=2) # good f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given -Pybind11 provides a ``py::args_kw_only`` object that allows you to implement +Pybind11 provides a ``py::kwonly`` object that allows you to implement the same behaviour by specifying the object between positional and keyword-only argument annotations when registering the function: .. code-block:: cpp m.def("f", [](int a, int b) { /* ... */ }, - py::arg("a"), py::args_kw_only(), py::arg("b")); + py::arg("a"), py::kwonly(), py::arg("b")); Note that, as in Python, you cannot combine this with a ``py::args`` argument. This feature does *not* require Python 3 to work. diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 70325510..58390239 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -138,7 +138,7 @@ struct function_record { function_record() : is_constructor(false), is_new_style_constructor(false), is_stateless(false), is_operator(false), is_method(false), - has_args(false), has_kwargs(false), has_kw_only_args(false) { } + has_args(false), has_kwargs(false), has_kwonly_args(false) { } /// Function name char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ @@ -185,8 +185,8 @@ struct function_record { /// True if the function has a '**kwargs' argument bool has_kwargs : 1; - /// True once a 'py::args_kw_only' is encountered (any following args are keyword-only) - bool has_kw_only_args : 1; + /// True once a 'py::kwonly' is encountered (any following args are keyword-only) + bool has_kwonly_args : 1; /// Number of arguments (including py::args and/or py::kwargs, if present) std::uint16_t nargs; @@ -368,7 +368,7 @@ template <> struct process_attribute : process_attribu inline void process_kwonly_arg(const arg &a, function_record *r) { if (!a.name || strlen(a.name) == 0) - pybind11_fail("arg(): cannot specify an unnamed argument after an args_kw_only() annotation"); + pybind11_fail("arg(): cannot specify an unnamed argument after an kwonly() annotation"); ++r->nargs_kwonly; } @@ -379,7 +379,7 @@ template <> struct process_attribute : process_attribute_default { r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kwonly_arg(a, r); + if (r->has_kwonly_args) process_kwonly_arg(a, r); } }; @@ -412,14 +412,14 @@ template <> struct process_attribute : process_attribute_default { } r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kwonly_arg(a, r); + if (r->has_kwonly_args) process_kwonly_arg(a, r); } }; /// Process a keyword-only-arguments-follow pseudo argument -template <> struct process_attribute : process_attribute_default { - static void init(const args_kw_only &, function_record *r) { - r->has_kw_only_args = true; +template <> struct process_attribute : process_attribute_default { + static void init(const kwonly &, function_record *r) { + r->has_kwonly_args = true; } }; diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 2ea1c635..91c9ce75 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1890,7 +1890,7 @@ struct arg_v : arg { /// \ingroup annotations /// Annotation indicating that all following arguments are keyword-only; the is the equivalent of an /// unnamed '*' argument (in Python 3) -struct args_kw_only {}; +struct kwonly {}; template arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index ff586033..722a19a1 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -169,11 +169,11 @@ class cpp_function : public function { process_attributes::init(extra..., rec); { - constexpr bool has_kw_only_args = any_of...>::value, + constexpr bool has_kwonly_args = any_of...>::value, has_args = any_of...>::value, has_arg_annotations = any_of...>::value; - static_assert(has_arg_annotations || !has_kw_only_args, "py::args_kw_only requires the use of argument annotations"); - static_assert(!(has_args && has_kw_only_args), "py::args_kw_only cannot be combined with a py::args argument"); + static_assert(has_arg_annotations || !has_kwonly_args, "py::kwonly requires the use of argument annotations"); + static_assert(!(has_args && has_kwonly_args), "py::kwonly cannot be combined with a py::args argument"); } /* Generate a readable signature describing the function's arguments and return value types */ diff --git a/tests/test_kwargs_and_defaults.cpp b/tests/test_kwargs_and_defaults.cpp index 6c497140..8f095fe4 100644 --- a/tests/test_kwargs_and_defaults.cpp +++ b/tests/test_kwargs_and_defaults.cpp @@ -96,27 +96,27 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { // test_keyword_only_args m.def("kwonly_all", [](int i, int j) { return py::make_tuple(i, j); }, - py::args_kw_only(), py::arg("i"), py::arg("j")); + py::kwonly(), py::arg("i"), py::arg("j")); m.def("kwonly_some", [](int i, int j, int k) { return py::make_tuple(i, j, k); }, - py::arg(), py::args_kw_only(), py::arg("j"), py::arg("k")); + py::arg(), py::kwonly(), py::arg("j"), py::arg("k")); m.def("kwonly_with_defaults", [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, - py::arg() = 3, "j"_a = 4, py::args_kw_only(), "k"_a = 5, "z"_a); + py::arg() = 3, "j"_a = 4, py::kwonly(), "k"_a = 5, "z"_a); m.def("kwonly_mixed", [](int i, int j) { return py::make_tuple(i, j); }, - "i"_a, py::args_kw_only(), "j"_a); + "i"_a, py::kwonly(), "j"_a); m.def("kwonly_plus_more", [](int i, int j, int k, py::kwargs kwargs) { return py::make_tuple(i, j, k, kwargs); }, - py::arg() /* positional */, py::arg("j") = -1 /* both */, py::args_kw_only(), py::arg("k") /* kw-only */); + py::arg() /* positional */, py::arg("j") = -1 /* both */, py::kwonly(), py::arg("k") /* kw-only */); m.def("register_invalid_kwonly", [](py::module m) { m.def("bad_kwonly", [](int i, int j) { return py::make_tuple(i, j); }, - py::args_kw_only(), py::arg() /* invalid unnamed argument */, "j"_a); + py::kwonly(), py::arg() /* invalid unnamed argument */, "j"_a); }); // These should fail to compile: - // argument annotations are required when using args_kw_only -// m.def("bad_kwonly1", [](int) {}, py::args_kw_only()); - // can't specify both `py::args_kw_only` and a `py::args` argument -// m.def("bad_kwonly2", [](int i, py::args) {}, py::args_kw_only(), "i"_a); + // argument annotations are required when using kwonly +// m.def("bad_kwonly1", [](int) {}, py::kwonly()); + // can't specify both `py::kwonly` and a `py::args` argument +// m.def("bad_kwonly2", [](int i, py::args) {}, py::kwonly(), "i"_a); // test_function_signatures (along with most of the above) struct KWClass { void foo(int, float) {} }; diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index fa90140f..bad6636c 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -141,7 +141,7 @@ def test_keyword_only_args(msg): with pytest.raises(RuntimeError) as excinfo: m.register_invalid_kwonly(m) assert msg(excinfo.value) == """ - arg(): cannot specify an unnamed argument after an args_kw_only() annotation + arg(): cannot specify an unnamed argument after an kwonly() annotation """ From 805c5862b61204cb2e43911c794d78326e947cb7 Mon Sep 17 00:00:00 2001 From: Yannick Jadoul Date: Sat, 25 Jan 2020 23:38:01 +0100 Subject: [PATCH 0236/1206] Adding method names to cpp_function constructor calls in enum_base --- include/pybind11/pybind11.h | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 722a19a1..dee2423d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1427,7 +1427,7 @@ struct enum_base { return pybind11::str("{}.{}").format(type_name, kv.first); } return pybind11::str("{}.???").format(type_name); - }, is_method(m_base) + }, name("__repr__"), is_method(m_base) ); m_base.attr("name") = property(cpp_function( @@ -1438,7 +1438,7 @@ struct enum_base { return pybind11::str(kv.first); } return "???"; - }, is_method(m_base) + }, name("name"), is_method(m_base) )); m_base.attr("__doc__") = static_property(cpp_function( @@ -1456,7 +1456,7 @@ struct enum_base { docstring += " : " + (std::string) pybind11::str(comment); } return docstring; - } + }, name("__doc__") ), none(), none(), ""); m_base.attr("__members__") = static_property(cpp_function( @@ -1465,7 +1465,7 @@ struct enum_base { for (const auto &kv : entries) m[kv.first] = kv.second[int_(0)]; return m; - }), none(), none(), "" + }, name("__members__")), none(), none(), "" ); #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ @@ -1475,7 +1475,7 @@ struct enum_base { strict_behavior; \ return expr; \ }, \ - is_method(m_base)) + name(op), is_method(m_base)) #define PYBIND11_ENUM_OP_CONV(op, expr) \ m_base.attr(op) = cpp_function( \ @@ -1483,7 +1483,7 @@ struct enum_base { int_ a(a_), b(b_); \ return expr; \ }, \ - is_method(m_base)) + name(op), is_method(m_base)) #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \ m_base.attr(op) = cpp_function( \ @@ -1491,7 +1491,7 @@ struct enum_base { int_ a(a_); \ return expr; \ }, \ - is_method(m_base)) + name(op), is_method(m_base)) if (is_convertible) { PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b)); @@ -1509,7 +1509,7 @@ struct enum_base { PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); m_base.attr("__invert__") = cpp_function( - [](object arg) { return ~(int_(arg)); }, is_method(m_base)); + [](object arg) { return ~(int_(arg)); }, name("__invert__"), is_method(m_base)); } } else { PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); @@ -1529,11 +1529,11 @@ struct enum_base { #undef PYBIND11_ENUM_OP_CONV #undef PYBIND11_ENUM_OP_STRICT - object getstate = cpp_function( - [](object arg) { return int_(arg); }, is_method(m_base)); + m_base.attr("__getstate__") = cpp_function( + [](object arg) { return int_(arg); }, name("__getstate__"), is_method(m_base)); - m_base.attr("__getstate__") = getstate; - m_base.attr("__hash__") = getstate; + m_base.attr("__hash__") = cpp_function( + [](object arg) { return int_(arg); }, name("__hash__"), is_method(m_base)); } PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { @@ -1586,10 +1586,12 @@ template class enum_ : public class_ { def("__index__", [](Type value) { return (Scalar) value; }); #endif - cpp_function setstate( - [](Type &value, Scalar arg) { value = static_cast(arg); }, - is_method(*this)); - attr("__setstate__") = setstate; + attr("__setstate__") = cpp_function( + [](detail::value_and_holder &v_h, Scalar arg) { + detail::initimpl::setstate(v_h, static_cast(arg), + Py_TYPE(v_h.inst) != v_h.type->type); }, + detail::is_new_style_constructor(), + pybind11::name("__setstate__"), is_method(*this)); } /// Export enumeration entries into the parent scope From 02c83dba0f6efeef536f0bb8c1aac99f603c96b0 Mon Sep 17 00:00:00 2001 From: Nicholas Musolino Date: Sun, 26 Jan 2020 11:49:32 -0500 Subject: [PATCH 0237/1206] Propagate exceptions in sequence::size() (#2076) --- include/pybind11/pytypes.h | 7 ++++++- tests/test_sequences_and_iterators.cpp | 3 +++ tests/test_sequences_and_iterators.py | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 4003d691..63cbf2e5 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1242,7 +1242,12 @@ class dict : public object { class sequence : public object { public: PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) - size_t size() const { return (size_t) PySequence_Size(m_ptr); } + size_t size() const { + ssize_t result = PySequence_Size(m_ptr); + if (result == -1) + throw error_already_set(); + return (size_t) result; + } bool empty() const { return size() == 0; } detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } detail::item_accessor operator[](handle h) const { return object::operator[](h); } diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index 87ccf99d..05f999bb 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -319,6 +319,9 @@ TEST_SUBMODULE(sequences_and_iterators, m) { return l; }); + // test_sequence_length: check that Python sequences can be converted to py::sequence. + m.def("sequence_length", [](py::sequence seq) { return seq.size(); }); + // Make sure that py::iterator works with std algorithms m.def("count_none", [](py::object o) { return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index 6bd16064..d839dbef 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -100,6 +100,25 @@ def test_sequence(): assert cstats.move_assignments == 0 +def test_sequence_length(): + """#2076: Exception raised by len(arg) should be propagated """ + class BadLen(RuntimeError): + pass + + class SequenceLike(): + def __getitem__(self, i): + return None + + def __len__(self): + raise BadLen() + + with pytest.raises(BadLen): + m.sequence_length(SequenceLike()) + + assert m.sequence_length([1, 2, 3]) == 3 + assert m.sequence_length("hello") == 5 + + def test_map_iterator(): sm = m.StringMap({'hi': 'bye', 'black': 'white'}) assert sm['hi'] == 'bye' From 2c4cd8419dd81f0563373150c5c1474bfc308352 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 24 Nov 2019 02:36:48 -0500 Subject: [PATCH 0238/1206] Add AutoWIG to list of binding generators (#1990) * Add AutoWIG to list of binding generators --- docs/compiling.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/compiling.rst b/docs/compiling.rst index ea8d1ec1..bfb1cd80 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -283,3 +283,11 @@ code by introspecting existing C++ codebases using LLVM/Clang. See the [binder]_ documentation for details. .. [binder] http://cppbinder.readthedocs.io/en/latest/about.html + +[AutoWIG]_ is a Python library that wraps automatically compiled libraries into +high-level languages. It parses C++ code using LLVM/Clang technologies and +generates the wrappers using the Mako templating engine. The approach is automatic, +extensible, and applies to very complex C++ libraries, composed of thousands of +classes or incorporating modern meta-programming constructs. + +.. [AutoWIG] https://github.com/StatisKit/AutoWIG From a54eab92d265337996b8e4b4149d9176c2d428a6 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 26 Apr 2020 22:53:50 +0200 Subject: [PATCH 0239/1206] Revert "Change __init__(self) to __new__(cls)" This reverts commit 9ed8b44033e64f2990bf123b5057e92b8142edae. --- docs/advanced/cast/custom.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index f8035943..e4f99ac5 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -23,7 +23,7 @@ The following Python snippet demonstrates the intended usage from the Python sid .. code-block:: python class A: - def __new__(cls): + def __int__(self): return 123 from example import print From a38e5331d7b98f99ac4f7284ef3359b327d99241 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 31 May 2020 00:29:30 -0400 Subject: [PATCH 0240/1206] Fix CI, prepare test on Python 3.9 beta (#2233) * Test on Python 3.9 beta * Pin Sphinx * Newer version of PyPy --- .travis.yml | 58 ++++++++++++++++++++++++++++----------- tests/test_stl_binders.py | 1 + 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index d81cd8c7..f9a90654 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,8 @@ matrix: - $PY_CMD -m pip install --user --upgrade pip wheel setuptools install: # 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 + # Latest breathe + Sphinx causes warnings and errors out + - $PY_CMD -m pip install --user --upgrade "sphinx<3" 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: @@ -109,7 +110,7 @@ matrix: - os: linux dist: xenial env: PYTHON=3.8 CPP=17 GCC=7 - name: Python 3.8, c++17, gcc 7 (w/o numpy/scipy) # TODO: update build name when the numpy/scipy wheels become available + name: Python 3.8, c++17, gcc 7 addons: apt: sources: @@ -119,12 +120,21 @@ matrix: - g++-7 - python3.8-dev - python3.8-venv - # Currently there is no numpy/scipy wheels available for python3.8 - # TODO: remove next before_install, install and script clause when the wheels become available - before_install: - - pyenv global $(pyenv whence 2to3) # activate all python versions - - PY_CMD=python3 - - $PY_CMD -m pip install --user --upgrade pip wheel setuptools + - os: linux + dist: xenial + env: PYTHON=3.9 CPP=17 GCC=7 + name: Python 3.9 beta, c++17, gcc 7 (w/o numpy/scipy) # TODO: update build name when the numpy/scipy wheels become available + addons: + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - g++-7 + - python3.9-dev + - python3.9-venv + # Currently there are no numpy/scipy wheels available for python3.9 + # TODO: remove next install and script clause when the wheels become available install: - $PY_CMD -m pip install --user --upgrade pytest script: @@ -143,14 +153,25 @@ matrix: # Test a PyPy 2.7 build - os: linux dist: trusty - env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8 - name: PyPy 5.8, Python 2.7, c++11, gcc 4.8 + env: PYPY=7.3.1 PYTHON=2.7 CPP=11 GCC=4.8 + name: PyPy 7.3, Python 2.7, c++11, gcc 4.8 + addons: + apt: + packages: + - libblas-dev + - liblapack-dev + - gfortran + - os: linux + dist: xenial + env: PYPY=7.3.1 PYTHON=3.6 CPP=11 GCC=5 + name: PyPy 7.3, Python 3.6, c++11, gcc 5 addons: apt: packages: - libblas-dev - liblapack-dev - gfortran + - g++-5 # Build in 32-bit mode and tests against the CMake-installed version - os: linux dist: trusty @@ -170,6 +191,10 @@ matrix: cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON make pytest -j 2" set +ex + allow_failures: + - name: PyPy 7.3, Python 2.7, c++11, gcc 4.8 + - name: PyPy 7.3, Python 3.6, c++11, gcc 5 + - name: Python 3.9 beta, c++17, gcc 7 (w/o numpy/scipy) cache: directories: - $HOME/.local/bin @@ -211,9 +236,9 @@ before_install: SCRIPT_RUN_PREFIX="docker exec --tty $containerid" $SCRIPT_RUN_PREFIX sh -c 'for s in 0 15; do sleep $s; apt-get update && apt-get -qy dist-upgrade && break; done' else - if [ "$PYPY" = "5.8" ]; then - curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux64.tar.bz2 | tar xj - PY_CMD=$(echo `pwd`/pypy2-v5.8.0-linux64/bin/pypy) + if [ -n "$PYPY" ]; then + curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy$PYTHON-v$PYPY-linux64.tar.bz2 | tar xj + PY_CMD=$(echo `pwd`/pypy$PYTHON-v$PYPY-linux64/bin/pypy$PY) CMAKE_EXTRA_ARGS+=" -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD" else PY_CMD=python$PYTHON @@ -255,11 +280,12 @@ install: export NPY_NUM_BUILD_JOBS=2 echo "Installing pytest, numpy, scipy..." local PIP_CMD="" - if [ -n $PYPY ]; then + if [ -n "$PYPY" ]; then # For expediency, install only versions that are available on the extra index. travis_wait 30 \ - $PY_CMD -m pip install --user --upgrade --extra-index-url https://imaginary.ca/trusty-pypi \ - pytest numpy==1.15.4 scipy==1.2.0 + $PY_CMD -m pip install --user --upgrade --extra-index-url https://antocuni.github.io/pypy-wheels/manylinux2010 \ + numpy scipy + $PY_CMD -m pip install --user --upgrade pytest else $PY_CMD -m pip install --user --upgrade pytest numpy scipy fi diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index c7b7e853..c1264c01 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -67,6 +67,7 @@ def test_vector_int(): v_int2.clear() assert len(v_int2) == 0 + # related to the PyPy's buffer protocol. @pytest.unsupported_on_pypy def test_vector_buffer(): From a3118130c6cf6307bf21fdb7ce9c00a71aec1360 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" <48421688+ahesford@users.noreply.github.com> Date: Sun, 31 May 2020 00:59:50 -0400 Subject: [PATCH 0241/1206] pytypes.h: fix docs generation (#2220) --- include/pybind11/pytypes.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 63cbf2e5..6cf7fe17 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -980,6 +980,9 @@ class bytes : public object { return std::string(buffer, (size_t) length); } }; +// Note: breathe >= 4.17.0 will fail to build docs if the below two constructors +// are included in the doxygen group; close here and reopen after as a workaround +/// @} pytypes inline bytes::bytes(const pybind11::str &s) { object temp = s; @@ -1009,6 +1012,8 @@ inline str::str(const bytes& b) { m_ptr = obj.release().ptr(); } +/// \addtogroup pytypes +/// @{ class none : public object { public: PYBIND11_OBJECT(none, object, detail::PyNone_Check) From 2c30e0a11867f09ff21292f435fd99e3ab973de9 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Sun, 31 May 2020 01:00:55 -0400 Subject: [PATCH 0242/1206] cmake: Expose `PYBIND11_TEST_OVERRIDE` (#2218) Primarily for the ccmake curses GUI --- tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 765c47ad..58993d15 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ cmake_minimum_required(VERSION 2.8.12) option(PYBIND11_WERROR "Report all warnings as errors" OFF) +set(PYBIND11_TEST_OVERRIDE "" CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) # We're being loaded directly, i.e. not via add_subdirectory, so make this From 5309573005eddbdcebaffa4f1ef88178d24497e9 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Mon, 18 May 2020 15:33:23 -0400 Subject: [PATCH 0243/1206] operators: Move hash check to before mutations, tweak whitespace --- tests/test_operator_overloading.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index bd36ac2a..f283f5b3 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -24,6 +24,8 @@ def test_operator_overloading(): assert str(v1 * v2) == "[3.000000, -2.000000]" assert str(v2 / v1) == "[3.000000, -0.500000]" + assert hash(v1) == 4 + v1 += 2 * v2 assert str(v1) == "[7.000000, 0.000000]" v1 -= v2 @@ -37,22 +39,30 @@ def test_operator_overloading(): v2 /= v1 assert str(v2) == "[2.000000, 8.000000]" - assert hash(v1) == 4 - cstats = ConstructorStats.get(m.Vector2) assert cstats.alive() == 2 del v1 assert cstats.alive() == 1 del v2 assert cstats.alive() == 0 - assert cstats.values() == ['[1.000000, 2.000000]', '[3.000000, -1.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.values() == [ + '[1.000000, 2.000000]', + '[3.000000, -1.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 From 4e3d9fea74ed50a042d98f68fa35a3133482289b Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Fri, 22 May 2020 00:43:01 -0400 Subject: [PATCH 0244/1206] operators: Explicitly expose `py::hash(py::self)` Add warnings about extending STL --- include/pybind11/operators.h | 5 +++++ tests/test_operator_overloading.cpp | 25 ++++++++++++++++++++++++- tests/test_operator_overloading.py | 14 ++++++++++++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/pybind11/operators.h b/include/pybind11/operators.h index b3dd62c3..293d5abd 100644 --- a/include/pybind11/operators.h +++ b/include/pybind11/operators.h @@ -147,6 +147,9 @@ PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) PYBIND11_UNARY_OPERATOR(neg, operator-, -l) PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +// WARNING: This usage of `abs` should only be done for existing STL overloads. +// Adding overloads directly in to the `std::` namespace is advised against: +// https://en.cppreference.com/w/cpp/language/extending_std PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) @@ -160,6 +163,8 @@ PYBIND11_UNARY_OPERATOR(float, float_, (double) l) NAMESPACE_END(detail) using detail::self; +// Add named operators so that they are accessible via `py::`. +using detail::hash; NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index 7b111704..52fcd338 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -43,6 +43,13 @@ class Vector2 { friend Vector2 operator-(float f, const Vector2 &v) { return Vector2(f - v.x, f - v.y); } friend Vector2 operator*(float f, const Vector2 &v) { return Vector2(f * v.x, f * v.y); } friend Vector2 operator/(float f, const Vector2 &v) { return Vector2(f / v.x, f / v.y); } + + bool operator==(const Vector2 &v) const { + return x == v.x && y == v.y; + } + bool operator!=(const Vector2 &v) const { + return x != v.x || y != v.y; + } private: float x, y; }; @@ -55,6 +62,11 @@ int operator+(const C2 &, const C2 &) { return 22; } int operator+(const C2 &, const C1 &) { return 21; } int operator+(const C1 &, const C2 &) { return 12; } +// Note: Specializing explicit within `namespace std { ... }` is done due to a +// bug in GCC<7. If you are supporting compilers later than this, consider +// specializing `using template<> struct std::hash<...>` in the global +// namespace instead, per this recommendation: +// https://en.cppreference.com/w/cpp/language/extending_std#Adding_template_specializations namespace std { template<> struct hash { @@ -63,6 +75,11 @@ namespace std { }; } +// Not a good abs function, but easy to test. +std::string abs(const Vector2&) { + return "abs(Vector2)"; +} + // MSVC warns about unknown pragmas, and warnings are errors. #ifndef _MSC_VER #pragma GCC diagnostic push @@ -107,7 +124,13 @@ TEST_SUBMODULE(operators, m) { .def(float() / py::self) .def(-py::self) .def("__str__", &Vector2::toString) - .def(hash(py::self)) + .def("__repr__", &Vector2::toString) + .def(py::self == py::self) + .def(py::self != py::self) + .def(py::hash(py::self)) + // N.B. See warning about usage of `py::detail::abs(py::self)` in + // `operators.h`. + .def("__abs__", [](const Vector2& v) { return abs(v); }) ; m.attr("Vector") = m.attr("Vector2"); diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index f283f5b3..1cee2988 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -6,6 +6,9 @@ def test_operator_overloading(): v1 = m.Vector2(1, 2) v2 = m.Vector(3, -1) + v3 = m.Vector2(1, 2) # Same value as v1, but different instance. + assert v1 is not v3 + assert str(v1) == "[1.000000, 2.000000]" assert str(v2) == "[3.000000, -1.000000]" @@ -24,7 +27,11 @@ def test_operator_overloading(): assert str(v1 * v2) == "[3.000000, -2.000000]" assert str(v2 / v1) == "[3.000000, -0.500000]" + assert v1 == v3 + assert v1 != v2 assert hash(v1) == 4 + # TODO(eric.cousineau): Make this work. + # assert abs(v1) == "abs(Vector2)" v1 += 2 * v2 assert str(v1) == "[7.000000, 0.000000]" @@ -40,14 +47,17 @@ def test_operator_overloading(): assert str(v2) == "[2.000000, 8.000000]" cstats = ConstructorStats.get(m.Vector2) - assert cstats.alive() == 2 + assert cstats.alive() == 3 del v1 - assert cstats.alive() == 1 + assert cstats.alive() == 2 del v2 + assert cstats.alive() == 1 + del v3 assert cstats.alive() == 0 assert cstats.values() == [ '[1.000000, 2.000000]', '[3.000000, -1.000000]', + '[1.000000, 2.000000]', '[-3.000000, 1.000000]', '[4.000000, 1.000000]', '[-2.000000, 3.000000]', From 1817d2116a3b2b9f5239b25b87657a324695fb5a Mon Sep 17 00:00:00 2001 From: Andrey Dorozhkin Date: Tue, 2 Jun 2020 16:43:07 +0300 Subject: [PATCH 0245/1206] Disable defining (v)snprintf as macro in modern Visual Studio --- include/pybind11/detail/common.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index dd626793..fb7c22f6 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -96,6 +96,12 @@ #define PYBIND11_VERSION_MINOR 5 #define PYBIND11_VERSION_PATCH dev1 +/* Don't let Python.h #define (v)snprintf as macro because they are implemented + properly in Visual Studio since 2015. */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +# define HAVE_SNPRINTF 1 +#endif + /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode #if defined(_MSC_VER) # if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) From eeb1044818af5b70761deae602c49eba439164dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Wed, 3 Jun 2020 13:51:40 +0100 Subject: [PATCH 0246/1206] [common.h] Mark entry point as "unused". This change defines a new, portable macro PYBIND11_MAYBE_UNUSED to mark declarations as unused, and annotates the PYBIND11_MODULE entry point with this attribute. The purpose of this annotation is to facilitate dead code detection, which might otherwise consider the module entry point function dead, since it isn't otherwise used. (It is only used via FFI.) --- include/pybind11/detail/common.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index fb7c22f6..d466925d 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -92,6 +92,14 @@ # define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) #endif +#if defined(PYBIND11_CPP17) +# define PYBIND11_MAYBE_UNUSED [[maybe_unused]] +#elif defined(_MSC_VER) && !defined(__clang__) +# define PYBIND11_MAYBE_UNUSED +#else +# define PYBIND11_MAYBE_UNUSED __attribute__ ((__unused__)) +#endif + #define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MINOR 5 #define PYBIND11_VERSION_PATCH dev1 @@ -285,6 +293,10 @@ extern "C" { should not be in quotes. The second macro argument defines a variable of type `py::module` which can be used to initialize the module. + The entry point is marked as "maybe unused" to aid dead-code detection analysis: + since the entry point is typically only looked up at runtime and not referenced + during translation, it would otherwise appear as unused ("dead") code. + .. code-block:: cpp PYBIND11_MODULE(example, m) { @@ -297,6 +309,7 @@ extern "C" { } \endrst */ #define PYBIND11_MODULE(name, variable) \ + PYBIND11_MAYBE_UNUSED \ static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ PYBIND11_PLUGIN_IMPL(name) { \ PYBIND11_CHECK_PYTHON_VERSION \ From c776e9ef937e3286c96f801ce2c4221f245e1bff Mon Sep 17 00:00:00 2001 From: Simeon Ehrig Date: Wed, 3 Jun 2020 12:12:51 +0200 Subject: [PATCH 0247/1206] Fix compiler error with MSVC 17 and CUDA 10.2 --- include/pybind11/cast.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 91c9ce75..2b382ddf 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1498,7 +1498,9 @@ struct copyable_holder_caster : public type_caster_base { } explicit operator type*() { return this->value; } - explicit operator type&() { return *(this->value); } + // static_cast works around compiler error with MSVC 17 and CUDA 10.2 + // see issue #2180 + explicit operator type&() { return *(static_cast(this->value)); } explicit operator holder_type*() { return std::addressof(holder); } // Workaround for Intel compiler bug From 1e14930dfcee467da6a28b44f0f276451935970b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Wed, 3 Jun 2020 13:51:40 +0100 Subject: [PATCH 0248/1206] [common.h] Mark another entry point as "unused". For rationale, see #2241, eeb1044818af5b70761deae602c49eba439164dc; there is a second entry point function defined by the PYBIND11_MODULE macro that also needs to be annotated as unused. --- include/pybind11/detail/common.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index d466925d..f4840dae 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -185,9 +185,10 @@ #define PYBIND11_STR_TYPE ::pybind11::str #define PYBIND11_BOOL_ATTR "__bool__" #define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) -// Providing a separate declaration to make Clang's -Wmissing-prototypes happy +// Providing a separate declaration to make Clang's -Wmissing-prototypes happy. +// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". #define PYBIND11_PLUGIN_IMPL(name) \ - extern "C" PYBIND11_EXPORT PyObject *PyInit_##name(); \ + extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \ extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() #else @@ -211,13 +212,14 @@ #define PYBIND11_STR_TYPE ::pybind11::bytes #define PYBIND11_BOOL_ATTR "__nonzero__" #define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) -// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy +// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy. +// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". #define PYBIND11_PLUGIN_IMPL(name) \ - static PyObject *pybind11_init_wrapper(); \ - extern "C" PYBIND11_EXPORT void init##name(); \ - extern "C" PYBIND11_EXPORT void init##name() { \ - (void)pybind11_init_wrapper(); \ - } \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ PyObject *pybind11_init_wrapper() #endif From b524008967daf913deccffaf4e06285e2c4f5923 Mon Sep 17 00:00:00 2001 From: Matthijs van der Burgh Date: Wed, 10 Jun 2020 13:30:41 +0200 Subject: [PATCH 0249/1206] Deepcopy documentation (#2242) * (docs) convert note to real note * (docs) Add information about (deep)copy --- docs/advanced/classes.rst | 50 +++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 20760b70..70393b55 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -768,13 +768,17 @@ An instance can now be pickled as follows: p.setExtra(15) data = pickle.dumps(p, 2) -Note that only the cPickle module is supported on Python 2.7. The second -argument to ``dumps`` is also crucial: it selects the pickle protocol version -2, since the older version 1 is not supported. Newer versions are also fine—for -instance, specify ``-1`` to always use the latest available version. Beware: -failure to follow these instructions will cause important pybind11 memory -allocation routines to be skipped during unpickling, which will likely lead to -memory corruption and/or segmentation faults. + +.. note:: + Note that only the cPickle module is supported on Python 2.7. + + The second argument to ``dumps`` is also crucial: it selects the pickle + protocol version 2, since the older version 1 is not supported. Newer + versions are also fine—for instance, specify ``-1`` to always use the + latest available version. Beware: failure to follow these instructions + will cause important pybind11 memory allocation routines to be skipped + during unpickling, which will likely lead to memory corruption and/or + segmentation faults. .. seealso:: @@ -784,6 +788,38 @@ memory corruption and/or segmentation faults. .. [#f3] http://docs.python.org/3/library/pickle.html#pickling-class-instances +Deepcopy support +================ + +Python normally uses references in assignments. Sometimes a real copy is needed +to prevent changing all copies. The ``copy`` module [#f5]_ provides these +capabilities. + +On Python 3, a class with pickle support is automatically also (deep)copy +compatible. However, performance can be improved by adding custom +``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods +are mandatory for (deep)copy compatibility, because pybind11 only supports +cPickle. + +For simple classes (deep)copy can be enabled by using the copy constructor, +which should look as follows: + +.. code-block:: cpp + + py::class_(m, "Copyable") + .def("__copy__", [](const Copyable &self) { + return Copyable(self); + }) + .def("__deepcopy__", [](const Copyable &self, py::dict) { + return Copyable(self); + }, "memo"_a); + +.. note:: + + Dynamic attributes will not be copied in this example. + +.. [#f5] https://docs.python.org/3/library/copy.html + Multiple Inheritance ==================== From 63df87fa490d49244b76249854559ec8db22f119 Mon Sep 17 00:00:00 2001 From: Clemens Sielaff Date: Wed, 10 Jun 2020 04:35:10 -0700 Subject: [PATCH 0250/1206] Add lvalue ref-qualified cpp_function constructors (#2213) * added overload for l-value ref-qualified methods * Added test. Before, the code would have failed to build. --- include/pybind11/pybind11.h | 22 ++++++++++++++++++++-- tests/test_methods_and_attributes.cpp | 15 +++++++++++++++ tests/test_methods_and_attributes.py | 11 +++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index dee2423d..ac13616d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -72,20 +72,38 @@ class cpp_function : public function { (detail::function_signature_t *) nullptr, extra...); } - /// Construct a cpp_function from a class method (non-const) + /// Construct a cpp_function from a class method (non-const, no ref-qualifier) template cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*) (Class *, Arg...)) nullptr, extra...); } - /// Construct a cpp_function from a class method (const) + /// Construct a cpp_function from a class method (non-const, lvalue ref-qualifier) + /// A copy of the overload for non-const functions without explicit ref-qualifier + /// but with an added `&`. + template + cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const, no ref-qualifier) template cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, (Return (*)(const Class *, Arg ...)) nullptr, extra...); } + /// Construct a cpp_function from a class method (const, lvalue ref-qualifier) + /// A copy of the overload for const functions without explicit ref-qualifier + /// but with an added `&`. + template + cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + /// Return the function name object name() const { return attr("__name__"); } diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index c7b82f13..13376209 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -207,6 +207,14 @@ class RegisteredDerived : public UnregisteredBase { double sum() const { return rw_value + ro_value; } }; +// Test explicit lvalue ref-qualification +struct RefQualified { + int value = 0; + + void refQualified(int other) & { value += other; } + int constRefQualified(int other) const & { return value + other; } +}; + TEST_SUBMODULE(methods_and_attributes, m) { // test_methods_and_attributes py::class_ emna(m, "ExampleMandA"); @@ -457,4 +465,11 @@ TEST_SUBMODULE(methods_and_attributes, m) { m.def("custom_caster_destroy_const", []() -> const DestructionTester * { return new DestructionTester(); }, py::return_value_policy::take_ownership); // Likewise (const doesn't inhibit destruction) m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); + + // test_methods_and_attributes + py::class_(m, "RefQualified") + .def(py::init<>()) + .def_readonly("value", &RefQualified::value) + .def("refQualified", &RefQualified::refQualified) + .def("constRefQualified", &RefQualified::constRefQualified); } diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index f1c862be..5a10fbfa 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -510,3 +510,14 @@ def test_custom_caster_destruction(): # Make sure we still only have the original object (from ..._no_destroy()) alive: assert cstats.alive() == 1 + + +def test_ref_qualified(): + """Tests that explicit lvalue ref-qualified methods can be called just like their + non ref-qualified counterparts.""" + + r = m.RefQualified() + assert r.value == 0 + r.refQualified(17) + assert r.value == 17 + assert r.constRefQualified(23) == 40 From 22b2504080d37e035bbf3df88edb7abffb317ce5 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Thu, 14 May 2020 15:34:48 +0300 Subject: [PATCH 0251/1206] Render full numpy numeric names (e.g. numpy.int32) --- include/pybind11/numpy.h | 6 +++--- tests/test_eigen.py | 34 +++++++++++++++++++--------------- tests/test_numpy_array.py | 18 +++++++++--------- tests/test_numpy_vectorize.py | 10 +++++----- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 01daf3ac..f26d7aa6 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1007,14 +1007,14 @@ struct npy_format_descriptor_name; template struct npy_format_descriptor_name::value>> { static constexpr auto name = _::value>( - _("bool"), _::value>("int", "uint") + _() + _("bool"), _::value>("numpy.int", "numpy.uint") + _() ); }; template struct npy_format_descriptor_name::value>> { static constexpr auto name = _::value || std::is_same::value>( - _("float") + _(), _("longdouble") + _("numpy.float") + _(), _("numpy.longdouble") ); }; @@ -1022,7 +1022,7 @@ template struct npy_format_descriptor_name::value>> { static constexpr auto name = _::value || std::is_same::value>( - _("complex") + _(), _("longcomplex") + _("numpy.complex") + _(), _("numpy.longcomplex") ); }; diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 55d93517..d836398a 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -79,15 +79,17 @@ def test_mutator_descriptors(): m.fixed_mutator_a(zc) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_r(zc) - assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable, flags.c_contiguous]) -> None' + assert ('(arg0: numpy.ndarray[numpy.float32[5, 6],' + ' flags.writeable, flags.c_contiguous]) -> None' in str(excinfo.value)) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_c(zr) - assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable, flags.f_contiguous]) -> None' + assert ('(arg0: numpy.ndarray[numpy.float32[5, 6],' + ' flags.writeable, flags.f_contiguous]) -> None' in str(excinfo.value)) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype='float32')) - assert ('(arg0: numpy.ndarray[float32[5, 6], flags.writeable]) -> None' + assert ('(arg0: numpy.ndarray[numpy.float32[5, 6], flags.writeable]) -> None' in str(excinfo.value)) zr.flags.writeable = False with pytest.raises(TypeError): @@ -179,7 +181,7 @@ def test_negative_stride_from_python(msg): m.double_threer(second_row) assert msg(excinfo.value) == """ double_threer(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[float32[1, 3], flags.writeable]) -> None + 1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None Invoked with: """ + repr(np.array([ 5., 4., 3.], dtype='float32')) # noqa: E501 line too long @@ -187,7 +189,7 @@ def test_negative_stride_from_python(msg): m.double_threec(second_col) assert msg(excinfo.value) == """ double_threec(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[float32[3, 1], flags.writeable]) -> None + 1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None Invoked with: """ + repr(np.array([ 7., 4., 1.], dtype='float32')) # noqa: E501 line too long @@ -607,17 +609,19 @@ def test_special_matrix_objects(): def test_dense_signature(doc): assert doc(m.double_col) == """ - double_col(arg0: numpy.ndarray[float32[m, 1]]) -> numpy.ndarray[float32[m, 1]] + double_col(arg0: numpy.ndarray[numpy.float32[m, 1]]) -> numpy.ndarray[numpy.float32[m, 1]] """ assert doc(m.double_row) == """ - double_row(arg0: numpy.ndarray[float32[1, n]]) -> numpy.ndarray[float32[1, n]] - """ - assert doc(m.double_complex) == """ - double_complex(arg0: numpy.ndarray[complex64[m, 1]]) -> numpy.ndarray[complex64[m, 1]] - """ - assert doc(m.double_mat_rm) == """ - double_mat_rm(arg0: numpy.ndarray[float32[m, n]]) -> numpy.ndarray[float32[m, n]] + double_row(arg0: numpy.ndarray[numpy.float32[1, n]]) -> numpy.ndarray[numpy.float32[1, n]] """ + assert doc(m.double_complex) == (""" + double_complex(arg0: numpy.ndarray[numpy.complex64[m, 1]])""" + """ -> numpy.ndarray[numpy.complex64[m, 1]] + """) + assert doc(m.double_mat_rm) == (""" + double_mat_rm(arg0: numpy.ndarray[numpy.float32[m, n]])""" + """ -> numpy.ndarray[numpy.float32[m, n]] + """) def test_named_arguments(): @@ -654,10 +658,10 @@ def test_sparse(): @pytest.requires_eigen_and_scipy def test_sparse_signature(doc): assert doc(m.sparse_copy_r) == """ - sparse_copy_r(arg0: scipy.sparse.csr_matrix[float32]) -> scipy.sparse.csr_matrix[float32] + sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32] """ # noqa: E501 line too long assert doc(m.sparse_copy_c) == """ - sparse_copy_c(arg0: scipy.sparse.csc_matrix[float32]) -> scipy.sparse.csc_matrix[float32] + sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32] """ # noqa: E501 line too long diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index d0a6324d..55571964 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -286,13 +286,13 @@ def test_overload_resolution(msg): m.overloaded("not an array") assert msg(excinfo.value) == """ overloaded(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[float64]) -> str - 2. (arg0: numpy.ndarray[float32]) -> str - 3. (arg0: numpy.ndarray[int32]) -> str - 4. (arg0: numpy.ndarray[uint16]) -> str - 5. (arg0: numpy.ndarray[int64]) -> str - 6. (arg0: numpy.ndarray[complex128]) -> str - 7. (arg0: numpy.ndarray[complex64]) -> str + 1. (arg0: numpy.ndarray[numpy.float64]) -> str + 2. (arg0: numpy.ndarray[numpy.float32]) -> str + 3. (arg0: numpy.ndarray[numpy.int32]) -> str + 4. (arg0: numpy.ndarray[numpy.uint16]) -> str + 5. (arg0: numpy.ndarray[numpy.int64]) -> str + 6. (arg0: numpy.ndarray[numpy.complex128]) -> str + 7. (arg0: numpy.ndarray[numpy.complex64]) -> str Invoked with: 'not an array' """ @@ -307,8 +307,8 @@ def test_overload_resolution(msg): assert m.overloaded3(np.array([1], dtype='intc')) == 'int' expected_exc = """ overloaded3(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[int32]) -> str - 2. (arg0: numpy.ndarray[float64]) -> str + 1. (arg0: numpy.ndarray[numpy.int32]) -> str + 2. (arg0: numpy.ndarray[numpy.float64]) -> str Invoked with: """ diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index 0e9c8839..c078ee7c 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -109,7 +109,7 @@ def test_type_selection(): def test_docs(doc): assert doc(m.vectorized_func) == """ - vectorized_func(arg0: numpy.ndarray[int32], arg1: numpy.ndarray[float32], arg2: numpy.ndarray[float64]) -> object + vectorized_func(arg0: numpy.ndarray[numpy.int32], arg1: numpy.ndarray[numpy.float32], arg2: numpy.ndarray[numpy.float64]) -> object """ # noqa: E501 line too long @@ -160,12 +160,12 @@ def test_passthrough_arguments(doc): assert doc(m.vec_passthrough) == ( "vec_passthrough(" + ", ".join([ "arg0: float", - "arg1: numpy.ndarray[float64]", - "arg2: numpy.ndarray[float64]", - "arg3: numpy.ndarray[int32]", + "arg1: numpy.ndarray[numpy.float64]", + "arg2: numpy.ndarray[numpy.float64]", + "arg3: numpy.ndarray[numpy.int32]", "arg4: int", "arg5: m.numpy_vectorize.NonPODClass", - "arg6: numpy.ndarray[float64]"]) + ") -> object") + "arg6: numpy.ndarray[numpy.float64]"]) + ") -> object") b = np.array([[10, 20, 30]], dtype='float64') c = np.array([100, 200]) # NOT a vectorized argument From 57070fb0a084e40ceab8a9f80b9e4436a48b9e53 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sat, 16 May 2020 17:28:29 +0300 Subject: [PATCH 0252/1206] Render py::iterator/py::iterable as Iterator/Iterable in docstrings --- include/pybind11/cast.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 2b382ddf..5384ecaf 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1606,6 +1606,8 @@ template struct is_holder_type struct handle_type_name { static constexpr auto name = _(); }; template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("Iterable"); }; +template <> struct handle_type_name { static constexpr auto name = _("Iterator"); }; template <> struct handle_type_name { static constexpr auto name = _("*args"); }; template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; From 90d99b56a00a00cf23568435ae5f44ea0c8c3bf1 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Wed, 20 May 2020 12:42:07 +0300 Subject: [PATCH 0253/1206] Render pybind11::array as numpy.ndarray in docstrings --- include/pybind11/numpy.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index f26d7aa6..49a1e4f5 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -40,6 +40,9 @@ NAMESPACE_BEGIN(PYBIND11_NAMESPACE) class array; // Forward declaration NAMESPACE_BEGIN(detail) + +template <> struct handle_type_name { static constexpr auto name = _("numpy.ndarray"); }; + template struct npy_format_descriptor; struct PyArrayDescr_Proxy { From 4f1531c454ada656884c6f69b56050ac9fd18b2a Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Fri, 15 May 2020 00:11:39 +0300 Subject: [PATCH 0254/1206] Render `py::int_` as `int` in docstrings --- include/pybind11/cast.h | 1 + tests/test_pytypes.cpp | 2 ++ tests/test_pytypes.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 5384ecaf..be4f66b0 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1606,6 +1606,7 @@ template struct is_holder_type struct handle_type_name { static constexpr auto name = _(); }; template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("int"); }; template <> struct handle_type_name { static constexpr auto name = _("Iterable"); }; template <> struct handle_type_name { static constexpr auto name = _("Iterator"); }; template <> struct handle_type_name { static constexpr auto name = _("*args"); }; diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 244e1db0..da9fcaea 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -11,6 +11,8 @@ TEST_SUBMODULE(pytypes, m) { + // test_int + m.def("get_int", []{return py::int_(0);}); // test_list m.def("get_list", []() { py::list list; diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 0e8d6c33..89ec2858 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -5,6 +5,8 @@ from pybind11_tests import pytypes as m from pybind11_tests import debug_enabled +def test_int(doc): + assert doc(m.get_int) == "get_int() -> int" def test_list(capture, doc): with capture: From ab323e04f3157cfe57c49d6f95fd232190d7032c Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Fri, 5 Jun 2020 21:29:16 +0300 Subject: [PATCH 0255/1206] Test py::iterable/py::iterator representation in docstrings --- tests/test_pytypes.cpp | 4 ++++ tests/test_pytypes.py | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index da9fcaea..e70ffae9 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -13,6 +13,10 @@ TEST_SUBMODULE(pytypes, m) { // test_int m.def("get_int", []{return py::int_(0);}); + // test_iterator + m.def("get_iterator", []{return py::iterator();}); + // test_iterable + m.def("get_iterable", []{return py::iterable();}); // test_list m.def("get_list", []() { py::list list; diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 89ec2858..d6223b9b 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -5,9 +5,19 @@ from pybind11_tests import pytypes as m from pybind11_tests import debug_enabled + def test_int(doc): assert doc(m.get_int) == "get_int() -> int" + +def test_iterator(doc): + assert doc(m.get_iterator) == "get_iterator() -> Iterator" + + +def test_iterable(doc): + assert doc(m.get_iterable) == "get_iterable() -> Iterable" + + def test_list(capture, doc): with capture: lst = m.get_list() From e107fc2a054fe3c71bbd8c4ed2539fdb80e9f1b5 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Sun, 26 Apr 2020 14:56:10 +0000 Subject: [PATCH 0256/1206] Fix setuptools record of headers --- setup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/setup.py b/setup.py index 473ea1ee..668b56a5 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,13 @@ def build_package_data(self): self.mkpath(os.path.dirname(target)) self.copy_file(header, target, preserve_mode=False) + def get_outputs(self, include_bytecode=1): + outputs = build_py.get_outputs(self, include_bytecode=include_bytecode) + for header in package_data: + target = os.path.join(self.build_lib, 'pybind11', header) + outputs.append(target) + return outputs + setup( name='pybind11', From d96c34516d276f4c33d25df23d5934af1c5b2406 Mon Sep 17 00:00:00 2001 From: methylDragon Date: Tue, 16 Jun 2020 03:34:45 +0800 Subject: [PATCH 0257/1206] Fix docs typo --- include/pybind11/pybind11.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index ac13616d..7a6e5630 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2152,8 +2152,8 @@ template function get_overload(const T *this_ptr, const char *name) { PYBIND11_OVERLOAD_NAME( std::string, // Return type (ret_type) Animal, // Parent class (cname) - toString, // Name of function in C++ (name) - "__str__", // Name of method in Python (fn) + "__str__", // Name of method in Python (name) + toString, // Name of function in C++ (fn) ); } \endrst */ From 8c0cd94465fbc1dbc34217e5a614edc784f0913e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 18 Jun 2020 12:14:59 +0200 Subject: [PATCH 0258/1206] ignore another type of visual studio project file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 979fd443..244bbcaa 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ cmake_install.cmake *.sdf *.opensdf *.vcxproj +*.vcxproj.user *.filters example.dir Win32 From 8e85fadff28988f036182cc9345dae124ce7cd17 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Sat, 27 Jun 2020 18:33:20 -0700 Subject: [PATCH 0259/1206] Render `py::none` as `None` in docstrings Closes #2270 --- include/pybind11/cast.h | 1 + tests/test_pytypes.cpp | 5 +++++ tests/test_pytypes.py | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index be4f66b0..28283da8 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1609,6 +1609,7 @@ template <> struct handle_type_name { static constexpr auto name = _(PYBI template <> struct handle_type_name { static constexpr auto name = _("int"); }; template <> struct handle_type_name { static constexpr auto name = _("Iterable"); }; template <> struct handle_type_name { static constexpr auto name = _("Iterator"); }; +template <> struct handle_type_name { static constexpr auto name = _("None"); }; template <> struct handle_type_name { static constexpr auto name = _("*args"); }; template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index e70ffae9..f0d86d87 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -32,6 +32,11 @@ TEST_SUBMODULE(pytypes, m) { for (auto item : list) py::print("list item {}: {}"_s.format(index++, item)); }); + // test_none + m.def("get_none", []{return py::none();}); + m.def("print_none", [](py::none none) { + py::print("none: {}"_s.format(none)); + }); // test_set m.def("get_set", []() { diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index d6223b9b..79bee877 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -37,6 +37,11 @@ def test_list(capture, doc): assert doc(m.print_list) == "print_list(arg0: list) -> None" +def test_none(capture, doc): + assert doc(m.get_none) == "get_none() -> None" + assert doc(m.print_none) == "print_none(arg0: None) -> None" + + def test_set(capture, doc): s = m.get_set() assert s == {"key1", "key2", "key3"} From a3daf87d45eb5a26c12ec44b7878f81e1dc8b8d5 Mon Sep 17 00:00:00 2001 From: fatvlady Date: Sat, 14 Mar 2020 15:15:12 +0200 Subject: [PATCH 0260/1206] Add failing optional test --- tests/test_stl.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ tests/test_stl.py | 10 ++++++++++ 2 files changed, 50 insertions(+) diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 207c9fb2..4c5ed853 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -50,6 +50,17 @@ namespace std { } +template