Unverified Commit e315e1fe authored by Henry Schreiner's avatar Henry Schreiner
Browse files

Merge branch 'master' into stable

parents 71fd5241 97976c16
...@@ -9,29 +9,13 @@ ...@@ -9,29 +9,13 @@
#pragma once #pragma once
#include "numpy.h" /* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
See also:
#if defined(__INTEL_COMPILER) https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
#elif defined(__GNUG__) || defined(__clang__) */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
# ifdef __clang__
// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated
// under Clang, so disable that warning here:
# pragma GCC diagnostic ignored "-Wdeprecated"
# endif
# if __GNUC__ >= 7
# pragma GCC diagnostic ignored "-Wint-in-bool-context"
# endif
#endif
#if defined(_MSC_VER) #include "numpy.h"
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17
#endif
#include <Eigen/Core> #include <Eigen/Core>
#include <Eigen/SparseCore> #include <Eigen/SparseCore>
...@@ -77,6 +61,7 @@ template <bool EigenRowMajor> struct EigenConformable { ...@@ -77,6 +61,7 @@ template <bool EigenRowMajor> struct EigenConformable {
EigenDStride stride{0, 0}; // Only valid if negativestrides is false! EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride! bool negativestrides = false; // If true, do not use stride!
// NOLINTNEXTLINE(google-explicit-constructor)
EigenConformable(bool fits = false) : conformable{fits} {} EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type: // Matrix type:
EigenConformable(EigenIndex r, EigenIndex c, EigenConformable(EigenIndex r, EigenIndex c,
...@@ -104,6 +89,7 @@ template <bool EigenRowMajor> struct EigenConformable { ...@@ -104,6 +89,7 @@ template <bool EigenRowMajor> struct EigenConformable {
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
(EigenRowMajor ? rows : cols) == 1); (EigenRowMajor ? rows : cols) == 1);
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return conformable; } operator bool() const { return conformable; }
}; };
...@@ -153,7 +139,8 @@ template <typename Type_> struct EigenProps { ...@@ -153,7 +139,8 @@ template <typename Type_> struct EigenProps {
np_cols = a.shape(1), np_cols = a.shape(1),
np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)), np_rstride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)),
np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar)); np_cstride = a.strides(1) / static_cast<ssize_t>(sizeof(Scalar));
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) if ((PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && np_rows != rows) ||
(PYBIND11_SILENCE_MSVC_C4127(fixed_cols) && np_cols != cols))
return false; return false;
return {np_rows, np_cols, np_rstride, np_cstride}; return {np_rows, np_cols, np_rstride, np_cstride};
...@@ -165,7 +152,7 @@ template <typename Type_> struct EigenProps { ...@@ -165,7 +152,7 @@ template <typename Type_> struct EigenProps {
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar)); stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
if (vector) { // Eigen type is a compile-time vector if (vector) { // Eigen type is a compile-time vector
if (fixed && size != n) if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n)
return false; // Vector size mismatch return false; // Vector size mismatch
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
} }
...@@ -179,7 +166,7 @@ template <typename Type_> struct EigenProps { ...@@ -179,7 +166,7 @@ template <typename Type_> struct EigenProps {
if (cols != n) return false; if (cols != n) return false;
return {1, n, stride}; return {1, n, stride};
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector } // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
if (fixed_rows && rows != n) return false; if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) return false;
return {n, 1, stride}; return {n, 1, stride};
} }
...@@ -341,8 +328,11 @@ public: ...@@ -341,8 +328,11 @@ public:
static constexpr auto name = props::descriptor; static constexpr auto name = props::descriptor;
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return &value; } operator Type*() { return &value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return value; } operator Type&() { return value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&&() && { return std::move(value); } operator Type&&() && { return std::move(value); }
template <typename T> using cast_op_type = movable_cast_op_type<T>; template <typename T> using cast_op_type = movable_cast_op_type<T>;
...@@ -466,7 +456,9 @@ public: ...@@ -466,7 +456,9 @@ public:
return true; return true;
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return ref.get(); } operator Type*() { return ref.get(); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return *ref; } operator Type&() { return *ref; }
template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>; template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
...@@ -596,9 +588,3 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> { ...@@ -596,9 +588,3 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUG__) || defined(__clang__)
# pragma GCC diagnostic pop
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
#include "pybind11.h" #include "pybind11.h"
#include "eval.h" #include "eval.h"
#include <memory>
#include <vector>
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
# error Embedding the interpreter is not supported with PyPy # error Embedding the interpreter is not supported with PyPy
#endif #endif
...@@ -45,25 +48,23 @@ ...@@ -45,25 +48,23 @@
}); });
} }
\endrst */ \endrst */
#define PYBIND11_EMBEDDED_MODULE(name, variable) \ #define PYBIND11_EMBEDDED_MODULE(name, variable) \
static ::pybind11::module_::module_def \ static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
PYBIND11_CONCAT(pybind11_module_def_, name); \ static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ auto m = ::pybind11::module_::create_extension_module( \
auto m = ::pybind11::module_::create_extension_module( \ PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
PYBIND11_TOSTRING(name), nullptr, \ try { \
&PYBIND11_CONCAT(pybind11_module_def_, name)); \ PYBIND11_CONCAT(pybind11_init_, name)(m); \
try { \ return m.ptr(); \
PYBIND11_CONCAT(pybind11_init_, name)(m); \ } \
return m.ptr(); \ PYBIND11_CATCH_INIT_EXCEPTIONS \
} PYBIND11_CATCH_INIT_EXCEPTIONS \ } \
} \ PYBIND11_EMBEDDED_MODULE_IMPL(name) \
PYBIND11_EMBEDDED_MODULE_IMPL(name) \ ::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \ PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
(PYBIND11_TOSTRING(name), \ void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
PYBIND11_CONCAT(pybind11_init_impl_, name)); \ & variable) // NOLINT(bugprone-macro-parentheses)
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -85,29 +86,106 @@ struct embedded_module { ...@@ -85,29 +86,106 @@ struct embedded_module {
} }
}; };
struct wide_char_arg_deleter {
void operator()(wchar_t *ptr) const {
#if PY_VERSION_HEX >= 0x030500f0
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
PyMem_RawFree(ptr);
#else
delete[] ptr;
#endif
}
};
inline wchar_t *widen_chars(const char *safe_arg) {
#if PY_VERSION_HEX >= 0x030500f0
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
#else
wchar_t *widened_arg = nullptr;
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
size_t count = strlen(safe_arg);
# else
size_t count = mbstowcs(nullptr, safe_arg, 0);
# endif
if (count != static_cast<size_t>(-1)) {
widened_arg = new wchar_t[count + 1];
mbstowcs(widened_arg, safe_arg, count + 1);
}
#endif
return widened_arg;
}
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
// Before it was special-cased in python 3.8, passing an empty or null argv
// caused a segfault, so we have to reimplement the special case ourselves.
bool special_case = (argv == nullptr || argc <= 0);
const char *const empty_argv[]{"\0"};
const char *const *safe_argv = special_case ? empty_argv : argv;
if (special_case)
argc = 1;
auto argv_size = static_cast<size_t>(argc);
#if PY_MAJOR_VERSION >= 3
// SetArgv* on python 3 takes wchar_t, so we have to convert.
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
widened_argv_entries.reserve(argv_size);
for (size_t ii = 0; ii < argv_size; ++ii) {
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
if (!widened_argv_entries.back()) {
// A null here indicates a character-encoding failure or the python
// interpreter out of memory. Give up.
return;
}
widened_argv[ii] = widened_argv_entries.back().get();
}
auto pysys_argv = widened_argv.get();
#else
// python 2.x
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
std::vector<char *> char_strings{argv_size};
for (std::size_t i = 0; i < argv_size; ++i)
char_strings[i] = &strings[i][0];
char **pysys_argv = char_strings.data();
#endif
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
}
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/** \rst /** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be 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 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 optional `init_signal_handlers` parameter can be used to skip the registration of
`Python documentation`_ for details). Calling this function again after the interpreter signal handlers (see the `Python documentation`_ for details). Calling this function
has already been initialized is a fatal error. again after the interpreter has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This 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 is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.) of throwing exceptions on errors.)
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
used to populate ``sys.argv`` and ``sys.path``.
See the |PySys_SetArgvEx documentation|_ for details.
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
\endrst */ \endrst */
inline void initialize_interpreter(bool init_signal_handlers = true) { inline void initialize_interpreter(bool init_signal_handlers = true,
int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
if (Py_IsInitialized() != 0) if (Py_IsInitialized() != 0)
pybind11_fail("The interpreter is already running"); pybind11_fail("The interpreter is already running");
Py_InitializeEx(init_signal_handlers ? 1 : 0); Py_InitializeEx(init_signal_handlers ? 1 : 0);
// Make .py files in the working directory available by default detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
module_::import("sys").attr("path").cast<list>().append(".");
} }
/** \rst /** \rst
...@@ -169,6 +247,8 @@ inline void finalize_interpreter() { ...@@ -169,6 +247,8 @@ inline void finalize_interpreter() {
Scope guard version of `initialize_interpreter` and `finalize_interpreter`. Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
This a move-only guard and only a single instance can exist. This a move-only guard and only a single instance can exist.
See `initialize_interpreter` for a discussion of its constructor arguments.
.. code-block:: cpp .. code-block:: cpp
#include <pybind11/embed.h> #include <pybind11/embed.h>
...@@ -180,8 +260,11 @@ inline void finalize_interpreter() { ...@@ -180,8 +260,11 @@ inline void finalize_interpreter() {
\endrst */ \endrst */
class scoped_interpreter { class scoped_interpreter {
public: public:
scoped_interpreter(bool init_signal_handlers = true) { explicit scoped_interpreter(bool init_signal_handlers = true,
initialize_interpreter(init_signal_handlers); int argc = 0,
const char *const *argv = nullptr,
bool add_program_dir_to_path = true) {
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
} }
scoped_interpreter(const scoped_interpreter &) = delete; scoped_interpreter(const scoped_interpreter &) = delete;
......
...@@ -136,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object()) ...@@ -136,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object())
pybind11_fail("File \"" + fname_str + "\" could not be opened!"); pybind11_fail("File \"" + fname_str + "\" could not be opened!");
} }
// In Python2, this should be encoded by getfilesystemencoding.
// We don't boher setting it since Python2 is past EOL anyway.
// See PR#3233
#if PY_VERSION_HEX >= 0x03000000
if (!global.contains("__file__")) {
global["__file__"] = std::move(fname);
}
#endif
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) #if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
local.ptr()); local.ptr());
......
...@@ -69,6 +69,10 @@ public: ...@@ -69,6 +69,10 @@ public:
// ensure GIL is held during functor destruction // ensure GIL is held during functor destruction
struct func_handle { struct func_handle {
function f; function f;
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17) && PY_MAJOR_VERSION < 3)
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept : f(std::move(f_)) {} func_handle(function &&f_) noexcept : f(std::move(f_)) {}
func_handle(const func_handle &f_) { operator=(f_); } func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) { func_handle &operator=(const func_handle &f_) {
...@@ -85,7 +89,7 @@ public: ...@@ -85,7 +89,7 @@ public:
// to emulate 'move initialization capture' in C++11 // to emulate 'move initialization capture' in C++11
struct func_wrapper { struct func_wrapper {
func_handle hfunc; func_handle hfunc;
func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {} explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
Return operator()(Args... args) const { Return operator()(Args... args) const {
gil_scoped_acquire acq; gil_scoped_acquire acq;
object retval(hfunc.f(std::forward<Args>(args)...)); object retval(hfunc.f(std::forward<Args>(args)...));
......
...@@ -50,7 +50,7 @@ PYBIND11_NAMESPACE_END(detail) ...@@ -50,7 +50,7 @@ PYBIND11_NAMESPACE_END(detail)
class gil_scoped_acquire { class gil_scoped_acquire {
public: public:
PYBIND11_NOINLINE gil_scoped_acquire() { PYBIND11_NOINLINE gil_scoped_acquire() {
auto const &internals = detail::get_internals(); auto &internals = detail::get_internals();
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
if (!tstate) { if (!tstate) {
...@@ -132,7 +132,7 @@ public: ...@@ -132,7 +132,7 @@ public:
// `get_internals()` must be called here unconditionally in order to initialize // `get_internals()` must be called here unconditionally in order to initialize
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
// initialization race could occur as multiple threads try `gil_scoped_acquire`. // initialization race could occur as multiple threads try `gil_scoped_acquire`.
const auto &internals = detail::get_internals(); auto &internals = detail::get_internals();
tstate = PyEval_SaveThread(); tstate = PyEval_SaveThread();
if (disassoc) { if (disassoc) {
auto key = internals.tstate; auto key = internals.tstate;
......
...@@ -123,7 +123,7 @@ private: ...@@ -123,7 +123,7 @@ private:
} }
public: public:
pythonbuf(const object &pyostream, size_t buffer_size = 1024) explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
: buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")), : buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
pyflush(pyostream.attr("flush")) { pyflush(pyostream.attr("flush")) {
setp(d_buffer.get(), d_buffer.get() + buf_size - 1); setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
...@@ -171,8 +171,9 @@ protected: ...@@ -171,8 +171,9 @@ protected:
detail::pythonbuf buffer; detail::pythonbuf buffer;
public: public:
scoped_ostream_redirect(std::ostream &costream = std::cout, explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
const object &pyostream = module_::import("sys").attr("stdout")) const object &pyostream
= module_::import("sys").attr("stdout"))
: costream(costream), buffer(pyostream) { : costream(costream), buffer(pyostream) {
old = costream.rdbuf(&buffer); old = costream.rdbuf(&buffer);
} }
...@@ -201,8 +202,9 @@ public: ...@@ -201,8 +202,9 @@ public:
\endrst */ \endrst */
class scoped_estream_redirect : public scoped_ostream_redirect { class scoped_estream_redirect : public scoped_ostream_redirect {
public: public:
scoped_estream_redirect(std::ostream &costream = std::cerr, explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
const object &pyostream = module_::import("sys").attr("stderr")) const object &pyostream
= module_::import("sys").attr("stderr"))
: scoped_ostream_redirect(costream, pyostream) {} : scoped_ostream_redirect(costream, pyostream) {}
}; };
...@@ -217,7 +219,7 @@ class OstreamRedirect { ...@@ -217,7 +219,7 @@ class OstreamRedirect {
std::unique_ptr<scoped_estream_redirect> redirect_stderr; std::unique_ptr<scoped_estream_redirect> redirect_stderr;
public: public:
OstreamRedirect(bool do_stdout = true, bool do_stderr = true) explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
: do_stdout_(do_stdout), do_stderr_(do_stderr) {} : do_stdout_(do_stdout), do_stderr_(do_stderr) {}
void enter() { void enter() {
......
...@@ -25,11 +25,6 @@ ...@@ -25,11 +25,6 @@
#include <vector> #include <vector>
#include <typeindex> #include <typeindex>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
/* This will be true on all flat address space platforms and allows us to reduce the /* This will be true on all flat address space platforms and allows us to reduce the
whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
and dimension types (e.g. shape, strides, indexing), instead of inflicting this and dimension types (e.g. shape, strides, indexing), instead of inflicting this
...@@ -104,7 +99,7 @@ struct numpy_internals { ...@@ -104,7 +99,7 @@ struct numpy_internals {
} }
}; };
inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) {
ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals"); ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
} }
...@@ -203,6 +198,9 @@ struct npy_api { ...@@ -203,6 +198,9 @@ struct npy_api {
// Unused. Not removed because that affects ABI of the class. // Unused. Not removed because that affects ABI of the class.
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int);
PyObject* (*PyArray_Newshape_)(PyObject*, PyArray_Dims*, int);
PyObject* (*PyArray_View_)(PyObject*, PyObject*, PyObject*);
private: private:
enum functions { enum functions {
API_PyArray_GetNDArrayCFeatureVersion = 211, API_PyArray_GetNDArrayCFeatureVersion = 211,
...@@ -217,10 +215,12 @@ private: ...@@ -217,10 +215,12 @@ private:
API_PyArray_NewCopy = 85, API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94, API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 96, API_PyArray_DescrNewFromType = 96,
API_PyArray_Newshape = 135,
API_PyArray_Squeeze = 136,
API_PyArray_View = 137,
API_PyArray_DescrConverter = 174, API_PyArray_DescrConverter = 174,
API_PyArray_EquivTypes = 182, API_PyArray_EquivTypes = 182,
API_PyArray_GetArrayParamsFromObject = 278, API_PyArray_GetArrayParamsFromObject = 278,
API_PyArray_Squeeze = 136,
API_PyArray_SetBaseObject = 282 API_PyArray_SetBaseObject = 282
}; };
...@@ -248,11 +248,14 @@ private: ...@@ -248,11 +248,14 @@ private:
DECL_NPY_API(PyArray_NewCopy); DECL_NPY_API(PyArray_NewCopy);
DECL_NPY_API(PyArray_NewFromDescr); DECL_NPY_API(PyArray_NewFromDescr);
DECL_NPY_API(PyArray_DescrNewFromType); DECL_NPY_API(PyArray_DescrNewFromType);
DECL_NPY_API(PyArray_Newshape);
DECL_NPY_API(PyArray_Squeeze);
DECL_NPY_API(PyArray_View);
DECL_NPY_API(PyArray_DescrConverter); DECL_NPY_API(PyArray_DescrConverter);
DECL_NPY_API(PyArray_EquivTypes); DECL_NPY_API(PyArray_EquivTypes);
DECL_NPY_API(PyArray_GetArrayParamsFromObject); DECL_NPY_API(PyArray_GetArrayParamsFromObject);
DECL_NPY_API(PyArray_Squeeze);
DECL_NPY_API(PyArray_SetBaseObject); DECL_NPY_API(PyArray_SetBaseObject);
#undef DECL_NPY_API #undef DECL_NPY_API
return api; return api;
} }
...@@ -319,7 +322,7 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type; ...@@ -319,7 +322,7 @@ template <typename T> using remove_all_extents_t = typename array_info<T>::type;
template <typename T> using is_pod_struct = all_of< template <typename T> using is_pod_struct = all_of<
std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type
#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803) #if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
// libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5) // libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5)
// don't implement is_trivially_copyable, so approximate it // don't implement is_trivially_copyable, so approximate it
std::is_trivially_destructible<T>, std::is_trivially_destructible<T>,
...@@ -474,15 +477,15 @@ public: ...@@ -474,15 +477,15 @@ public:
m_ptr = from_args(pybind11::str(format)).release().ptr(); m_ptr = from_args(pybind11::str(format)).release().ptr();
} }
dtype(const char *format) : dtype(std::string(format)) { } explicit dtype(const char *format) : dtype(std::string(format)) {}
dtype(list names, list formats, list offsets, ssize_t itemsize) { dtype(list names, list formats, list offsets, ssize_t itemsize) {
dict args; dict args;
args["names"] = names; args["names"] = std::move(names);
args["formats"] = formats; args["formats"] = std::move(formats);
args["offsets"] = offsets; args["offsets"] = std::move(offsets);
args["itemsize"] = pybind11::int_(itemsize); args["itemsize"] = pybind11::int_(itemsize);
m_ptr = from_args(args).release().ptr(); m_ptr = from_args(std::move(args)).release().ptr();
} }
/// This is essentially the same as calling numpy.dtype(args) in Python. /// This is essentially the same as calling numpy.dtype(args) in Python.
...@@ -560,7 +563,7 @@ private: ...@@ -560,7 +563,7 @@ private:
formats.append(descr.format); formats.append(descr.format);
offsets.append(descr.offset); offsets.append(descr.offset);
} }
return dtype(names, formats, offsets, itemsize); return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
} }
}; };
...@@ -747,7 +750,7 @@ public: ...@@ -747,7 +750,7 @@ public:
* and the caller must take care not to access invalid dimensions or dimension indices. * and the caller must take care not to access invalid dimensions or dimension indices.
*/ */
template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & { template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
if (Dims >= 0 && ndim() != Dims) if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims)); "; expected " + std::to_string(Dims));
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim()); return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
...@@ -761,7 +764,7 @@ public: ...@@ -761,7 +764,7 @@ public:
* invalid dimensions or dimension indices. * invalid dimensions or dimension indices.
*/ */
template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & { template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & {
if (Dims >= 0 && ndim() != Dims) if (PYBIND11_SILENCE_MSVC_C4127(Dims >= 0) && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims)); "; expected " + std::to_string(Dims));
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim()); return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
...@@ -790,6 +793,33 @@ public: ...@@ -790,6 +793,33 @@ public:
if (isinstance<array>(new_array)) { *this = std::move(new_array); } if (isinstance<array>(new_array)) { *this = std::move(new_array); }
} }
/// Optional `order` parameter omitted, to be added as needed.
array reshape(ShapeContainer new_shape) {
detail::npy_api::PyArray_Dims d
= {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
auto new_array
= reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
if (!new_array) {
throw error_already_set();
}
return new_array;
}
/// Create a view of an array in a different data type.
/// This function may fundamentally reinterpret the data in the array.
/// It is the responsibility of the caller to ensure that this is safe.
/// Only supports the `dtype` argument, the `type` argument is omitted,
/// to be added as needed.
array view(const std::string &dtype) {
auto &api = detail::npy_api::get();
auto new_view = reinterpret_steal<array>(api.PyArray_View_(
m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
if (!new_view) {
throw error_already_set();
}
return new_view;
}
/// Ensure that the argument is a NumPy array /// Ensure that the argument is a NumPy array
/// In case of an error, nullptr is returned and the Python error is cleared. /// In case of an error, nullptr is returned and the Python error is cleared.
static array ensure(handle h, int ExtraFlags = 0) { static array ensure(handle h, int ExtraFlags = 0) {
...@@ -864,6 +894,7 @@ public: ...@@ -864,6 +894,7 @@ public:
if (!is_borrowed) Py_XDECREF(h.ptr()); if (!is_borrowed) Py_XDECREF(h.ptr());
} }
// NOLINTNEXTLINE(google-explicit-constructor)
array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
if (!m_ptr) throw error_already_set(); if (!m_ptr) throw error_already_set();
} }
...@@ -1110,7 +1141,7 @@ struct field_descriptor { ...@@ -1110,7 +1141,7 @@ struct field_descriptor {
dtype descr; dtype descr;
}; };
inline PYBIND11_NOINLINE void register_structured_dtype( PYBIND11_NOINLINE void register_structured_dtype(
any_container<field_descriptor> fields, any_container<field_descriptor> fields,
const std::type_info& tinfo, ssize_t itemsize, const std::type_info& tinfo, ssize_t itemsize,
bool (*direct_converter)(PyObject *, void *&)) { bool (*direct_converter)(PyObject *, void *&)) {
...@@ -1134,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype( ...@@ -1134,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
formats.append(field.descr); formats.append(field.descr);
offsets.append(pybind11::int_(field.offset)); offsets.append(pybind11::int_(field.offset));
} }
auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); auto dtype_ptr
= pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
.release()
.ptr();
// There is an existing bug in NumPy (as of v1.11): trailing bytes are // There is an existing bug in NumPy (as of v1.11): trailing bytes are
// not encoded explicitly into the format string. This will supposedly // not encoded explicitly into the format string. This will supposedly
...@@ -1551,8 +1585,11 @@ private: ...@@ -1551,8 +1585,11 @@ private:
"pybind11::vectorize(...) requires a function with at least one vectorizable argument"); "pybind11::vectorize(...) requires a function with at least one vectorizable argument");
public: public:
template <typename T> template <typename T,
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) { } // SFINAE to prevent shadowing the copy constructor.
typename = detail::enable_if_t<
!std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
object operator()(typename vectorize_arg<Args>::type... args) { object operator()(typename vectorize_arg<Args>::type... args) {
return run(args..., return run(args...,
...@@ -1702,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) { ...@@ -1702,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) {
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
...@@ -11,13 +11,6 @@ ...@@ -11,13 +11,6 @@
#include "pybind11.h" #include "pybind11.h"
#if defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type()))
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
...@@ -58,7 +51,8 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ { ...@@ -58,7 +51,8 @@ template <op_id id, op_type ot, typename L, typename R> struct op_ {
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute, is_operator(), extra...); cl.def(op::name(), &op::execute, is_operator(), extra...);
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (id == op_truediv || id == op_itruediv) if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) ||
PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv))
cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__",
&op::execute, is_operator(), extra...); &op::execute, is_operator(), extra...);
#endif #endif
...@@ -167,7 +161,3 @@ using detail::self; ...@@ -167,7 +161,3 @@ using detail::self;
using detail::hash; using detail::hash;
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
...@@ -10,22 +10,13 @@ ...@@ -10,22 +10,13 @@
#pragma once #pragma once
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma warning(push)
# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#elif defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
# pragma GCC diagnostic ignored "-Wattributes"
#endif
#include "attr.h" #include "attr.h"
#include "gil.h" #include "gil.h"
#include "options.h" #include "options.h"
#include "detail/class.h" #include "detail/class.h"
#include "detail/init.h" #include "detail/init.h"
#include <cstdlib>
#include <memory> #include <memory>
#include <new> #include <new>
#include <vector> #include <vector>
...@@ -59,20 +50,44 @@ ...@@ -59,20 +50,44 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// Apply all the extensions translators from a list
// Return true if one of the translators completed without raising an exception
// itself. Return of false indicates that if there are other translators
// available, they should be tried.
inline bool apply_exception_translators(std::forward_list<ExceptionTranslator>& translators) {
auto last_exception = std::current_exception();
for (auto &translator : translators) {
try {
translator(last_exception);
return true;
} catch (...) {
last_exception = std::current_exception();
}
}
return false;
}
#if defined(_MSC_VER) #if defined(_MSC_VER)
# define PYBIND11_COMPAT_STRDUP _strdup # define PYBIND11_COMPAT_STRDUP _strdup
#else #else
# define PYBIND11_COMPAT_STRDUP strdup # define PYBIND11_COMPAT_STRDUP strdup
#endif #endif
PYBIND11_NAMESPACE_END(detail)
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function { class cpp_function : public function {
public: public:
cpp_function() = default; cpp_function() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(std::nullptr_t) { } cpp_function(std::nullptr_t) { }
/// Construct a cpp_function from a vanilla function pointer /// Construct a cpp_function from a vanilla function pointer
template <typename Return, typename... Args, typename... Extra> template <typename Return, typename... Args, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (*f)(Args...), const Extra&... extra) { cpp_function(Return (*f)(Args...), const Extra&... extra) {
initialize(f, f, extra...); initialize(f, f, extra...);
} }
...@@ -80,6 +95,7 @@ public: ...@@ -80,6 +95,7 @@ public:
/// Construct a cpp_function from a lambda function (possibly with internal state) /// Construct a cpp_function from a lambda function (possibly with internal state)
template <typename Func, typename... Extra, template <typename Func, typename... Extra,
typename = detail::enable_if_t<detail::is_lambda<Func>::value>> typename = detail::enable_if_t<detail::is_lambda<Func>::value>>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Func &&f, const Extra&... extra) { cpp_function(Func &&f, const Extra&... extra) {
initialize(std::forward<Func>(f), initialize(std::forward<Func>(f),
(detail::function_signature_t<Func> *) nullptr, extra...); (detail::function_signature_t<Func> *) nullptr, extra...);
...@@ -87,6 +103,7 @@ public: ...@@ -87,6 +103,7 @@ public:
/// Construct a cpp_function from a class method (non-const, no ref-qualifier) /// Construct a cpp_function from a class method (non-const, no ref-qualifier)
template <typename Return, typename Class, typename... Arg, typename... Extra> template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(std::forward<Arg>(args)...); }, initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(std::forward<Arg>(args)...); },
(Return (*) (Class *, Arg...)) nullptr, extra...); (Return (*) (Class *, Arg...)) nullptr, extra...);
...@@ -96,6 +113,7 @@ public: ...@@ -96,6 +113,7 @@ public:
/// A copy of the overload for non-const functions without explicit ref-qualifier /// A copy of the overload for non-const functions without explicit ref-qualifier
/// but with an added `&`. /// but with an added `&`.
template <typename Return, typename Class, typename... Arg, typename... Extra> template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) { cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...)) nullptr, extra...); (Return (*) (Class *, Arg...)) nullptr, extra...);
...@@ -103,6 +121,7 @@ public: ...@@ -103,6 +121,7 @@ public:
/// Construct a cpp_function from a class method (const, no ref-qualifier) /// Construct a cpp_function from a class method (const, no ref-qualifier)
template <typename Return, typename Class, typename... Arg, typename... Extra> template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(std::forward<Arg>(args)...); }, initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(std::forward<Arg>(args)...); },
(Return (*)(const Class *, Arg ...)) nullptr, extra...); (Return (*)(const Class *, Arg ...)) nullptr, extra...);
...@@ -112,6 +131,7 @@ public: ...@@ -112,6 +131,7 @@ public:
/// A copy of the overload for const functions without explicit ref-qualifier /// A copy of the overload for const functions without explicit ref-qualifier
/// but with an added `&`. /// but with an added `&`.
template <typename Return, typename Class, typename... Arg, typename... Extra> template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) { cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*)(const Class *, Arg ...)) nullptr, extra...); (Return (*)(const Class *, Arg ...)) nullptr, extra...);
...@@ -145,7 +165,7 @@ protected: ...@@ -145,7 +165,7 @@ protected:
auto rec = unique_rec.get(); auto rec = unique_rec.get();
/* Store the capture object directly in the function record if there is enough space */ /* Store the capture object directly in the function record if there is enough space */
if (sizeof(capture) <= sizeof(rec->data)) { if (PYBIND11_SILENCE_MSVC_C4127(sizeof(capture) <= sizeof(rec->data))) {
/* Without these pragmas, GCC warns that there might not be /* Without these pragmas, GCC warns that there might not be
enough space to use the placement new operator. However, the enough space to use the placement new operator. However, the
'if' statement above ensures that this is the case. */ 'if' statement above ensures that this is the case. */
...@@ -163,7 +183,7 @@ protected: ...@@ -163,7 +183,7 @@ protected:
#endif #endif
// UB without std::launder, but without breaking ABI and/or // UB without std::launder, but without breaking ABI and/or
// a significant refactoring it's "impossible" to solve. // a significant refactoring it's "impossible" to solve.
if (!std::is_trivially_destructible<Func>::value) if (!std::is_trivially_destructible<capture>::value)
rec->free_data = [](function_record *r) { rec->free_data = [](function_record *r) {
auto data = PYBIND11_STD_LAUNDER((capture *) &r->data); auto data = PYBIND11_STD_LAUNDER((capture *) &r->data);
(void) data; (void) data;
...@@ -397,7 +417,8 @@ protected: ...@@ -397,7 +417,8 @@ protected:
detail::function_record *chain = nullptr, *chain_start = rec; detail::function_record *chain = nullptr, *chain_start = rec;
if (rec->sibling) { if (rec->sibling) {
if (PyCFunction_Check(rec->sibling.ptr())) { if (PyCFunction_Check(rec->sibling.ptr())) {
auto rec_capsule = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(rec->sibling.ptr())); auto *self = PyCFunction_GET_SELF(rec->sibling.ptr());
capsule rec_capsule = isinstance<capsule>(self) ? reinterpret_borrow<capsule>(self) : capsule(self);
chain = (detail::function_record *) rec_capsule; chain = (detail::function_record *) rec_capsule;
/* Never append a method to an overload chain of a parent class; /* Never append a method to an overload chain of a parent class;
instead, hide the parent's overloads in this case */ instead, hide the parent's overloads in this case */
...@@ -561,6 +582,7 @@ protected: ...@@ -561,6 +582,7 @@ protected:
} }
} }
/// Main dispatch logic for calls to functions bound using pybind11 /// Main dispatch logic for calls to functions bound using pybind11
static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) {
using namespace detail; using namespace detail;
...@@ -841,8 +863,12 @@ protected: ...@@ -841,8 +863,12 @@ protected:
#endif #endif
} catch (...) { } catch (...) {
/* When an exception is caught, give each registered exception /* When an exception is caught, give each registered exception
translator a chance to translate it to a Python exception translator a chance to translate it to a Python exception. First
in reverse order of registration. all module-local translators will be tried in reverse order of
registration. If none of the module-locale translators handle
the exception (or there are no module-locale translators) then
the global translators will be tried, also in reverse order of
registration.
A translator may choose to do one of the following: A translator may choose to do one of the following:
...@@ -851,17 +877,15 @@ protected: ...@@ -851,17 +877,15 @@ protected:
- do nothing and let the exception fall through to the next translator, or - do nothing and let the exception fall through to the next translator, or
- delegate translation to the next translator by throwing a new type of exception. */ - delegate translation to the next translator by throwing a new type of exception. */
auto last_exception = std::current_exception(); auto &local_exception_translators = get_local_internals().registered_exception_translators;
auto &registered_exception_translators = get_internals().registered_exception_translators; if (detail::apply_exception_translators(local_exception_translators)) {
for (auto& translator : registered_exception_translators) {
try {
translator(last_exception);
} catch (...) {
last_exception = std::current_exception();
continue;
}
return nullptr; return nullptr;
} }
auto &exception_translators = get_internals().registered_exception_translators;
if (detail::apply_exception_translators(exception_translators)) {
return nullptr;
}
PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!");
return nullptr; return nullptr;
} }
...@@ -962,6 +986,7 @@ protected: ...@@ -962,6 +986,7 @@ protected:
} }
}; };
/// Wrapper for Python extension modules /// Wrapper for Python extension modules
class module_ : public object { class module_ : public object {
public: public:
...@@ -1136,7 +1161,7 @@ protected: ...@@ -1136,7 +1161,7 @@ protected:
auto tindex = std::type_index(*rec.type); auto tindex = std::type_index(*rec.type);
tinfo->direct_conversions = &internals.direct_conversions[tindex]; tinfo->direct_conversions = &internals.direct_conversions[tindex];
if (rec.module_local) if (rec.module_local)
registered_local_types_cpp()[tindex] = tinfo; get_local_internals().registered_types_cpp[tindex] = tinfo;
else else
internals.registered_types_cpp[tindex] = tinfo; internals.registered_types_cpp[tindex] = tinfo;
internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo };
...@@ -1323,7 +1348,7 @@ public: ...@@ -1323,7 +1348,7 @@ public:
generic_type::initialize(record); generic_type::initialize(record);
if (has_alias) { if (has_alias) {
auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; auto &instances = record.module_local ? get_local_internals().registered_types_cpp : get_internals().registered_types_cpp;
instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))];
} }
} }
...@@ -1370,12 +1395,14 @@ public: ...@@ -1370,12 +1395,14 @@ public:
template <typename... Args, typename... Extra> template <typename... Args, typename... Extra>
class_ &def(const detail::initimpl::constructor<Args...> &init, const Extra&... extra) { class_ &def(const detail::initimpl::constructor<Args...> &init, const Extra&... extra) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(init);
init.execute(*this, extra...); init.execute(*this, extra...);
return *this; return *this;
} }
template <typename... Args, typename... Extra> template <typename... Args, typename... Extra>
class_ &def(const detail::initimpl::alias_constructor<Args...> &init, const Extra&... extra) { class_ &def(const detail::initimpl::alias_constructor<Args...> &init, const Extra&... extra) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(init);
init.execute(*this, extra...); init.execute(*this, extra...);
return *this; return *this;
} }
...@@ -1510,7 +1537,7 @@ public: ...@@ -1510,7 +1537,7 @@ public:
char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
detail::process_attributes<Extra...>::init(extra..., rec_fget); detail::process_attributes<Extra...>::init(extra..., rec_fget);
if (rec_fget->doc && rec_fget->doc != doc_prev) { if (rec_fget->doc && rec_fget->doc != doc_prev) {
free(doc_prev); std::free(doc_prev);
rec_fget->doc = PYBIND11_COMPAT_STRDUP(rec_fget->doc); rec_fget->doc = PYBIND11_COMPAT_STRDUP(rec_fget->doc);
} }
} }
...@@ -1518,7 +1545,7 @@ public: ...@@ -1518,7 +1545,7 @@ public:
char *doc_prev = rec_fset->doc; char *doc_prev = rec_fset->doc;
detail::process_attributes<Extra...>::init(extra..., rec_fset); detail::process_attributes<Extra...>::init(extra..., rec_fset);
if (rec_fset->doc && rec_fset->doc != doc_prev) { if (rec_fset->doc && rec_fset->doc != doc_prev) {
free(doc_prev); std::free(doc_prev);
rec_fset->doc = PYBIND11_COMPAT_STRDUP(rec_fset->doc); rec_fset->doc = PYBIND11_COMPAT_STRDUP(rec_fset->doc);
} }
if (! rec_active) rec_active = rec_fset; if (! rec_active) rec_active = rec_fset;
...@@ -1646,7 +1673,7 @@ inline str enum_name(handle arg) { ...@@ -1646,7 +1673,7 @@ inline str enum_name(handle arg) {
} }
struct enum_base { struct enum_base {
enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } enum_base(const handle &base, const handle &parent) : m_base(base), m_parent(parent) { }
PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) {
m_base.attr("__entries") = dict(); m_base.attr("__entries") = dict();
...@@ -1702,7 +1729,7 @@ struct enum_base { ...@@ -1702,7 +1729,7 @@ struct enum_base {
m_base.attr(op) = cpp_function( \ m_base.attr(op) = cpp_function( \
[](const object &a, const object &b) { \ [](const object &a, const object &b) { \
if (!type::handle_of(a).is(type::handle_of(b))) \ if (!type::handle_of(a).is(type::handle_of(b))) \
strict_behavior; \ strict_behavior; /* NOLINT(bugprone-macro-parentheses) */ \
return expr; \ return expr; \
}, \ }, \
name(op), \ name(op), \
...@@ -1796,6 +1823,19 @@ struct enum_base { ...@@ -1796,6 +1823,19 @@ struct enum_base {
handle m_parent; handle m_parent;
}; };
template <bool is_signed, size_t length> struct equivalent_integer {};
template <> struct equivalent_integer<true, 1> { using type = int8_t; };
template <> struct equivalent_integer<false, 1> { using type = uint8_t; };
template <> struct equivalent_integer<true, 2> { using type = int16_t; };
template <> struct equivalent_integer<false, 2> { using type = uint16_t; };
template <> struct equivalent_integer<true, 4> { using type = int32_t; };
template <> struct equivalent_integer<false, 4> { using type = uint32_t; };
template <> struct equivalent_integer<true, 8> { using type = int64_t; };
template <> struct equivalent_integer<false, 8> { using type = uint64_t; };
template <typename IntLike>
using equivalent_integer_t = typename equivalent_integer<std::is_signed<IntLike>::value, sizeof(IntLike)>::type;
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
/// Binds C++ enumerations and enumeration classes to Python /// Binds C++ enumerations and enumeration classes to Python
...@@ -1806,13 +1846,17 @@ public: ...@@ -1806,13 +1846,17 @@ public:
using Base::attr; using Base::attr;
using Base::def_property_readonly; using Base::def_property_readonly;
using Base::def_property_readonly_static; using Base::def_property_readonly_static;
using Scalar = typename std::underlying_type<Type>::type; using Underlying = typename std::underlying_type<Type>::type;
// Scalar is the integer representation of underlying type
using Scalar = detail::conditional_t<detail::any_of<
detail::is_std_char_type<Underlying>, std::is_same<Underlying, bool>
>::value, detail::equivalent_integer_t<Underlying>, Underlying>;
template <typename... Extra> template <typename... Extra>
enum_(const handle &scope, const char *name, const Extra&... extra) enum_(const handle &scope, const char *name, const Extra&... extra)
: class_<Type>(scope, name, extra...), m_base(*this, scope) { : class_<Type>(scope, name, extra...), m_base(*this, scope) {
constexpr bool is_arithmetic = detail::any_of<std::is_same<arithmetic, Extra>...>::value; constexpr bool is_arithmetic = detail::any_of<std::is_same<arithmetic, Extra>...>::value;
constexpr bool is_convertible = std::is_convertible<Type, Scalar>::value; constexpr bool is_convertible = std::is_convertible<Type, Underlying>::value;
m_base.init(is_arithmetic, is_convertible); m_base.init(is_arithmetic, is_convertible);
def(init([](Scalar i) { return static_cast<Type>(i); }), arg("value")); def(init([](Scalar i) { return static_cast<Type>(i); }), arg("value"));
...@@ -1852,7 +1896,7 @@ private: ...@@ -1852,7 +1896,7 @@ private:
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
inline void keep_alive_impl(handle nurse, handle patient) { PYBIND11_NOINLINE void keep_alive_impl(handle nurse, handle patient) {
if (!nurse || !patient) if (!nurse || !patient)
pybind11_fail("Could not activate keep_alive!"); pybind11_fail("Could not activate keep_alive!");
...@@ -1879,7 +1923,7 @@ inline void keep_alive_impl(handle nurse, handle patient) { ...@@ -1879,7 +1923,7 @@ inline void keep_alive_impl(handle nurse, handle patient) {
} }
} }
PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { PYBIND11_NOINLINE void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) {
auto get_arg = [&](size_t n) { auto get_arg = [&](size_t n) {
if (n == 0) if (n == 0)
return ret; return ret;
...@@ -1912,25 +1956,54 @@ inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_t ...@@ -1912,25 +1956,54 @@ inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_t
return res; return res;
} }
template <typename Iterator, typename Sentinel, bool KeyIterator, return_value_policy Policy> /* There are a large number of apparently unused template arguments because
* each combination requires a separate py::class_ registration.
*/
template <typename Access, return_value_policy Policy, typename Iterator, typename Sentinel, typename ValueType, typename... Extra>
struct iterator_state { struct iterator_state {
Iterator it; Iterator it;
Sentinel end; Sentinel end;
bool first_or_done; bool first_or_done;
}; };
PYBIND11_NAMESPACE_END(detail) // Note: these helpers take the iterator by non-const reference because some
// iterators in the wild can't be dereferenced when const. C++ needs the extra parens in decltype
// to enforce an lvalue. The & after Iterator is required for MSVC < 16.9. SFINAE cannot be
// reused for result_type due to bugs in ICC, NVCC, and PGI compilers. See PR #3293.
template <typename Iterator, typename SFINAE = decltype((*std::declval<Iterator &>()))>
struct iterator_access {
using result_type = decltype((*std::declval<Iterator &>()));
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
result_type operator()(Iterator &it) const {
return *it;
}
};
/// Makes a python iterator from a first and past-the-end C++ InputIterator. template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).first)) >
template <return_value_policy Policy = return_value_policy::reference_internal, struct iterator_key_access {
using result_type = decltype(((*std::declval<Iterator &>()).first));
result_type operator()(Iterator &it) const {
return (*it).first;
}
};
template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).second))>
struct iterator_value_access {
using result_type = decltype(((*std::declval<Iterator &>()).second));
result_type operator()(Iterator &it) const {
return (*it).second;
}
};
template <typename Access,
return_value_policy Policy,
typename Iterator, typename Iterator,
typename Sentinel, typename Sentinel,
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Issue in breathe 4.26.1 typename ValueType,
typename ValueType = decltype(*std::declval<Iterator>()),
#endif
typename... Extra> typename... Extra>
iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&... extra) {
using state = detail::iterator_state<Iterator, Sentinel, false, Policy>; using state = detail::iterator_state<Access, Policy, Iterator, Sentinel, ValueType, Extra...>;
// TODO: state captures only the types of Extra, not the values
if (!detail::get_type_info(typeid(state), false)) { if (!detail::get_type_info(typeid(state), false)) {
class_<state>(handle(), "iterator", pybind11::module_local()) class_<state>(handle(), "iterator", pybind11::module_local())
...@@ -1944,42 +2017,63 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { ...@@ -1944,42 +2017,63 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
s.first_or_done = true; s.first_or_done = true;
throw stop_iteration(); throw stop_iteration();
} }
return *s.it; return Access()(s.it);
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
}, std::forward<Extra>(extra)..., Policy); }, std::forward<Extra>(extra)..., Policy);
} }
return cast(state{first, last, true}); return cast(state{first, last, true});
} }
/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a PYBIND11_NAMESPACE_END(detail)
/// first and past-the-end InputIterator.
/// Makes a python iterator from a first and past-the-end C++ InputIterator.
template <return_value_policy Policy = return_value_policy::reference_internal, template <return_value_policy Policy = return_value_policy::reference_internal,
typename Iterator, typename Iterator,
typename Sentinel, typename Sentinel,
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Issue in breathe 4.26.1 typename ValueType = typename detail::iterator_access<Iterator>::result_type,
typename KeyType = decltype((*std::declval<Iterator>()).first),
#endif
typename... Extra> typename... Extra>
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
using state = detail::iterator_state<Iterator, Sentinel, true, Policy>; return detail::make_iterator_impl<
detail::iterator_access<Iterator>,
Policy,
Iterator,
Sentinel,
ValueType,
Extra...>(first, last, std::forward<Extra>(extra)...);
}
if (!detail::get_type_info(typeid(state), false)) { /// Makes a python iterator over the keys (`.first`) of a iterator over pairs from a
class_<state>(handle(), "iterator", pybind11::module_local()) /// first and past-the-end InputIterator.
.def("__iter__", [](state &s) -> state& { return s; }) template <return_value_policy Policy = return_value_policy::reference_internal,
.def("__next__", [](state &s) -> KeyType { typename Iterator,
if (!s.first_or_done) typename Sentinel,
++s.it; typename KeyType = typename detail::iterator_key_access<Iterator>::result_type,
else typename... Extra>
s.first_or_done = false; iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
if (s.it == s.end) { return detail::make_iterator_impl<
s.first_or_done = true; detail::iterator_key_access<Iterator>,
throw stop_iteration(); Policy,
} Iterator,
return (*s.it).first; Sentinel,
}, std::forward<Extra>(extra)..., Policy); KeyType,
} Extra...>(first, last, std::forward<Extra>(extra)...);
}
return cast(state{first, last, true}); /// Makes a python iterator over the values (`.second`) of a iterator over pairs from a
/// first and past-the-end InputIterator.
template <return_value_policy Policy = return_value_policy::reference_internal,
typename Iterator,
typename Sentinel,
typename ValueType = typename detail::iterator_value_access<Iterator>::result_type,
typename... Extra>
iterator make_value_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<
detail::iterator_value_access<Iterator>,
Policy, Iterator,
Sentinel,
ValueType,
Extra...>(first, last, std::forward<Extra>(extra)...);
} }
/// Makes an iterator over values of an stl container or other container supporting /// Makes an iterator over values of an stl container or other container supporting
...@@ -1996,10 +2090,17 @@ template <return_value_policy Policy = return_value_policy::reference_internal, ...@@ -1996,10 +2090,17 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
return make_key_iterator<Policy>(std::begin(value), std::end(value), extra...); return make_key_iterator<Policy>(std::begin(value), std::end(value), extra...);
} }
/// Makes an iterator over the values (`.second`) of a stl map-like container supporting
/// `std::begin()`/`std::end()`
template <return_value_policy Policy = return_value_policy::reference_internal,
typename Type, typename... Extra> iterator make_value_iterator(Type &value, Extra&&... extra) {
return make_value_iterator<Policy>(std::begin(value), std::end(value), extra...);
}
template <typename InputType, typename OutputType> void implicitly_convertible() { template <typename InputType, typename OutputType> void implicitly_convertible() {
struct set_flag { struct set_flag {
bool &flag; bool &flag;
set_flag(bool &flag_) : flag(flag_) { flag_ = true; } explicit set_flag(bool &flag_) : flag(flag_) { flag_ = true; }
~set_flag() { flag = false; } ~set_flag() { flag = false; }
}; };
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
...@@ -2023,12 +2124,24 @@ template <typename InputType, typename OutputType> void implicitly_convertible() ...@@ -2023,12 +2124,24 @@ template <typename InputType, typename OutputType> void implicitly_convertible()
pybind11_fail("implicitly_convertible: Unable to find type " + type_id<OutputType>()); pybind11_fail("implicitly_convertible: Unable to find type " + type_id<OutputType>());
} }
template <typename ExceptionTranslator>
void register_exception_translator(ExceptionTranslator&& translator) { inline void register_exception_translator(ExceptionTranslator &&translator) {
detail::get_internals().registered_exception_translators.push_front( detail::get_internals().registered_exception_translators.push_front(
std::forward<ExceptionTranslator>(translator)); std::forward<ExceptionTranslator>(translator));
} }
/**
* Add a new module-local exception translator. Locally registered functions
* will be tried before any globally registered exception translators, which
* will only be invoked if the module-local handlers do not deal with
* the exception.
*/
inline void register_local_exception_translator(ExceptionTranslator &&translator) {
detail::get_local_internals().registered_exception_translators.push_front(
std::forward<ExceptionTranslator>(translator));
}
/** /**
* Wrapper to generate a new Python exception type. * Wrapper to generate a new Python exception type.
* *
...@@ -2062,22 +2175,20 @@ PYBIND11_NAMESPACE_BEGIN(detail) ...@@ -2062,22 +2175,20 @@ PYBIND11_NAMESPACE_BEGIN(detail)
// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). // directly in register_exception, but that makes clang <3.5 segfault - issue #1349).
template <typename CppException> template <typename CppException>
exception<CppException> &get_exception_object() { static exception<CppException> ex; return ex; } exception<CppException> &get_exception_object() { static exception<CppException> ex; return ex; }
PYBIND11_NAMESPACE_END(detail)
/** // Helper function for register_exception and register_local_exception
* 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.
* This is intended for simple exception translations; for more complex translation, register the
* exception object and translator directly.
*/
template <typename CppException> template <typename CppException>
exception<CppException> &register_exception(handle scope, exception<CppException> &register_exception_impl(handle scope,
const char *name, const char *name,
handle base = PyExc_Exception) { handle base,
bool isLocal) {
auto &ex = detail::get_exception_object<CppException>(); auto &ex = detail::get_exception_object<CppException>();
if (!ex) ex = exception<CppException>(scope, name, base); if (!ex) ex = exception<CppException>(scope, name, base);
register_exception_translator([](std::exception_ptr p) { auto register_func = isLocal ? &register_local_exception_translator
: &register_exception_translator;
register_func([](std::exception_ptr p) {
if (!p) return; if (!p) return;
try { try {
std::rethrow_exception(p); std::rethrow_exception(p);
...@@ -2088,8 +2199,38 @@ exception<CppException> &register_exception(handle scope, ...@@ -2088,8 +2199,38 @@ exception<CppException> &register_exception(handle scope,
return ex; return ex;
} }
PYBIND11_NAMESPACE_END(detail)
/**
* Registers a Python exception in `m` of the given `name` and installs a translator to
* translate the C++ exception to the created Python exception using the what() method.
* This is intended for simple exception translations; for more complex translation, register the
* exception object and translator directly.
*/
template <typename CppException>
exception<CppException> &register_exception(handle scope,
const char *name,
handle base = PyExc_Exception) {
return detail::register_exception_impl<CppException>(scope, name, base, false /* isLocal */);
}
/**
* Registers a Python exception in `m` of the given `name` and installs a translator to
* translate the C++ exception to the created Python exception using the what() method.
* This translator will only be used for exceptions that are thrown in this module and will be
* tried before global exception translators, including those registered with register_exception.
* This is intended for simple exception translations; for more complex translation, register the
* exception object and translator directly.
*/
template <typename CppException>
exception<CppException> &register_local_exception(handle scope,
const char *name,
handle base = PyExc_Exception) {
return detail::register_exception_impl<CppException>(scope, name, base, true /* isLocal */);
}
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
PYBIND11_NOINLINE inline void print(const tuple &args, const dict &kwargs) { PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
auto strings = tuple(args.size()); auto strings = tuple(args.size());
for (size_t i = 0; i < args.size(); ++i) { for (size_t i = 0; i < args.size(); ++i) {
strings[i] = str(args[i]); strings[i] = str(args[i]);
...@@ -2320,9 +2461,3 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) ...@@ -2320,9 +2461,3 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUC__) && __GNUC__ == 7 #if defined(__GNUC__) && __GNUC__ == 7
# pragma GCC diagnostic pop // -Wnoexcept-type # pragma GCC diagnostic pop // -Wnoexcept-type
#endif #endif
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma warning(pop)
#elif defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#if defined(PYBIND11_HAS_OPTIONAL)
# include <optional>
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/* A few forward declarations */ /* A few forward declarations */
...@@ -24,7 +28,7 @@ struct arg; struct arg_v; ...@@ -24,7 +28,7 @@ struct arg; struct arg_v;
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
class args_proxy; class args_proxy;
inline bool isinstance_generic(handle obj, const std::type_info &tp); bool isinstance_generic(handle obj, const std::type_info &tp);
// Accessor forward declarations // Accessor forward declarations
template <typename Policy> class accessor; template <typename Policy> class accessor;
...@@ -178,6 +182,7 @@ public: ...@@ -178,6 +182,7 @@ public:
/// The default constructor creates a handle with a ``nullptr``-valued pointer /// The default constructor creates a handle with a ``nullptr``-valued pointer
handle() = default; handle() = default;
/// Creates a ``handle`` from the given raw Python object pointer /// Creates a ``handle`` from the given raw Python object pointer
// NOLINTNEXTLINE(google-explicit-constructor)
handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject*
/// Return the underlying ``PyObject *`` pointer /// Return the underlying ``PyObject *`` pointer
...@@ -254,8 +259,11 @@ public: ...@@ -254,8 +259,11 @@ public:
object& operator=(const object &other) { object& operator=(const object &other) {
other.inc_ref(); other.inc_ref();
dec_ref(); // Use temporary variable to ensure `*this` remains valid while
// `Py_XDECREF` executes, in case `*this` is accessible from Python.
handle temp(m_ptr);
m_ptr = other.m_ptr; m_ptr = other.m_ptr;
temp.dec_ref();
return *this; return *this;
} }
...@@ -316,7 +324,7 @@ template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrow ...@@ -316,7 +324,7 @@ template <typename T> T reinterpret_borrow(handle h) { return {h, object::borrow
template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } template <typename T> T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; }
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
inline std::string error_string(); std::string error_string();
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
#if defined(_MSC_VER) #if defined(_MSC_VER)
...@@ -382,6 +390,47 @@ private: ...@@ -382,6 +390,47 @@ private:
# pragma warning(pop) # pragma warning(pop)
#endif #endif
#if PY_VERSION_HEX >= 0x03030000
/// Replaces the current Python error indicator with the chosen error, performing a
/// 'raise from' to indicate that the chosen error was caused by the original error.
inline void raise_from(PyObject *type, const char *message) {
// Based on _PyErr_FormatVFromCause:
// https://github.com/python/cpython/blob/467ab194fc6189d9f7310c89937c51abeac56839/Python/errors.c#L405
// See https://github.com/pybind/pybind11/pull/2112 for details.
PyObject *exc = nullptr, *val = nullptr, *val2 = nullptr, *tb = nullptr;
assert(PyErr_Occurred());
PyErr_Fetch(&exc, &val, &tb);
PyErr_NormalizeException(&exc, &val, &tb);
if (tb != nullptr) {
PyException_SetTraceback(val, tb);
Py_DECREF(tb);
}
Py_DECREF(exc);
assert(!PyErr_Occurred());
PyErr_SetString(type, message);
PyErr_Fetch(&exc, &val2, &tb);
PyErr_NormalizeException(&exc, &val2, &tb);
Py_INCREF(val);
PyException_SetCause(val2, val);
PyException_SetContext(val2, val);
PyErr_Restore(exc, val2, tb);
}
/// Sets the current Python error indicator with the chosen error, performing a 'raise from'
/// from the error contained in error_already_set to indicate that the chosen error was
/// caused by the original error. After this function is called error_already_set will
/// no longer contain an error.
inline void raise_from(error_already_set& err, PyObject *type, const char *message) {
err.restore();
raise_from(type, message);
}
#endif
/** \defgroup python_builtins _ /** \defgroup python_builtins _
Unless stated otherwise, the following C++ functions behave the same Unless stated otherwise, the following C++ functions behave the same
as their Python counterparts. as their Python counterparts.
...@@ -571,6 +620,7 @@ public: ...@@ -571,6 +620,7 @@ public:
return obj.contains(key); return obj.contains(key);
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator object() const { return get_cache(); } operator object() const { return get_cache(); }
PyObject *ptr() const { return get_cache().ptr(); } PyObject *ptr() const { return get_cache().ptr(); }
template <typename T> T cast() const { return get_cache().template cast<T>(); } template <typename T> T cast() const { return get_cache().template cast<T>(); }
...@@ -620,15 +670,17 @@ struct generic_item { ...@@ -620,15 +670,17 @@ struct generic_item {
struct sequence_item { struct sequence_item {
using key_type = size_t; using key_type = size_t;
static object get(handle obj, size_t index) { template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index)); static object get(handle obj, const IdxType &index) {
PyObject *result = PySequence_GetItem(obj.ptr(), ssize_t_cast(index));
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return reinterpret_steal<object>(result); return reinterpret_steal<object>(result);
} }
static void set(handle obj, size_t index, handle val) { template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
static void set(handle obj, const IdxType &index, handle val) {
// PySequence_SetItem does not steal a reference to 'val' // PySequence_SetItem does not steal a reference to 'val'
if (PySequence_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.ptr()) != 0) { if (PySequence_SetItem(obj.ptr(), ssize_t_cast(index), val.ptr()) != 0) {
throw error_already_set(); throw error_already_set();
} }
} }
...@@ -637,15 +689,17 @@ struct sequence_item { ...@@ -637,15 +689,17 @@ struct sequence_item {
struct list_item { struct list_item {
using key_type = size_t; using key_type = size_t;
static object get(handle obj, size_t index) { template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index)); static object get(handle obj, const IdxType &index) {
PyObject *result = PyList_GetItem(obj.ptr(), ssize_t_cast(index));
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return reinterpret_borrow<object>(result); return reinterpret_borrow<object>(result);
} }
static void set(handle obj, size_t index, handle val) { template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
static void set(handle obj, const IdxType &index, handle val) {
// PyList_SetItem steals a reference to 'val' // PyList_SetItem steals a reference to 'val'
if (PyList_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) { if (PyList_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
throw error_already_set(); throw error_already_set();
} }
} }
...@@ -654,15 +708,17 @@ struct list_item { ...@@ -654,15 +708,17 @@ struct list_item {
struct tuple_item { struct tuple_item {
using key_type = size_t; using key_type = size_t;
static object get(handle obj, size_t index) { template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index)); static object get(handle obj, const IdxType &index) {
PyObject *result = PyTuple_GetItem(obj.ptr(), ssize_t_cast(index));
if (!result) { throw error_already_set(); } if (!result) { throw error_already_set(); }
return reinterpret_borrow<object>(result); return reinterpret_borrow<object>(result);
} }
static void set(handle obj, size_t index, handle val) { template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
static void set(handle obj, const IdxType &index, handle val) {
// PyTuple_SetItem steals a reference to 'val' // PyTuple_SetItem steals a reference to 'val'
if (PyTuple_SetItem(obj.ptr(), static_cast<ssize_t>(index), val.inc_ref().ptr()) != 0) { if (PyTuple_SetItem(obj.ptr(), ssize_t_cast(index), val.inc_ref().ptr()) != 0) {
throw error_already_set(); throw error_already_set();
} }
} }
...@@ -684,7 +740,9 @@ public: ...@@ -684,7 +740,9 @@ public:
generic_iterator() = default; generic_iterator() = default;
generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator*() const { return Policy::dereference(); } reference operator*() const { return Policy::dereference(); }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator[](difference_type n) const { return *(*this + n); } reference operator[](difference_type n) const { return *(*this + n); }
pointer operator->() const { return **this; } pointer operator->() const { return **this; }
...@@ -714,7 +772,8 @@ template <typename T> ...@@ -714,7 +772,8 @@ template <typename T>
struct arrow_proxy { struct arrow_proxy {
T value; T value;
arrow_proxy(T &&value) : value(std::move(value)) { } // NOLINTNEXTLINE(google-explicit-constructor)
arrow_proxy(T &&value) noexcept : value(std::move(value)) { }
T *operator->() const { return &value; } T *operator->() const { return &value; }
}; };
...@@ -723,11 +782,12 @@ class sequence_fast_readonly { ...@@ -723,11 +782,12 @@ class sequence_fast_readonly {
protected: protected:
using iterator_category = std::random_access_iterator_tag; using iterator_category = std::random_access_iterator_tag;
using value_type = handle; using value_type = handle;
using reference = const handle; using reference = const handle; // PR #3263
using pointer = arrow_proxy<const handle>; using pointer = arrow_proxy<const handle>;
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference dereference() const { return *ptr; } reference dereference() const { return *ptr; }
void increment() { ++ptr; } void increment() { ++ptr; }
void decrement() { --ptr; } void decrement() { --ptr; }
...@@ -766,12 +826,13 @@ class dict_readonly { ...@@ -766,12 +826,13 @@ class dict_readonly {
protected: protected:
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using value_type = std::pair<handle, handle>; using value_type = std::pair<handle, handle>;
using reference = const value_type; using reference = const value_type; // PR #3263
using pointer = arrow_proxy<const value_type>; using pointer = arrow_proxy<const value_type>;
dict_readonly() = default; dict_readonly() = default;
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference dereference() const { return {key, value}; } reference dereference() const { return {key, value}; }
void increment() { void increment() {
if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) { if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) {
...@@ -862,14 +923,17 @@ PYBIND11_NAMESPACE_END(detail) ...@@ -862,14 +923,17 @@ PYBIND11_NAMESPACE_END(detail)
bool check() const { return m_ptr != nullptr && (CheckFun(m_ptr) != 0); } \ bool check() const { return m_ptr != nullptr && (CheckFun(m_ptr) != 0); } \
static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \ static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \
template <typename Policy_> \ template <typename Policy_> \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { } Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { }
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ #define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(const object &o) \ Name(const object &o) \
: Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
{ if (!m_ptr) throw error_already_set(); } \ { if (!m_ptr) throw error_already_set(); } \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(object &&o) \ Name(object &&o) \
: Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
{ if (!m_ptr) throw error_already_set(); } { if (!m_ptr) throw error_already_set(); }
...@@ -886,8 +950,10 @@ PYBIND11_NAMESPACE_END(detail) ...@@ -886,8 +950,10 @@ PYBIND11_NAMESPACE_END(detail)
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ #define PYBIND11_OBJECT(Name, Parent, CheckFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(const object &o) : Parent(o) \ Name(const object &o) : Parent(o) \
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } \ { if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(object &&o) : Parent(std::move(o)) \ Name(object &&o) : Parent(std::move(o)) \
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); } { if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); }
...@@ -911,7 +977,7 @@ public: ...@@ -911,7 +977,7 @@ public:
using iterator_category = std::input_iterator_tag; using iterator_category = std::input_iterator_tag;
using difference_type = ssize_t; using difference_type = ssize_t;
using value_type = handle; using value_type = handle;
using reference = const handle; using reference = const handle; // PR #3263
using pointer = const handle *; using pointer = const handle *;
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
...@@ -927,6 +993,7 @@ public: ...@@ -927,6 +993,7 @@ public:
return rv; return rv;
} }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator*() const { reference operator*() const {
if (m_ptr && !value.ptr()) { if (m_ptr && !value.ptr()) {
auto& self = const_cast<iterator &>(*this); auto& self = const_cast<iterator &>(*this);
...@@ -1002,17 +1069,20 @@ class str : public object { ...@@ -1002,17 +1069,20 @@ class str : public object {
public: public:
PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str) PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str)
str(const char *c, size_t n) template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { str(const char *c, const SzType &n)
: object(PyUnicode_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate string object!"); if (!m_ptr) pybind11_fail("Could not allocate string object!");
} }
// 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects
// NOLINTNEXTLINE(google-explicit-constructor)
str(const char *c = "") str(const char *c = "")
: object(PyUnicode_FromString(c), stolen_t{}) { : object(PyUnicode_FromString(c), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate string object!"); if (!m_ptr) pybind11_fail("Could not allocate string object!");
} }
// NOLINTNEXTLINE(google-explicit-constructor)
str(const std::string &s) : str(s.data(), s.size()) { } str(const std::string &s) : str(s.data(), s.size()) { }
explicit str(const bytes &b); explicit str(const bytes &b);
...@@ -1023,6 +1093,7 @@ public: ...@@ -1023,6 +1093,7 @@ public:
\endrst */ \endrst */
explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); } explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { if (!m_ptr) throw error_already_set(); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::string() const { operator std::string() const {
object temp = *this; object temp = *this;
if (PyUnicode_Check(m_ptr)) { if (PyUnicode_Check(m_ptr)) {
...@@ -1070,21 +1141,25 @@ public: ...@@ -1070,21 +1141,25 @@ public:
PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK)
// Allow implicit conversion: // Allow implicit conversion:
// NOLINTNEXTLINE(google-explicit-constructor)
bytes(const char *c = "") bytes(const char *c = "")
: object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
} }
bytes(const char *c, size_t n) template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { bytes(const char *c, const SzType &n)
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, ssize_t_cast(n)), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
} }
// Allow implicit conversion: // Allow implicit conversion:
// NOLINTNEXTLINE(google-explicit-constructor)
bytes(const std::string &s) : bytes(s.data(), s.size()) { } bytes(const std::string &s) : bytes(s.data(), s.size()) { }
explicit bytes(const pybind11::str &s); explicit bytes(const pybind11::str &s);
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::string() const { operator std::string() const {
char *buffer = nullptr; char *buffer = nullptr;
ssize_t length = 0; ssize_t length = 0;
...@@ -1119,7 +1194,7 @@ inline str::str(const bytes& b) { ...@@ -1119,7 +1194,7 @@ inline str::str(const bytes& b) {
ssize_t length = 0; ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
pybind11_fail("Unable to extract bytes contents!"); pybind11_fail("Unable to extract bytes contents!");
auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); auto obj = reinterpret_steal<object>(PyUnicode_FromStringAndSize(buffer, length));
if (!obj) if (!obj)
pybind11_fail("Could not allocate string object!"); pybind11_fail("Could not allocate string object!");
m_ptr = obj.release().ptr(); m_ptr = obj.release().ptr();
...@@ -1131,8 +1206,9 @@ class bytearray : public object { ...@@ -1131,8 +1206,9 @@ class bytearray : public object {
public: public:
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject) PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
bytearray(const char *c, size_t n) template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
: object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { bytearray(const char *c, const SzType &n)
: object(PyByteArray_FromStringAndSize(c, ssize_t_cast(n)), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate bytearray object!"); if (!m_ptr) pybind11_fail("Could not allocate bytearray object!");
} }
...@@ -1172,7 +1248,9 @@ public: ...@@ -1172,7 +1248,9 @@ public:
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
bool_() : object(Py_False, borrowed_t{}) { } bool_() : object(Py_False, borrowed_t{}) { }
// Allow implicit conversion from and to `bool`: // Allow implicit conversion from and to `bool`:
// NOLINTNEXTLINE(google-explicit-constructor)
bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { }
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return (m_ptr != nullptr) && PyLong_AsLong(m_ptr) != 0; } operator bool() const { return (m_ptr != nullptr) && PyLong_AsLong(m_ptr) != 0; }
private: private:
...@@ -1191,9 +1269,9 @@ PYBIND11_NAMESPACE_BEGIN(detail) ...@@ -1191,9 +1269,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). // unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
template <typename Unsigned> template <typename Unsigned>
Unsigned as_unsigned(PyObject *o) { Unsigned as_unsigned(PyObject *o) {
if (sizeof(Unsigned) <= sizeof(unsigned long) if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))
#if PY_VERSION_HEX < 0x03000000 #if PY_VERSION_HEX < 0x03000000
|| PyInt_Check(o) || PyInt_Check(o)
#endif #endif
) { ) {
unsigned long v = PyLong_AsUnsignedLong(o); unsigned long v = PyLong_AsUnsignedLong(o);
...@@ -1211,8 +1289,9 @@ public: ...@@ -1211,8 +1289,9 @@ public:
// Allow implicit conversion from C++ integral types: // Allow implicit conversion from C++ integral types:
template <typename T, template <typename T,
detail::enable_if_t<std::is_integral<T>::value, int> = 0> detail::enable_if_t<std::is_integral<T>::value, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
int_(T value) { int_(T value) {
if (sizeof(T) <= sizeof(long)) { if (PYBIND11_SILENCE_MSVC_C4127(sizeof(T) <= sizeof(long))) {
if (std::is_signed<T>::value) if (std::is_signed<T>::value)
m_ptr = PyLong_FromLong((long) value); m_ptr = PyLong_FromLong((long) value);
else else
...@@ -1228,6 +1307,7 @@ public: ...@@ -1228,6 +1307,7 @@ public:
template <typename T, template <typename T,
detail::enable_if_t<std::is_integral<T>::value, int> = 0> detail::enable_if_t<std::is_integral<T>::value, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
operator T() const { operator T() const {
return std::is_unsigned<T>::value return std::is_unsigned<T>::value
? detail::as_unsigned<T>(m_ptr) ? detail::as_unsigned<T>(m_ptr)
...@@ -1241,13 +1321,17 @@ class float_ : public object { ...@@ -1241,13 +1321,17 @@ class float_ : public object {
public: public:
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
// Allow implicit conversion from float/double: // Allow implicit conversion from float/double:
// NOLINTNEXTLINE(google-explicit-constructor)
float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate float object!"); if (!m_ptr) pybind11_fail("Could not allocate float object!");
} }
// NOLINTNEXTLINE(google-explicit-constructor)
float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate float object!"); if (!m_ptr) pybind11_fail("Could not allocate float object!");
} }
// NOLINTNEXTLINE(google-explicit-constructor)
operator float() const { return (float) PyFloat_AsDouble(m_ptr); } operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator double() const { return (double) PyFloat_AsDouble(m_ptr); } operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
}; };
...@@ -1268,11 +1352,20 @@ private: ...@@ -1268,11 +1352,20 @@ private:
class slice : public object { class slice : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
slice(ssize_t start_, ssize_t stop_, ssize_t step_) { slice(handle start, handle stop, handle step) {
int_ start(start_), stop(stop_), step(step_);
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
if (!m_ptr) pybind11_fail("Could not allocate slice object!"); if (!m_ptr)
pybind11_fail("Could not allocate slice object!");
} }
#ifdef PYBIND11_HAS_OPTIONAL
slice(std::optional<ssize_t> start, std::optional<ssize_t> stop, std::optional<ssize_t> step)
: slice(index_to_object(start), index_to_object(stop), index_to_object(step)) {}
#else
slice(ssize_t start_, ssize_t stop_, ssize_t step_)
: slice(int_(start_), int_(stop_), int_(step_)) {}
#endif
bool compute(size_t length, size_t *start, size_t *stop, size_t *step, bool compute(size_t length, size_t *start, size_t *stop, size_t *step,
size_t *slicelength) const { size_t *slicelength) const {
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
...@@ -1287,6 +1380,12 @@ public: ...@@ -1287,6 +1380,12 @@ public:
stop, step, stop, step,
slicelength) == 0; slicelength) == 0;
} }
private:
template <typename T>
static object index_to_object(T index) {
return index ? object(int_(*index)) : object(none());
}
}; };
class capsule : public object { class capsule : public object {
...@@ -1322,7 +1421,7 @@ public: ...@@ -1322,7 +1421,7 @@ public:
pybind11_fail("Could not set capsule context!"); pybind11_fail("Could not set capsule context!");
} }
capsule(void (*destructor)()) { explicit capsule(void (*destructor)()) {
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) { m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr)); auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
destructor(); destructor();
...@@ -1332,6 +1431,7 @@ public: ...@@ -1332,6 +1431,7 @@ public:
pybind11_fail("Could not allocate capsule object!"); pybind11_fail("Could not allocate capsule object!");
} }
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename T> operator T *() const { template <typename T> operator T *() const {
return get_pointer<T>(); return get_pointer<T>();
} }
...@@ -1341,14 +1441,19 @@ public: ...@@ -1341,14 +1441,19 @@ public:
T* get_pointer() const { T* get_pointer() const {
auto name = this->name(); auto name = this->name();
T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name)); T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name));
if (!result) pybind11_fail("Unable to extract capsule contents!"); if (!result) {
PyErr_Clear();
pybind11_fail("Unable to extract capsule contents!");
}
return result; return result;
} }
/// Replaces a capsule's pointer *without* calling the destructor on the existing one. /// Replaces a capsule's pointer *without* calling the destructor on the existing one.
void set_pointer(const void *value) { void set_pointer(const void *value) {
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) {
PyErr_Clear();
pybind11_fail("Could not set capsule pointer"); pybind11_fail("Could not set capsule pointer");
}
} }
const char *name() const { return PyCapsule_GetName(m_ptr); } const char *name() const { return PyCapsule_GetName(m_ptr); }
...@@ -1357,7 +1462,10 @@ public: ...@@ -1357,7 +1462,10 @@ public:
class tuple : public object { class tuple : public object {
public: public:
PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple)
explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { template <typename SzType = ssize_t,
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
// Some compilers generate link errors when using `const SzType &` here:
explicit tuple(SzType size = 0) : object(PyTuple_New(ssize_t_cast(size)), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
} }
size_t size() const { return (size_t) PyTuple_Size(m_ptr); } size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
...@@ -1393,7 +1501,7 @@ public: ...@@ -1393,7 +1501,7 @@ public:
bool empty() const { return size() == 0; } bool empty() const { return size() == 0; }
detail::dict_iterator begin() const { return {*this, 0}; } detail::dict_iterator begin() const { return {*this, 0}; }
detail::dict_iterator end() const { return {}; } detail::dict_iterator end() const { return {}; }
void clear() const { PyDict_Clear(ptr()); } void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
template <typename T> bool contains(T &&key) const { template <typename T> bool contains(T &&key) const {
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1; return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
} }
...@@ -1426,7 +1534,10 @@ public: ...@@ -1426,7 +1534,10 @@ public:
class list : public object { class list : public object {
public: public:
PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List)
explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { template <typename SzType = ssize_t,
detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
// Some compilers generate link errors when using `const SzType &` here:
explicit list(SzType size = 0) : object(PyList_New(ssize_t_cast(size)), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate list object!"); if (!m_ptr) pybind11_fail("Could not allocate list object!");
} }
size_t size() const { return (size_t) PyList_Size(m_ptr); } size_t size() const { return (size_t) PyList_Size(m_ptr); }
...@@ -1435,12 +1546,15 @@ public: ...@@ -1435,12 +1546,15 @@ public:
detail::item_accessor operator[](handle h) const { return object::operator[](h); } detail::item_accessor operator[](handle h) const { return object::operator[](h); }
detail::list_iterator begin() const { return {*this, 0}; } detail::list_iterator begin() const { return {*this, 0}; }
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
template <typename T> void append(T &&val) const { template <typename T> void append(T &&val) /* py-non-const */ {
PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()); PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
} }
template <typename T> void insert(size_t index, T &&val) const { template <typename IdxType,
PyList_Insert(m_ptr, static_cast<ssize_t>(index), typename ValType,
detail::object_or_cast(std::forward<T>(val)).ptr()); detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
void insert(const IdxType &index, ValType &&val) /* py-non-const */ {
PyList_Insert(
m_ptr, ssize_t_cast(index), detail::object_or_cast(std::forward<ValType>(val)).ptr());
} }
}; };
...@@ -1455,10 +1569,10 @@ public: ...@@ -1455,10 +1569,10 @@ public:
} }
size_t size() const { return (size_t) PySet_Size(m_ptr); } size_t size() const { return (size_t) PySet_Size(m_ptr); }
bool empty() const { return size() == 0; } bool empty() const { return size() == 0; }
template <typename T> bool add(T &&val) const { template <typename T> bool add(T &&val) /* py-non-const */ {
return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0; return PySet_Add(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 0;
} }
void clear() const { PySet_Clear(m_ptr); } void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
template <typename T> bool contains(T &&val) const { template <typename T> bool contains(T &&val) const {
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1; return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#pragma once #pragma once
#include "detail/common.h"
#include "pybind11.h" #include "pybind11.h"
#include <set> #include <set>
#include <unordered_set> #include <unordered_set>
...@@ -19,33 +20,15 @@ ...@@ -19,33 +20,15 @@
#include <deque> #include <deque>
#include <valarray> #include <valarray>
#if defined(_MSC_VER) // See `detail/common.h` for implementation of these guards.
#pragma warning(push) #if defined(PYBIND11_HAS_OPTIONAL)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # include <optional>
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
# include <experimental/optional>
#endif #endif
#ifdef __has_include #if defined(PYBIND11_HAS_VARIANT)
// std::optional (but including it in c++14 mode isn't allowed)
# if defined(PYBIND11_CPP17) && __has_include(<optional>)
# include <optional>
# define PYBIND11_HAS_OPTIONAL 1
# endif
// std::experimental::optional (but not allowed in c++11 mode)
# if defined(PYBIND11_CPP14) && (__has_include(<experimental/optional>) && \
!__has_include(<optional>))
# include <experimental/optional>
# define PYBIND11_HAS_EXP_OPTIONAL 1
# endif
// std::variant
# if defined(PYBIND11_CPP17) && __has_include(<variant>)
# include <variant>
# define PYBIND11_HAS_VARIANT 1
# endif
#elif defined(_MSC_VER) && defined(PYBIND11_CPP17)
# include <optional>
# include <variant> # include <variant>
# define PYBIND11_HAS_OPTIONAL 1
# define PYBIND11_HAS_VARIANT 1
#endif #endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
...@@ -173,12 +156,12 @@ public: ...@@ -173,12 +156,12 @@ public:
if (!std::is_lvalue_reference<T>::value) if (!std::is_lvalue_reference<T>::value)
policy = return_value_policy_override<Value>::policy(policy); policy = return_value_policy_override<Value>::policy(policy);
list l(src.size()); list l(src.size());
size_t index = 0; ssize_t index = 0;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) if (!value_)
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
...@@ -230,12 +213,12 @@ public: ...@@ -230,12 +213,12 @@ public:
template <typename T> template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) { static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size()); list l(src.size());
size_t index = 0; ssize_t index = 0;
for (auto &&value : src) { for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent)); auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_) if (!value_)
return handle(); return handle();
PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
...@@ -390,7 +373,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) { ...@@ -390,7 +373,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
} }
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
...@@ -595,6 +595,23 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ & ...@@ -595,6 +595,23 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
); );
} }
template<typename Map>
struct keys_view
{
Map &map;
};
template<typename Map>
struct values_view
{
Map &map;
};
template<typename Map>
struct items_view
{
Map &map;
};
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)
...@@ -602,6 +619,9 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... ...@@ -602,6 +619,9 @@ template <typename Map, typename holder_type = std::unique_ptr<Map>, typename...
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) { class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) {
using KeyType = typename Map::key_type; using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type; using MappedType = typename Map::mapped_type;
using KeysView = detail::keys_view<Map>;
using ValuesView = detail::values_view<Map>;
using ItemsView = detail::items_view<Map>;
using Class_ = class_<Map, holder_type>; using Class_ = class_<Map, holder_type>;
// If either type is a non-module-local bound type then make the map binding non-local as well; // If either type is a non-module-local bound type then make the map binding non-local as well;
...@@ -615,6 +635,12 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -615,6 +635,12 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
} }
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
class_<KeysView> keys_view(
scope, ("KeysView[" + name + "]").c_str(), pybind11::module_local(local));
class_<ValuesView> values_view(
scope, ("ValuesView[" + name + "]").c_str(), pybind11::module_local(local));
class_<ItemsView> items_view(
scope, ("ItemsView[" + name + "]").c_str(), pybind11::module_local(local));
cl.def(init<>()); cl.def(init<>());
...@@ -628,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -628,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
cl.def("__iter__", cl.def("__iter__",
[](Map &m) { return make_key_iterator(m.begin(), m.end()); }, [](Map &m) { return make_key_iterator(m.begin(), m.end()); },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
);
cl.def("keys",
[](Map &m) { return KeysView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
cl.def("values",
[](Map &m) { return ValuesView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("items", cl.def("items",
[](Map &m) { return make_iterator(m.begin(), m.end()); }, [](Map &m) { return ItemsView{m}; },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ keep_alive<0, 1>() /* Essential: keep map alive while view exists */
); );
cl.def("__getitem__", cl.def("__getitem__",
...@@ -654,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -654,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
return true; return true;
} }
); );
// Fallback for when the object is not of the key type
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
// Assignment provided only if the type is copyable // Assignment provided only if the type is copyable
detail::map_assignment<Map, Class_>(cl); detail::map_assignment<Map, Class_>(cl);
...@@ -669,6 +707,40 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&. ...@@ -669,6 +707,40 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
cl.def("__len__", &Map::size); cl.def("__len__", &Map::size);
keys_view.def("__len__", [](KeysView &view) { return view.map.size(); });
keys_view.def("__iter__",
[](KeysView &view) {
return make_key_iterator(view.map.begin(), view.map.end());
},
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
keys_view.def("__contains__",
[](KeysView &view, const KeyType &k) -> bool {
auto it = view.map.find(k);
if (it == view.map.end())
return false;
return true;
}
);
// Fallback for when the object is not of the key type
keys_view.def("__contains__", [](KeysView &, const object &) -> bool { return false; });
values_view.def("__len__", [](ValuesView &view) { return view.map.size(); });
values_view.def("__iter__",
[](ValuesView &view) {
return make_value_iterator(view.map.begin(), view.map.end());
},
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
items_view.def("__len__", [](ItemsView &view) { return view.map.size(); });
items_view.def("__iter__",
[](ItemsView &view) {
return make_iterator(view.map.begin(), view.map.end());
},
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
);
return cl; return cl;
} }
......
import nox import nox
nox.options.sessions = ["lint", "tests", "tests_packaging"] nox.options.sessions = ["lint", "tests", "tests_packaging"]
PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"]
@nox.session(reuse_venv=True) @nox.session(reuse_venv=True)
def lint(session: nox.Session) -> None: def lint(session: nox.Session) -> None:
...@@ -13,7 +14,7 @@ def lint(session: nox.Session) -> None: ...@@ -13,7 +14,7 @@ def lint(session: nox.Session) -> None:
session.run("pre-commit", "run", "-a") session.run("pre-commit", "run", "-a")
@nox.session @nox.session(python=PYTHON_VERISONS)
def tests(session: nox.Session) -> None: def tests(session: nox.Session) -> None:
""" """
Run the tests (requires a compiler). Run the tests (requires a compiler).
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ._version import version_info, __version__ from ._version import __version__, version_info
from .commands import get_include, get_cmake_dir from .commands import get_cmake_dir, get_include
__all__ = ( __all__ = (
"version_info", "version_info",
......
...@@ -5,7 +5,7 @@ import argparse ...@@ -5,7 +5,7 @@ import argparse
import sys import sys
import sysconfig import sysconfig
from .commands import get_include, get_cmake_dir from .commands import get_cmake_dir, get_include
def print_includes(): def print_includes():
......
...@@ -8,5 +8,5 @@ def _to_int(s): ...@@ -8,5 +8,5 @@ def _to_int(s):
return s return s
__version__ = "2.7.1" __version__ = "2.8.0"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))
from typing import Union, Tuple from typing import Tuple, Union
def _to_int(s: str) -> Union[int, str]: ... def _to_int(s: str) -> Union[int, str]: ...
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))
......
...@@ -41,23 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -41,23 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import contextlib import contextlib
import os import os
import platform
import shutil import shutil
import sys import sys
import sysconfig
import tempfile import tempfile
import threading import threading
import platform
import warnings import warnings
import sysconfig
try: try:
from setuptools.command.build_ext import build_ext as _build_ext
from setuptools import Extension as _Extension from setuptools import Extension as _Extension
from setuptools.command.build_ext import build_ext as _build_ext
except ImportError: except ImportError:
from distutils.command.build_ext import build_ext as _build_ext from distutils.command.build_ext import build_ext as _build_ext
from distutils.extension import Extension as _Extension from distutils.extension import Extension as _Extension
import distutils.errors
import distutils.ccompiler import distutils.ccompiler
import distutils.errors
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
PY2 = sys.version_info[0] < 3 PY2 = sys.version_info[0] < 3
......
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / # IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
# pre-commit). # pre-commit).
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union import contextlib
from types import TracebackType import distutils.ccompiler
from distutils.command.build_ext import build_ext as _build_ext # type: ignore from distutils.command.build_ext import build_ext as _build_ext # type: ignore
from distutils.extension import Extension as _Extension from distutils.extension import Extension as _Extension
import distutils.ccompiler from types import TracebackType
import contextlib from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
WIN: bool WIN: bool
PY2: bool PY2: bool
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment