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 @@
#pragma once
#include "numpy.h"
#if defined(__INTEL_COMPILER)
# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem)
#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
/* HINT: To suppress warnings originating from the Eigen headers, use -isystem.
See also:
https://stackoverflow.com/questions/2579576/i-dir-vs-isystem-dir
https://stackoverflow.com/questions/1741816/isystem-for-ms-visual-studio-c-compiler
*/
#if defined(_MSC_VER)
# 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 "numpy.h"
#include <Eigen/Core>
#include <Eigen/SparseCore>
......@@ -77,6 +61,7 @@ template <bool EigenRowMajor> struct EigenConformable {
EigenDStride stride{0, 0}; // Only valid if negativestrides is false!
bool negativestrides = false; // If true, do not use stride!
// NOLINTNEXTLINE(google-explicit-constructor)
EigenConformable(bool fits = false) : conformable{fits} {}
// Matrix type:
EigenConformable(EigenIndex r, EigenIndex c,
......@@ -104,6 +89,7 @@ template <bool EigenRowMajor> struct EigenConformable {
(props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() ||
(EigenRowMajor ? rows : cols) == 1);
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return conformable; }
};
......@@ -153,7 +139,8 @@ template <typename Type_> struct EigenProps {
np_cols = a.shape(1),
np_rstride = a.strides(0) / 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 {np_rows, np_cols, np_rstride, np_cstride};
......@@ -165,7 +152,7 @@ template <typename Type_> struct EigenProps {
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
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 {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
}
......@@ -179,7 +166,7 @@ template <typename Type_> struct EigenProps {
if (cols != n) return false;
return {1, n, stride};
} // 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};
}
......@@ -341,8 +328,11 @@ public:
static constexpr auto name = props::descriptor;
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return &value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return value; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&&() && { return std::move(value); }
template <typename T> using cast_op_type = movable_cast_op_type<T>;
......@@ -466,7 +456,9 @@ public:
return true;
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type*() { return ref.get(); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator Type&() { return *ref; }
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>> {
PYBIND11_NAMESPACE_END(detail)
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 @@
#include "pybind11.h"
#include "eval.h"
#include <memory>
#include <vector>
#if defined(PYPY_VERSION)
# error Embedding the interpreter is not supported with PyPy
#endif
......@@ -45,25 +48,23 @@
});
}
\endrst */
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
static ::pybind11::module_::module_def \
PYBIND11_CONCAT(pybind11_module_def_, name); \
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
auto m = ::pybind11::module_::create_extension_module( \
PYBIND11_TOSTRING(name), nullptr, \
&PYBIND11_CONCAT(pybind11_module_def_, name)); \
try { \
PYBIND11_CONCAT(pybind11_init_, name)(m); \
return m.ptr(); \
} PYBIND11_CATCH_INIT_EXCEPTIONS \
} \
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name) \
(PYBIND11_TOSTRING(name), \
PYBIND11_CONCAT(pybind11_init_impl_, name)); \
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &variable)
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
auto m = ::pybind11::module_::create_extension_module( \
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
try { \
PYBIND11_CONCAT(pybind11_init_, name)(m); \
return m.ptr(); \
} \
PYBIND11_CATCH_INIT_EXCEPTIONS \
} \
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
& variable) // NOLINT(bugprone-macro-parentheses)
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
......@@ -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)
/** \rst
Initialize the Python interpreter. No other pybind11 or CPython API functions can be
called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The
optional parameter can be used to skip the registration of signal handlers (see the
`Python documentation`_ for details). Calling this function again after the interpreter
has already been initialized is a fatal error.
optional `init_signal_handlers` parameter can be used to skip the registration of
signal handlers (see the `Python documentation`_ for details). Calling this function
again after the interpreter has already been initialized is a fatal error.
If initializing the Python interpreter fails, then the program is terminated. (This
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
of throwing exceptions on errors.)
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
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
\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)
pybind11_fail("The interpreter is already running");
Py_InitializeEx(init_signal_handlers ? 1 : 0);
// Make .py files in the working directory available by default
module_::import("sys").attr("path").cast<list>().append(".");
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
}
/** \rst
......@@ -169,6 +247,8 @@ inline void finalize_interpreter() {
Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
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
#include <pybind11/embed.h>
......@@ -180,8 +260,11 @@ inline void finalize_interpreter() {
\endrst */
class scoped_interpreter {
public:
scoped_interpreter(bool init_signal_handlers = true) {
initialize_interpreter(init_signal_handlers);
explicit scoped_interpreter(bool init_signal_handlers = true,
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;
......
......@@ -136,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object())
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)
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
local.ptr());
......
......@@ -69,6 +69,10 @@ public:
// ensure GIL is held during functor destruction
struct func_handle {
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(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) {
......@@ -85,7 +89,7 @@ public:
// to emulate 'move initialization capture' in C++11
struct func_wrapper {
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 {
gil_scoped_acquire acq;
object retval(hfunc.f(std::forward<Args>(args)...));
......
......@@ -50,7 +50,7 @@ PYBIND11_NAMESPACE_END(detail)
class gil_scoped_acquire {
public:
PYBIND11_NOINLINE gil_scoped_acquire() {
auto const &internals = detail::get_internals();
auto &internals = detail::get_internals();
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
if (!tstate) {
......@@ -132,7 +132,7 @@ public:
// `get_internals()` must be called here unconditionally in order to initialize
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
// 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();
if (disassoc) {
auto key = internals.tstate;
......
......@@ -123,7 +123,7 @@ private:
}
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")),
pyflush(pyostream.attr("flush")) {
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
......@@ -171,8 +171,9 @@ protected:
detail::pythonbuf buffer;
public:
scoped_ostream_redirect(std::ostream &costream = std::cout,
const object &pyostream = module_::import("sys").attr("stdout"))
explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
const object &pyostream
= module_::import("sys").attr("stdout"))
: costream(costream), buffer(pyostream) {
old = costream.rdbuf(&buffer);
}
......@@ -201,8 +202,9 @@ public:
\endrst */
class scoped_estream_redirect : public scoped_ostream_redirect {
public:
scoped_estream_redirect(std::ostream &costream = std::cerr,
const object &pyostream = module_::import("sys").attr("stderr"))
explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
const object &pyostream
= module_::import("sys").attr("stderr"))
: scoped_ostream_redirect(costream, pyostream) {}
};
......@@ -217,7 +219,7 @@ class OstreamRedirect {
std::unique_ptr<scoped_estream_redirect> redirect_stderr;
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) {}
void enter() {
......
......@@ -25,11 +25,6 @@
#include <vector>
#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
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
......@@ -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");
}
......@@ -203,6 +198,9 @@ struct npy_api {
// Unused. Not removed because that affects ABI of the class.
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int);
PyObject* (*PyArray_Newshape_)(PyObject*, PyArray_Dims*, int);
PyObject* (*PyArray_View_)(PyObject*, PyObject*, PyObject*);
private:
enum functions {
API_PyArray_GetNDArrayCFeatureVersion = 211,
......@@ -217,10 +215,12 @@ private:
API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 96,
API_PyArray_Newshape = 135,
API_PyArray_Squeeze = 136,
API_PyArray_View = 137,
API_PyArray_DescrConverter = 174,
API_PyArray_EquivTypes = 182,
API_PyArray_GetArrayParamsFromObject = 278,
API_PyArray_Squeeze = 136,
API_PyArray_SetBaseObject = 282
};
......@@ -248,11 +248,14 @@ private:
DECL_NPY_API(PyArray_NewCopy);
DECL_NPY_API(PyArray_NewFromDescr);
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_EquivTypes);
DECL_NPY_API(PyArray_GetArrayParamsFromObject);
DECL_NPY_API(PyArray_Squeeze);
DECL_NPY_API(PyArray_SetBaseObject);
#undef DECL_NPY_API
return api;
}
......@@ -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<
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)
// don't implement is_trivially_copyable, so approximate it
std::is_trivially_destructible<T>,
......@@ -474,15 +477,15 @@ public:
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) {
dict args;
args["names"] = names;
args["formats"] = formats;
args["offsets"] = offsets;
args["names"] = std::move(names);
args["formats"] = std::move(formats);
args["offsets"] = std::move(offsets);
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.
......@@ -560,7 +563,7 @@ private:
formats.append(descr.format);
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:
* 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() & {
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()) +
"; expected " + std::to_string(Dims));
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
......@@ -761,7 +764,7 @@ public:
* invalid dimensions or dimension indices.
*/
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()) +
"; expected " + std::to_string(Dims));
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
......@@ -790,6 +793,33 @@ public:
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
/// In case of an error, nullptr is returned and the Python error is cleared.
static array ensure(handle h, int ExtraFlags = 0) {
......@@ -864,6 +894,7 @@ public:
if (!is_borrowed) Py_XDECREF(h.ptr());
}
// NOLINTNEXTLINE(google-explicit-constructor)
array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
if (!m_ptr) throw error_already_set();
}
......@@ -1110,7 +1141,7 @@ struct field_descriptor {
dtype descr;
};
inline PYBIND11_NOINLINE void register_structured_dtype(
PYBIND11_NOINLINE void register_structured_dtype(
any_container<field_descriptor> fields,
const std::type_info& tinfo, ssize_t itemsize,
bool (*direct_converter)(PyObject *, void *&)) {
......@@ -1134,7 +1165,10 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
formats.append(field.descr);
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
// not encoded explicitly into the format string. This will supposedly
......@@ -1551,8 +1585,11 @@ private:
"pybind11::vectorize(...) requires a function with at least one vectorizable argument");
public:
template <typename T>
explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) { }
template <typename T,
// 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) {
return run(args...,
......@@ -1702,7 +1739,3 @@ Helper vectorize(Return (Class::*f)(Args...) const) {
}
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
......@@ -11,13 +11,6 @@
#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(detail)
......@@ -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>;
cl.def(op::name(), &op::execute, is_operator(), extra...);
#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__",
&op::execute, is_operator(), extra...);
#endif
......@@ -167,7 +161,3 @@ using detail::self;
using detail::hash;
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
......@@ -10,22 +10,13 @@
#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 "gil.h"
#include "options.h"
#include "detail/class.h"
#include "detail/init.h"
#include <cstdlib>
#include <memory>
#include <new>
#include <vector>
......@@ -59,20 +50,44 @@
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)
# define PYBIND11_COMPAT_STRDUP _strdup
#else
# define PYBIND11_COMPAT_STRDUP strdup
#endif
PYBIND11_NAMESPACE_END(detail)
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function {
public:
cpp_function() = default;
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(std::nullptr_t) { }
/// Construct a cpp_function from a vanilla function pointer
template <typename Return, typename... Args, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (*f)(Args...), const Extra&... extra) {
initialize(f, f, extra...);
}
......@@ -80,6 +95,7 @@ public:
/// Construct a cpp_function from a lambda function (possibly with internal state)
template <typename Func, typename... Extra,
typename = detail::enable_if_t<detail::is_lambda<Func>::value>>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Func &&f, const Extra&... extra) {
initialize(std::forward<Func>(f),
(detail::function_signature_t<Func> *) nullptr, extra...);
......@@ -87,6 +103,7 @@ public:
/// Construct a cpp_function from a class method (non-const, no ref-qualifier)
template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(std::forward<Arg>(args)...); },
(Return (*) (Class *, Arg...)) nullptr, extra...);
......@@ -96,6 +113,7 @@ public:
/// A copy of the overload for non-const functions without explicit ref-qualifier
/// but with an added `&`.
template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...)&, const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...)) nullptr, extra...);
......@@ -103,6 +121,7 @@ public:
/// Construct a cpp_function from a class method (const, no ref-qualifier)
template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
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)...); },
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
......@@ -112,6 +131,7 @@ public:
/// A copy of the overload for const functions without explicit ref-qualifier
/// but with an added `&`.
template <typename Return, typename Class, typename... Arg, typename... Extra>
// NOLINTNEXTLINE(google-explicit-constructor)
cpp_function(Return (Class::*f)(Arg...) const&, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
......@@ -145,7 +165,7 @@ protected:
auto rec = unique_rec.get();
/* 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
enough space to use the placement new operator. However, the
'if' statement above ensures that this is the case. */
......@@ -163,7 +183,7 @@ protected:
#endif
// UB without std::launder, but without breaking ABI and/or
// 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) {
auto data = PYBIND11_STD_LAUNDER((capture *) &r->data);
(void) data;
......@@ -397,7 +417,8 @@ protected:
detail::function_record *chain = nullptr, *chain_start = rec;
if (rec->sibling) {
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;
/* Never append a method to an overload chain of a parent class;
instead, hide the parent's overloads in this case */
......@@ -561,6 +582,7 @@ protected:
}
}
/// Main dispatch logic for calls to functions bound using pybind11
static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) {
using namespace detail;
......@@ -841,8 +863,12 @@ protected:
#endif
} catch (...) {
/* When an exception is caught, give each registered exception
translator a chance to translate it to a Python exception
in reverse order of registration.
translator a chance to translate it to a Python exception. First
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:
......@@ -851,17 +877,15 @@ protected:
- 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. */
auto last_exception = std::current_exception();
auto &registered_exception_translators = get_internals().registered_exception_translators;
for (auto& translator : registered_exception_translators) {
try {
translator(last_exception);
} catch (...) {
last_exception = std::current_exception();
continue;
}
auto &local_exception_translators = get_local_internals().registered_exception_translators;
if (detail::apply_exception_translators(local_exception_translators)) {
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!");
return nullptr;
}
......@@ -962,6 +986,7 @@ protected:
}
};
/// Wrapper for Python extension modules
class module_ : public object {
public:
......@@ -1136,7 +1161,7 @@ protected:
auto tindex = std::type_index(*rec.type);
tinfo->direct_conversions = &internals.direct_conversions[tindex];
if (rec.module_local)
registered_local_types_cpp()[tindex] = tinfo;
get_local_internals().registered_types_cpp[tindex] = tinfo;
else
internals.registered_types_cpp[tindex] = tinfo;
internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo };
......@@ -1323,7 +1348,7 @@ public:
generic_type::initialize(record);
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))];
}
}
......@@ -1370,12 +1395,14 @@ public:
template <typename... Args, typename... Extra>
class_ &def(const detail::initimpl::constructor<Args...> &init, const Extra&... extra) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(init);
init.execute(*this, extra...);
return *this;
}
template <typename... Args, typename... Extra>
class_ &def(const detail::initimpl::alias_constructor<Args...> &init, const Extra&... extra) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(init);
init.execute(*this, extra...);
return *this;
}
......@@ -1510,7 +1537,7 @@ public:
char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
detail::process_attributes<Extra...>::init(extra..., rec_fget);
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);
}
}
......@@ -1518,7 +1545,7 @@ public:
char *doc_prev = rec_fset->doc;
detail::process_attributes<Extra...>::init(extra..., rec_fset);
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);
}
if (! rec_active) rec_active = rec_fset;
......@@ -1646,7 +1673,7 @@ inline str enum_name(handle arg) {
}
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) {
m_base.attr("__entries") = dict();
......@@ -1702,7 +1729,7 @@ struct enum_base {
m_base.attr(op) = cpp_function( \
[](const object &a, const object &b) { \
if (!type::handle_of(a).is(type::handle_of(b))) \
strict_behavior; \
strict_behavior; /* NOLINT(bugprone-macro-parentheses) */ \
return expr; \
}, \
name(op), \
......@@ -1796,6 +1823,19 @@ struct enum_base {
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)
/// Binds C++ enumerations and enumeration classes to Python
......@@ -1806,13 +1846,17 @@ public:
using Base::attr;
using Base::def_property_readonly;
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>
enum_(const handle &scope, const char *name, const Extra&... extra)
: 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_convertible = std::is_convertible<Type, Scalar>::value;
constexpr bool is_convertible = std::is_convertible<Type, Underlying>::value;
m_base.init(is_arithmetic, is_convertible);
def(init([](Scalar i) { return static_cast<Type>(i); }), arg("value"));
......@@ -1852,7 +1896,7 @@ private:
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)
pybind11_fail("Could not activate keep_alive!");
......@@ -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) {
if (n == 0)
return ret;
......@@ -1912,25 +1956,54 @@ inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_t
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 {
Iterator it;
Sentinel end;
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 <return_value_policy Policy = return_value_policy::reference_internal,
template <typename Iterator, typename SFINAE = decltype(((*std::declval<Iterator &>()).first)) >
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 Sentinel,
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Issue in breathe 4.26.1
typename ValueType = decltype(*std::declval<Iterator>()),
#endif
typename ValueType,
typename... Extra>
iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
using state = detail::iterator_state<Iterator, Sentinel, false, Policy>;
iterator make_iterator_impl(Iterator first, Sentinel last, Extra &&... extra) {
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)) {
class_<state>(handle(), "iterator", pybind11::module_local())
......@@ -1944,42 +2017,63 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
s.first_or_done = true;
throw stop_iteration();
}
return *s.it;
return Access()(s.it);
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
}, std::forward<Extra>(extra)..., Policy);
}
return cast(state{first, last, true});
}
/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a
/// first and past-the-end InputIterator.
PYBIND11_NAMESPACE_END(detail)
/// Makes a python iterator from a first and past-the-end C++ InputIterator.
template <return_value_policy Policy = return_value_policy::reference_internal,
typename Iterator,
typename Sentinel,
#ifndef DOXYGEN_SHOULD_SKIP_THIS // Issue in breathe 4.26.1
typename KeyType = decltype((*std::declval<Iterator>()).first),
#endif
typename ValueType = typename detail::iterator_access<Iterator>::result_type,
typename... Extra>
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
using state = detail::iterator_state<Iterator, Sentinel, true, Policy>;
iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
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)) {
class_<state>(handle(), "iterator", pybind11::module_local())
.def("__iter__", [](state &s) -> state& { return s; })
.def("__next__", [](state &s) -> KeyType {
if (!s.first_or_done)
++s.it;
else
s.first_or_done = false;
if (s.it == s.end) {
s.first_or_done = true;
throw stop_iteration();
}
return (*s.it).first;
}, std::forward<Extra>(extra)..., Policy);
}
/// Makes a python iterator over the keys (`.first`) 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 KeyType = typename detail::iterator_key_access<Iterator>::result_type,
typename... Extra>
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
return detail::make_iterator_impl<
detail::iterator_key_access<Iterator>,
Policy,
Iterator,
Sentinel,
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
......@@ -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...);
}
/// 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() {
struct set_flag {
bool &flag;
set_flag(bool &flag_) : flag(flag_) { flag_ = true; }
explicit set_flag(bool &flag_) : flag(flag_) { flag_ = true; }
~set_flag() { flag = false; }
};
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
......@@ -2023,12 +2124,24 @@ template <typename InputType, typename OutputType> void implicitly_convertible()
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(
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.
*
......@@ -2062,22 +2175,20 @@ PYBIND11_NAMESPACE_BEGIN(detail)
// directly in register_exception, but that makes clang <3.5 segfault - issue #1349).
template <typename CppException>
exception<CppException> &get_exception_object() { static exception<CppException> ex; return ex; }
PYBIND11_NAMESPACE_END(detail)
/**
* Registers a Python exception in `m` of the given `name` and installs an exception translator to
* translate the C++ exception to the created Python exception using the exceptions what() method.
* This is intended for simple exception translations; for more complex translation, register the
* exception object and translator directly.
*/
// Helper function for register_exception and register_local_exception
template <typename CppException>
exception<CppException> &register_exception(handle scope,
const char *name,
handle base = PyExc_Exception) {
exception<CppException> &register_exception_impl(handle scope,
const char *name,
handle base,
bool isLocal) {
auto &ex = detail::get_exception_object<CppException>();
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;
try {
std::rethrow_exception(p);
......@@ -2088,8 +2199,38 @@ exception<CppException> &register_exception(handle scope,
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_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());
for (size_t i = 0; i < args.size(); ++i) {
strings[i] = str(args[i]);
......@@ -2320,9 +2461,3 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(__GNUC__) && __GNUC__ == 7
# pragma GCC diagnostic pop // -Wnoexcept-type
#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 @@
#include <utility>
#include <type_traits>
#if defined(PYBIND11_HAS_OPTIONAL)
# include <optional>
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
/* A few forward declarations */
......@@ -24,7 +28,7 @@ struct arg; struct arg_v;
PYBIND11_NAMESPACE_BEGIN(detail)
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
template <typename Policy> class accessor;
......@@ -178,6 +182,7 @@ public:
/// The default constructor creates a handle with a ``nullptr``-valued pointer
handle() = default;
/// 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*
/// Return the underlying ``PyObject *`` pointer
......@@ -254,8 +259,11 @@ public:
object& operator=(const object &other) {
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;
temp.dec_ref();
return *this;
}
......@@ -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{}}; }
PYBIND11_NAMESPACE_BEGIN(detail)
inline std::string error_string();
std::string error_string();
PYBIND11_NAMESPACE_END(detail)
#if defined(_MSC_VER)
......@@ -382,6 +390,47 @@ private:
# pragma warning(pop)
#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 _
Unless stated otherwise, the following C++ functions behave the same
as their Python counterparts.
......@@ -571,6 +620,7 @@ public:
return obj.contains(key);
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator object() const { return get_cache(); }
PyObject *ptr() const { return get_cache().ptr(); }
template <typename T> T cast() const { return get_cache().template cast<T>(); }
......@@ -620,15 +670,17 @@ struct generic_item {
struct sequence_item {
using key_type = size_t;
static object get(handle obj, size_t index) {
PyObject *result = PySequence_GetItem(obj.ptr(), static_cast<ssize_t>(index));
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
static object get(handle obj, const IdxType &index) {
PyObject *result = PySequence_GetItem(obj.ptr(), ssize_t_cast(index));
if (!result) { throw error_already_set(); }
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'
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();
}
}
......@@ -637,15 +689,17 @@ struct sequence_item {
struct list_item {
using key_type = size_t;
static object get(handle obj, size_t index) {
PyObject *result = PyList_GetItem(obj.ptr(), static_cast<ssize_t>(index));
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
static object get(handle obj, const IdxType &index) {
PyObject *result = PyList_GetItem(obj.ptr(), ssize_t_cast(index));
if (!result) { throw error_already_set(); }
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'
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();
}
}
......@@ -654,15 +708,17 @@ struct list_item {
struct tuple_item {
using key_type = size_t;
static object get(handle obj, size_t index) {
PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast<ssize_t>(index));
template <typename IdxType, detail::enable_if_t<std::is_integral<IdxType>::value, int> = 0>
static object get(handle obj, const IdxType &index) {
PyObject *result = PyTuple_GetItem(obj.ptr(), ssize_t_cast(index));
if (!result) { throw error_already_set(); }
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'
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();
}
}
......@@ -684,7 +740,9 @@ public:
generic_iterator() = default;
generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator*() const { return Policy::dereference(); }
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator[](difference_type n) const { return *(*this + n); }
pointer operator->() const { return **this; }
......@@ -714,7 +772,8 @@ template <typename T>
struct arrow_proxy {
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; }
};
......@@ -723,11 +782,12 @@ class sequence_fast_readonly {
protected:
using iterator_category = std::random_access_iterator_tag;
using value_type = handle;
using reference = const handle;
using reference = const handle; // PR #3263
using pointer = arrow_proxy<const handle>;
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; }
void increment() { ++ptr; }
void decrement() { --ptr; }
......@@ -766,12 +826,13 @@ class dict_readonly {
protected:
using iterator_category = std::forward_iterator_tag;
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>;
dict_readonly() = default;
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}; }
void increment() {
if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) {
......@@ -862,14 +923,17 @@ PYBIND11_NAMESPACE_END(detail)
bool check() const { return m_ptr != nullptr && (CheckFun(m_ptr) != 0); } \
static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } \
template <typename Policy_> \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(const ::pybind11::detail::accessor<Policy_> &a) : Name(object(a)) { }
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(const object &o) \
: Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
{ if (!m_ptr) throw error_already_set(); } \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(object &&o) \
: Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \
{ if (!m_ptr) throw error_already_set(); }
......@@ -886,8 +950,10 @@ PYBIND11_NAMESPACE_END(detail)
#define PYBIND11_OBJECT(Name, Parent, CheckFun) \
PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \
/* This is deliberately not 'explicit' to allow implicit conversion from object: */ \
/* NOLINTNEXTLINE(google-explicit-constructor) */ \
Name(const object &o) : Parent(o) \
{ 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)) \
{ if (m_ptr && !check_(m_ptr)) throw PYBIND11_OBJECT_CHECK_FAILED(Name, m_ptr); }
......@@ -911,7 +977,7 @@ public:
using iterator_category = std::input_iterator_tag;
using difference_type = ssize_t;
using value_type = handle;
using reference = const handle;
using reference = const handle; // PR #3263
using pointer = const handle *;
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
......@@ -927,6 +993,7 @@ public:
return rv;
}
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
reference operator*() const {
if (m_ptr && !value.ptr()) {
auto& self = const_cast<iterator &>(*this);
......@@ -1002,17 +1069,20 @@ class str : public object {
public:
PYBIND11_OBJECT_CVT(str, object, PYBIND11_STR_CHECK_FUN, raw_str)
str(const char *c, size_t n)
: object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
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!");
}
// '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 = "")
: object(PyUnicode_FromString(c), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate string object!");
}
// NOLINTNEXTLINE(google-explicit-constructor)
str(const std::string &s) : str(s.data(), s.size()) { }
explicit str(const bytes &b);
......@@ -1023,6 +1093,7 @@ public:
\endrst */
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 {
object temp = *this;
if (PyUnicode_Check(m_ptr)) {
......@@ -1070,21 +1141,25 @@ public:
PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK)
// Allow implicit conversion:
// NOLINTNEXTLINE(google-explicit-constructor)
bytes(const char *c = "")
: object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
}
bytes(const char *c, size_t n)
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) {
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
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!");
}
// Allow implicit conversion:
// NOLINTNEXTLINE(google-explicit-constructor)
bytes(const std::string &s) : bytes(s.data(), s.size()) { }
explicit bytes(const pybind11::str &s);
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::string() const {
char *buffer = nullptr;
ssize_t length = 0;
......@@ -1119,7 +1194,7 @@ inline str::str(const bytes& b) {
ssize_t length = 0;
if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length))
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)
pybind11_fail("Could not allocate string object!");
m_ptr = obj.release().ptr();
......@@ -1131,8 +1206,9 @@ class bytearray : public object {
public:
PYBIND11_OBJECT_CVT(bytearray, object, PyByteArray_Check, PyByteArray_FromObject)
bytearray(const char *c, size_t n)
: object(PyByteArray_FromStringAndSize(c, (ssize_t) n), stolen_t{}) {
template <typename SzType, detail::enable_if_t<std::is_integral<SzType>::value, int> = 0>
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!");
}
......@@ -1172,7 +1248,9 @@ public:
PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool)
bool_() : object(Py_False, borrowed_t{}) { }
// Allow implicit conversion from and to `bool`:
// NOLINTNEXTLINE(google-explicit-constructor)
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; }
private:
......@@ -1191,9 +1269,9 @@ PYBIND11_NAMESPACE_BEGIN(detail)
// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
template <typename Unsigned>
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
|| PyInt_Check(o)
|| PyInt_Check(o)
#endif
) {
unsigned long v = PyLong_AsUnsignedLong(o);
......@@ -1211,8 +1289,9 @@ public:
// Allow implicit conversion from C++ integral types:
template <typename T,
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
int_(T value) {
if (sizeof(T) <= sizeof(long)) {
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(T) <= sizeof(long))) {
if (std::is_signed<T>::value)
m_ptr = PyLong_FromLong((long) value);
else
......@@ -1228,6 +1307,7 @@ public:
template <typename T,
detail::enable_if_t<std::is_integral<T>::value, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
operator T() const {
return std::is_unsigned<T>::value
? detail::as_unsigned<T>(m_ptr)
......@@ -1241,13 +1321,17 @@ class float_ : public object {
public:
PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float)
// Allow implicit conversion from float/double:
// NOLINTNEXTLINE(google-explicit-constructor)
float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) {
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{}) {
if (!m_ptr) pybind11_fail("Could not allocate float object!");
}
// NOLINTNEXTLINE(google-explicit-constructor)
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
// NOLINTNEXTLINE(google-explicit-constructor)
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
};
......@@ -1268,11 +1352,20 @@ private:
class slice : public object {
public:
PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check)
slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
int_ start(start_), stop(stop_), step(step_);
slice(handle start, handle stop, handle step) {
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,
size_t *slicelength) const {
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr,
......@@ -1287,6 +1380,12 @@ public:
stop, step,
slicelength) == 0;
}
private:
template <typename T>
static object index_to_object(T index) {
return index ? object(int_(*index)) : object(none());
}
};
class capsule : public object {
......@@ -1322,7 +1421,7 @@ public:
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) {
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
destructor();
......@@ -1332,6 +1431,7 @@ public:
pybind11_fail("Could not allocate capsule object!");
}
// NOLINTNEXTLINE(google-explicit-constructor)
template <typename T> operator T *() const {
return get_pointer<T>();
}
......@@ -1341,14 +1441,19 @@ public:
T* get_pointer() const {
auto name = this->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;
}
/// Replaces a capsule's pointer *without* calling the destructor on the existing one.
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");
}
}
const char *name() const { return PyCapsule_GetName(m_ptr); }
......@@ -1357,7 +1462,10 @@ public:
class tuple : public object {
public:
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!");
}
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
......@@ -1393,7 +1501,7 @@ public:
bool empty() const { return size() == 0; }
detail::dict_iterator begin() const { return {*this, 0}; }
detail::dict_iterator end() const { return {}; }
void clear() const { PyDict_Clear(ptr()); }
void clear() /* py-non-const */ { PyDict_Clear(ptr()); }
template <typename T> bool contains(T &&key) const {
return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
}
......@@ -1426,7 +1534,10 @@ public:
class list : public object {
public:
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!");
}
size_t size() const { return (size_t) PyList_Size(m_ptr); }
......@@ -1435,12 +1546,15 @@ public:
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
detail::list_iterator begin() const { return {*this, 0}; }
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
template <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());
}
template <typename T> void insert(size_t index, T &&val) const {
PyList_Insert(m_ptr, static_cast<ssize_t>(index),
detail::object_or_cast(std::forward<T>(val)).ptr());
template <typename IdxType,
typename ValType,
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:
}
size_t size() const { return (size_t) PySet_Size(m_ptr); }
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;
}
void clear() const { PySet_Clear(m_ptr); }
void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
template <typename T> bool contains(T &&val) const {
return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
}
......
......@@ -9,6 +9,7 @@
#pragma once
#include "detail/common.h"
#include "pybind11.h"
#include <set>
#include <unordered_set>
......@@ -19,33 +20,15 @@
#include <deque>
#include <valarray>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
// See `detail/common.h` for implementation of these guards.
#if defined(PYBIND11_HAS_OPTIONAL)
# include <optional>
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
# include <experimental/optional>
#endif
#ifdef __has_include
// 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>
#if defined(PYBIND11_HAS_VARIANT)
# include <variant>
# define PYBIND11_HAS_OPTIONAL 1
# define PYBIND11_HAS_VARIANT 1
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
......@@ -173,12 +156,12 @@ public:
if (!std::is_lvalue_reference<T>::value)
policy = return_value_policy_override<Value>::policy(policy);
list l(src.size());
size_t index = 0;
ssize_t index = 0;
for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_)
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();
}
......@@ -230,12 +213,12 @@ public:
template <typename T>
static handle cast(T &&src, return_value_policy policy, handle parent) {
list l(src.size());
size_t index = 0;
ssize_t index = 0;
for (auto &&value : src) {
auto value_ = reinterpret_steal<object>(value_conv::cast(forward_like<T>(value), policy, parent));
if (!value_)
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();
}
......@@ -390,7 +373,3 @@ inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
}
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_ &
);
}
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)
......@@ -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) {
using KeyType = typename Map::key_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>;
// 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&&.
}
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<>());
......@@ -628,12 +654,22 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
cl.def("__iter__",
[](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",
[](Map &m) { return make_iterator(m.begin(), m.end()); },
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
[](Map &m) { return ItemsView{m}; },
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
);
cl.def("__getitem__",
......@@ -654,6 +690,8 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
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
detail::map_assignment<Map, Class_>(cl);
......@@ -669,6 +707,40 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&.
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;
}
......
import nox
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)
def lint(session: nox.Session) -> None:
......@@ -13,7 +14,7 @@ def lint(session: nox.Session) -> None:
session.run("pre-commit", "run", "-a")
@nox.session
@nox.session(python=PYTHON_VERISONS)
def tests(session: nox.Session) -> None:
"""
Run the tests (requires a compiler).
......
# -*- coding: utf-8 -*-
from ._version import version_info, __version__
from .commands import get_include, get_cmake_dir
from ._version import __version__, version_info
from .commands import get_cmake_dir, get_include
__all__ = (
"version_info",
......
......@@ -5,7 +5,7 @@ import argparse
import sys
import sysconfig
from .commands import get_include, get_cmake_dir
from .commands import get_cmake_dir, get_include
def print_includes():
......
......@@ -8,5 +8,5 @@ def _to_int(s):
return s
__version__ = "2.7.1"
__version__ = "2.8.0"
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]: ...
......
# -*- coding: utf-8 -*-
import os
DIR = os.path.abspath(os.path.dirname(__file__))
......
......@@ -41,23 +41,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import contextlib
import os
import platform
import shutil
import sys
import sysconfig
import tempfile
import threading
import platform
import warnings
import sysconfig
try:
from setuptools.command.build_ext import build_ext as _build_ext
from setuptools import Extension as _Extension
from setuptools.command.build_ext import build_ext as _build_ext
except ImportError:
from distutils.command.build_ext import build_ext as _build_ext
from distutils.extension import Extension as _Extension
import distutils.errors
import distutils.ccompiler
import distutils.errors
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
PY2 = sys.version_info[0] < 3
......
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
# pre-commit).
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
from types import TracebackType
import contextlib
import distutils.ccompiler
from distutils.command.build_ext import build_ext as _build_ext # type: ignore
from distutils.extension import Extension as _Extension
import distutils.ccompiler
import contextlib
from types import TracebackType
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
WIN: 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