Revert "Update pybind11 to version 2.10.4" for now.
This reverts commits9d077c9ba5
andb4938f5198
. They cause a crash-on-startup on Mac ARM machines, failing to get the locale encoding (nl_langinfo(CODESET) failed).
This commit is contained in:
parent
ba12f00c15
commit
2533141583
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
# See all possible options and defaults with:
|
||||
# clang-format --style=llvm --dump-config
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AllowShortLambdasOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BreakBeforeBinaryOperators: All
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
ColumnLimit: 99
|
||||
CommentPragmas: 'NOLINT:.*|^ IWYU pragma:'
|
||||
IncludeBlocks: Regroup
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
Language: Cpp
|
||||
SpaceAfterCStyleCast: true
|
||||
Standard: Cpp11
|
||||
StatementMacros: ['PyObject_HEAD']
|
||||
TabWidth: 4
|
||||
IncludeCategories:
|
||||
- Regex: '<pybind11/.*'
|
||||
Priority: -1
|
||||
- Regex: 'pybind11.h"$'
|
||||
Priority: 1
|
||||
- Regex: '^".*/?detail/'
|
||||
Priority: 1
|
||||
SortPriority: 2
|
||||
- Regex: '^"'
|
||||
Priority: 1
|
||||
SortPriority: 3
|
||||
- Regex: '<[[:alnum:]._]+>'
|
||||
Priority: 4
|
||||
- Regex: '.*'
|
||||
Priority: 5
|
||||
...
|
|
@ -1,77 +1,13 @@
|
|||
FormatStyle: file
|
||||
|
||||
Checks: |
|
||||
*bugprone*,
|
||||
*performance*,
|
||||
clang-analyzer-optin.cplusplus.VirtualCall,
|
||||
clang-analyzer-optin.performance.Padding,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-prefer-member-initializer,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-slicing,
|
||||
google-explicit-constructor,
|
||||
llvm-namespace-comment,
|
||||
misc-definitions-in-headers,
|
||||
misc-misplaced-const,
|
||||
misc-non-copyable-objects,
|
||||
misc-static-assert,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-uniqueptr-reset-release,
|
||||
misc-unused-parameters,
|
||||
modernize-avoid-bind,
|
||||
modernize-loop-convert,
|
||||
modernize-make-shared,
|
||||
modernize-redundant-void-arg,
|
||||
modernize-replace-auto-ptr,
|
||||
modernize-replace-disallow-copy-and-assign-macro,
|
||||
modernize-replace-random-shuffle,
|
||||
modernize-shrink-to-fit,
|
||||
modernize-use-auto,
|
||||
modernize-use-bool-literals,
|
||||
modernize-use-default-member-init,
|
||||
modernize-use-emplace,
|
||||
modernize-use-equals-default,
|
||||
modernize-use-equals-delete,
|
||||
modernize-use-noexcept,
|
||||
modernize-use-nullptr,
|
||||
modernize-use-override,
|
||||
modernize-use-using,
|
||||
readability-avoid-const-params-in-decls,
|
||||
readability-braces-around-statements,
|
||||
readability-const-return-type,
|
||||
readability-container-size-empty,
|
||||
readability-delete-null-pointer,
|
||||
readability-else-after-return,
|
||||
readability-implicit-bool-conversion,
|
||||
readability-inconsistent-declaration-parameter-name,
|
||||
readability-make-member-function-const,
|
||||
readability-misplaced-array-index,
|
||||
readability-non-const-parameter,
|
||||
readability-qualified-auto,
|
||||
readability-redundant-function-ptr-dereference,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-string-compare,
|
||||
readability-suspicious-call-argument,
|
||||
readability-uniqueptr-delete-release,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-reserved-identifier,
|
||||
-bugprone-unused-raii,
|
||||
|
||||
CheckOptions:
|
||||
- key: modernize-use-equals-default.IgnoreMacros
|
||||
value: false
|
||||
- key: performance-for-range-copy.WarnOnAllAutoCopies
|
||||
value: true
|
||||
- key: performance-inefficient-string-concatenation.StrictMode
|
||||
value: true
|
||||
- key: performance-unnecessary-value-param.AllowedTypes
|
||||
value: 'exception_ptr$;'
|
||||
- key: readability-implicit-bool-conversion.AllowPointerConditions
|
||||
value: true
|
||||
Checks: '
|
||||
llvm-namespace-comment,
|
||||
modernize-use-override,
|
||||
readability-container-size-empty,
|
||||
modernize-use-using,
|
||||
modernize-use-equals-default,
|
||||
modernize-use-auto,
|
||||
modernize-use-emplace,
|
||||
'
|
||||
|
||||
HeaderFilterRegex: 'pybind11/.*h'
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
parse:
|
||||
additional_commands:
|
||||
pybind11_add_module:
|
||||
flags:
|
||||
- THIN_LTO
|
||||
- MODULE
|
||||
- SHARED
|
||||
- NO_EXTRAS
|
||||
- EXCLUDE_FROM_ALL
|
||||
- SYSTEM
|
||||
|
||||
format:
|
||||
line_width: 99
|
||||
tab_size: 2
|
||||
|
||||
# If an argument group contains more than this many sub-groups
|
||||
# (parg or kwarg groups) then force it to a vertical layout.
|
||||
max_subgroups_hwrap: 2
|
||||
|
||||
# If a positional argument group contains more than this many
|
||||
# arguments, then force it to a vertical layout.
|
||||
max_pargs_hwrap: 6
|
||||
|
||||
# If a cmdline positional group consumes more than this many
|
||||
# lines without nesting, then invalidate the layout (and nest)
|
||||
max_rows_cmdline: 2
|
||||
separate_ctrl_name_with_space: false
|
||||
separate_fn_name_with_space: false
|
||||
dangle_parens: false
|
||||
|
||||
# If the trailing parenthesis must be 'dangled' on its on
|
||||
# 'line, then align it to this reference: `prefix`: the start'
|
||||
# 'of the statement, `prefix-indent`: the start of the'
|
||||
# 'statement, plus one indentation level, `child`: align to'
|
||||
# the column of the arguments
|
||||
dangle_align: prefix
|
||||
# If the statement spelling length (including space and
|
||||
# parenthesis) is smaller than this amount, then force reject
|
||||
# nested layouts.
|
||||
min_prefix_chars: 4
|
||||
|
||||
# If the statement spelling length (including space and
|
||||
# parenthesis) is larger than the tab width by more than this
|
||||
# amount, then force reject un-nested layouts.
|
||||
max_prefix_chars: 10
|
||||
|
||||
# If a candidate layout is wrapped horizontally but it exceeds
|
||||
# this many lines, then reject the layout.
|
||||
max_lines_hwrap: 2
|
||||
|
||||
line_ending: unix
|
||||
|
||||
# Format command names consistently as 'lower' or 'upper' case
|
||||
command_case: canonical
|
||||
|
||||
# Format keywords consistently as 'lower' or 'upper' case
|
||||
# unchanged is valid too
|
||||
keyword_case: 'upper'
|
||||
|
||||
# A list of command names which should always be wrapped
|
||||
always_wrap: []
|
||||
|
||||
# If true, the argument lists which are known to be sortable
|
||||
# will be sorted lexicographically
|
||||
enable_sort: true
|
||||
|
||||
# If true, the parsers may infer whether or not an argument
|
||||
# list is sortable (without annotation).
|
||||
autosort: false
|
||||
|
||||
# Causes a few issues - can be solved later, possibly.
|
||||
markup:
|
||||
enable_markup: false
|
|
@ -1,24 +0,0 @@
|
|||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
|
||||
template <typename ThisT>
|
||||
auto &this_ = static_cast<ThisT &>(*this);
|
||||
if (load_impl<ThisT>(temp, false)) {
|
||||
ssize_t nd = 0;
|
||||
auto trivial = broadcast(buffers, nd, shape);
|
||||
auto ndim = (size_t) nd;
|
||||
int nd;
|
||||
ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
|
||||
struct IntStruct {
|
||||
explicit IntStruct(int v) : value(v){};
|
||||
~IntStruct() { value = -value; }
|
||||
IntStruct(const IntStruct &) = default;
|
||||
IntStruct &operator=(const IntStruct &) = default;
|
||||
py::class_<IntStruct>(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); }));
|
||||
py::implicitly_convertible<int, IntStruct>();
|
||||
m.def("test", [](int expected, const IntStruct &in) {
|
||||
[](int expected, const IntStruct &in) {
|
|
@ -1 +0,0 @@
|
|||
docs/*.svg binary
|
|
@ -41,6 +41,3 @@ pybind11Targets.cmake
|
|||
/.vscode
|
||||
/pybind11/include/*
|
||||
/pybind11/share/*
|
||||
/docs/_build/*
|
||||
.ipynb_checkpoints/
|
||||
tests/main.cpp
|
||||
|
|
|
@ -91,16 +91,10 @@ endif()
|
|||
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT})
|
||||
option(PYBIND11_NOPYTHON "Disable search for Python" OFF)
|
||||
option(PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
"Use simpler GIL management logic that does not support disassociation" OFF)
|
||||
set(PYBIND11_INTERNALS_VERSION
|
||||
""
|
||||
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.")
|
||||
|
||||
if(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
endif()
|
||||
|
||||
cmake_dependent_option(
|
||||
USE_PYTHON_INCLUDE_DIR
|
||||
"Install pybind11 headers in Python include directory instead of default installation prefix"
|
||||
|
@ -126,8 +120,6 @@ set(PYBIND11_HEADERS
|
|||
include/pybind11/complex.h
|
||||
include/pybind11/options.h
|
||||
include/pybind11/eigen.h
|
||||
include/pybind11/eigen/matrix.h
|
||||
include/pybind11/eigen/tensor.h
|
||||
include/pybind11/embed.h
|
||||
include/pybind11/eval.h
|
||||
include/pybind11/gil.h
|
||||
|
@ -206,9 +198,6 @@ else()
|
|||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake")
|
||||
# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files
|
||||
# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake")
|
||||
|
||||
# Relative directory setting
|
||||
if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS)
|
||||
|
@ -273,16 +262,6 @@ if(PYBIND11_INSTALL)
|
|||
NAMESPACE "pybind11::"
|
||||
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR})
|
||||
|
||||
# pkg-config support
|
||||
if(NOT prefix_for_pc_file)
|
||||
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}")
|
||||
endif()
|
||||
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
|
||||
|
||||
# Uninstall target
|
||||
if(PYBIND11_MASTER_PROJECT)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
prune tests
|
||||
recursive-include pybind11/include/pybind11 *.h
|
||||
recursive-include pybind11 *.py
|
||||
recursive-include pybind11 py.typed
|
||||
recursive-include pybind11 *.pyi
|
||||
include pybind11/share/cmake/pybind11/*.cmake
|
||||
include LICENSE README.rst pyproject.toml setup.py setup.cfg
|
||||
|
|
|
@ -32,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary
|
|||
dependency.
|
||||
|
||||
Think of this library as a tiny self-contained version of Boost.Python
|
||||
with everything stripped away that isn't relevant for binding
|
||||
with everything stripped away that isn’t relevant for binding
|
||||
generation. Without comments, the core header files only require ~4K
|
||||
lines of code and depend on Python (3.6+, or PyPy) and the C++
|
||||
lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++
|
||||
standard library. This compact implementation was possible thanks to
|
||||
some of the new C++11 language features (specifically: tuples, lambda
|
||||
functions and variadic templates). Since its creation, this library has
|
||||
|
@ -78,8 +78,8 @@ Goodies
|
|||
In addition to the core functionality, pybind11 provides some extra
|
||||
goodies:
|
||||
|
||||
- Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic
|
||||
interface (pybind11 2.9 was the last version to support Python 2 and 3.5).
|
||||
- Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an
|
||||
implementation-agnostic interface.
|
||||
|
||||
- It is possible to bind C++11 lambda functions with captured
|
||||
variables. The lambda capture data is stored inside the resulting
|
||||
|
@ -88,8 +88,8 @@ goodies:
|
|||
- pybind11 uses C++11 move constructors and move assignment operators
|
||||
whenever possible to efficiently transfer custom data types.
|
||||
|
||||
- It's easy to expose the internal storage of custom data types through
|
||||
Pythons' buffer protocols. This is handy e.g. for fast conversion
|
||||
- It’s easy to expose the internal storage of custom data types through
|
||||
Pythons’ buffer protocols. This is handy e.g. for fast conversion
|
||||
between C++ matrix classes like Eigen and NumPy without expensive
|
||||
copy operations.
|
||||
|
||||
|
@ -119,10 +119,10 @@ goodies:
|
|||
Supported compilers
|
||||
-------------------
|
||||
|
||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or
|
||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode’s clang, this is 5.0.0 or
|
||||
newer)
|
||||
2. GCC 4.8 or newer
|
||||
3. Microsoft Visual Studio 2017 or newer
|
||||
3. Microsoft Visual Studio 2015 Update 3 or newer
|
||||
4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI)
|
||||
5. Cygwin/GCC (previously tested on 2.5.1)
|
||||
6. NVCC (CUDA 11.0 tested in CI)
|
||||
|
|
|
@ -18,4 +18,5 @@ ALIASES += "endrst=\endverbatim"
|
|||
QUIET = YES
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
PREDEFINED = PYBIND11_NOINLINE
|
||||
PREDEFINED = PY_MAJOR_VERSION=3 \
|
||||
PYBIND11_NOINLINE
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.highlight .go {
|
||||
color: #707070;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
.wy-table-responsive table td,
|
||||
.wy-table-responsive table th {
|
||||
white-space: initial !important;
|
||||
}
|
||||
.rst-content table.docutils td {
|
||||
vertical-align: top !important;
|
||||
}
|
||||
div[class^='highlight'] pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
}
|
|
@ -38,7 +38,7 @@ type is explicitly allowed.
|
|||
|
||||
.. code-block:: cpp
|
||||
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
namespace pybind11 { namespace detail {
|
||||
template <> struct type_caster<inty> {
|
||||
public:
|
||||
/**
|
||||
|
@ -78,7 +78,7 @@ type is explicitly allowed.
|
|||
return PyLong_FromLong(src.long_value);
|
||||
}
|
||||
};
|
||||
}} // namespace PYBIND11_NAMESPACE::detail
|
||||
}} // namespace pybind11::detail
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -167,4 +167,5 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
|
|||
+------------------------------------+---------------------------+-----------------------------------+
|
||||
|
||||
.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and
|
||||
``os.PathLike`` is converted to ``std::filesystem::path``.
|
||||
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires
|
||||
Python 3.6 (for ``__fspath__`` support).
|
||||
|
|
|
@ -42,7 +42,7 @@ types:
|
|||
.. code-block:: cpp
|
||||
|
||||
// `boost::optional` as an example -- can be any `std::optional`-like container
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
namespace pybind11 { namespace detail {
|
||||
template <typename T>
|
||||
struct type_caster<boost::optional<T>> : optional_caster<boost::optional<T>> {};
|
||||
}}
|
||||
|
@ -54,7 +54,7 @@ for custom variant types:
|
|||
.. code-block:: cpp
|
||||
|
||||
// `boost::variant` as an example -- can be any `std::variant`-like container
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
namespace pybind11 { namespace detail {
|
||||
template <typename... Ts>
|
||||
struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
|
||||
|
||||
|
@ -66,7 +66,7 @@ for custom variant types:
|
|||
return boost::apply_visitor(args...);
|
||||
}
|
||||
};
|
||||
}} // namespace PYBIND11_NAMESPACE::detail
|
||||
}} // namespace pybind11::detail
|
||||
|
||||
The ``visit_helper`` specialization is not required if your ``name::variant`` provides
|
||||
a ``name::visit()`` function. For any other function name, the specialization must be
|
||||
|
@ -87,6 +87,8 @@ included to tell pybind11 how to visit the variant.
|
|||
|
||||
pybind11 only supports the modern implementation of ``boost::variant``
|
||||
which makes use of variadic templates. This requires Boost 1.56 or newer.
|
||||
Additionally, on Windows, MSVC 2017 is required because ``boost::variant``
|
||||
falls back to the old non-variadic implementation on MSVC 2015.
|
||||
|
||||
.. _opaque:
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
Strings, bytes and Unicode conversions
|
||||
######################################
|
||||
|
||||
.. note::
|
||||
|
||||
This section discusses string handling in terms of Python 3 strings. For
|
||||
Python 2.7, replace all occurrences of ``str`` with ``unicode`` and
|
||||
``bytes`` with ``str``. Python 2.7 users may find it best to use ``from
|
||||
__future__ import unicode_literals`` to avoid unintentionally using ``str``
|
||||
instead of ``unicode``.
|
||||
|
||||
Passing Python strings to C++
|
||||
=============================
|
||||
|
||||
|
@ -50,9 +58,9 @@ Passing bytes to C++
|
|||
--------------------
|
||||
|
||||
A Python ``bytes`` object will be passed to C++ functions that accept
|
||||
``std::string`` or ``char*`` *without* conversion. In order to make a function
|
||||
*only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes``
|
||||
argument.
|
||||
``std::string`` or ``char*`` *without* conversion. On Python 3, in order to
|
||||
make a function *only* accept ``bytes`` (and not ``str``), declare it as taking
|
||||
a ``py::bytes`` argument.
|
||||
|
||||
|
||||
Returning C++ strings to Python
|
||||
|
@ -196,6 +204,11 @@ decoded to Python ``str``.
|
|||
}
|
||||
);
|
||||
|
||||
.. warning::
|
||||
|
||||
Wide character strings may not work as described on Python 2.7 or Python
|
||||
3.3 compiled with ``--enable-unicode=ucs2``.
|
||||
|
||||
Strings in multibyte encodings such as Shift-JIS must transcoded to a
|
||||
UTF-8/16/32 before being returned to Python.
|
||||
|
||||
|
|
|
@ -133,14 +133,14 @@ a virtual method call.
|
|||
>>> from example import *
|
||||
>>> d = Dog()
|
||||
>>> call_go(d)
|
||||
'woof! woof! woof! '
|
||||
u'woof! woof! woof! '
|
||||
>>> class Cat(Animal):
|
||||
... def go(self, n_times):
|
||||
... return "meow! " * n_times
|
||||
...
|
||||
>>> c = Cat()
|
||||
>>> call_go(c)
|
||||
'meow! meow! meow! '
|
||||
u'meow! meow! meow! '
|
||||
|
||||
If you are defining a custom constructor in a derived Python class, you *must*
|
||||
ensure that you explicitly call the bound C++ constructor using ``__init__``,
|
||||
|
@ -813,21 +813,26 @@ An instance can now be pickled as follows:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
import pickle
|
||||
try:
|
||||
import cPickle as pickle # Use cPickle on Python 2.7
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
p = Pickleable("test_value")
|
||||
p.setExtra(15)
|
||||
data = pickle.dumps(p)
|
||||
data = pickle.dumps(p, 2)
|
||||
|
||||
|
||||
.. note::
|
||||
If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are
|
||||
not supported. Newer versions are also fine; for instance, specify ``-1`` to
|
||||
always use the latest available version. Beware: failure to follow these
|
||||
instructions will cause important pybind11 memory allocation routines to be
|
||||
skipped during unpickling, which will likely lead to memory corruption
|
||||
and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and
|
||||
version 4 for Python 3.8+.
|
||||
Note that only the cPickle module is supported on Python 2.7.
|
||||
|
||||
The second argument to ``dumps`` is also crucial: it selects the pickle
|
||||
protocol version 2, since the older version 1 is not supported. Newer
|
||||
versions are also fine—for instance, specify ``-1`` to always use the
|
||||
latest available version. Beware: failure to follow these instructions
|
||||
will cause important pybind11 memory allocation routines to be skipped
|
||||
during unpickling, which will likely lead to memory corruption and/or
|
||||
segmentation faults.
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
@ -844,9 +849,11 @@ Python normally uses references in assignments. Sometimes a real copy is needed
|
|||
to prevent changing all copies. The ``copy`` module [#f5]_ provides these
|
||||
capabilities.
|
||||
|
||||
A class with pickle support is automatically also (deep)copy
|
||||
On Python 3, a class with pickle support is automatically also (deep)copy
|
||||
compatible. However, performance can be improved by adding custom
|
||||
``__copy__`` and ``__deepcopy__`` methods.
|
||||
``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods
|
||||
are mandatory for (deep)copy compatibility, because pybind11 only supports
|
||||
cPickle.
|
||||
|
||||
For simple classes (deep)copy can be enabled by using the copy constructor,
|
||||
which should look as follows:
|
||||
|
@ -1118,6 +1125,13 @@ described trampoline:
|
|||
py::class_<A, Trampoline>(m, "A") // <-- `Trampoline` here
|
||||
.def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`!
|
||||
|
||||
.. note::
|
||||
|
||||
MSVC 2015 has a compiler bug (fixed in version 2017) which
|
||||
requires a more explicit function binding in the form of
|
||||
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
|
||||
where ``int (A::*)() const`` is the type of ``A::foo``.
|
||||
|
||||
Binding final classes
|
||||
=====================
|
||||
|
||||
|
@ -1228,7 +1242,7 @@ whether a downcast is safe, you can proceed by specializing the
|
|||
std::string bark() const { return sound; }
|
||||
};
|
||||
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace pybind11 {
|
||||
template<> struct polymorphic_type_hook<Pet> {
|
||||
static const void *get(const Pet *src, const std::type_info*& type) {
|
||||
// note that src may be nullptr
|
||||
|
@ -1239,7 +1253,7 @@ whether a downcast is safe, you can proceed by specializing the
|
|||
return src;
|
||||
}
|
||||
};
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
} // namespace pybind11
|
||||
|
||||
When pybind11 wants to convert a C++ pointer of type ``Base*`` to a
|
||||
Python object, it calls ``polymorphic_type_hook<Base>::get()`` to
|
||||
|
|
|
@ -177,12 +177,9 @@ section.
|
|||
may be explicitly (re-)thrown to delegate it to the other,
|
||||
previously-declared existing exception translators.
|
||||
|
||||
Note that ``libc++`` and ``libstdc++`` `behave differently under macOS
|
||||
<https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
|
||||
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI
|
||||
boundaries need to be explicitly exported, as exercised in
|
||||
``tests/test_exceptions.h``. See also:
|
||||
"Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
|
||||
Note that ``libc++`` and ``libstdc++`` `behave differently <https://stackoverflow.com/questions/19496643/using-clang-fvisibility-hidden-and-typeinfo-and-type-erasure/28827430>`_
|
||||
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``.
|
||||
See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
|
||||
|
||||
|
||||
Local vs Global Exception Translators
|
||||
|
@ -331,8 +328,8 @@ an invalid state.
|
|||
Chaining exceptions ('raise from')
|
||||
==================================
|
||||
|
||||
Python has a mechanism for indicating that exceptions were caused by other
|
||||
exceptions:
|
||||
In Python 3.3 a mechanism for indicating that exceptions were caused by other
|
||||
exceptions was introduced:
|
||||
|
||||
.. code-block:: py
|
||||
|
||||
|
@ -343,7 +340,7 @@ exceptions:
|
|||
|
||||
To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It
|
||||
sets the current python error indicator, so to continue propagating the exception
|
||||
you should ``throw py::error_already_set()``.
|
||||
you should ``throw py::error_already_set()`` (Python 3 only).
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
|
|
|
@ -372,7 +372,7 @@ like so:
|
|||
Keyword-only arguments
|
||||
======================
|
||||
|
||||
Python implements keyword-only arguments by specifying an unnamed ``*``
|
||||
Python 3 introduced keyword-only arguments by specifying an unnamed ``*``
|
||||
argument in a function definition:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -395,18 +395,19 @@ argument annotations when registering the function:
|
|||
m.def("f", [](int a, int b) { /* ... */ },
|
||||
py::arg("a"), py::kw_only(), py::arg("b"));
|
||||
|
||||
Note that you currently cannot combine this with a ``py::args`` argument. This
|
||||
feature does *not* require Python 3 to work.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
|
||||
A ``py::args`` argument implies that any following arguments are keyword-only,
|
||||
as if ``py::kw_only()`` had been specified in the same relative location of the
|
||||
argument list as the ``py::args`` argument. The ``py::kw_only()`` may be
|
||||
included to be explicit about this, but is not required.
|
||||
|
||||
.. versionchanged:: 2.9
|
||||
This can now be combined with ``py::args``. Before, ``py::args`` could only
|
||||
occur at the end of the argument list, or immediately before a ``py::kwargs``
|
||||
argument at the end.
|
||||
As of pybind11 2.9, a ``py::args`` argument implies that any following arguments
|
||||
are keyword-only, as if ``py::kw_only()`` had been specified in the same
|
||||
relative location of the argument list as the ``py::args`` argument. The
|
||||
``py::kw_only()`` may be included to be explicit about this, but is not
|
||||
required. (Prior to 2.9 ``py::args`` may only occur at the end of the argument
|
||||
list, or immediately before a ``py::kwargs`` argument at the end).
|
||||
|
||||
.. versionadded:: 2.9
|
||||
|
||||
Positional-only arguments
|
||||
=========================
|
||||
|
|
|
@ -39,42 +39,15 @@ The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds.
|
|||
Global Interpreter Lock (GIL)
|
||||
=============================
|
||||
|
||||
The Python C API dictates that the Global Interpreter Lock (GIL) must always
|
||||
be held by the current thread to safely access Python objects. As a result,
|
||||
when Python calls into C++ via pybind11 the GIL must be held, and pybind11
|
||||
will never implicitly release the GIL.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void my_function() {
|
||||
/* GIL is held when this function is called from Python */
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(example, m) {
|
||||
m.def("my_function", &my_function);
|
||||
}
|
||||
|
||||
pybind11 will ensure that the GIL is held when it knows that it is calling
|
||||
Python code. For example, if a Python callback is passed to C++ code via
|
||||
``std::function``, when C++ code calls the function the built-in wrapper
|
||||
will acquire the GIL before calling the Python callback. Similarly, the
|
||||
``PYBIND11_OVERRIDE`` family of macros will acquire the GIL before calling
|
||||
back into Python.
|
||||
|
||||
When writing C++ code that is called from other C++ code, if that code accesses
|
||||
Python state, it must explicitly acquire and release the GIL.
|
||||
|
||||
When calling a C++ function from Python, the GIL is always held.
|
||||
The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be
|
||||
used to acquire and release the global interpreter lock in the body of a C++
|
||||
function call. In this way, long-running C++ code can be parallelized using
|
||||
multiple Python threads, **but great care must be taken** when any
|
||||
:class:`gil_scoped_release` appear: if there is any way that the C++ code
|
||||
can access Python objects, :class:`gil_scoped_acquire` should be used to
|
||||
reacquire the GIL. Taking :ref:`overriding_virtuals` as an example, this
|
||||
multiple Python threads. Taking :ref:`overriding_virtuals` as an example, this
|
||||
could be realized as follows (important changes highlighted):
|
||||
|
||||
.. code-block:: cpp
|
||||
:emphasize-lines: 8,30,31
|
||||
:emphasize-lines: 8,9,31,32
|
||||
|
||||
class PyAnimal : public Animal {
|
||||
public:
|
||||
|
@ -83,7 +56,9 @@ could be realized as follows (important changes highlighted):
|
|||
|
||||
/* Trampoline (need one for each virtual function) */
|
||||
std::string go(int n_times) {
|
||||
/* PYBIND11_OVERRIDE_PURE will acquire the GIL before accessing Python state */
|
||||
/* Acquire GIL before calling Python code */
|
||||
py::gil_scoped_acquire acquire;
|
||||
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
std::string, /* Return type */
|
||||
Animal, /* Parent class */
|
||||
|
@ -103,8 +78,7 @@ could be realized as follows (important changes highlighted):
|
|||
.def(py::init<>());
|
||||
|
||||
m.def("call_go", [](Animal *animal) -> std::string {
|
||||
// GIL is held when called from Python code. Release GIL before
|
||||
// calling into (potentially long-running) C++ code
|
||||
/* Release GIL before calling into (potentially long-running) C++ code */
|
||||
py::gil_scoped_release release;
|
||||
return call_go(animal);
|
||||
});
|
||||
|
@ -118,34 +92,6 @@ The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
|
|||
m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>());
|
||||
|
||||
|
||||
Common Sources Of Global Interpreter Lock Errors
|
||||
==================================================================
|
||||
|
||||
Failing to properly hold the Global Interpreter Lock (GIL) is one of the
|
||||
more common sources of bugs within code that uses pybind11. If you are
|
||||
running into GIL related errors, we highly recommend you consult the
|
||||
following checklist.
|
||||
|
||||
- Do you have any global variables that are pybind11 objects or invoke
|
||||
pybind11 functions in either their constructor or destructor? You are generally
|
||||
not allowed to invoke any Python function in a global static context. We recommend
|
||||
using lazy initialization and then intentionally leaking at the end of the program.
|
||||
|
||||
- Do you have any pybind11 objects that are members of other C++ structures? One
|
||||
commonly overlooked requirement is that pybind11 objects have to increase their reference count
|
||||
whenever their copy constructor is called. Thus, you need to be holding the GIL to invoke
|
||||
the copy constructor of any C++ class that has a pybind11 member. This can sometimes be very
|
||||
tricky to track for complicated programs Think carefully when you make a pybind11 object
|
||||
a member in another struct.
|
||||
|
||||
- C++ destructors that invoke Python functions can be particularly troublesome as
|
||||
destructors can sometimes get invoked in weird and unexpected circumstances as a result
|
||||
of exceptions.
|
||||
|
||||
- You should try running your code in a debug build. That will enable additional assertions
|
||||
within pybind11 that will throw exceptions on certain GIL handling errors
|
||||
(reference counting operations).
|
||||
|
||||
Binding sequence data types, iterators, the slicing protocol, etc.
|
||||
==================================================================
|
||||
|
||||
|
@ -352,15 +298,6 @@ The class ``options`` allows you to selectively suppress auto-generated signatur
|
|||
m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers");
|
||||
}
|
||||
|
||||
pybind11 also appends all members of an enum to the resulting enum docstring.
|
||||
This default behavior can be disabled by using the ``disable_enum_members_docstring()``
|
||||
function of the ``options`` class.
|
||||
|
||||
With ``disable_user_defined_docstrings()`` all user defined docstrings of
|
||||
``module_::def()``, ``class_::def()`` and ``enum_()`` are disabled, but the
|
||||
function signatures and enum members are included in the docstring, unless they
|
||||
are disabled separately.
|
||||
|
||||
Note that changes to the settings affect only function bindings created during the
|
||||
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
|
||||
the default settings are restored to prevent unwanted side effects.
|
||||
|
|
|
@ -87,7 +87,7 @@ buffer objects (e.g. a NumPy matrix).
|
|||
/* Request a buffer descriptor from Python */
|
||||
py::buffer_info info = b.request();
|
||||
|
||||
/* Some basic validation checks ... */
|
||||
/* Some sanity checks ... */
|
||||
if (info.format != py::format_descriptor<Scalar>::format())
|
||||
throw std::runtime_error("Incompatible format: expected a double array!");
|
||||
|
||||
|
@ -395,9 +395,11 @@ uses of ``py::array``:
|
|||
Ellipsis
|
||||
========
|
||||
|
||||
Python provides a convenient ``...`` ellipsis notation that is often used to
|
||||
Python 3 provides a convenient ``...`` ellipsis notation that is often used to
|
||||
slice multidimensional arrays. For instance, the following snippet extracts the
|
||||
middle dimensions of a tensor with the first and last index set to zero.
|
||||
In Python 2, the syntactic sugar ``...`` is not available, but the singleton
|
||||
``Ellipsis`` (of type ``ellipsis``) can still be used directly.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -412,6 +414,8 @@ operation on the C++ side:
|
|||
py::array a = /* A NumPy array */;
|
||||
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)];
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
``py::ellipsis()`` is now also available in Python 2.
|
||||
|
||||
Memory view
|
||||
===========
|
||||
|
@ -433,7 +437,7 @@ following:
|
|||
{ 2, 4 }, // shape (rows, cols)
|
||||
{ sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes
|
||||
);
|
||||
});
|
||||
})
|
||||
|
||||
This approach is meant for providing a ``memoryview`` for a C/C++ buffer not
|
||||
managed by Python. The user is responsible for managing the lifetime of the
|
||||
|
@ -449,7 +453,11 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer:
|
|||
buffer, // buffer pointer
|
||||
sizeof(uint8_t) * 8 // buffer size
|
||||
);
|
||||
});
|
||||
})
|
||||
|
||||
.. note::
|
||||
|
||||
``memoryview::from_memory`` is not available in Python 2.
|
||||
|
||||
.. versionchanged:: 2.6
|
||||
``memoryview::from_memory`` added.
|
||||
|
|
|
@ -157,7 +157,7 @@ specialized:
|
|||
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
|
||||
|
||||
// Only needed if the type's `.get()` goes by another name
|
||||
namespace PYBIND11_NAMESPACE { namespace detail {
|
||||
namespace pybind11 { namespace detail {
|
||||
template <typename T>
|
||||
struct holder_helper<SmartPtr<T>> { // <-- specialization
|
||||
static const T *get(const SmartPtr<T> &p) { return p.getPointer(); }
|
||||
|
|
|
@ -32,7 +32,8 @@ The last line will both compile and run the tests.
|
|||
Windows
|
||||
-------
|
||||
|
||||
On Windows, only **Visual Studio 2017** and newer are supported.
|
||||
On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies
|
||||
on various C++11 language features that break older versions of Visual Studio.
|
||||
|
||||
.. Note::
|
||||
|
||||
|
@ -165,12 +166,12 @@ load and execute the example:
|
|||
.. code-block:: pycon
|
||||
|
||||
$ python
|
||||
Python 3.9.10 (main, Jan 15 2022, 11:48:04)
|
||||
[Clang 13.0.0 (clang-1300.0.29.3)] on darwin
|
||||
Python 2.7.10 (default, Aug 22 2015, 20:33:39)
|
||||
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>> import example
|
||||
>>> example.add(1, 2)
|
||||
3
|
||||
3L
|
||||
>>>
|
||||
|
||||
.. _keyword_args:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime as dt
|
||||
import os
|
||||
import random
|
||||
|
@ -11,20 +12,20 @@ def generate_dummy_code_pybind11(nclasses=10):
|
|||
bindings = ""
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += f"class cl{cl:03};\n"
|
||||
decl += "class cl%03i;\n" % cl
|
||||
decl += "\n"
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += f"class {cl:03} {{\n"
|
||||
decl += "class cl%03i {\n" % cl
|
||||
decl += "public:\n"
|
||||
bindings += f' py::class_<cl{cl:03}>(m, "cl{cl:03}")\n'
|
||||
bindings += ' py::class_<cl%03i>(m, "cl%03i")\n' % (cl, cl)
|
||||
for fn in range(nfns):
|
||||
ret = random.randint(0, nclasses - 1)
|
||||
params = [random.randint(0, nclasses - 1) for i in range(nargs)]
|
||||
decl += f" cl{ret:03} *fn_{fn:03}("
|
||||
decl += ", ".join(f"cl{p:03} *" for p in params)
|
||||
decl += " cl%03i *fn_%03i(" % (ret, fn)
|
||||
decl += ", ".join("cl%03i *" % p for p in params)
|
||||
decl += ");\n"
|
||||
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n'
|
||||
bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn)
|
||||
decl += "};\n\n"
|
||||
bindings += " ;\n"
|
||||
|
||||
|
@ -42,20 +43,23 @@ def generate_dummy_code_boost(nclasses=10):
|
|||
bindings = ""
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += f"class cl{cl:03};\n"
|
||||
decl += "class cl%03i;\n" % cl
|
||||
decl += "\n"
|
||||
|
||||
for cl in range(nclasses):
|
||||
decl += "class cl%03i {\n" % cl
|
||||
decl += "public:\n"
|
||||
bindings += f' py::class_<cl{cl:03}>("cl{cl:03}")\n'
|
||||
bindings += ' py::class_<cl%03i>("cl%03i")\n' % (cl, cl)
|
||||
for fn in range(nfns):
|
||||
ret = random.randint(0, nclasses - 1)
|
||||
params = [random.randint(0, nclasses - 1) for i in range(nargs)]
|
||||
decl += f" cl{ret:03} *fn_{fn:03}("
|
||||
decl += ", ".join(f"cl{p:03} *" for p in params)
|
||||
decl += " cl%03i *fn_%03i(" % (ret, fn)
|
||||
decl += ", ".join("cl%03i *" % p for p in params)
|
||||
decl += ");\n"
|
||||
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy<py::manage_new_object>())\n'
|
||||
bindings += (
|
||||
' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n'
|
||||
% (fn, cl, fn)
|
||||
)
|
||||
decl += "};\n\n"
|
||||
bindings += " ;\n"
|
||||
|
||||
|
@ -71,7 +75,7 @@ def generate_dummy_code_boost(nclasses=10):
|
|||
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
|
||||
print("{")
|
||||
for i in range(0, 10):
|
||||
nclasses = 2**i
|
||||
nclasses = 2 ** i
|
||||
with open("test.cpp", "w") as f:
|
||||
f.write(codegen(nclasses))
|
||||
n1 = dt.datetime.now()
|
||||
|
|
|
@ -9,446 +9,49 @@ Starting with version 1.8.0, pybind11 releases use a `semantic versioning
|
|||
Changes will be added here periodically from the "Suggested changelog entry"
|
||||
block in pull request descriptions.
|
||||
|
||||
|
||||
IN DEVELOPMENT
|
||||
--------------
|
||||
|
||||
Changes will be summarized here periodically.
|
||||
|
||||
Changes:
|
||||
|
||||
* ``PyGILState_Check()``'s in ``pybind11::handle``'s ``inc_ref()`` &
|
||||
``dec_ref()`` are now enabled by default again.
|
||||
`#4246 <https://github.com/pybind/pybind11/pull/4246>`_
|
||||
|
||||
* ``py::initialize_interpreter()`` using ``PyConfig_InitPythonConfig()``
|
||||
instead of ``PyConfig_InitIsolatedConfig()``, to obtain complete
|
||||
``sys.path``.
|
||||
`#4473 <https://github.com/pybind/pybind11/pull/4473>`_
|
||||
|
||||
* Cast errors now always include Python type information, even if
|
||||
``PYBIND11_DETAILED_ERROR_MESSAGES`` is not defined. This increases binary
|
||||
sizes slightly (~1.5%) but the error messages are much more informative.
|
||||
`#4463 <https://github.com/pybind/pybind11/pull/4463>`_
|
||||
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* Update clang-tidy to 15 in CI.
|
||||
`#4387 <https://github.com/pybind/pybind11/pull/4387>`_
|
||||
|
||||
* Moved the linting framework over to Ruff.
|
||||
`#4483 <https://github.com/pybind/pybind11/pull/4483>`_
|
||||
|
||||
Version 2.10.4 (Mar 16, 2023)
|
||||
----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* ``python3 -m pybind11`` gained a ``--version`` option (prints the version and
|
||||
exits).
|
||||
`#4526 <https://github.com/pybind/pybind11/pull/4526>`_
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
* Fix a warning when pydebug is enabled on Python 3.11.
|
||||
`#4461 <https://github.com/pybind/pybind11/pull/4461>`_
|
||||
|
||||
* Ensure ``gil_scoped_release`` RAII is non-copyable.
|
||||
`#4490 <https://github.com/pybind/pybind11/pull/4490>`_
|
||||
|
||||
* Ensure the tests dir does not show up with new versions of setuptools.
|
||||
`#4510 <https://github.com/pybind/pybind11/pull/4510>`_
|
||||
|
||||
* Better stacklevel for a warning in setuptools helpers.
|
||||
`#4516 <https://github.com/pybind/pybind11/pull/4516>`_
|
||||
|
||||
Version 2.10.3 (Jan 3, 2023)
|
||||
----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* Temporarily made our GIL status assertions (added in 2.10.2) disabled by
|
||||
default (re-enable manually by defining
|
||||
``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, will be enabled in 2.11).
|
||||
`#4432 <https://github.com/pybind/pybind11/pull/4432>`_
|
||||
|
||||
* Improved error messages when ``inc_ref``/``dec_ref`` are called with an
|
||||
invalid GIL state.
|
||||
`#4427 <https://github.com/pybind/pybind11/pull/4427>`_
|
||||
`#4436 <https://github.com/pybind/pybind11/pull/4436>`_
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
* Some minor touchups found by static analyzers.
|
||||
`#4440 <https://github.com/pybind/pybind11/pull/4440>`_
|
||||
|
||||
|
||||
Version 2.10.2 (Dec 20, 2022)
|
||||
-----------------------------
|
||||
|
||||
Changes:
|
||||
|
||||
* ``scoped_interpreter`` constructor taking ``PyConfig``.
|
||||
`#4330 <https://github.com/pybind/pybind11/pull/4330>`_
|
||||
|
||||
* ``pybind11/eigen/tensor.h`` adds converters to and from ``Eigen::Tensor`` and
|
||||
``Eigen::TensorMap``.
|
||||
`#4201 <https://github.com/pybind/pybind11/pull/4201>`_
|
||||
|
||||
* ``PyGILState_Check()``'s were integrated to ``pybind11::handle``
|
||||
``inc_ref()`` & ``dec_ref()``. The added GIL checks are guarded by
|
||||
``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, which is the default only if
|
||||
``NDEBUG`` is not defined. (Made non-default in 2.10.3, will be active in 2.11)
|
||||
`#4246 <https://github.com/pybind/pybind11/pull/4246>`_
|
||||
|
||||
* Add option for enable/disable enum members in docstring.
|
||||
`#2768 <https://github.com/pybind/pybind11/pull/2768>`_
|
||||
|
||||
* Fixed typing of ``KeysView``, ``ValuesView`` and ``ItemsView`` in ``bind_map``.
|
||||
`#4353 <https://github.com/pybind/pybind11/pull/4353>`_
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Bug fix affecting only Python 3.6 under very specific, uncommon conditions:
|
||||
move ``PyEval_InitThreads()`` call to the correct location.
|
||||
`#4350 <https://github.com/pybind/pybind11/pull/4350>`_
|
||||
|
||||
* Fix segfault bug when passing foreign native functions to functional.h.
|
||||
`#4254 <https://github.com/pybind/pybind11/pull/4254>`_
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* Support setting PYTHON_LIBRARIES manually for Windows ARM cross-compilation
|
||||
(classic mode).
|
||||
`#4406 <https://github.com/pybind/pybind11/pull/4406>`_
|
||||
|
||||
* Extend IPO/LTO detection for ICX (a.k.a IntelLLVM) compiler.
|
||||
`#4402 <https://github.com/pybind/pybind11/pull/4402>`_
|
||||
|
||||
* Allow calling ``find_package(pybind11 CONFIG)`` multiple times from separate
|
||||
directories in the same CMake project and properly link Python (new mode).
|
||||
`#4401 <https://github.com/pybind/pybind11/pull/4401>`_
|
||||
|
||||
* ``multiprocessing_set_spawn`` in pytest fixture for added safety.
|
||||
`#4377 <https://github.com/pybind/pybind11/pull/4377>`_
|
||||
|
||||
* Fixed a bug in two pybind11/tools cmake scripts causing "Unknown arguments specified" errors.
|
||||
`#4327 <https://github.com/pybind/pybind11/pull/4327>`_
|
||||
|
||||
|
||||
|
||||
Version 2.10.1 (Oct 31, 2022)
|
||||
-----------------------------
|
||||
|
||||
This is the first version to fully support embedding the newly released Python 3.11.
|
||||
|
||||
Changes:
|
||||
|
||||
* Allow ``pybind11::capsule`` constructor to take null destructor pointers.
|
||||
`#4221 <https://github.com/pybind/pybind11/pull/4221>`_
|
||||
|
||||
* ``embed.h`` was changed so that ``PYTHONPATH`` is used also with Python 3.11
|
||||
(established behavior).
|
||||
`#4119 <https://github.com/pybind/pybind11/pull/4119>`_
|
||||
|
||||
* A ``PYBIND11_SIMPLE_GIL_MANAGEMENT`` option was added (cmake, C++ define),
|
||||
along with many additional tests in ``test_gil_scoped.py``. The option may be
|
||||
useful to try when debugging GIL-related issues, to determine if the more
|
||||
complex default implementation is or is not to blame. See #4216 for
|
||||
background. WARNING: Please be careful to not create ODR violations when
|
||||
using the option: everything that is linked together with mutual symbol
|
||||
visibility needs to be rebuilt.
|
||||
`#4216 <https://github.com/pybind/pybind11/pull/4216>`_
|
||||
|
||||
* ``PYBIND11_EXPORT_EXCEPTION`` was made non-empty only under macOS. This makes
|
||||
Linux builds safer, and enables the removal of warning suppression pragmas for
|
||||
Windows.
|
||||
`#4298 <https://github.com/pybind/pybind11/pull/4298>`_
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fixed a bug where ``UnicodeDecodeError`` was not propagated from various
|
||||
``py::str`` ctors when decoding surrogate utf characters.
|
||||
`#4294 <https://github.com/pybind/pybind11/pull/4294>`_
|
||||
|
||||
* Revert perfect forwarding for ``make_iterator``. This broke at least one
|
||||
valid use case. May revisit later.
|
||||
`#4234 <https://github.com/pybind/pybind11/pull/4234>`_
|
||||
|
||||
* Fix support for safe casts to ``void*`` (regression in 2.10.0).
|
||||
`#4275 <https://github.com/pybind/pybind11/pull/4275>`_
|
||||
|
||||
* Fix ``char8_t`` support (regression in 2.9).
|
||||
`#4278 <https://github.com/pybind/pybind11/pull/4278>`_
|
||||
|
||||
* Unicode surrogate character in Python exception message leads to process
|
||||
termination in ``error_already_set::what()``.
|
||||
`#4297 <https://github.com/pybind/pybind11/pull/4297>`_
|
||||
|
||||
* Fix MSVC 2019 v.1924 & C++14 mode error for ``overload_cast``.
|
||||
`#4188 <https://github.com/pybind/pybind11/pull/4188>`_
|
||||
|
||||
* Make augmented assignment operators non-const for the object-api. Behavior
|
||||
was previously broken for augmented assignment operators.
|
||||
`#4065 <https://github.com/pybind/pybind11/pull/4065>`_
|
||||
|
||||
* Add proper error checking to C++ bindings for Python list append and insert.
|
||||
`#4208 <https://github.com/pybind/pybind11/pull/4208>`_
|
||||
|
||||
* Work-around for Nvidia's CUDA nvcc compiler in versions 11.4.0 - 11.8.0.
|
||||
`#4220 <https://github.com/pybind/pybind11/pull/4220>`_
|
||||
|
||||
* A workaround for PyPy was added in the ``py::error_already_set``
|
||||
implementation, related to PR `#1895 <https://github.com/pybind/pybind11/pull/1895>`_
|
||||
released with v2.10.0.
|
||||
`#4079 <https://github.com/pybind/pybind11/pull/4079>`_
|
||||
|
||||
* Fixed compiler errors when C++23 ``std::forward_like`` is available.
|
||||
`#4136 <https://github.com/pybind/pybind11/pull/4136>`_
|
||||
|
||||
* Properly raise exceptions in contains methods (like when an object in unhashable).
|
||||
`#4209 <https://github.com/pybind/pybind11/pull/4209>`_
|
||||
|
||||
* Further improve another error in exception handling.
|
||||
`#4232 <https://github.com/pybind/pybind11/pull/4232>`_
|
||||
|
||||
* ``get_local_internals()`` was made compatible with
|
||||
``finalize_interpreter()``, fixing potential freezes during interpreter
|
||||
finalization.
|
||||
`#4192 <https://github.com/pybind/pybind11/pull/4192>`_
|
||||
|
||||
Performance and style:
|
||||
|
||||
* Reserve space in set and STL map casters if possible. This will prevent
|
||||
unnecessary rehashing / resizing by knowing the number of keys ahead of time
|
||||
for Python to C++ casting. This improvement will greatly speed up the casting
|
||||
of large unordered maps and sets.
|
||||
`#4194 <https://github.com/pybind/pybind11/pull/4194>`_
|
||||
|
||||
* GIL RAII scopes are non-copyable to avoid potential bugs.
|
||||
`#4183 <https://github.com/pybind/pybind11/pull/4183>`_
|
||||
|
||||
* Explicitly default all relevant ctors for pytypes in the ``PYBIND11_OBJECT``
|
||||
macros and enforce the clang-tidy checks ``modernize-use-equals-default`` in
|
||||
macros as well.
|
||||
`#4017 <https://github.com/pybind/pybind11/pull/4017>`_
|
||||
|
||||
* Optimize iterator advancement in C++ bindings.
|
||||
`#4237 <https://github.com/pybind/pybind11/pull/4237>`_
|
||||
|
||||
* Use the modern ``PyObject_GenericGetDict`` and ``PyObject_GenericSetDict``
|
||||
for handling dynamic attribute dictionaries.
|
||||
`#4106 <https://github.com/pybind/pybind11/pull/4106>`_
|
||||
|
||||
* Document that users should use ``PYBIND11_NAMESPACE`` instead of using ``pybind11`` when
|
||||
opening namespaces. Using namespace declarations and namespace qualification
|
||||
remain the same as ``pybind11``. This is done to ensure consistent symbol
|
||||
visibility.
|
||||
`#4098 <https://github.com/pybind/pybind11/pull/4098>`_
|
||||
|
||||
* Mark ``detail::forward_like`` as constexpr.
|
||||
`#4147 <https://github.com/pybind/pybind11/pull/4147>`_
|
||||
|
||||
* Optimize unpacking_collector when processing ``arg_v`` arguments.
|
||||
`#4219 <https://github.com/pybind/pybind11/pull/4219>`_
|
||||
|
||||
* Optimize casting C++ object to ``None``.
|
||||
`#4269 <https://github.com/pybind/pybind11/pull/4269>`_
|
||||
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* CMake: revert overwrite behavior, now opt-in with ``PYBIND11_PYTHONLIBS_OVERRWRITE OFF``.
|
||||
`#4195 <https://github.com/pybind/pybind11/pull/4195>`_
|
||||
|
||||
* Include a pkg-config file when installing pybind11, such as in the Python
|
||||
package.
|
||||
`#4077 <https://github.com/pybind/pybind11/pull/4077>`_
|
||||
|
||||
* Avoid stripping debug symbols when ``CMAKE_BUILD_TYPE`` is set to ``DEBUG``
|
||||
instead of ``Debug``.
|
||||
`#4078 <https://github.com/pybind/pybind11/pull/4078>`_
|
||||
|
||||
* Followup to `#3948 <https://github.com/pybind/pybind11/pull/3948>`_, fixing vcpkg again.
|
||||
`#4123 <https://github.com/pybind/pybind11/pull/4123>`_
|
||||
|
||||
Version 2.10.0 (Jul 15, 2022)
|
||||
-----------------------------
|
||||
|
||||
Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC
|
||||
2017 is limited due to availability of CI runners; we highly recommend MSVC
|
||||
2019 or 2022 be used. Initial support added for Python 3.11.
|
||||
2019 or 2022 be used.
|
||||
|
||||
New features:
|
||||
|
||||
* ``py::anyset`` & ``py::frozenset`` were added, with copying (cast) to
|
||||
``std::set`` (similar to ``set``).
|
||||
`#3901 <https://github.com/pybind/pybind11/pull/3901>`_
|
||||
|
||||
* Support bytearray casting to string.
|
||||
`#3707 <https://github.com/pybind/pybind11/pull/3707>`_
|
||||
|
||||
* ``type_caster<std::monostate>`` was added. ``std::monostate`` is a tag type
|
||||
that allows ``std::variant`` to act as an optional, or allows default
|
||||
construction of a ``std::variant`` holding a non-default constructible type.
|
||||
`#3818 <https://github.com/pybind/pybind11/pull/3818>`_
|
||||
|
||||
* ``pybind11::capsule::set_name`` added to mutate the name of the capsule instance.
|
||||
`#3866 <https://github.com/pybind/pybind11/pull/3866>`_
|
||||
|
||||
* NumPy: dtype constructor from type number added, accessors corresponding to
|
||||
Python API ``dtype.num``, ``dtype.byteorder``, ``dtype.flags`` and
|
||||
``dtype.alignment`` added.
|
||||
`#3868 <https://github.com/pybind/pybind11/pull/3868>`_
|
||||
|
||||
* Support bytearray casting to string.
|
||||
`#3707 <https://github.com/pybind/pybind11/pull/3707>`_
|
||||
|
||||
Changes:
|
||||
|
||||
* Python 3.6 is now the minimum supported version.
|
||||
* Python 2 support was removed completely.
|
||||
`#3688 <https://github.com/pybind/pybind11/pull/3688>`_
|
||||
`#3719 <https://github.com/pybind/pybind11/pull/3719>`_
|
||||
|
||||
* The minimum version for MSVC is now 2017.
|
||||
`#3722 <https://github.com/pybind/pybind11/pull/3722>`_
|
||||
|
||||
* Fix issues with CPython 3.11 betas and add to supported test matrix.
|
||||
`#3923 <https://github.com/pybind/pybind11/pull/3923>`_
|
||||
|
||||
* ``error_already_set`` is now safer and more performant, especially for
|
||||
exceptions with long tracebacks, by delaying computation.
|
||||
`#1895 <https://github.com/pybind/pybind11/pull/1895>`_
|
||||
|
||||
* Improve exception handling in python ``str`` bindings.
|
||||
`#3826 <https://github.com/pybind/pybind11/pull/3826>`_
|
||||
|
||||
* The bindings for capsules now have more consistent exception handling.
|
||||
`#3825 <https://github.com/pybind/pybind11/pull/3825>`_
|
||||
|
||||
* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can now be
|
||||
used to define classes in namespaces other than pybind11.
|
||||
`#3797 <https://github.com/pybind/pybind11/pull/3797>`_
|
||||
|
||||
* Error printing code now uses ``PYBIND11_DETAILED_ERROR_MESSAGES`` instead of
|
||||
requiring ``NDEBUG``, allowing use with release builds if desired.
|
||||
`#3913 <https://github.com/pybind/pybind11/pull/3913>`_
|
||||
|
||||
* Implicit conversion of the literal ``0`` to ``pybind11::handle`` is now disabled.
|
||||
`#4008 <https://github.com/pybind/pybind11/pull/4008>`_
|
||||
* Fix exception handling when ``pybind11::weakref()`` fails.
|
||||
`#3739 <https://github.com/pybind/pybind11/pull/3739>`_
|
||||
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fix exception handling when ``pybind11::weakref()`` fails.
|
||||
`#3739 <https://github.com/pybind/pybind11/pull/3739>`_
|
||||
|
||||
* ``module_::def_submodule`` was missing proper error handling. This is fixed now.
|
||||
`#3973 <https://github.com/pybind/pybind11/pull/3973>`_
|
||||
|
||||
* The behavior or ``error_already_set`` was made safer and the highly opaque
|
||||
"Unknown internal error occurred" message was replaced with a more helpful
|
||||
message.
|
||||
`#3982 <https://github.com/pybind/pybind11/pull/3982>`_
|
||||
|
||||
* ``error_already_set::what()`` now handles non-normalized exceptions correctly.
|
||||
`#3971 <https://github.com/pybind/pybind11/pull/3971>`_
|
||||
|
||||
* Support older C++ compilers where filesystem is not yet part of the standard
|
||||
library and is instead included in ``std::experimental::filesystem``.
|
||||
`#3840 <https://github.com/pybind/pybind11/pull/3840>`_
|
||||
|
||||
* Fix ``-Wfree-nonheap-object`` warnings produced by GCC by avoiding returning
|
||||
pointers to static objects with ``return_value_policy::take_ownership``.
|
||||
`#3946 <https://github.com/pybind/pybind11/pull/3946>`_
|
||||
|
||||
* Fix cast from pytype rvalue to another pytype.
|
||||
`#3949 <https://github.com/pybind/pybind11/pull/3949>`_
|
||||
|
||||
* Ensure proper behavior when garbage collecting classes with dynamic attributes in Python >=3.9.
|
||||
`#4051 <https://github.com/pybind/pybind11/pull/4051>`_
|
||||
|
||||
* A couple long-standing ``PYBIND11_NAMESPACE``
|
||||
``__attribute__((visibility("hidden")))`` inconsistencies are now fixed
|
||||
(affects only unusual environments).
|
||||
`#4043 <https://github.com/pybind/pybind11/pull/4043>`_
|
||||
|
||||
* ``pybind11::detail::get_internals()`` is now resilient to in-flight Python
|
||||
exceptions.
|
||||
`#3981 <https://github.com/pybind/pybind11/pull/3981>`_
|
||||
|
||||
* Arrays with a dimension of size 0 are now properly converted to dynamic Eigen
|
||||
matrices (more common in NumPy 1.23).
|
||||
`#4038 <https://github.com/pybind/pybind11/pull/4038>`_
|
||||
|
||||
* Avoid catching unrelated errors when importing NumPy.
|
||||
`#3974 <https://github.com/pybind/pybind11/pull/3974>`_
|
||||
|
||||
Performance and style:
|
||||
|
||||
* Added an accessor overload of ``(object &&key)`` to reference steal the
|
||||
object when using python types as keys. This prevents unnecessary reference
|
||||
count overhead for attr, dictionary, tuple, and sequence look ups. Added
|
||||
additional regression tests. Fixed a performance bug the caused accessor
|
||||
assignments to potentially perform unnecessary copies.
|
||||
`#3970 <https://github.com/pybind/pybind11/pull/3970>`_
|
||||
|
||||
* Perfect forward all args of ``make_iterator``.
|
||||
`#3980 <https://github.com/pybind/pybind11/pull/3980>`_
|
||||
|
||||
* Avoid potential bug in pycapsule destructor by adding an ``error_guard`` to
|
||||
one of the dtors.
|
||||
`#3958 <https://github.com/pybind/pybind11/pull/3958>`_
|
||||
|
||||
* Optimize dictionary access in ``strip_padding`` for numpy.
|
||||
`#3994 <https://github.com/pybind/pybind11/pull/3994>`_
|
||||
|
||||
* ``stl_bind.h`` bindings now take slice args as a const-ref.
|
||||
`#3852 <https://github.com/pybind/pybind11/pull/3852>`_
|
||||
|
||||
* Made slice constructor more consistent, and improve performance of some
|
||||
casters by allowing reference stealing.
|
||||
`#3845 <https://github.com/pybind/pybind11/pull/3845>`_
|
||||
|
||||
* Change numpy dtype from_args method to use const ref.
|
||||
`#3878 <https://github.com/pybind/pybind11/pull/3878>`_
|
||||
|
||||
* Follow rule of three to ensure ``PyErr_Restore`` is called only once.
|
||||
`#3872 <https://github.com/pybind/pybind11/pull/3872>`_
|
||||
|
||||
* Added missing perfect forwarding for ``make_iterator`` functions.
|
||||
`#3860 <https://github.com/pybind/pybind11/pull/3860>`_
|
||||
|
||||
* Optimize c++ to python function casting by using the rvalue caster.
|
||||
`#3966 <https://github.com/pybind/pybind11/pull/3966>`_
|
||||
|
||||
* Optimize Eigen sparse matrix casting by removing unnecessary temporary.
|
||||
`#4064 <https://github.com/pybind/pybind11/pull/4064>`_
|
||||
|
||||
* Avoid potential implicit copy/assignment constructors causing double free in
|
||||
``strdup_gaurd``.
|
||||
`#3905 <https://github.com/pybind/pybind11/pull/3905>`_
|
||||
|
||||
* Enable clang-tidy checks ``misc-definitions-in-headers``,
|
||||
``modernize-loop-convert``, and ``modernize-use-nullptr``.
|
||||
`#3881 <https://github.com/pybind/pybind11/pull/3881>`_
|
||||
`#3988 <https://github.com/pybind/pybind11/pull/3988>`_
|
||||
|
||||
* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can be used
|
||||
to define classes in namespaces other than pybind11.
|
||||
`#3797 <https://github.com/pybind/pybind11/pull/3797>`_
|
||||
|
||||
Build system improvements:
|
||||
|
||||
* CMake: Fix file extension on Windows with cp36 and cp37 using FindPython.
|
||||
`#3919 <https://github.com/pybind/pybind11/pull/3919>`_
|
||||
|
||||
* CMake: Support multiple Python targets (such as on vcpkg).
|
||||
`#3948 <https://github.com/pybind/pybind11/pull/3948>`_
|
||||
|
||||
* CMake: Fix issue with NVCC on Windows.
|
||||
`#3947 <https://github.com/pybind/pybind11/pull/3947>`_
|
||||
|
||||
* CMake: Drop the bitness check on cross compiles (like targeting WebAssembly
|
||||
via Emscripten).
|
||||
`#3959 <https://github.com/pybind/pybind11/pull/3959>`_
|
||||
|
||||
* Add MSVC builds in debug mode to CI.
|
||||
`#3784 <https://github.com/pybind/pybind11/pull/3784>`_
|
||||
|
||||
|
@ -456,23 +59,15 @@ Build system improvements:
|
|||
`#3732 <https://github.com/pybind/pybind11/pull/3732>`_,
|
||||
`#3741 <https://github.com/pybind/pybind11/pull/3741>`_
|
||||
|
||||
* Avoid ``setup.py <command>`` usage in internal tests.
|
||||
`#3734 <https://github.com/pybind/pybind11/pull/3734>`_
|
||||
|
||||
|
||||
Backend and tidying up:
|
||||
|
||||
* New theme for the documentation.
|
||||
`#3109 <https://github.com/pybind/pybind11/pull/3109>`_
|
||||
|
||||
* Remove idioms in code comments. Use more inclusive language.
|
||||
* Remove idioms in code comments. Use inclusive language.
|
||||
`#3809 <https://github.com/pybind/pybind11/pull/3809>`_
|
||||
|
||||
* ``#include <iostream>`` was removed from the ``pybind11/stl.h`` header. Your
|
||||
project may break if it has a transitive dependency on this include. The fix
|
||||
is to "Include What You Use".
|
||||
`#3928 <https://github.com/pybind/pybind11/pull/3928>`_
|
||||
|
||||
* Avoid ``setup.py <command>`` usage in internal tests.
|
||||
`#3734 <https://github.com/pybind/pybind11/pull/3734>`_
|
||||
|
||||
|
||||
Version 2.9.2 (Mar 29, 2022)
|
||||
----------------------------
|
||||
|
@ -1324,7 +919,7 @@ Packaging / building improvements:
|
|||
`#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and
|
||||
`#2370 <https://github.com/pybind/pybind11/pull/2370>`_
|
||||
|
||||
* Full integration with CMake's C++ standard system and compile features
|
||||
* Full integration with CMake’s C++ standard system and compile features
|
||||
replaces ``PYBIND11_CPP_STANDARD``.
|
||||
|
||||
* Generated config file is now portable to different Python/compiler/CMake
|
||||
|
|
|
@ -48,10 +48,10 @@ interactive Python session demonstrating this example is shown below:
|
|||
>>> print(p)
|
||||
<example.Pet object at 0x10cd98060>
|
||||
>>> p.getName()
|
||||
'Molly'
|
||||
u'Molly'
|
||||
>>> p.setName("Charly")
|
||||
>>> p.getName()
|
||||
'Charly'
|
||||
u'Charly'
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
@ -124,10 +124,10 @@ This makes it possible to write
|
|||
|
||||
>>> p = example.Pet("Molly")
|
||||
>>> p.name
|
||||
'Molly'
|
||||
u'Molly'
|
||||
>>> p.name = "Charly"
|
||||
>>> p.name
|
||||
'Charly'
|
||||
u'Charly'
|
||||
|
||||
Now suppose that ``Pet::name`` was a private internal variable
|
||||
that can only be accessed via setters and getters.
|
||||
|
@ -282,9 +282,9 @@ expose fields and methods of both types:
|
|||
|
||||
>>> p = example.Dog("Molly")
|
||||
>>> p.name
|
||||
'Molly'
|
||||
u'Molly'
|
||||
>>> p.bark()
|
||||
'woof!'
|
||||
u'woof!'
|
||||
|
||||
The C++ classes defined above are regular non-polymorphic types with an
|
||||
inheritance relationship. This is reflected in Python:
|
||||
|
@ -332,7 +332,7 @@ will automatically recognize this:
|
|||
>>> type(p)
|
||||
PolymorphicDog # automatically downcast
|
||||
>>> p.bark()
|
||||
'woof!'
|
||||
u'woof!'
|
||||
|
||||
Given a pointer to a polymorphic base, pybind11 performs automatic downcasting
|
||||
to the actual derived type. Note that this goes beyond the usual situation in
|
||||
|
@ -434,7 +434,8 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth
|
|||
.def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age")
|
||||
.def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name");
|
||||
|
||||
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag.
|
||||
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag
|
||||
or Visual Studio 2015 Update 2 and newer.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -482,7 +483,7 @@ The binding code for this example looks as follows:
|
|||
.value("Cat", Pet::Kind::Cat)
|
||||
.export_values();
|
||||
|
||||
py::class_<Pet::Attributes>(pet, "Attributes")
|
||||
py::class_<Pet::Attributes> attributes(pet, "Attributes")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("age", &Pet::Attributes::age);
|
||||
|
||||
|
|
|
@ -417,10 +417,10 @@ existing targets instead:
|
|||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.15...3.22)
|
||||
cmake_minimum_required(VERSION 3.15...3.19)
|
||||
project(example LANGUAGES CXX)
|
||||
|
||||
find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED)
|
||||
find_package(Python COMPONENTS Interpreter Development REQUIRED)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
# or add_subdirectory(pybind11)
|
||||
|
||||
|
@ -433,8 +433,9 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``.
|
|||
|
||||
.. warning::
|
||||
|
||||
If you use FindPython to multi-target Python versions, use the individual
|
||||
targets listed below, and avoid targets that directly include Python parts.
|
||||
If you use FindPython2 and FindPython3 to dual-target Python, use the
|
||||
individual targets listed below, and avoid targets that directly include
|
||||
Python parts.
|
||||
|
||||
There are `many ways to hint or force a discovery of a specific Python
|
||||
installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_),
|
||||
|
@ -461,8 +462,11 @@ available in all modes. The targets provided are:
|
|||
``pybind11::headers``
|
||||
Just the pybind11 headers and minimum compile requirements
|
||||
|
||||
``pybind11::python2_no_register``
|
||||
Quiets the warning/error when mixing C++14 or higher and Python 2
|
||||
|
||||
``pybind11::pybind11``
|
||||
Python headers + ``pybind11::headers``
|
||||
Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only)
|
||||
|
||||
``pybind11::python_link_helper``
|
||||
Just the "linking" part of pybind11:module
|
||||
|
@ -505,10 +509,7 @@ You can use these targets to build complex applications. For example, the
|
|||
target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras)
|
||||
|
||||
pybind11_extension(example)
|
||||
if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo)
|
||||
# Strip unnecessary sections of the binary on Linux/macOS
|
||||
pybind11_strip(example)
|
||||
endif()
|
||||
pybind11_strip(example)
|
||||
|
||||
set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden"
|
||||
CUDA_VISIBILITY_PRESET "hidden")
|
||||
|
@ -576,12 +577,21 @@ On Linux, you can compile an example such as the one given in
|
|||
|
||||
$ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
|
||||
|
||||
The flags given here assume that you're using Python 3. For Python 2, just
|
||||
change the executable appropriately (to ``python`` or ``python2``).
|
||||
|
||||
The ``python3 -m pybind11 --includes`` command fetches the include paths for
|
||||
both pybind11 and Python headers. This assumes that pybind11 has been installed
|
||||
using ``pip`` or ``conda``. If it hasn't, you can also manually specify
|
||||
``-I <path-to-pybind11>/include`` together with the Python includes path
|
||||
``python3-config --includes``.
|
||||
|
||||
Note that Python 2.7 modules don't use a special suffix, so you should simply
|
||||
use ``example.so`` instead of ``example$(python3-config --extension-suffix)``.
|
||||
Besides, the ``--extension-suffix`` option may or may not be available, depending
|
||||
on the distribution; in the latter case, the module extension can be manually
|
||||
set to ``.so``.
|
||||
|
||||
On macOS: the build command is almost the same but it also requires passing
|
||||
the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when
|
||||
building the module:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# pybind11 documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sun Oct 11 19:23:48 2015.
|
||||
|
@ -35,7 +36,6 @@ DIR = Path(__file__).parent.resolve()
|
|||
# ones.
|
||||
extensions = [
|
||||
"breathe",
|
||||
"sphinx_copybutton",
|
||||
"sphinxcontrib.rsvgconverter",
|
||||
"sphinxcontrib.moderncmakedomain",
|
||||
]
|
||||
|
@ -126,7 +126,23 @@ todo_include_todos = False
|
|||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
|
||||
html_theme = "furo"
|
||||
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
|
||||
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
html_context = {"css_files": ["_static/theme_overrides.css"]}
|
||||
else:
|
||||
html_context = {
|
||||
"css_files": [
|
||||
"//media.readthedocs.org/css/sphinx_rtd_theme.css",
|
||||
"//media.readthedocs.org/css/readthedocs-doc-embed.css",
|
||||
"_static/theme_overrides.css",
|
||||
]
|
||||
}
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
@ -157,10 +173,6 @@ html_theme = "furo"
|
|||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
|
||||
html_css_files = [
|
||||
"css/custom.css",
|
||||
]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
|
@ -333,9 +345,9 @@ def generate_doxygen_xml(app):
|
|||
subprocess.call(["doxygen", "--version"])
|
||||
retcode = subprocess.call(["doxygen"], cwd=app.confdir)
|
||||
if retcode < 0:
|
||||
sys.stderr.write(f"doxygen error code: {-retcode}\n")
|
||||
sys.stderr.write("doxygen error code: {}\n".format(-retcode))
|
||||
except OSError as e:
|
||||
sys.stderr.write(f"doxygen execution failed: {e}\n")
|
||||
sys.stderr.write("doxygen execution failed: {}\n".format(e))
|
||||
|
||||
|
||||
def prepare(app):
|
||||
|
@ -358,6 +370,7 @@ def clean_up(app, exception):
|
|||
|
||||
|
||||
def setup(app):
|
||||
|
||||
# Add hook for building doxygen xml when needed
|
||||
app.connect("builder-inited", generate_doxygen_xml)
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ Frequently asked questions
|
|||
filename of the extension library (without suffixes such as ``.so``).
|
||||
|
||||
2. If the above did not fix the issue, you are likely using an incompatible
|
||||
version of Python that does not match what you compiled with.
|
||||
version of Python (for instance, the extension library was compiled against
|
||||
Python 2, while the interpreter is running on top of some version of Python
|
||||
3, or vice versa).
|
||||
|
||||
"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``"
|
||||
========================================================================
|
||||
|
@ -145,7 +147,7 @@ using C++14 template metaprogramming.
|
|||
|
||||
.. _`faq:hidden_visibility`:
|
||||
|
||||
"'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]"
|
||||
"‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]"
|
||||
============================================================================================================
|
||||
|
||||
This error typically indicates that you are compiling without the required
|
||||
|
@ -220,6 +222,20 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids
|
|||
potential serious issues when loading multiple modules and is required for
|
||||
proper pybind operation. See the previous FAQ entry for more details.
|
||||
|
||||
Working with ancient Visual Studio 2008 builds on Windows
|
||||
=========================================================
|
||||
|
||||
The official Windows distributions of Python are compiled using truly
|
||||
ancient versions of Visual Studio that lack good C++11 support. Some users
|
||||
implicitly assume that it would be impossible to load a plugin built with
|
||||
Visual Studio 2015 into a Python distribution that was compiled using Visual
|
||||
Studio 2008. However, no such issue exists: it's perfectly legitimate to
|
||||
interface DLLs that are built with different compilers and/or C libraries.
|
||||
Common gotchas to watch out for involve not ``free()``-ing memory region
|
||||
that that were ``malloc()``-ed in another shared library, using data
|
||||
structures with incompatible ABIs, and so on. pybind11 is very careful not
|
||||
to make these types of mistakes.
|
||||
|
||||
How can I properly handle Ctrl-C in long-running functions?
|
||||
===========================================================
|
||||
|
||||
|
@ -273,7 +289,27 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses
|
|||
the CMake Python detection in a system with several Python versions installed.
|
||||
|
||||
This difference may cause inconsistencies and errors if *both* mechanisms are
|
||||
used in the same project.
|
||||
used in the same project. Consider the following CMake code executed in a
|
||||
system with Python 2.7 and 3.x installed:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs)
|
||||
find_package(pybind11)
|
||||
|
||||
It will detect Python 2.7 and pybind11 will pick it as well.
|
||||
|
||||
In contrast this code:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(pybind11)
|
||||
find_package(PythonInterp)
|
||||
find_package(PythonLibs)
|
||||
|
||||
will detect Python 3.x for pybind11 and may crash on
|
||||
``find_package(PythonLibs)`` afterwards.
|
||||
|
||||
There are three possible solutions:
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
breathe==4.34.0
|
||||
furo==2022.6.21
|
||||
sphinx==5.0.2
|
||||
sphinx-copybutton==0.5.0
|
||||
sphinxcontrib-moderncmakedomain==3.21.4
|
||||
sphinxcontrib-svg2pdfconverter==1.2.0
|
||||
breathe==4.31.0
|
||||
sphinx==3.5.4
|
||||
sphinx_rtd_theme==1.0.0
|
||||
sphinxcontrib-moderncmakedomain==3.19
|
||||
sphinxcontrib-svg2pdfconverter==1.1.1
|
||||
|
|
|
@ -524,7 +524,7 @@ include a declaration of the form:
|
|||
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
|
||||
|
||||
Continuing to do so won't cause an error or even a deprecation warning,
|
||||
Continuing to do so won’t cause an error or even a deprecation warning,
|
||||
but it's completely redundant.
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "cast.h"
|
||||
|
||||
#include <functional>
|
||||
|
@ -62,7 +61,7 @@ struct base {
|
|||
|
||||
PYBIND11_DEPRECATED(
|
||||
"base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
||||
base() = default;
|
||||
base() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
};
|
||||
|
||||
/// Keep patient alive while nurse lives
|
||||
|
@ -83,7 +82,8 @@ struct metaclass {
|
|||
handle value;
|
||||
|
||||
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
|
||||
metaclass() = default;
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
|
||||
metaclass() {}
|
||||
|
||||
/// Override pybind11's default metaclass
|
||||
explicit metaclass(handle value) : value(value) {}
|
||||
|
@ -345,11 +345,9 @@ struct type_record {
|
|||
|
||||
bases.append((PyObject *) base_info->type);
|
||||
|
||||
#if PY_VERSION_HEX < 0x030B0000
|
||||
dynamic_attr |= base_info->type->tp_dictoffset != 0;
|
||||
#else
|
||||
dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0;
|
||||
#endif
|
||||
if (base_info->type->tp_dictoffset != 0) {
|
||||
dynamic_attr = true;
|
||||
}
|
||||
|
||||
if (caster) {
|
||||
base_info->implicit_casts.emplace_back(type, caster);
|
||||
|
@ -399,7 +397,7 @@ struct process_attribute<doc> : process_attribute_default<doc> {
|
|||
template <>
|
||||
struct process_attribute<const char *> : process_attribute_default<const char *> {
|
||||
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
|
||||
static void init(const char *d, type_record *r) { r->doc = d; }
|
||||
static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
|
||||
};
|
||||
template <>
|
||||
struct process_attribute<char *> : process_attribute<const char *> {};
|
||||
|
@ -480,7 +478,7 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
|||
}
|
||||
|
||||
if (!a.value) {
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if !defined(NDEBUG)
|
||||
std::string descr("'");
|
||||
if (a.name) {
|
||||
descr += std::string(a.name) + ": ";
|
||||
|
@ -501,8 +499,7 @@ struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
|||
#else
|
||||
pybind11_fail("arg(): could not convert default argument "
|
||||
"into a Python object (type not registered yet?). "
|
||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
|
||||
"more information.");
|
||||
"Compile in debug mode for more information.");
|
||||
#endif
|
||||
}
|
||||
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
|
||||
|
|
|
@ -29,9 +29,6 @@
|
|||
#include <vector>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename type, typename SFINAE = void>
|
||||
|
@ -91,8 +88,7 @@ public:
|
|||
template <typename T_, \
|
||||
::pybind11::detail::enable_if_t< \
|
||||
std::is_same<type, ::pybind11::detail::remove_cv_t<T_>>::value, \
|
||||
int> \
|
||||
= 0> \
|
||||
int> = 0> \
|
||||
static ::pybind11::handle cast( \
|
||||
T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \
|
||||
if (!src) \
|
||||
|
@ -252,7 +248,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
static handle cast(T, return_value_policy /* policy */, handle /* parent */) {
|
||||
return none().release();
|
||||
return none().inc_ref();
|
||||
}
|
||||
PYBIND11_TYPE_CASTER(T, const_name("None"));
|
||||
};
|
||||
|
@ -295,7 +291,7 @@ public:
|
|||
if (ptr) {
|
||||
return capsule(ptr).release();
|
||||
}
|
||||
return none().release();
|
||||
return none().inc_ref();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -333,7 +329,7 @@ public:
|
|||
res = 0; // None is implicitly converted to False
|
||||
}
|
||||
#if defined(PYPY_VERSION)
|
||||
// On PyPy, check that "__bool__" attr exists
|
||||
// On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists
|
||||
else if (hasattr(src, PYBIND11_BOOL_ATTR)) {
|
||||
res = PyObject_IsTrue(src.ptr());
|
||||
}
|
||||
|
@ -383,17 +379,38 @@ struct string_caster {
|
|||
static constexpr size_t UTF_N = 8 * sizeof(CharT);
|
||||
|
||||
bool load(handle src, bool) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
object temp;
|
||||
#endif
|
||||
handle load_src = src;
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
if (!PyUnicode_Check(load_src.ptr())) {
|
||||
return load_raw(load_src);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return load_bytes(load_src);
|
||||
#else
|
||||
if (std::is_same<CharT, char>::value) {
|
||||
return load_bytes(load_src);
|
||||
}
|
||||
|
||||
// The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false
|
||||
if (!PYBIND11_BYTES_CHECK(load_src.ptr()))
|
||||
return false;
|
||||
|
||||
temp = reinterpret_steal<object>(PyUnicode_FromObject(load_src.ptr()));
|
||||
if (!temp) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
load_src = temp;
|
||||
#endif
|
||||
}
|
||||
|
||||
// For UTF-8 we avoid the need for a temporary `bytes` object by using
|
||||
// `PyUnicode_AsUTF8AndSize`.
|
||||
if (UTF_N == 8) {
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes`
|
||||
// object by using `PyUnicode_AsUTF8AndSize`.
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) {
|
||||
Py_ssize_t size = -1;
|
||||
const auto *buffer
|
||||
= reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size));
|
||||
|
@ -404,6 +421,7 @@ struct string_caster {
|
|||
value = StringType(buffer, static_cast<size_t>(size));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto utfNbytes
|
||||
= reinterpret_steal<object>(PyUnicode_AsEncodedString(load_src.ptr(),
|
||||
|
@ -420,7 +438,7 @@ struct string_caster {
|
|||
= reinterpret_cast<const CharT *>(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr()));
|
||||
size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT);
|
||||
// Skip BOM for UTF-16/32
|
||||
if (UTF_N > 8) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) {
|
||||
buffer++;
|
||||
length--;
|
||||
}
|
||||
|
@ -466,37 +484,26 @@ private:
|
|||
#endif
|
||||
}
|
||||
|
||||
// When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e.
|
||||
// When loading into a std::string or char*, accept a bytes object as-is (i.e.
|
||||
// without any encoding/decoding attempt). For other C++ char sizes this is a no-op.
|
||||
// which supports loading a unicode from a str, doesn't take this path.
|
||||
template <typename C = CharT>
|
||||
bool load_raw(enable_if_t<std::is_same<C, char>::value, handle> src) {
|
||||
bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src) {
|
||||
if (PYBIND11_BYTES_CHECK(src.ptr())) {
|
||||
// We were passed raw bytes; accept it into a std::string or char*
|
||||
// We were passed a Python 3 raw bytes; accept it into a std::string or char*
|
||||
// without any encoding attempt.
|
||||
const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr());
|
||||
if (!bytes) {
|
||||
pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure.");
|
||||
if (bytes) {
|
||||
value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr()));
|
||||
return true;
|
||||
}
|
||||
value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr()));
|
||||
return true;
|
||||
}
|
||||
if (PyByteArray_Check(src.ptr())) {
|
||||
// We were passed a bytearray; accept it into a std::string or char*
|
||||
// without any encoding attempt.
|
||||
const char *bytearray = PyByteArray_AsString(src.ptr());
|
||||
if (!bytearray) {
|
||||
pybind11_fail("Unexpected PyByteArray_AsString() failure.");
|
||||
}
|
||||
value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr()));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename C = CharT>
|
||||
bool load_raw(enable_if_t<!std::is_same<C, char>::value, handle>) {
|
||||
bool load_bytes(enable_if_t<!std::is_same<C, char>::value, handle>) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -518,7 +525,7 @@ struct type_caster<std::basic_string_view<CharT, Traits>,
|
|||
template <typename CharT>
|
||||
struct type_caster<CharT, enable_if_t<is_std_char_type<CharT>::value>> {
|
||||
using StringType = std::basic_string<CharT>;
|
||||
using StringCaster = make_caster<StringType>;
|
||||
using StringCaster = type_caster<StringType>;
|
||||
StringCaster str_caster;
|
||||
bool none = false;
|
||||
CharT one_char = 0;
|
||||
|
@ -541,7 +548,7 @@ public:
|
|||
|
||||
static handle cast(const CharT *src, return_value_policy policy, handle parent) {
|
||||
if (src == nullptr) {
|
||||
return pybind11::none().release();
|
||||
return pybind11::none().inc_ref();
|
||||
}
|
||||
return StringCaster::cast(StringType(src), policy, parent);
|
||||
}
|
||||
|
@ -576,7 +583,7 @@ public:
|
|||
// figure out how long the first encoded character is in bytes to distinguish between these
|
||||
// two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as
|
||||
// those can fit into a single char value.
|
||||
if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 8) && str_len > 1 && str_len <= 4) {
|
||||
auto v0 = static_cast<unsigned char>(value[0]);
|
||||
// low bits only: 0-127
|
||||
// 0b110xxxxx - start of 2-byte sequence
|
||||
|
@ -602,7 +609,7 @@ public:
|
|||
// UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a
|
||||
// surrogate pair with total length 2 instantly indicates a range error (but not a "your
|
||||
// string was too long" error).
|
||||
else if (StringCaster::UTF_N == 16 && str_len == 2) {
|
||||
else if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 16) && str_len == 2) {
|
||||
one_char = static_cast<CharT>(value[0]);
|
||||
if (one_char >= 0xD800 && one_char < 0xE000) {
|
||||
throw value_error("Character code point not in range(0x10000)");
|
||||
|
@ -781,9 +788,8 @@ protected:
|
|||
return true;
|
||||
}
|
||||
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
|
||||
"type information)");
|
||||
#if defined(NDEBUG)
|
||||
"(compile in debug mode for type information)");
|
||||
#else
|
||||
"of type '"
|
||||
+ type_id<holder_type>() + "''");
|
||||
|
@ -850,7 +856,7 @@ struct always_construct_holder {
|
|||
|
||||
/// Create a specialization for custom holder types (silently ignores std::shared_ptr)
|
||||
#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
|
||||
namespace pybind11 { \
|
||||
namespace detail { \
|
||||
template <typename type> \
|
||||
struct always_construct_holder<holder_type> : always_construct_holder<void, ##__VA_ARGS__> { \
|
||||
|
@ -859,7 +865,7 @@ struct always_construct_holder {
|
|||
class type_caster<holder_type, enable_if_t<!is_shared_ptr<holder_type>::value>> \
|
||||
: public type_caster_holder<type, holder_type> {}; \
|
||||
} \
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
}
|
||||
|
||||
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
|
||||
template <typename base, typename holder>
|
||||
|
@ -912,14 +918,6 @@ struct handle_type_name<kwargs> {
|
|||
|
||||
template <typename type>
|
||||
struct pyobject_caster {
|
||||
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
|
||||
pyobject_caster() : value() {}
|
||||
|
||||
// `type` may not be default constructible (e.g. frozenset, anyset). Initializing `value`
|
||||
// to a nil handle is safe since it will only be accessed if `load` succeeds.
|
||||
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
|
||||
pyobject_caster() : value(reinterpret_steal<type>(handle())) {}
|
||||
|
||||
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
|
||||
bool load(handle src, bool /* convert */) {
|
||||
value = src;
|
||||
|
@ -928,6 +926,18 @@ struct pyobject_caster {
|
|||
|
||||
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
|
||||
bool load(handle src, bool /* convert */) {
|
||||
#if PY_MAJOR_VERSION < 3 && !defined(PYBIND11_STR_LEGACY_PERMISSIVE)
|
||||
// For Python 2, without this implicit conversion, Python code would
|
||||
// need to be cluttered with six.ensure_text() or similar, only to be
|
||||
// un-cluttered later after Python 2 support is dropped.
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(std::is_same<T, str>::value) && isinstance<bytes>(src)) {
|
||||
PyObject *str_from_bytes = PyUnicode_FromEncodedObject(src.ptr(), "utf-8", nullptr);
|
||||
if (!str_from_bytes)
|
||||
throw error_already_set();
|
||||
value = reinterpret_steal<type>(str_from_bytes);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (!isinstance<type>(src)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1013,12 +1023,10 @@ struct return_value_policy_override<
|
|||
// Basic python -> C++ casting; throws if casting fails
|
||||
template <typename T, typename SFINAE>
|
||||
type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &handle) {
|
||||
static_assert(!detail::is_pyobject<T>::value,
|
||||
"Internal error: type_caster should only be used for C++ types");
|
||||
if (!conv.load(handle, true)) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
throw cast_error("Unable to cast Python instance to C++ type (#define "
|
||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error(
|
||||
"Unable to cast Python instance to C++ type (compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to cast Python instance of type "
|
||||
+ (std::string) str(type::handle_of(handle)) + " to C++ type '"
|
||||
|
@ -1083,10 +1091,10 @@ inline void handle::cast() const {
|
|||
template <typename T>
|
||||
detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
|
||||
if (obj.ref_count() > 1) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error(
|
||||
"Unable to cast Python instance to C++ rvalue: instance has multiple references"
|
||||
" (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
" (compile in debug mode for details)");
|
||||
#else
|
||||
throw cast_error("Unable to move from Python " + (std::string) str(type::handle_of(obj))
|
||||
+ " instance to C++ " + type_id<T>()
|
||||
|
@ -1105,30 +1113,21 @@ detail::enable_if_t<!detail::move_never<T>::value, T> move(object &&obj) {
|
|||
// - If both movable and copyable, check ref count: if 1, move; otherwise copy
|
||||
// - Otherwise (not movable), copy.
|
||||
template <typename T>
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_always<T>::value, T>
|
||||
cast(object &&object) {
|
||||
detail::enable_if_t<detail::move_always<T>::value, T> cast(object &&object) {
|
||||
return move<T>(std::move(object));
|
||||
}
|
||||
template <typename T>
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_if_unreferenced<T>::value, T>
|
||||
cast(object &&object) {
|
||||
detail::enable_if_t<detail::move_if_unreferenced<T>::value, T> cast(object &&object) {
|
||||
if (object.ref_count() > 1) {
|
||||
return cast<T>(object);
|
||||
}
|
||||
return move<T>(std::move(object));
|
||||
}
|
||||
template <typename T>
|
||||
detail::enable_if_t<!detail::is_pyobject<T>::value && detail::move_never<T>::value, T>
|
||||
cast(object &&object) {
|
||||
detail::enable_if_t<detail::move_never<T>::value, T> cast(object &&object) {
|
||||
return cast<T>(object);
|
||||
}
|
||||
|
||||
// pytype rvalue -> pytype (calls converting constructor)
|
||||
template <typename T>
|
||||
detail::enable_if_t<detail::is_pyobject<T>::value, T> cast(object &&object) {
|
||||
return T(std::move(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T object::cast() const & {
|
||||
return pybind11::cast<T>(*this);
|
||||
|
@ -1179,25 +1178,24 @@ enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_ref(object &&,
|
|||
// static_assert, even though if it's in dead code, so we provide a "trampoline" to pybind11::cast
|
||||
// that only does anything in cases where pybind11::cast is valid.
|
||||
template <typename T>
|
||||
enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&o) {
|
||||
return pybind11::cast<T>(std::move(o));
|
||||
}
|
||||
template <typename T>
|
||||
enable_if_t<cast_is_temporary_value_reference<T>::value, T> cast_safe(object &&) {
|
||||
pybind11_fail("Internal error: cast_safe fallback invoked");
|
||||
}
|
||||
template <typename T>
|
||||
enable_if_t<std::is_void<T>::value, void> cast_safe(object &&) {}
|
||||
template <typename T>
|
||||
enable_if_t<detail::none_of<cast_is_temporary_value_reference<T>, std::is_void<T>>::value, T>
|
||||
cast_safe(object &&o) {
|
||||
return pybind11::cast<T>(std::move(o));
|
||||
}
|
||||
template <>
|
||||
inline void cast_safe<void>(object &&) {}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
// The overloads could coexist, i.e. the #if is not strictly speaking needed,
|
||||
// but it is an easy minor optimization.
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
inline cast_error cast_error_unable_to_convert_call_arg() {
|
||||
return cast_error("Unable to convert call argument to Python object (#define "
|
||||
"PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
return cast_error(
|
||||
"Unable to convert call argument to Python object (compile in debug mode for details)");
|
||||
}
|
||||
#else
|
||||
inline cast_error cast_error_unable_to_convert_call_arg(const std::string &name,
|
||||
|
@ -1219,7 +1217,7 @@ tuple make_tuple(Args &&...args_) {
|
|||
detail::make_caster<Args>::cast(std::forward<Args>(args_), policy, nullptr))...}};
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
if (!args[i]) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
#else
|
||||
std::array<std::string, size> argtypes{{type_id<Args>()...}};
|
||||
|
@ -1268,10 +1266,10 @@ struct arg_v : arg {
|
|||
private:
|
||||
template <typename T>
|
||||
arg_v(arg &&base, T &&x, const char *descr = nullptr)
|
||||
: arg(base), value(reinterpret_steal<object>(detail::make_caster<T>::cast(
|
||||
std::forward<T>(x), return_value_policy::automatic, {}))),
|
||||
: arg(base), value(reinterpret_steal<object>(
|
||||
detail::make_caster<T>::cast(x, return_value_policy::automatic, {}))),
|
||||
descr(descr)
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if !defined(NDEBUG)
|
||||
,
|
||||
type(type_id<T>())
|
||||
#endif
|
||||
|
@ -1311,7 +1309,7 @@ public:
|
|||
object value;
|
||||
/// The (optional) description of the default value
|
||||
const char *descr;
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if !defined(NDEBUG)
|
||||
/// The C++ type name of the default value (only available when compiled in debug mode)
|
||||
std::string type;
|
||||
#endif
|
||||
|
@ -1319,7 +1317,7 @@ public:
|
|||
|
||||
/// \ingroup annotations
|
||||
/// Annotation indicating that all following arguments are keyword-only; the is the equivalent of
|
||||
/// an unnamed '*' argument
|
||||
/// an unnamed '*' argument (in Python 3)
|
||||
struct kw_only {};
|
||||
|
||||
/// \ingroup annotations
|
||||
|
@ -1509,14 +1507,14 @@ private:
|
|||
auto o = reinterpret_steal<object>(
|
||||
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
|
||||
if (!o) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
#else
|
||||
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
|
||||
type_id<T>());
|
||||
#endif
|
||||
}
|
||||
args_list.append(std::move(o));
|
||||
args_list.append(o);
|
||||
}
|
||||
|
||||
void process(list &args_list, detail::args_proxy ap) {
|
||||
|
@ -1527,27 +1525,27 @@ private:
|
|||
|
||||
void process(list & /*args_list*/, arg_v a) {
|
||||
if (!a.name) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
nameless_argument_error();
|
||||
#else
|
||||
nameless_argument_error(a.type);
|
||||
#endif
|
||||
}
|
||||
if (m_kwargs.contains(a.name)) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
multiple_values_error();
|
||||
#else
|
||||
multiple_values_error(a.name);
|
||||
#endif
|
||||
}
|
||||
if (!a.value) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error_unable_to_convert_call_arg();
|
||||
#else
|
||||
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
|
||||
#endif
|
||||
}
|
||||
m_kwargs[a.name] = std::move(a.value);
|
||||
m_kwargs[a.name] = a.value;
|
||||
}
|
||||
|
||||
void process(list & /*args_list*/, detail::kwargs_proxy kp) {
|
||||
|
@ -1556,7 +1554,7 @@ private:
|
|||
}
|
||||
for (auto k : reinterpret_borrow<dict>(kp)) {
|
||||
if (m_kwargs.contains(k.first)) {
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
multiple_values_error();
|
||||
#else
|
||||
multiple_values_error(str(k.first));
|
||||
|
@ -1567,10 +1565,9 @@ private:
|
|||
}
|
||||
|
||||
[[noreturn]] static void nameless_argument_error() {
|
||||
throw type_error(
|
||||
"Got kwargs without a name; only named arguments "
|
||||
"may be passed via py::arg() to a python function call. "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
throw type_error("Got kwargs without a name; only named arguments "
|
||||
"may be passed via py::arg() to a python function call. "
|
||||
"(compile in debug mode for details)");
|
||||
}
|
||||
[[noreturn]] static void nameless_argument_error(const std::string &type) {
|
||||
throw type_error("Got kwargs without a name of type '" + type
|
||||
|
@ -1578,9 +1575,8 @@ private:
|
|||
"arguments may be passed via py::arg() to a python function call. ");
|
||||
}
|
||||
[[noreturn]] static void multiple_values_error() {
|
||||
throw type_error(
|
||||
"Got multiple values for keyword argument "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
|
||||
throw type_error("Got multiple values for keyword argument "
|
||||
"(compile in debug mode for details)");
|
||||
}
|
||||
|
||||
[[noreturn]] static void multiple_values_error(const std::string &name) {
|
||||
|
@ -1627,7 +1623,7 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
|
|||
template <typename Derived>
|
||||
template <return_value_policy policy, typename... Args>
|
||||
object object_api<Derived>::operator()(Args &&...args) const {
|
||||
#ifndef NDEBUG
|
||||
#if !defined(NDEBUG) && PY_VERSION_HEX >= 0x03060000
|
||||
if (!PyGILState_Check()) {
|
||||
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
|
||||
}
|
||||
|
@ -1652,12 +1648,12 @@ handle type::handle_of() {
|
|||
}
|
||||
|
||||
#define PYBIND11_MAKE_OPAQUE(...) \
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
|
||||
namespace pybind11 { \
|
||||
namespace detail { \
|
||||
template <> \
|
||||
class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> {}; \
|
||||
} \
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
}
|
||||
|
||||
/// Lets you pass a type containing a `,` through a macro parameter without needing a separate
|
||||
/// typedef, e.g.:
|
||||
|
|
|
@ -18,6 +18,17 @@
|
|||
#include <datetime.h>
|
||||
#include <mutex>
|
||||
|
||||
// Backport the PyDateTime_DELTA functions from Python3.3 if required
|
||||
#ifndef PyDateTime_DELTA_GET_DAYS
|
||||
# define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta *) o)->days)
|
||||
#endif
|
||||
#ifndef PyDateTime_DELTA_GET_SECONDS
|
||||
# define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta *) o)->seconds)
|
||||
#endif
|
||||
#ifndef PyDateTime_DELTA_GET_MICROSECONDS
|
||||
# define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta *) o)->microseconds)
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION)
|
||||
# define PYBIND11_BUILTIN_QUALNAME
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
|
||||
#else
|
||||
// In PyPy, we still set __qualname__ so that we can produce reliable function type
|
||||
// signatures; in CPython this macro expands to nothing:
|
||||
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type
|
||||
// signatures; in 3.3+ this macro expands to nothing:
|
||||
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \
|
||||
setattr((PyObject *) obj, "__qualname__", nameobj)
|
||||
#endif
|
||||
|
@ -55,9 +55,6 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec
|
|||
return PyProperty_Type.tp_descr_set(self, cls, value);
|
||||
}
|
||||
|
||||
// Forward declaration to use in `make_static_property_type()`
|
||||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type);
|
||||
|
||||
/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()`
|
||||
methods are modified to always use the object type instead of a concrete instance.
|
||||
Return value: New reference. */
|
||||
|
@ -90,13 +87,6 @@ inline PyTypeObject *make_static_property_type() {
|
|||
pybind11_fail("make_static_property_type(): failure in PyType_Ready()!");
|
||||
}
|
||||
|
||||
# if PY_VERSION_HEX >= 0x030C0000
|
||||
// PRE 3.12 FEATURE FREEZE. PLEASE REVIEW AFTER FREEZE.
|
||||
// Since Python-3.12 property-derived types are required to
|
||||
// have dynamic attributes (to set `__doc__`)
|
||||
enable_dynamic_attributes(heap_type);
|
||||
# endif
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||
|
||||
|
@ -165,6 +155,7 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb
|
|||
}
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
/**
|
||||
* Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing
|
||||
* methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function,
|
||||
|
@ -179,6 +170,7 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name
|
|||
}
|
||||
return PyType_Type.tp_getattro(obj, name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// metaclass `__call__` function that is used to create all pybind11 objects.
|
||||
extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) {
|
||||
|
@ -274,7 +266,9 @@ inline PyTypeObject *make_default_metaclass() {
|
|||
type->tp_call = pybind11_meta_call;
|
||||
|
||||
type->tp_setattro = pybind11_meta_setattro;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
type->tp_getattro = pybind11_meta_getattro;
|
||||
#endif
|
||||
|
||||
type->tp_dealloc = pybind11_meta_dealloc;
|
||||
|
||||
|
@ -445,17 +439,9 @@ inline void clear_instance(PyObject *self) {
|
|||
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
|
||||
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
|
||||
extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
||||
auto *type = Py_TYPE(self);
|
||||
|
||||
// If this is a GC tracked object, untrack it first
|
||||
// Note that the track call is implicitly done by the
|
||||
// default tp_alloc, which we never override.
|
||||
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) != 0) {
|
||||
PyObject_GC_UnTrack(self);
|
||||
}
|
||||
|
||||
clear_instance(self);
|
||||
|
||||
auto *type = Py_TYPE(self);
|
||||
type->tp_free(self);
|
||||
|
||||
#if PY_VERSION_HEX < 0x03080000
|
||||
|
@ -473,8 +459,6 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string error_string();
|
||||
|
||||
/** Create the type which can be used as a common base for all classes. This is
|
||||
needed in order to satisfy Python's requirements for multiple inheritance.
|
||||
Return value: New reference. */
|
||||
|
@ -510,7 +494,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
|||
type->tp_weaklistoffset = offsetof(instance, weakrefs);
|
||||
|
||||
if (PyType_Ready(type) < 0) {
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string());
|
||||
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
||||
}
|
||||
|
||||
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||
|
@ -520,14 +504,35 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
|||
return (PyObject *) heap_type;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Support for `d = instance.__dict__`.
|
||||
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
if (!dict) {
|
||||
dict = PyDict_New();
|
||||
}
|
||||
Py_XINCREF(dict);
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
|
||||
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
|
||||
if (!PyDict_Check(new_dict)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"__dict__ must be set to a dictionary, not a '%.200s'",
|
||||
get_fully_qualified_tp_name(Py_TYPE(new_dict)).c_str());
|
||||
return -1;
|
||||
}
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_INCREF(new_dict);
|
||||
Py_CLEAR(dict);
|
||||
dict = new_dict;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
|
||||
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
|
||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||
Py_VISIT(dict);
|
||||
// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse
|
||||
#if PY_VERSION_HEX >= 0x03090000
|
||||
Py_VISIT(Py_TYPE(self));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -542,26 +547,14 @@ extern "C" inline int pybind11_clear(PyObject *self) {
|
|||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
||||
auto *type = &heap_type->ht_type;
|
||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
||||
#if PY_VERSION_HEX < 0x030B0000
|
||||
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
|
||||
type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
|
||||
#else
|
||||
type->tp_flags |= Py_TPFLAGS_MANAGED_DICT;
|
||||
#endif
|
||||
type->tp_traverse = pybind11_traverse;
|
||||
type->tp_clear = pybind11_clear;
|
||||
|
||||
static PyGetSetDef getset[] = {{
|
||||
#if PY_VERSION_HEX < 0x03070000
|
||||
const_cast<char *>("__dict__"),
|
||||
#else
|
||||
"__dict__",
|
||||
#endif
|
||||
PyObject_GenericGetDict,
|
||||
PyObject_GenericSetDict,
|
||||
nullptr,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}};
|
||||
static PyGetSetDef getset[] = {
|
||||
{const_cast<char *>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr}};
|
||||
type->tp_getset = getset;
|
||||
}
|
||||
|
||||
|
@ -620,6 +613,9 @@ extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
|
|||
/// Give this type a buffer interface.
|
||||
inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
|
||||
heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer;
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
|
||||
#endif
|
||||
|
||||
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
|
||||
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
|
||||
|
@ -632,8 +628,12 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
|||
|
||||
auto qualname = name;
|
||||
if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
qualname = reinterpret_steal<object>(
|
||||
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
|
||||
#else
|
||||
qualname = str(rec.scope.attr("__qualname__").cast<std::string>() + "." + rec.name);
|
||||
#endif
|
||||
}
|
||||
|
||||
object module_;
|
||||
|
@ -697,10 +697,15 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
|||
type->tp_as_number = &heap_type->as_number;
|
||||
type->tp_as_sequence = &heap_type->as_sequence;
|
||||
type->tp_as_mapping = &heap_type->as_mapping;
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
type->tp_as_async = &heap_type->as_async;
|
||||
#endif
|
||||
|
||||
/* Flags */
|
||||
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
||||
#endif
|
||||
if (!rec.is_final) {
|
||||
type->tp_flags |= Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
|
@ -718,7 +723,7 @@ inline PyObject *make_new_python_type(const type_record &rec) {
|
|||
}
|
||||
|
||||
if (PyType_Ready(type) < 0) {
|
||||
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string());
|
||||
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
|
||||
}
|
||||
|
||||
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||
|
|
|
@ -10,76 +10,15 @@
|
|||
#pragma once
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 10
|
||||
#define PYBIND11_VERSION_PATCH 4
|
||||
#define PYBIND11_VERSION_MINOR 9
|
||||
#define PYBIND11_VERSION_PATCH 2
|
||||
|
||||
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
|
||||
// Additional convention: 0xD = dev
|
||||
#define PYBIND11_VERSION_HEX 0x020A0400
|
||||
#define PYBIND11_VERSION_HEX 0x02090200
|
||||
|
||||
// Define some generic pybind11 helper macros for warning management.
|
||||
//
|
||||
// Note that compiler-specific push/pop pairs are baked into the
|
||||
// PYBIND11_NAMESPACE_BEGIN/PYBIND11_NAMESPACE_END pair of macros. Therefore manual
|
||||
// PYBIND11_WARNING_PUSH/PYBIND11_WARNING_POP are usually only needed in `#include` sections.
|
||||
//
|
||||
// If you find you need to suppress a warning, please try to make the suppression as local as
|
||||
// possible using these macros. Please also be sure to push/pop with the pybind11 macros. Please
|
||||
// only use compiler specifics if you need to check specific versions, e.g. Apple Clang vs. vanilla
|
||||
// Clang.
|
||||
#if defined(_MSC_VER)
|
||||
# define PYBIND11_COMPILER_MSVC
|
||||
# define PYBIND11_PRAGMA(...) __pragma(__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(warning(push))
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(warning(pop))
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
# define PYBIND11_COMPILER_INTEL
|
||||
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(warning push)
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(warning pop)
|
||||
#elif defined(__clang__)
|
||||
# define PYBIND11_COMPILER_CLANG
|
||||
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(clang diagnostic push)
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(clang diagnostic push)
|
||||
#elif defined(__GNUC__)
|
||||
# define PYBIND11_COMPILER_GCC
|
||||
# define PYBIND11_PRAGMA(...) _Pragma(#__VA_ARGS__)
|
||||
# define PYBIND11_WARNING_PUSH PYBIND11_PRAGMA(GCC diagnostic push)
|
||||
# define PYBIND11_WARNING_POP PYBIND11_PRAGMA(GCC diagnostic pop)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_MSVC
|
||||
# define PYBIND11_WARNING_DISABLE_MSVC(name) PYBIND11_PRAGMA(warning(disable : name))
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_MSVC(name)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_CLANG
|
||||
# define PYBIND11_WARNING_DISABLE_CLANG(name) PYBIND11_PRAGMA(clang diagnostic ignored name)
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_CLANG(name)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_GCC
|
||||
# define PYBIND11_WARNING_DISABLE_GCC(name) PYBIND11_PRAGMA(GCC diagnostic ignored name)
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_GCC(name)
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_COMPILER_INTEL
|
||||
# define PYBIND11_WARNING_DISABLE_INTEL(name) PYBIND11_PRAGMA(warning disable name)
|
||||
#else
|
||||
# define PYBIND11_WARNING_DISABLE_INTEL(name)
|
||||
#endif
|
||||
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) \
|
||||
namespace name { \
|
||||
PYBIND11_WARNING_PUSH
|
||||
|
||||
#define PYBIND11_NAMESPACE_END(name) \
|
||||
PYBIND11_WARNING_POP \
|
||||
}
|
||||
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
|
||||
#define PYBIND11_NAMESPACE_END(name) }
|
||||
|
||||
// Robust support for some features and loading modules compiled against different pybind versions
|
||||
// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute
|
||||
|
@ -99,7 +38,6 @@
|
|||
# define PYBIND11_CPP17
|
||||
# if __cplusplus >= 202002L
|
||||
# define PYBIND11_CPP20
|
||||
// Please update tests/pybind11_tests.cpp `cpp_std()` when adding a macro here.
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
|
@ -109,7 +47,7 @@
|
|||
// or newer.
|
||||
# if _MSVC_LANG >= 201402L
|
||||
# define PYBIND11_CPP14
|
||||
# if _MSVC_LANG > 201402L
|
||||
# if _MSVC_LANG > 201402L && _MSC_VER >= 1910
|
||||
# define PYBIND11_CPP17
|
||||
# if _MSVC_LANG >= 202002L
|
||||
# define PYBIND11_CPP20
|
||||
|
@ -143,8 +81,10 @@
|
|||
# error pybind11 requires gcc 4.8 or newer
|
||||
# endif
|
||||
#elif defined(_MSC_VER)
|
||||
# if _MSC_VER < 1910
|
||||
# error pybind11 2.10+ requires MSVC 2017 or newer
|
||||
// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features
|
||||
// (e.g. std::negation) added in 2015u3:
|
||||
# if _MSC_FULL_VER < 190024210
|
||||
# error pybind11 requires MSVC 2015 update 3 or newer
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
@ -157,10 +97,13 @@
|
|||
#endif
|
||||
|
||||
#if !defined(PYBIND11_EXPORT_EXCEPTION)
|
||||
# if defined(__apple_build_version__)
|
||||
# define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT
|
||||
# else
|
||||
# ifdef __MINGW32__
|
||||
// workaround for:
|
||||
// error: 'dllexport' implies default visibility, but xxx has already been declared with a
|
||||
// different visibility
|
||||
# define PYBIND11_EXPORT_EXCEPTION
|
||||
# else
|
||||
# define PYBIND11_EXPORT_EXCEPTION PYBIND11_EXPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
@ -206,14 +149,15 @@
|
|||
|
||||
/* Don't let Python.h #define (v)snprintf as macro because they are implemented
|
||||
properly in Visual Studio since 2015. */
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define HAVE_SNPRINTF 1
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4505)
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4510 4610 4512 4005)
|
||||
# pragma warning(push)
|
||||
// C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only)
|
||||
# pragma warning(disable : 4505)
|
||||
# pragma warning(disable: 4510 4610 4512 4005)
|
||||
#endif
|
||||
|
||||
// https://en.cppreference.com/w/c/chrono/localtime
|
||||
|
@ -250,11 +194,11 @@ PYBIND11_WARNING_DISABLE_MSVC(4510 4610 4512 4005)
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
// Reminder: WITH_THREAD is always defined if PY_VERSION_HEX >= 0x03070000
|
||||
#if PY_VERSION_HEX < 0x03060000
|
||||
# error "PYTHON < 3.6 IS UNSUPPORTED. pybind11 v2.9 was the last to support Python 2 and 3.5."
|
||||
#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
|
||||
# define PYBIND11_HAS_U8STRING
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#include <frameobject.h>
|
||||
#include <pythread.h>
|
||||
|
||||
|
@ -275,16 +219,12 @@ PYBIND11_WARNING_DISABLE_MSVC(4510 4610 4512 4005)
|
|||
# undef copysign
|
||||
#endif
|
||||
|
||||
#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# define PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# if defined(PYBIND11_DEBUG_MARKER)
|
||||
# define _DEBUG
|
||||
# undef PYBIND11_DEBUG_MARKER
|
||||
# endif
|
||||
PYBIND11_WARNING_POP
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
@ -305,21 +245,6 @@ PYBIND11_WARNING_POP
|
|||
# endif
|
||||
#endif
|
||||
|
||||
// Must be after including <version> or one of the other headers specified by the standard
|
||||
#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
|
||||
# define PYBIND11_HAS_U8STRING
|
||||
#endif
|
||||
|
||||
// See description of PR #4246:
|
||||
#if !defined(NDEBUG) && !defined(PY_ASSERT_GIL_HELD_INCREF_DECREF) \
|
||||
&& !(defined(PYPY_VERSION) \
|
||||
&& defined(_MSC_VER)) /* PyPy Windows: pytest hangs indefinitely at the end of the \
|
||||
process (see PR #4268) */ \
|
||||
&& !defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
|
||||
// The following define will be enabled by default in the 2.11 release
|
||||
// define PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||
#endif
|
||||
|
||||
// #define PYBIND11_STR_LEGACY_PERMISSIVE
|
||||
// If DEFINED, pybind11::str can hold PyUnicodeObject or PyBytesObject
|
||||
// (probably surprising and never documented, but this was the
|
||||
|
@ -329,37 +254,78 @@ PYBIND11_WARNING_POP
|
|||
// If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and
|
||||
// pybind11::isinstance<str>() is true only for pybind11::str.
|
||||
// However, for Python 2 only (!), the pybind11::str caster
|
||||
// implicitly decoded bytes to PyUnicodeObject. This was to ease
|
||||
// implicitly decodes bytes to PyUnicodeObject. This is to ease
|
||||
// the transition from the legacy behavior to the non-permissive
|
||||
// behavior.
|
||||
|
||||
/// Compatibility macros for Python 2 / Python 3 versions TODO: remove
|
||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
|
||||
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
|
||||
#define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
|
||||
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
|
||||
#define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
|
||||
#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
|
||||
#define PYBIND11_BYTES_NAME "bytes"
|
||||
#define PYBIND11_STRING_NAME "str"
|
||||
#define PYBIND11_SLICE_OBJECT PyObject
|
||||
#define PYBIND11_FROM_STRING PyUnicode_FromString
|
||||
#define PYBIND11_STR_TYPE ::pybind11::str
|
||||
#define PYBIND11_BOOL_ATTR "__bool__"
|
||||
#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
|
||||
#define PYBIND11_BUILTINS_MODULE "builtins"
|
||||
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
||||
# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||
# define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
|
||||
# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
|
||||
# define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||
# define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
||||
# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING PyBytes_AsString
|
||||
# define PYBIND11_BYTES_SIZE PyBytes_Size
|
||||
# define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
|
||||
# define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
|
||||
# define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
|
||||
# define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
|
||||
# define PYBIND11_BYTES_NAME "bytes"
|
||||
# define PYBIND11_STRING_NAME "str"
|
||||
# define PYBIND11_SLICE_OBJECT PyObject
|
||||
# define PYBIND11_FROM_STRING PyUnicode_FromString
|
||||
# define PYBIND11_STR_TYPE ::pybind11::str
|
||||
# define PYBIND11_BOOL_ATTR "__bool__"
|
||||
# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
|
||||
# define PYBIND11_BUILTINS_MODULE "builtins"
|
||||
// Providing a separate declaration to make Clang's -Wmissing-prototypes happy.
|
||||
// See comment for PYBIND11_MODULE below for why this is marked "maybe unused".
|
||||
#define PYBIND11_PLUGIN_IMPL(name) \
|
||||
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||
# define PYBIND11_PLUGIN_IMPL(name) \
|
||||
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||
|
||||
#else
|
||||
# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
|
||||
# define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check
|
||||
# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION
|
||||
# define PYBIND11_BYTES_CHECK PyString_Check
|
||||
# define PYBIND11_BYTES_FROM_STRING PyString_FromString
|
||||
# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
|
||||
# define PYBIND11_BYTES_AS_STRING PyString_AsString
|
||||
# define PYBIND11_BYTES_SIZE PyString_Size
|
||||
# define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
|
||||
# define PYBIND11_LONG_AS_LONGLONG(o) \
|
||||
(PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
|
||||
# define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed.
|
||||
# define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed.
|
||||
# define PYBIND11_BYTES_NAME "str"
|
||||
# define PYBIND11_STRING_NAME "unicode"
|
||||
# define PYBIND11_SLICE_OBJECT PySliceObject
|
||||
# define PYBIND11_FROM_STRING PyString_FromString
|
||||
# define PYBIND11_STR_TYPE ::pybind11::bytes
|
||||
# define PYBIND11_BOOL_ATTR "__nonzero__"
|
||||
# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero)
|
||||
# define PYBIND11_BUILTINS_MODULE "__builtin__"
|
||||
// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy.
|
||||
// See comment for PYBIND11_MODULE below for why this is marked "maybe unused".
|
||||
# define PYBIND11_PLUGIN_IMPL(name) \
|
||||
static PyObject *pybind11_init_wrapper(); \
|
||||
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \
|
||||
extern "C" PYBIND11_EXPORT void init##name() { (void) pybind11_init_wrapper(); } \
|
||||
PyObject *pybind11_init_wrapper()
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200
|
||||
extern "C" {
|
||||
struct _Py_atomic_address {
|
||||
void *value;
|
||||
};
|
||||
PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
|
||||
#define PYBIND11_STRINGIFY(x) #x
|
||||
|
@ -384,15 +350,31 @@ PYBIND11_WARNING_POP
|
|||
} \
|
||||
}
|
||||
|
||||
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
|
||||
# define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# define PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||
catch (pybind11::error_already_set & e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
} \
|
||||
catch (const std::exception &e) { \
|
||||
PyErr_SetString(PyExc_ImportError, e.what()); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
***Deprecated in favor of PYBIND11_MODULE***
|
||||
|
@ -424,7 +406,7 @@ PYBIND11_WARNING_POP
|
|||
|
||||
/** \rst
|
||||
This macro creates the entry point that will be invoked when the Python interpreter
|
||||
imports an extension module. The module name is given as the first argument and it
|
||||
imports an extension module. The module name is given as the fist argument and it
|
||||
should not be in quotes. The second macro argument defines a variable of type
|
||||
`py::module_` which can be used to initialize the module.
|
||||
|
||||
|
@ -489,7 +471,7 @@ enum class return_value_policy : uint8_t {
|
|||
|
||||
/** Reference an existing object (i.e. do not create a new copy) and take
|
||||
ownership. Python will call the destructor and delete operator when the
|
||||
object's reference count reaches zero. Undefined behavior ensues when
|
||||
object’s reference count reaches zero. Undefined behavior ensues when
|
||||
the C++ side does the same.. */
|
||||
take_ownership,
|
||||
|
||||
|
@ -505,7 +487,7 @@ enum class return_value_policy : uint8_t {
|
|||
move,
|
||||
|
||||
/** Reference an existing object, but do not take ownership. The C++ side
|
||||
is responsible for managing the object's lifetime and deallocating it
|
||||
is responsible for managing the object’s lifetime and deallocating it
|
||||
when it is no longer used. Warning: undefined behavior will ensue when
|
||||
the C++ side deletes an object that is still referenced and used by
|
||||
Python. */
|
||||
|
@ -514,7 +496,7 @@ enum class return_value_policy : uint8_t {
|
|||
/** This policy only applies to methods and properties. It references the
|
||||
object without taking ownership similar to the above
|
||||
return_value_policy::reference policy. In contrast to that policy, the
|
||||
function or property's implicit this argument (called the parent) is
|
||||
function or property’s implicit this argument (called the parent) is
|
||||
considered to be the the owner of the return value (the child).
|
||||
pybind11 then couples the lifetime of the parent to the child via a
|
||||
reference relationship that ensures that the parent cannot be garbage
|
||||
|
@ -621,7 +603,7 @@ static_assert(std::is_standard_layout<instance>::value,
|
|||
"Internal error: `pybind11::detail::instance` is not standard layout!");
|
||||
|
||||
/// from __cpp_future__ import (convenient aliases from C++14/17)
|
||||
#if defined(PYBIND11_CPP14)
|
||||
#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910)
|
||||
using std::conditional_t;
|
||||
using std::enable_if_t;
|
||||
using std::remove_cv_t;
|
||||
|
@ -884,12 +866,10 @@ struct is_template_base_of_impl {
|
|||
/// Check if a template is the base of a type. For example:
|
||||
/// `is_template_base_of<Base, T>` is true if `struct T : Base<U> {}` where U can be anything
|
||||
template <template <typename...> class Base, typename T>
|
||||
// Sadly, all MSVC versions incl. 2022 need the workaround, even in C++20 mode.
|
||||
// See also: https://github.com/pybind/pybind11/pull/3741
|
||||
#if !defined(_MSC_VER)
|
||||
using is_template_base_of
|
||||
= decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr));
|
||||
#else
|
||||
#else // MSVC2015 has trouble with decltype in template aliases
|
||||
struct is_template_base_of
|
||||
: decltype(is_template_base_of_impl<Base>::check((intrinsic_t<T> *) nullptr)) {
|
||||
};
|
||||
|
@ -959,6 +939,12 @@ using expand_side_effects = bool[];
|
|||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4275)
|
||||
// warning C4275: An exported class was derived from a class that wasn't exported.
|
||||
// Can be ignored when derived from a STL class.
|
||||
#endif
|
||||
/// C++ bindings of builtin Python exceptions
|
||||
class PYBIND11_EXPORT_EXCEPTION builtin_exception : public std::runtime_error {
|
||||
public:
|
||||
|
@ -966,6 +952,9 @@ public:
|
|||
/// Set the error using the Python C API
|
||||
virtual void set_error() const = 0;
|
||||
};
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define PYBIND11_RUNTIME_EXCEPTION(name, type) \
|
||||
class PYBIND11_EXPORT_EXCEPTION name : public builtin_exception { \
|
||||
|
@ -989,11 +978,9 @@ PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybin
|
|||
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
|
||||
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason) {
|
||||
assert(!PyErr_Occurred());
|
||||
throw std::runtime_error(reason);
|
||||
}
|
||||
[[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const std::string &reason) {
|
||||
assert(!PyErr_Occurred());
|
||||
throw std::runtime_error(reason);
|
||||
}
|
||||
|
||||
|
@ -1045,8 +1032,6 @@ constexpr const char
|
|||
struct error_scope {
|
||||
PyObject *type, *value, *trace;
|
||||
error_scope() { PyErr_Fetch(&type, &value, &trace); }
|
||||
error_scope(const error_scope &) = delete;
|
||||
error_scope &operator=(const error_scope &) = delete;
|
||||
~error_scope() { PyErr_Restore(type, value, trace); }
|
||||
};
|
||||
|
||||
|
@ -1059,6 +1044,9 @@ struct nodelete {
|
|||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
struct overload_cast_impl {
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default): MSVC 2015 needs this
|
||||
constexpr overload_cast_impl() {}
|
||||
|
||||
template <typename Return>
|
||||
constexpr auto operator()(Return (*pf)(Args...)) const noexcept -> decltype(pf) {
|
||||
return pf;
|
||||
|
@ -1085,7 +1073,8 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
|
||||
template <typename... Args>
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast{};
|
||||
static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
|
||||
// MSVC 2015 only accepts this particular initialization syntax for this variable template.
|
||||
#endif
|
||||
|
||||
/// Const member function selector for overload_cast
|
||||
|
@ -1171,7 +1160,7 @@ try_get_shared_from_this(std::enable_shared_from_this<T> *holder_value_ptr) {
|
|||
|
||||
// For silencing "unused" compiler warnings in special situations.
|
||||
template <typename... Args>
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1920 // MSVC 2017
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER < 1920 // MSVC 2017
|
||||
constexpr
|
||||
#endif
|
||||
inline void
|
||||
|
@ -1194,29 +1183,15 @@ constexpr
|
|||
# define PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(...)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) \
|
||||
&& (defined(__apple_build_version__) /* AppleClang 13.0.0.13000029 was the only data point \
|
||||
available. */ \
|
||||
|| (__clang_major__ >= 7 \
|
||||
&& __clang_major__ <= 12) /* Clang 3, 5, 13, 14, 15 do not generate the warning. */ \
|
||||
)
|
||||
# define PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
|
||||
// Example:
|
||||
// tests/test_kwargs_and_defaults.cpp:46:68: error: local variable 'args' will be copied despite
|
||||
// being returned by name [-Werror,-Wreturn-std-move]
|
||||
// m.def("args_function", [](py::args args) -> py::tuple { return args; });
|
||||
// ^~~~
|
||||
// test_kwargs_and_defaults.cpp:46:68: note: call 'std::move' explicitly to avoid copying
|
||||
// m.def("args_function", [](py::args args) -> py::tuple { return args; });
|
||||
// ^~~~
|
||||
// std::move(args)
|
||||
#endif
|
||||
#if defined(_MSC_VER) // All versions (as of July 2021).
|
||||
|
||||
// Pybind offers detailed error messages by default for all builts that are debug (through the
|
||||
// negation of ndebug). This can also be manually enabled by users, for any builds, through
|
||||
// defining PYBIND11_DETAILED_ERROR_MESSAGES.
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(NDEBUG)
|
||||
# define PYBIND11_DETAILED_ERROR_MESSAGES
|
||||
// warning C4127: Conditional expression is constant
|
||||
constexpr inline bool silence_msvc_c4127(bool cond) { return cond; }
|
||||
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) ::pybind11::detail::silence_msvc_c4127(__VA_ARGS__)
|
||||
|
||||
#else
|
||||
# define PYBIND11_SILENCE_MSVC_C4127(...) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
#include "class.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <>
|
||||
|
@ -118,7 +115,7 @@ template <typename Class>
|
|||
void construct(value_and_holder &v_h, Cpp<Class> *ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
no_nullptr(ptr);
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
|
||||
// We're going to try to construct an alias by moving the cpp type. Whether or not
|
||||
// that succeeds, we still need to destroy the original cpp pointer (either the
|
||||
// moved away leftover, if the alias construction works, or the value itself if we
|
||||
|
@ -159,7 +156,7 @@ void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
|||
auto *ptr = holder_helper<Holder<Class>>::get(holder);
|
||||
no_nullptr(ptr);
|
||||
// If we need an alias, check that the held pointer is actually an alias instance
|
||||
if (Class::has_alias && need_alias && !is_alias<Class>(ptr)) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias && !is_alias<Class>(ptr)) {
|
||||
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
|
||||
"is not an alias instance");
|
||||
}
|
||||
|
@ -177,7 +174,7 @@ void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
|||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
static_assert(std::is_move_constructible<Cpp<Class>>::value,
|
||||
"pybind11::init() return-by-value factory function requires a movable class");
|
||||
if (Class::has_alias && need_alias) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(Class::has_alias) && need_alias) {
|
||||
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(result));
|
||||
} else {
|
||||
v_h.value_ptr() = new Cpp<Class>(std::move(result));
|
||||
|
@ -209,11 +206,10 @@ struct constructor {
|
|||
extra...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value, int>
|
||||
= 0>
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Cpp<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
|
@ -230,11 +226,10 @@ struct constructor {
|
|||
extra...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value, int>
|
||||
= 0>
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && !std::is_constructible<Cpp<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
|
@ -250,11 +245,10 @@ struct constructor {
|
|||
// Implementing class for py::init_alias<...>()
|
||||
template <typename... Args>
|
||||
struct alias_constructor {
|
||||
template <
|
||||
typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value, int>
|
||||
= 0>
|
||||
template <typename Class,
|
||||
typename... Extra,
|
||||
enable_if_t<Class::has_alias && std::is_constructible<Alias<Class>, Args...>::value,
|
||||
int> = 0>
|
||||
static void execute(Class &cl, const Extra &...extra) {
|
||||
cl.def(
|
||||
"__init__",
|
||||
|
@ -431,4 +425,4 @@ struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
|
|||
|
||||
PYBIND11_NAMESPACE_END(initimpl)
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
|
|
@ -9,12 +9,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# include "../gil.h"
|
||||
#endif
|
||||
|
||||
#include "../pytypes.h"
|
||||
|
||||
#include <exception>
|
||||
|
@ -43,8 +37,6 @@ using ExceptionTranslator = void (*)(std::exception_ptr);
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
constexpr const char *internals_function_record_capsule_name = "pybind11_function_record_capsule";
|
||||
|
||||
// Forward declarations
|
||||
inline PyTypeObject *make_static_property_type();
|
||||
inline PyTypeObject *make_default_metaclass();
|
||||
|
@ -57,7 +49,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
|||
// `Py_LIMITED_API` anyway.
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
# define PYBIND11_TLS_KEY_REF Py_tss_t &
|
||||
# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
|
||||
# ifdef __GNUC__
|
||||
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
|
||||
// for every field.
|
||||
# define PYBIND11_TLS_KEY_INIT(var) \
|
||||
|
@ -90,7 +82,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
|||
# define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
|
||||
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
|
||||
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key))
|
||||
# if defined(PYPY_VERSION)
|
||||
# if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION)
|
||||
// On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set
|
||||
// the value if it has already been set. Instead, it must first be deleted and
|
||||
// then set again.
|
||||
|
@ -177,23 +169,11 @@ struct internals {
|
|||
PyTypeObject *default_metaclass;
|
||||
PyObject *instance_base;
|
||||
#if defined(WITH_THREAD)
|
||||
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
|
||||
PYBIND11_TLS_KEY_INIT(tstate)
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||
// Unused if PYBIND11_SIMPLE_GIL_MANAGEMENT is defined:
|
||||
PyInterpreterState *istate = nullptr;
|
||||
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
// Note that we have to use a std::string to allocate memory to ensure a unique address
|
||||
// We want unique addresses since we use pointer equality to compare function records
|
||||
std::string function_record_capsule_name = internals_function_record_capsule_name;
|
||||
# endif
|
||||
|
||||
internals() = default;
|
||||
internals(const internals &other) = delete;
|
||||
internals &operator=(const internals &other) = delete;
|
||||
~internals() {
|
||||
# if PYBIND11_INTERNALS_VERSION > 4
|
||||
PYBIND11_TLS_FREE(loader_life_support_tls_key);
|
||||
|
@ -314,6 +294,7 @@ inline internals **&get_internals_pp() {
|
|||
return internals_pp;
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// forward decl
|
||||
inline void translate_exception(std::exception_ptr);
|
||||
|
||||
|
@ -337,11 +318,21 @@ bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template <class T>
|
||||
bool handle_nested_exception(const T &, std::exception_ptr &) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool raise_err(PyObject *exc_type, const char *msg) {
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
if (PyErr_Occurred()) {
|
||||
raise_from(exc_type, msg);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
PyErr_SetString(exc_type, msg);
|
||||
return false;
|
||||
}
|
||||
|
@ -428,22 +419,13 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||
return **internals_pp;
|
||||
}
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
# if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
gil_scoped_acquire gil;
|
||||
# else
|
||||
// Ensure that the GIL is held since we will need to make Python calls.
|
||||
// Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
|
||||
struct gil_scoped_acquire_local {
|
||||
gil_scoped_acquire_local() : state(PyGILState_Ensure()) {}
|
||||
gil_scoped_acquire_local(const gil_scoped_acquire_local &) = delete;
|
||||
gil_scoped_acquire_local &operator=(const gil_scoped_acquire_local &) = delete;
|
||||
~gil_scoped_acquire_local() { PyGILState_Release(state); }
|
||||
const PyGILState_STATE state;
|
||||
} gil;
|
||||
# endif
|
||||
#endif
|
||||
error_scope err_scope;
|
||||
|
||||
PYBIND11_STR_TYPE id(PYBIND11_INTERNALS_ID);
|
||||
auto builtins = handle(PyEval_GetBuiltins());
|
||||
|
@ -468,6 +450,9 @@ PYBIND11_NOINLINE internals &get_internals() {
|
|||
internals_ptr = new internals();
|
||||
#if defined(WITH_THREAD)
|
||||
|
||||
# if PY_VERSION_HEX < 0x03090000
|
||||
PyEval_InitThreads();
|
||||
# endif
|
||||
PyThreadState *tstate = PyThreadState_Get();
|
||||
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||
|
@ -537,13 +522,8 @@ struct local_internals {
|
|||
|
||||
/// Works like `get_internals`, but for things which are locally registered.
|
||||
inline local_internals &get_local_internals() {
|
||||
// Current static can be created in the interpreter finalization routine. If the later will be
|
||||
// destroyed in another static variable destructor, creation of this static there will cause
|
||||
// static deinitialization fiasco. In order to avoid it we avoid destruction of the
|
||||
// local_internals static. One can read more about the problem and current solution here:
|
||||
// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
|
||||
static auto *locals = new local_internals();
|
||||
return *locals;
|
||||
static local_internals locals;
|
||||
return locals;
|
||||
}
|
||||
|
||||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
||||
|
@ -557,25 +537,6 @@ const char *c_str(Args &&...args) {
|
|||
return strings.front().c_str();
|
||||
}
|
||||
|
||||
inline const char *get_function_record_capsule_name() {
|
||||
#if PYBIND11_INTERNALS_VERSION > 4
|
||||
return get_internals().function_record_capsule_name.c_str();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine whether or not the following capsule contains a pybind11 function record.
|
||||
// Note that we use `internals` to make sure that only ABI compatible records are touched.
|
||||
//
|
||||
// This check is currently used in two places:
|
||||
// - An important optimization in functional.h to avoid overhead in C++ -> Python -> C++
|
||||
// - The sibling feature of cpp_function to allow overloads
|
||||
inline bool is_function_record_capsule(const capsule &cap) {
|
||||
// Pointer equality as we rely on internals() to ensure unique pointers
|
||||
return cap.name() == get_function_record_capsule_name();
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Returns a named pointer that is shared among all extension modules (using the same
|
||||
|
|
|
@ -225,8 +225,8 @@ PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_index &tp,
|
|||
if (throw_if_missing) {
|
||||
std::string tname = tp.name();
|
||||
detail::clean_type_id(tname);
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \""
|
||||
+ std::move(tname) + '"');
|
||||
pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname
|
||||
+ "\"");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -394,16 +394,15 @@ instance::get_value_and_holder(const type_info *find_type /*= nullptr default in
|
|||
return value_and_holder();
|
||||
}
|
||||
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(compile in debug mode for type details)");
|
||||
#else
|
||||
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `"
|
||||
+ get_fully_qualified_tp_name(find_type->type)
|
||||
+ "' is not a pybind11 base of the given `"
|
||||
+ get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance");
|
||||
#else
|
||||
pybind11_fail(
|
||||
"pybind11::detail::instance::get_value_and_holder: "
|
||||
"type is not a pybind11 base of the given instance "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for type details)");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -440,15 +439,21 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
|
|||
// instance_registered)
|
||||
|
||||
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
|
||||
// in particular, need to be 0). Use Python's memory allocation
|
||||
// functions: Python is using pymalloc, which is designed to be
|
||||
// efficient for small allocations like the one we're doing here;
|
||||
// for larger allocations they are just wrappers around malloc.
|
||||
// TODO: is this still true for pure Python 3.6?
|
||||
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6
|
||||
// they default to using pymalloc, which is designed to be efficient for small allocations
|
||||
// like the one we're doing here; in earlier versions (and for larger allocations) they are
|
||||
// just wrappers around malloc.
|
||||
#if PY_VERSION_HEX >= 0x03050000
|
||||
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
|
||||
if (!nonsimple.values_and_holders) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
#else
|
||||
nonsimple.values_and_holders = (void **) PyMem_New(void *, space);
|
||||
if (!nonsimple.values_and_holders)
|
||||
throw std::bad_alloc();
|
||||
std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
|
||||
#endif
|
||||
nonsimple.status
|
||||
= reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
|
||||
}
|
||||
|
@ -470,6 +475,70 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp)
|
|||
return isinstance(obj, type);
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE std::string error_string() {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred");
|
||||
return "Unknown internal error occurred";
|
||||
}
|
||||
|
||||
error_scope scope; // Preserve error state
|
||||
|
||||
std::string errorString;
|
||||
if (scope.type) {
|
||||
errorString += handle(scope.type).attr("__name__").cast<std::string>();
|
||||
errorString += ": ";
|
||||
}
|
||||
if (scope.value) {
|
||||
errorString += (std::string) str(scope.value);
|
||||
}
|
||||
|
||||
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if (scope.trace != nullptr) {
|
||||
PyException_SetTraceback(scope.value, scope.trace);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(PYPY_VERSION)
|
||||
if (scope.trace) {
|
||||
auto *trace = (PyTracebackObject *) scope.trace;
|
||||
|
||||
/* Get the deepest trace possible */
|
||||
while (trace->tb_next) {
|
||||
trace = trace->tb_next;
|
||||
}
|
||||
|
||||
PyFrameObject *frame = trace->tb_frame;
|
||||
Py_XINCREF(frame);
|
||||
errorString += "\n\nAt:\n";
|
||||
while (frame) {
|
||||
# if PY_VERSION_HEX >= 0x030900B1
|
||||
PyCodeObject *f_code = PyFrame_GetCode(frame);
|
||||
# else
|
||||
PyCodeObject *f_code = frame->f_code;
|
||||
Py_INCREF(f_code);
|
||||
# endif
|
||||
int lineno = PyFrame_GetLineNumber(frame);
|
||||
errorString += " " + handle(f_code->co_filename).cast<std::string>() + "("
|
||||
+ std::to_string(lineno)
|
||||
+ "): " + handle(f_code->co_name).cast<std::string>() + "\n";
|
||||
Py_DECREF(f_code);
|
||||
# if PY_VERSION_HEX >= 0x030900B1
|
||||
auto *b_frame = PyFrame_GetBack(frame);
|
||||
# else
|
||||
auto *b_frame = frame->f_back;
|
||||
Py_XINCREF(b_frame);
|
||||
# endif
|
||||
Py_DECREF(frame);
|
||||
frame = b_frame;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) {
|
||||
auto &instances = get_internals().registered_instances;
|
||||
auto range = instances.equal_range(ptr);
|
||||
|
@ -486,6 +555,12 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
|
|||
inline PyThreadState *get_thread_state_unchecked() {
|
||||
#if defined(PYPY_VERSION)
|
||||
return PyThreadState_GET();
|
||||
#elif PY_VERSION_HEX < 0x03000000
|
||||
return _PyThreadState_Current;
|
||||
#elif PY_VERSION_HEX < 0x03050000
|
||||
return (PyThreadState *) _Py_atomic_load_relaxed(&_PyThreadState_Current);
|
||||
#elif PY_VERSION_HEX < 0x03050200
|
||||
return (PyThreadState *) _PyThreadState_Current.value;
|
||||
#else
|
||||
return _PyThreadState_UncheckedGet();
|
||||
#endif
|
||||
|
@ -547,15 +622,14 @@ public:
|
|||
if (copy_constructor) {
|
||||
valueptr = copy_constructor(src);
|
||||
} else {
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = copy, but type is "
|
||||
"non-copyable! (compile in debug mode for details)");
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = copy, but type " + type_name
|
||||
+ " is non-copyable!");
|
||||
#else
|
||||
throw cast_error("return_value_policy = copy, but type is "
|
||||
"non-copyable! (#define PYBIND11_DETAILED_ERROR_MESSAGES or "
|
||||
"compile in debug mode for details)");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
|
@ -567,16 +641,15 @@ public:
|
|||
} else if (copy_constructor) {
|
||||
valueptr = copy_constructor(src);
|
||||
} else {
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if defined(NDEBUG)
|
||||
throw cast_error("return_value_policy = move, but type is neither "
|
||||
"movable nor copyable! "
|
||||
"(compile in debug mode for details)");
|
||||
#else
|
||||
std::string type_name(tinfo->cpptype->name());
|
||||
detail::clean_type_id(type_name);
|
||||
throw cast_error("return_value_policy = move, but type " + type_name
|
||||
+ " is neither movable nor copyable!");
|
||||
#else
|
||||
throw cast_error("return_value_policy = move, but type is neither "
|
||||
"movable nor copyable! "
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in "
|
||||
"debug mode for details)");
|
||||
#endif
|
||||
}
|
||||
wrapper->owned = true;
|
||||
|
@ -1006,14 +1079,5 @@ protected:
|
|||
static Constructor make_move_constructor(...) { return nullptr; }
|
||||
};
|
||||
|
||||
PYBIND11_NOINLINE std::string type_info_description(const std::type_info &ti) {
|
||||
if (auto *type_data = get_type_info(ti)) {
|
||||
handle th((PyObject *) type_data->type);
|
||||
return th.attr("__module__").cast<std::string>() + '.'
|
||||
+ th.attr("__qualname__").cast<std::string>();
|
||||
}
|
||||
return clean_type_id(ti.name());
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
/// Erase all occurrences of a substring
|
||||
inline void erase_all(std::string &string, const std::string &search) {
|
||||
for (size_t pos = 0;;) {
|
||||
|
@ -47,19 +46,14 @@ PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
|||
#endif
|
||||
detail::erase_all(name, "pybind11::");
|
||||
}
|
||||
|
||||
inline std::string clean_type_id(const char *typeid_name) {
|
||||
std::string name(typeid_name);
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
/// Return a string representation of a C++ type
|
||||
template <typename T>
|
||||
static std::string type_id() {
|
||||
return detail::clean_type_id(typeid(T).name());
|
||||
std::string name(typeid(T).name());
|
||||
detail::clean_type_id(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -9,4 +9,691 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "eigen/matrix.h"
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#include "numpy.h"
|
||||
|
||||
// The C4127 suppression was introduced for Eigen 3.4.0. In theory we could
|
||||
// make it version specific, or even remove it later, but considering that
|
||||
// 1. C4127 is generally far more distracting than useful for modern template code, and
|
||||
// 2. we definitely want to ignore any MSVC warnings originating from Eigen code,
|
||||
// it is probably best to keep this around indefinitely.
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// of matrices seems highly undesirable.
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
|
||||
"Eigen support in pybind11 requires Eigen >= 3.2.7");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
|
||||
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
|
||||
template <typename MatrixType>
|
||||
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#endif
|
||||
|
||||
// Matches Eigen::Map, Eigen::Ref, blocks, etc:
|
||||
template <typename T>
|
||||
using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
|
||||
std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
|
||||
template <typename T>
|
||||
using is_eigen_dense_plain
|
||||
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
|
||||
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
|
||||
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
|
||||
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
|
||||
// SelfAdjointView fall into this category.
|
||||
template <typename T>
|
||||
using is_eigen_other
|
||||
= all_of<is_template_base_of<Eigen::EigenBase, T>,
|
||||
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
|
||||
|
||||
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
|
||||
template <bool EigenRowMajor>
|
||||
struct EigenConformable {
|
||||
bool conformable = false;
|
||||
EigenIndex rows = 0, cols = 0;
|
||||
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, EigenIndex rstride, EigenIndex cstride)
|
||||
: conformable{true}, rows{r}, cols{c},
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
|
||||
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
|
||||
: (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0)
|
||||
: (rstride > 0 ? rstride : 0) /* inner stride */},
|
||||
negativestrides{rstride < 0 || cstride < 0} {}
|
||||
// Vector type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
|
||||
: EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
|
||||
|
||||
template <typename props>
|
||||
bool stride_compatible() const {
|
||||
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant)
|
||||
return !negativestrides
|
||||
&& (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct eigen_extract_stride {
|
||||
using type = Type;
|
||||
};
|
||||
template <typename PlainObjectType, int MapOptions, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
template <typename PlainObjectType, int Options, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
|
||||
// Helper struct for extracting information from an Eigen type
|
||||
template <typename Type_>
|
||||
struct EigenProps {
|
||||
using Type = Type_;
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StrideType = typename eigen_extract_stride<Type>::type;
|
||||
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool row_major = Type::IsRowMajor,
|
||||
vector
|
||||
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
|
||||
template <EigenIndex i, EigenIndex ifzero>
|
||||
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride
|
||||
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size
|
||||
: row_major ? cols
|
||||
: rows > ::value;
|
||||
static constexpr bool dynamic_stride
|
||||
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
|
||||
static constexpr bool requires_row_major
|
||||
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
|
||||
static constexpr bool requires_col_major
|
||||
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
|
||||
|
||||
// Takes an input array and determines whether we can make it fit into the Eigen type. If
|
||||
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
|
||||
// (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
|
||||
static EigenConformable<row_major> conformable(const array &a) {
|
||||
const auto dims = a.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dims == 2) { // Matrix type: require exact match (or dynamic)
|
||||
|
||||
EigenIndex np_rows = a.shape(0), 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 ((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};
|
||||
}
|
||||
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
|
||||
// whichever is used, we want the (single) numpy stride value.
|
||||
const EigenIndex n = a.shape(0),
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed) && size != n) {
|
||||
return false; // Vector size mismatch
|
||||
}
|
||||
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
|
||||
}
|
||||
if (fixed) {
|
||||
// The type has a fixed size, but is not a vector: abort
|
||||
return false;
|
||||
}
|
||||
if (fixed_cols) {
|
||||
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly
|
||||
// equals the number of elements (rows is Dynamic, and so 1 row is allowed).
|
||||
if (cols != n) {
|
||||
return false;
|
||||
}
|
||||
return {1, n, stride};
|
||||
} // Otherwise it's either fully dynamic, or column dynamic; both become a column vector
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(fixed_rows) && rows != n) {
|
||||
return false;
|
||||
}
|
||||
return {n, 1, stride};
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable
|
||||
= is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
static constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
static constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
static constexpr bool show_f_contiguous
|
||||
= !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
static constexpr auto descriptor
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
|
||||
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
|
||||
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
|
||||
+
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
|
||||
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's
|
||||
// stride options, possibly f_contiguous or c_contiguous. We include them in the
|
||||
// descriptor output to provide some hint as to why a TypeError is occurring (otherwise
|
||||
// it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
|
||||
// an error message that you *gave* a numpy.ndarray of the right type and dimensions.
|
||||
const_name<show_writeable>(", flags.writeable", "")
|
||||
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
|
||||
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
|
||||
};
|
||||
|
||||
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
|
||||
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
|
||||
template <typename props>
|
||||
handle
|
||||
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
|
||||
constexpr ssize_t elem_size = sizeof(typename props::Scalar);
|
||||
array a;
|
||||
if (props::vector) {
|
||||
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
|
||||
} else {
|
||||
a = array({src.rows(), src.cols()},
|
||||
{elem_size * src.rowStride(), elem_size * src.colStride()},
|
||||
src.data(),
|
||||
base);
|
||||
}
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return a.release();
|
||||
}
|
||||
|
||||
// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that
|
||||
// reference the Eigen object's data with `base` as the python-registered base class (if omitted,
|
||||
// the base will be set to None, and lifetime management is up to the caller). The numpy array is
|
||||
// non-writeable if the given type is const.
|
||||
template <typename props, typename Type>
|
||||
handle eigen_ref_array(Type &src, handle parent = none()) {
|
||||
// none here is to get past array's should-we-copy detection, which currently always
|
||||
// copies when there is no base. Setting the base to None should be harmless.
|
||||
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
|
||||
}
|
||||
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
|
||||
// numpy array that references the encapsulated data with a python-side reference to the capsule to
|
||||
// tie its destruction to that of any dependent python objects. Const-ness is determined by
|
||||
// whether or not the Type of the pointer given is const.
|
||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||
handle eigen_encapsulate(Type *src) {
|
||||
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||
return eigen_ref_array<props>(*src, base);
|
||||
}
|
||||
|
||||
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
|
||||
// types.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using props = EigenProps<Type>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
// If we're in no-convert mode, only load if given an array of the correct type
|
||||
if (!convert && !isinstance<array_t<Scalar>>(src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Coerce into an array, but don't do type conversion yet; the copy below handles it.
|
||||
auto buf = array::ensure(src);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dims = buf.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fits = props::conformable(buf);
|
||||
if (!fits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the new type, then build a numpy reference into it
|
||||
value = Type(fits.rows, fits.cols);
|
||||
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
|
||||
if (dims == 1) {
|
||||
ref = ref.squeeze();
|
||||
} else if (ref.ndim() == 1) {
|
||||
buf = buf.squeeze();
|
||||
}
|
||||
|
||||
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
|
||||
|
||||
if (result < 0) { // Copy failed!
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Cast implementation
|
||||
template <typename CType>
|
||||
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::take_ownership:
|
||||
case return_value_policy::automatic:
|
||||
return eigen_encapsulate<props>(src);
|
||||
case return_value_policy::move:
|
||||
return eigen_encapsulate<props>(new CType(std::move(*src)));
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(*src);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_ref_array<props>(*src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_ref_array<props>(*src, parent);
|
||||
default:
|
||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||
};
|
||||
}
|
||||
|
||||
public:
|
||||
// Normal returned non-reference, non-const value:
|
||||
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// If you return a non-reference const, we mark the numpy array readonly:
|
||||
static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
// const lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
// non-const pointer return
|
||||
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
// const pointer return
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// Base class for casting reference/map/block/etc. objects back to python.
|
||||
template <typename MapType>
|
||||
struct eigen_map_caster {
|
||||
private:
|
||||
using props = EigenProps<MapType>;
|
||||
|
||||
public:
|
||||
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing
|
||||
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
|
||||
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
|
||||
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
|
||||
// with an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_array_cast<props>(src, none(), is_eigen_mutable_map<MapType>::value);
|
||||
default:
|
||||
// move, take_ownership don't make any sense for a ref/map:
|
||||
pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator MapType() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = MapType;
|
||||
};
|
||||
|
||||
// We can return any map-like object (but can only load Refs, specialized next):
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
|
||||
|
||||
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without
|
||||
// copying (it requires some extra effort in many cases).
|
||||
template <typename PlainObjectType, typename StrideType>
|
||||
struct type_caster<
|
||||
Eigen::Ref<PlainObjectType, 0, StrideType>,
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
|
||||
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
private:
|
||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||
using props = EigenProps<Type>;
|
||||
using Scalar = typename props::Scalar;
|
||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||
using Array
|
||||
= array_t<Scalar,
|
||||
array::forcecast
|
||||
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
|
||||
? array::c_style
|
||||
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
|
||||
? array::f_style
|
||||
: 0)>;
|
||||
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
|
||||
// Delay construction (these have no default constructor)
|
||||
std::unique_ptr<MapType> map;
|
||||
std::unique_ptr<Type> ref;
|
||||
// Our array. When possible, this is just a numpy array pointing to the source data, but
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
|
||||
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
|
||||
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
|
||||
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
|
||||
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
Array copy_or_ref;
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
// First check whether what we have is already an array of the right type. If not, we
|
||||
// can't avoid a copy (because the copy is also going to do type conversion).
|
||||
bool need_copy = !isinstance<Array>(src);
|
||||
|
||||
EigenConformable<props::row_major> fits;
|
||||
if (!need_copy) {
|
||||
// We don't need a converting copy, but we also need to check whether the strides are
|
||||
// compatible with the Ref's stride requirements
|
||||
auto aref = reinterpret_borrow<Array>(src);
|
||||
|
||||
if (aref && (!need_writeable || aref.writeable())) {
|
||||
fits = props::conformable(aref);
|
||||
if (!fits) {
|
||||
return false; // Incompatible dimensions
|
||||
}
|
||||
if (!fits.template stride_compatible<props>()) {
|
||||
need_copy = true;
|
||||
} else {
|
||||
copy_or_ref = std::move(aref);
|
||||
}
|
||||
} else {
|
||||
need_copy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_copy) {
|
||||
// We need to copy: If we need a mutable reference, or we're not supposed to convert
|
||||
// (either because we're in the no-convert overload pass, or because we're explicitly
|
||||
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
|
||||
if (!convert || need_writeable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array copy = Array::ensure(src);
|
||||
if (!copy) {
|
||||
return false;
|
||||
}
|
||||
fits = props::conformable(copy);
|
||||
if (!fits || !fits.template stride_compatible<props>()) {
|
||||
return false;
|
||||
}
|
||||
copy_or_ref = std::move(copy);
|
||||
loader_life_support::add_patient(copy_or_ref);
|
||||
}
|
||||
|
||||
ref.reset();
|
||||
map.reset(new MapType(data(copy_or_ref),
|
||||
fits.rows,
|
||||
fits.cols,
|
||||
make_stride(fits.stride.outer(), fits.stride.inner())));
|
||||
ref.reset(new Type(*map));
|
||||
|
||||
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>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
|
||||
Scalar *data(Array &a) {
|
||||
return a.mutable_data();
|
||||
}
|
||||
|
||||
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
|
||||
const Scalar *data(Array &a) {
|
||||
return a.data();
|
||||
}
|
||||
|
||||
// Attempt to figure out a constructor of `Stride` that will work.
|
||||
// If both strides are fixed, use a default constructor:
|
||||
template <typename S>
|
||||
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_default_constructible<S>::value>;
|
||||
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
|
||||
// Eigen::Stride, and use it:
|
||||
template <typename S>
|
||||
using stride_ctor_dual
|
||||
= bool_constant<!stride_ctor_default<S>::value
|
||||
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
|
||||
// it (passing whichever stride is dynamic).
|
||||
template <typename S>
|
||||
using stride_ctor_outer
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_inner
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex) {
|
||||
return S();
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex inner) {
|
||||
return S(outer, inner);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex) {
|
||||
return S(outer);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex inner) {
|
||||
return S(inner);
|
||||
}
|
||||
};
|
||||
|
||||
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
|
||||
// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout).
|
||||
// load() is not supported, but we can cast them into the python domain by first copying to a
|
||||
// regular Eigen::Matrix, then casting that.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||
protected:
|
||||
using Matrix
|
||||
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||
using props = EigenProps<Matrix>;
|
||||
|
||||
public:
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
handle h = eigen_encapsulate<props>(new Matrix(src));
|
||||
return h;
|
||||
}
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator Type() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = Type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||
using Index = typename Type::Index;
|
||||
static constexpr bool rowMajor = Type::IsRowMajor;
|
||||
|
||||
bool load(handle src, bool) {
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obj = reinterpret_borrow<object>(src);
|
||||
object sparse_module = module_::import("scipy.sparse");
|
||||
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
if (!type::handle_of(obj).is(matrix_type)) {
|
||||
try {
|
||||
obj = matrix_type(obj);
|
||||
} catch (const error_already_set &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto values = array_t<Scalar>((object) obj.attr("data"));
|
||||
auto innerIndices = array_t<StorageIndex>((object) obj.attr("indices"));
|
||||
auto outerIndices = array_t<StorageIndex>((object) obj.attr("indptr"));
|
||||
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
|
||||
auto nnz = obj.attr("nnz").cast<Index>();
|
||||
|
||||
if (!values || !innerIndices || !outerIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = EigenMapSparseMatrix<Scalar,
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
nnz,
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type &>(src).makeCompressed();
|
||||
|
||||
object matrix_type
|
||||
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
array data(src.nonZeros(), src.valuePtr());
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(std::make_tuple(data, innerIndices, outerIndices),
|
||||
std::make_pair(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
|
||||
"scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -1,701 +0,0 @@
|
|||
/*
|
||||
pybind11/eigen/matrix.h: Transparent conversion for dense and sparse Eigen matrices
|
||||
|
||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../numpy.h"
|
||||
|
||||
/* 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
|
||||
*/
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(5054) // https://github.com/pybind/pybind11/pull/3741
|
||||
// C5054: operator '&': deprecated between enumerations of different types
|
||||
#if defined(__MINGW32__)
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
|
||||
#endif
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/SparseCore>
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit
|
||||
// move constructors that break things. We could detect this an explicitly copy, but an extra copy
|
||||
// of matrices seems highly undesirable.
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 2, 7),
|
||||
"Eigen matrix support in pybind11 requires Eigen >= 3.2.7");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides:
|
||||
using EigenDStride = Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic>;
|
||||
template <typename MatrixType>
|
||||
using EigenDRef = Eigen::Ref<MatrixType, 0, EigenDStride>;
|
||||
template <typename MatrixType>
|
||||
using EigenDMap = Eigen::Map<MatrixType, 0, EigenDStride>;
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 3, 0)
|
||||
using EigenIndex = Eigen::Index;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::Map<Eigen::SparseMatrix<Scalar, Flags, StorageIndex>>;
|
||||
#else
|
||||
using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE;
|
||||
template <typename Scalar, int Flags, typename StorageIndex>
|
||||
using EigenMapSparseMatrix = Eigen::MappedSparseMatrix<Scalar, Flags, StorageIndex>;
|
||||
#endif
|
||||
|
||||
// Matches Eigen::Map, Eigen::Ref, blocks, etc:
|
||||
template <typename T>
|
||||
using is_eigen_dense_map = all_of<is_template_base_of<Eigen::DenseBase, T>,
|
||||
std::is_base_of<Eigen::MapBase<T, Eigen::ReadOnlyAccessors>, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_mutable_map = std::is_base_of<Eigen::MapBase<T, Eigen::WriteAccessors>, T>;
|
||||
template <typename T>
|
||||
using is_eigen_dense_plain
|
||||
= all_of<negation<is_eigen_dense_map<T>>, is_template_base_of<Eigen::PlainObjectBase, T>>;
|
||||
template <typename T>
|
||||
using is_eigen_sparse = is_template_base_of<Eigen::SparseMatrixBase, T>;
|
||||
// Test for objects inheriting from EigenBase<Derived> that aren't captured by the above. This
|
||||
// basically covers anything that can be assigned to a dense matrix but that don't have a typical
|
||||
// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and
|
||||
// SelfAdjointView fall into this category.
|
||||
template <typename T>
|
||||
using is_eigen_other
|
||||
= all_of<is_template_base_of<Eigen::EigenBase, T>,
|
||||
negation<any_of<is_eigen_dense_map<T>, is_eigen_dense_plain<T>, is_eigen_sparse<T>>>>;
|
||||
|
||||
// Captures numpy/eigen conformability status (returned by EigenProps::conformable()):
|
||||
template <bool EigenRowMajor>
|
||||
struct EigenConformable {
|
||||
bool conformable = false;
|
||||
EigenIndex rows = 0, cols = 0;
|
||||
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, EigenIndex rstride, EigenIndex cstride)
|
||||
: conformable{true}, rows{r}, cols{c},
|
||||
// TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity.
|
||||
// http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747
|
||||
stride{EigenRowMajor ? (rstride > 0 ? rstride : 0)
|
||||
: (cstride > 0 ? cstride : 0) /* outer stride */,
|
||||
EigenRowMajor ? (cstride > 0 ? cstride : 0)
|
||||
: (rstride > 0 ? rstride : 0) /* inner stride */},
|
||||
negativestrides{rstride < 0 || cstride < 0} {}
|
||||
// Vector type:
|
||||
EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride)
|
||||
: EigenConformable(r, c, r == 1 ? c * stride : stride, c == 1 ? r : r * stride) {}
|
||||
|
||||
template <typename props>
|
||||
bool stride_compatible() const {
|
||||
// To have compatible strides, we need (on both dimensions) one of fully dynamic strides,
|
||||
// matching strides, or a dimension size of 1 (in which case the stride value is
|
||||
// irrelevant). Alternatively, if any dimension size is 0, the strides are not relevant
|
||||
// (and numpy ≥ 1.23 sets the strides to 0 in that case, so we need to check explicitly).
|
||||
if (negativestrides) {
|
||||
return false;
|
||||
}
|
||||
if (rows == 0 || cols == 0) {
|
||||
return true;
|
||||
}
|
||||
return (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner()
|
||||
|| (EigenRowMajor ? cols : rows) == 1)
|
||||
&& (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer()
|
||||
|| (EigenRowMajor ? rows : cols) == 1);
|
||||
}
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
operator bool() const { return conformable; }
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct eigen_extract_stride {
|
||||
using type = Type;
|
||||
};
|
||||
template <typename PlainObjectType, int MapOptions, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
template <typename PlainObjectType, int Options, typename StrideType>
|
||||
struct eigen_extract_stride<Eigen::Ref<PlainObjectType, Options, StrideType>> {
|
||||
using type = StrideType;
|
||||
};
|
||||
|
||||
// Helper struct for extracting information from an Eigen type
|
||||
template <typename Type_>
|
||||
struct EigenProps {
|
||||
using Type = Type_;
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StrideType = typename eigen_extract_stride<Type>::type;
|
||||
static constexpr EigenIndex rows = Type::RowsAtCompileTime, cols = Type::ColsAtCompileTime,
|
||||
size = Type::SizeAtCompileTime;
|
||||
static constexpr bool row_major = Type::IsRowMajor,
|
||||
vector
|
||||
= Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1
|
||||
fixed_rows = rows != Eigen::Dynamic, fixed_cols = cols != Eigen::Dynamic,
|
||||
fixed = size != Eigen::Dynamic, // Fully-fixed size
|
||||
dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size
|
||||
|
||||
template <EigenIndex i, EigenIndex ifzero>
|
||||
using if_zero = std::integral_constant<EigenIndex, i == 0 ? ifzero : i>;
|
||||
static constexpr EigenIndex inner_stride
|
||||
= if_zero<StrideType::InnerStrideAtCompileTime, 1>::value,
|
||||
outer_stride = if_zero < StrideType::OuterStrideAtCompileTime,
|
||||
vector ? size
|
||||
: row_major ? cols
|
||||
: rows > ::value;
|
||||
static constexpr bool dynamic_stride
|
||||
= inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic;
|
||||
static constexpr bool requires_row_major
|
||||
= !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1;
|
||||
static constexpr bool requires_col_major
|
||||
= !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1;
|
||||
|
||||
// Takes an input array and determines whether we can make it fit into the Eigen type. If
|
||||
// the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector
|
||||
// (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type).
|
||||
static EigenConformable<row_major> conformable(const array &a) {
|
||||
const auto dims = a.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dims == 2) { // Matrix type: require exact match (or dynamic)
|
||||
|
||||
EigenIndex np_rows = a.shape(0), 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)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {np_rows, np_cols, np_rstride, np_cstride};
|
||||
}
|
||||
|
||||
// Otherwise we're storing an n-vector. Only one of the strides will be used, but
|
||||
// whichever is used, we want the (single) numpy stride value.
|
||||
const EigenIndex n = a.shape(0),
|
||||
stride = a.strides(0) / static_cast<ssize_t>(sizeof(Scalar));
|
||||
|
||||
if (vector) { // Eigen type is a compile-time vector
|
||||
if (fixed && size != n) {
|
||||
return false; // Vector size mismatch
|
||||
}
|
||||
return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride};
|
||||
}
|
||||
if (fixed) {
|
||||
// The type has a fixed size, but is not a vector: abort
|
||||
return false;
|
||||
}
|
||||
if (fixed_cols) {
|
||||
// Since this isn't a vector, cols must be != 1. We allow this only if it exactly
|
||||
// equals the number of elements (rows is Dynamic, and so 1 row is allowed).
|
||||
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;
|
||||
}
|
||||
return {n, 1, stride};
|
||||
}
|
||||
|
||||
static constexpr bool show_writeable
|
||||
= is_eigen_dense_map<Type>::value && is_eigen_mutable_map<Type>::value;
|
||||
static constexpr bool show_order = is_eigen_dense_map<Type>::value;
|
||||
static constexpr bool show_c_contiguous = show_order && requires_row_major;
|
||||
static constexpr bool show_f_contiguous
|
||||
= !show_c_contiguous && show_order && requires_col_major;
|
||||
|
||||
static constexpr auto descriptor
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[")
|
||||
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
|
||||
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]")
|
||||
+
|
||||
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
|
||||
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's
|
||||
// stride options, possibly f_contiguous or c_contiguous. We include them in the
|
||||
// descriptor output to provide some hint as to why a TypeError is occurring (otherwise
|
||||
// it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and
|
||||
// an error message that you *gave* a numpy.ndarray of the right type and dimensions.
|
||||
const_name<show_writeable>(", flags.writeable", "")
|
||||
+ const_name<show_c_contiguous>(", flags.c_contiguous", "")
|
||||
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]");
|
||||
};
|
||||
|
||||
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
|
||||
// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array.
|
||||
template <typename props>
|
||||
handle
|
||||
eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) {
|
||||
constexpr ssize_t elem_size = sizeof(typename props::Scalar);
|
||||
array a;
|
||||
if (props::vector) {
|
||||
a = array({src.size()}, {elem_size * src.innerStride()}, src.data(), base);
|
||||
} else {
|
||||
a = array({src.rows(), src.cols()},
|
||||
{elem_size * src.rowStride(), elem_size * src.colStride()},
|
||||
src.data(),
|
||||
base);
|
||||
}
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return a.release();
|
||||
}
|
||||
|
||||
// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that
|
||||
// reference the Eigen object's data with `base` as the python-registered base class (if omitted,
|
||||
// the base will be set to None, and lifetime management is up to the caller). The numpy array is
|
||||
// non-writeable if the given type is const.
|
||||
template <typename props, typename Type>
|
||||
handle eigen_ref_array(Type &src, handle parent = none()) {
|
||||
// none here is to get past array's should-we-copy detection, which currently always
|
||||
// copies when there is no base. Setting the base to None should be harmless.
|
||||
return eigen_array_cast<props>(src, parent, !std::is_const<Type>::value);
|
||||
}
|
||||
|
||||
// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a
|
||||
// numpy array that references the encapsulated data with a python-side reference to the capsule to
|
||||
// tie its destruction to that of any dependent python objects. Const-ness is determined by
|
||||
// whether or not the Type of the pointer given is const.
|
||||
template <typename props, typename Type, typename = enable_if_t<is_eigen_dense_plain<Type>::value>>
|
||||
handle eigen_encapsulate(Type *src) {
|
||||
capsule base(src, [](void *o) { delete static_cast<Type *>(o); });
|
||||
return eigen_ref_array<props>(*src, base);
|
||||
}
|
||||
|
||||
// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense
|
||||
// types.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using props = EigenProps<Type>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
// If we're in no-convert mode, only load if given an array of the correct type
|
||||
if (!convert && !isinstance<array_t<Scalar>>(src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Coerce into an array, but don't do type conversion yet; the copy below handles it.
|
||||
auto buf = array::ensure(src);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto dims = buf.ndim();
|
||||
if (dims < 1 || dims > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fits = props::conformable(buf);
|
||||
if (!fits) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate the new type, then build a numpy reference into it
|
||||
value = Type(fits.rows, fits.cols);
|
||||
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
|
||||
if (dims == 1) {
|
||||
ref = ref.squeeze();
|
||||
} else if (ref.ndim() == 1) {
|
||||
buf = buf.squeeze();
|
||||
}
|
||||
|
||||
int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr());
|
||||
|
||||
if (result < 0) { // Copy failed!
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Cast implementation
|
||||
template <typename CType>
|
||||
static handle cast_impl(CType *src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::take_ownership:
|
||||
case return_value_policy::automatic:
|
||||
return eigen_encapsulate<props>(src);
|
||||
case return_value_policy::move:
|
||||
return eigen_encapsulate<props>(new CType(std::move(*src)));
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(*src);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_ref_array<props>(*src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_ref_array<props>(*src, parent);
|
||||
default:
|
||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||
};
|
||||
}
|
||||
|
||||
public:
|
||||
// Normal returned non-reference, non-const value:
|
||||
static handle cast(Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// If you return a non-reference const, we mark the numpy array readonly:
|
||||
static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) {
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
// lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
// const lvalue reference return; default (automatic) becomes copy
|
||||
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
// non-const pointer return
|
||||
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
// const pointer return
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
// Base class for casting reference/map/block/etc. objects back to python.
|
||||
template <typename MapType>
|
||||
struct eigen_map_caster {
|
||||
private:
|
||||
using props = EigenProps<MapType>;
|
||||
|
||||
public:
|
||||
// Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has
|
||||
// to stay around), but we'll allow it under the assumption that you know what you're doing
|
||||
// (and have an appropriate keep_alive in place). We return a numpy array pointing directly at
|
||||
// the ref's data (The numpy array ends up read-only if the ref was to a const matrix type.)
|
||||
// Note that this means you need to ensure you don't destroy the object in some other way (e.g.
|
||||
// with an appropriate keep_alive, or with a reference to a statically allocated matrix).
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
switch (policy) {
|
||||
case return_value_policy::copy:
|
||||
return eigen_array_cast<props>(src);
|
||||
case return_value_policy::reference_internal:
|
||||
return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
|
||||
case return_value_policy::reference:
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::automatic_reference:
|
||||
return eigen_array_cast<props>(src, none(), is_eigen_mutable_map<MapType>::value);
|
||||
default:
|
||||
// move, take_ownership don't make any sense for a ref/map:
|
||||
pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator MapType() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = MapType;
|
||||
};
|
||||
|
||||
// We can return any map-like object (but can only load Refs, specialized next):
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_dense_map<Type>::value>> : eigen_map_caster<Type> {};
|
||||
|
||||
// Loader for Ref<...> arguments. See the documentation for info on how to make this work without
|
||||
// copying (it requires some extra effort in many cases).
|
||||
template <typename PlainObjectType, typename StrideType>
|
||||
struct type_caster<
|
||||
Eigen::Ref<PlainObjectType, 0, StrideType>,
|
||||
enable_if_t<is_eigen_dense_map<Eigen::Ref<PlainObjectType, 0, StrideType>>::value>>
|
||||
: public eigen_map_caster<Eigen::Ref<PlainObjectType, 0, StrideType>> {
|
||||
private:
|
||||
using Type = Eigen::Ref<PlainObjectType, 0, StrideType>;
|
||||
using props = EigenProps<Type>;
|
||||
using Scalar = typename props::Scalar;
|
||||
using MapType = Eigen::Map<PlainObjectType, 0, StrideType>;
|
||||
using Array
|
||||
= array_t<Scalar,
|
||||
array::forcecast
|
||||
| ((props::row_major ? props::inner_stride : props::outer_stride) == 1
|
||||
? array::c_style
|
||||
: (props::row_major ? props::outer_stride : props::inner_stride) == 1
|
||||
? array::f_style
|
||||
: 0)>;
|
||||
static constexpr bool need_writeable = is_eigen_mutable_map<Type>::value;
|
||||
// Delay construction (these have no default constructor)
|
||||
std::unique_ptr<MapType> map;
|
||||
std::unique_ptr<Type> ref;
|
||||
// Our array. When possible, this is just a numpy array pointing to the source data, but
|
||||
// sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an
|
||||
// incompatible layout, or is an array of a type that needs to be converted). Using a numpy
|
||||
// temporary (rather than an Eigen temporary) saves an extra copy when we need both type
|
||||
// conversion and storage order conversion. (Note that we refuse to use this temporary copy
|
||||
// when loading an argument for a Ref<M> with M non-const, i.e. a read-write reference).
|
||||
Array copy_or_ref;
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
// First check whether what we have is already an array of the right type. If not, we
|
||||
// can't avoid a copy (because the copy is also going to do type conversion).
|
||||
bool need_copy = !isinstance<Array>(src);
|
||||
|
||||
EigenConformable<props::row_major> fits;
|
||||
if (!need_copy) {
|
||||
// We don't need a converting copy, but we also need to check whether the strides are
|
||||
// compatible with the Ref's stride requirements
|
||||
auto aref = reinterpret_borrow<Array>(src);
|
||||
|
||||
if (aref && (!need_writeable || aref.writeable())) {
|
||||
fits = props::conformable(aref);
|
||||
if (!fits) {
|
||||
return false; // Incompatible dimensions
|
||||
}
|
||||
if (!fits.template stride_compatible<props>()) {
|
||||
need_copy = true;
|
||||
} else {
|
||||
copy_or_ref = std::move(aref);
|
||||
}
|
||||
} else {
|
||||
need_copy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_copy) {
|
||||
// We need to copy: If we need a mutable reference, or we're not supposed to convert
|
||||
// (either because we're in the no-convert overload pass, or because we're explicitly
|
||||
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
|
||||
if (!convert || need_writeable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array copy = Array::ensure(src);
|
||||
if (!copy) {
|
||||
return false;
|
||||
}
|
||||
fits = props::conformable(copy);
|
||||
if (!fits || !fits.template stride_compatible<props>()) {
|
||||
return false;
|
||||
}
|
||||
copy_or_ref = std::move(copy);
|
||||
loader_life_support::add_patient(copy_or_ref);
|
||||
}
|
||||
|
||||
ref.reset();
|
||||
map.reset(new MapType(data(copy_or_ref),
|
||||
fits.rows,
|
||||
fits.cols,
|
||||
make_stride(fits.stride.outer(), fits.stride.inner())));
|
||||
ref.reset(new Type(*map));
|
||||
|
||||
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>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<is_eigen_mutable_map<T>::value, int> = 0>
|
||||
Scalar *data(Array &a) {
|
||||
return a.mutable_data();
|
||||
}
|
||||
|
||||
template <typename T = Type, enable_if_t<!is_eigen_mutable_map<T>::value, int> = 0>
|
||||
const Scalar *data(Array &a) {
|
||||
return a.data();
|
||||
}
|
||||
|
||||
// Attempt to figure out a constructor of `Stride` that will work.
|
||||
// If both strides are fixed, use a default constructor:
|
||||
template <typename S>
|
||||
using stride_ctor_default = bool_constant<S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_default_constructible<S>::value>;
|
||||
// Otherwise, if there is a two-index constructor, assume it is (outer,inner) like
|
||||
// Eigen::Stride, and use it:
|
||||
template <typename S>
|
||||
using stride_ctor_dual
|
||||
= bool_constant<!stride_ctor_default<S>::value
|
||||
&& std::is_constructible<S, EigenIndex, EigenIndex>::value>;
|
||||
// Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use
|
||||
// it (passing whichever stride is dynamic).
|
||||
template <typename S>
|
||||
using stride_ctor_outer
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::OuterStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::InnerStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
template <typename S>
|
||||
using stride_ctor_inner
|
||||
= bool_constant<!any_of<stride_ctor_default<S>, stride_ctor_dual<S>>::value
|
||||
&& S::InnerStrideAtCompileTime == Eigen::Dynamic
|
||||
&& S::OuterStrideAtCompileTime != Eigen::Dynamic
|
||||
&& std::is_constructible<S, EigenIndex>::value>;
|
||||
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_default<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex) {
|
||||
return S();
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_dual<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex inner) {
|
||||
return S(outer, inner);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_outer<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex outer, EigenIndex) {
|
||||
return S(outer);
|
||||
}
|
||||
template <typename S = StrideType, enable_if_t<stride_ctor_inner<S>::value, int> = 0>
|
||||
static S make_stride(EigenIndex, EigenIndex inner) {
|
||||
return S(inner);
|
||||
}
|
||||
};
|
||||
|
||||
// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not
|
||||
// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout).
|
||||
// load() is not supported, but we can cast them into the python domain by first copying to a
|
||||
// regular Eigen::Matrix, then casting that.
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_other<Type>::value>> {
|
||||
protected:
|
||||
using Matrix
|
||||
= Eigen::Matrix<typename Type::Scalar, Type::RowsAtCompileTime, Type::ColsAtCompileTime>;
|
||||
using props = EigenProps<Matrix>;
|
||||
|
||||
public:
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
handle h = eigen_encapsulate<props>(new Matrix(src));
|
||||
return h;
|
||||
}
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
return cast(*src, policy, parent);
|
||||
}
|
||||
|
||||
static constexpr auto name = props::descriptor;
|
||||
|
||||
// Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return
|
||||
// types but not bound arguments). We still provide them (with an explicitly delete) so that
|
||||
// you end up here if you try anyway.
|
||||
bool load(handle, bool) = delete;
|
||||
operator Type() = delete;
|
||||
template <typename>
|
||||
using cast_op_type = Type;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::value>> {
|
||||
using Scalar = typename Type::Scalar;
|
||||
using StorageIndex = remove_reference_t<decltype(*std::declval<Type>().outerIndexPtr())>;
|
||||
using Index = typename Type::Index;
|
||||
static constexpr bool rowMajor = Type::IsRowMajor;
|
||||
|
||||
bool load(handle src, bool) {
|
||||
if (!src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obj = reinterpret_borrow<object>(src);
|
||||
object sparse_module = module_::import("scipy.sparse");
|
||||
object matrix_type = sparse_module.attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
if (!type::handle_of(obj).is(matrix_type)) {
|
||||
try {
|
||||
obj = matrix_type(obj);
|
||||
} catch (const error_already_set &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto values = array_t<Scalar>((object) obj.attr("data"));
|
||||
auto innerIndices = array_t<StorageIndex>((object) obj.attr("indices"));
|
||||
auto outerIndices = array_t<StorageIndex>((object) obj.attr("indptr"));
|
||||
auto shape = pybind11::tuple((pybind11::object) obj.attr("shape"));
|
||||
auto nnz = obj.attr("nnz").cast<Index>();
|
||||
|
||||
if (!values || !innerIndices || !outerIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = EigenMapSparseMatrix<Scalar,
|
||||
Type::Flags &(Eigen::RowMajor | Eigen::ColMajor),
|
||||
StorageIndex>(shape[0].cast<Index>(),
|
||||
shape[1].cast<Index>(),
|
||||
std::move(nnz),
|
||||
outerIndices.mutable_data(),
|
||||
innerIndices.mutable_data(),
|
||||
values.mutable_data());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||
const_cast<Type &>(src).makeCompressed();
|
||||
|
||||
object matrix_type
|
||||
= module_::import("scipy.sparse").attr(rowMajor ? "csr_matrix" : "csc_matrix");
|
||||
|
||||
array data(src.nonZeros(), src.valuePtr());
|
||||
array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr());
|
||||
array innerIndices(src.nonZeros(), src.innerIndexPtr());
|
||||
|
||||
return matrix_type(pybind11::make_tuple(
|
||||
std::move(data), std::move(innerIndices), std::move(outerIndices)),
|
||||
pybind11::make_tuple(src.rows(), src.cols()))
|
||||
.release();
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(Type,
|
||||
const_name<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[",
|
||||
"scipy.sparse.csc_matrix[")
|
||||
+ npy_format_descriptor<Scalar>::name + const_name("]"));
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
@ -1,511 +0,0 @@
|
|||
/*
|
||||
pybind11/eigen/tensor.h: Transparent conversion for Eigen tensors
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../numpy.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
static_assert(__GNUC__ > 5, "Eigen Tensor support in pybind11 requires GCC > 5.0");
|
||||
#endif
|
||||
|
||||
// Disable warnings for Eigen
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4554)
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
#if defined(__MINGW32__)
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
|
||||
#endif
|
||||
|
||||
#include <unsupported/Eigen/CXX11/Tensor>
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
static_assert(EIGEN_VERSION_AT_LEAST(3, 3, 0),
|
||||
"Eigen Tensor support in pybind11 requires Eigen >= 3.3.0");
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline bool is_tensor_aligned(const void *data) {
|
||||
return (reinterpret_cast<std::size_t>(data) % EIGEN_DEFAULT_ALIGN_BYTES) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr int compute_array_flag_from_tensor() {
|
||||
static_assert((static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor))
|
||||
|| (static_cast<int>(T::Layout) == static_cast<int>(Eigen::ColMajor)),
|
||||
"Layout must be row or column major");
|
||||
return (static_cast<int>(T::Layout) == static_cast<int>(Eigen::RowMajor)) ? array::c_style
|
||||
: array::f_style;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct eigen_tensor_helper {};
|
||||
|
||||
template <typename Scalar_, int NumIndices_, int Options_, typename IndexType>
|
||||
struct eigen_tensor_helper<Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>> {
|
||||
using Type = Eigen::Tensor<Scalar_, NumIndices_, Options_, IndexType>;
|
||||
using ValidType = void;
|
||||
|
||||
static Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape(const Type &f) {
|
||||
return f.dimensions();
|
||||
}
|
||||
|
||||
static constexpr bool
|
||||
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> & /*shape*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct helper {};
|
||||
|
||||
template <size_t... Is>
|
||||
struct helper<index_sequence<Is...>> {
|
||||
static constexpr auto value = concat(const_name(((void) Is, "?"))...);
|
||||
};
|
||||
|
||||
static constexpr auto dimensions_descriptor
|
||||
= helper<decltype(make_index_sequence<Type::NumIndices>())>::value;
|
||||
|
||||
template <typename... Args>
|
||||
static Type *alloc(Args &&...args) {
|
||||
return new Type(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static void free(Type *tensor) { delete tensor; }
|
||||
};
|
||||
|
||||
template <typename Scalar_, typename std::ptrdiff_t... Indices, int Options_, typename IndexType>
|
||||
struct eigen_tensor_helper<
|
||||
Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>> {
|
||||
using Type = Eigen::TensorFixedSize<Scalar_, Eigen::Sizes<Indices...>, Options_, IndexType>;
|
||||
using ValidType = void;
|
||||
|
||||
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices>
|
||||
get_shape(const Type & /*f*/) {
|
||||
return get_shape();
|
||||
}
|
||||
|
||||
static constexpr Eigen::DSizes<typename Type::Index, Type::NumIndices> get_shape() {
|
||||
return Eigen::DSizes<typename Type::Index, Type::NumIndices>(Indices...);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_correct_shape(const Eigen::DSizes<typename Type::Index, Type::NumIndices> &shape) {
|
||||
return get_shape() == shape;
|
||||
}
|
||||
|
||||
static constexpr auto dimensions_descriptor = concat(const_name<Indices>()...);
|
||||
|
||||
template <typename... Args>
|
||||
static Type *alloc(Args &&...args) {
|
||||
Eigen::aligned_allocator<Type> allocator;
|
||||
return ::new (allocator.allocate(1)) Type(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static void free(Type *tensor) {
|
||||
Eigen::aligned_allocator<Type> allocator;
|
||||
tensor->~Type();
|
||||
allocator.deallocate(tensor, 1);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
|
||||
struct get_tensor_descriptor {
|
||||
static constexpr auto details
|
||||
= const_name<NeedsWriteable>(", flags.writeable", "")
|
||||
+ const_name<static_cast<int>(Type::Layout) == static_cast<int>(Eigen::RowMajor)>(
|
||||
", flags.c_contiguous", ", flags.f_contiguous");
|
||||
static constexpr auto value
|
||||
= const_name("numpy.ndarray[") + npy_format_descriptor<typename Type::Scalar>::name
|
||||
+ const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
|
||||
+ const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
|
||||
};
|
||||
|
||||
// When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
|
||||
// function. Falling back to a simple loop works around this issue.
|
||||
//
|
||||
// We need to disable the type-limits warning for the inner loop when size = 0.
|
||||
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wtype-limits")
|
||||
|
||||
template <typename T, int size>
|
||||
std::vector<T> convert_dsizes_to_vector(const Eigen::DSizes<T, size> &arr) {
|
||||
std::vector<T> result(size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
result[i] = arr[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, int size>
|
||||
Eigen::DSizes<T, size> get_shape_for_array(const array &arr) {
|
||||
Eigen::DSizes<T, size> result;
|
||||
const T *shape = arr.shape();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
result[i] = shape[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
template <typename Type>
|
||||
struct type_caster<Type, typename eigen_tensor_helper<Type>::ValidType> {
|
||||
using Helper = eigen_tensor_helper<Type>;
|
||||
static constexpr auto temp_name = get_tensor_descriptor<Type, false>::value;
|
||||
PYBIND11_TYPE_CASTER(Type, temp_name);
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if (!convert) {
|
||||
if (!isinstance<array>(src)) {
|
||||
return false;
|
||||
}
|
||||
array temp = array::ensure(src);
|
||||
if (!temp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!temp.dtype().is(dtype::of<typename Type::Scalar>())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()> arr(
|
||||
reinterpret_borrow<object>(src));
|
||||
|
||||
if (arr.ndim() != Type::NumIndices) {
|
||||
return false;
|
||||
}
|
||||
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
|
||||
|
||||
if (!Helper::is_correct_shape(shape)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
||||
auto data_pointer = arr.data();
|
||||
#else
|
||||
// Handle Eigen bug
|
||||
auto data_pointer = const_cast<typename Type::Scalar *>(arr.data());
|
||||
#endif
|
||||
|
||||
if (is_tensor_aligned(arr.data())) {
|
||||
value = Eigen::TensorMap<const Type, Eigen::Aligned>(data_pointer, shape);
|
||||
} else {
|
||||
value = Eigen::TensorMap<const Type>(data_pointer, shape);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(Type &&src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::reference
|
||||
|| policy == return_value_policy::reference_internal) {
|
||||
pybind11_fail("Cannot use a reference return value policy for an rvalue");
|
||||
}
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
static handle cast(const Type &&src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::reference
|
||||
|| policy == return_value_policy::reference_internal) {
|
||||
pybind11_fail("Cannot use a reference return value policy for an rvalue");
|
||||
}
|
||||
return cast_impl(&src, return_value_policy::move, parent);
|
||||
}
|
||||
|
||||
static handle cast(Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const Type &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(Type *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const Type *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
|
||||
object parent_object;
|
||||
bool writeable = false;
|
||||
switch (policy) {
|
||||
case return_value_policy::move:
|
||||
if (std::is_const<C>::value) {
|
||||
pybind11_fail("Cannot move from a constant reference");
|
||||
}
|
||||
|
||||
src = Helper::alloc(std::move(*src));
|
||||
|
||||
parent_object
|
||||
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
|
||||
writeable = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::take_ownership:
|
||||
if (std::is_const<C>::value) {
|
||||
// This cast is ugly, and might be UB in some cases, but we don't have an
|
||||
// alternative here as we must free that memory
|
||||
Helper::free(const_cast<Type *>(src));
|
||||
pybind11_fail("Cannot take ownership of a const reference");
|
||||
}
|
||||
|
||||
parent_object
|
||||
= capsule(src, [](void *ptr) { Helper::free(reinterpret_cast<Type *>(ptr)); });
|
||||
writeable = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::copy:
|
||||
writeable = true;
|
||||
break;
|
||||
|
||||
case return_value_policy::reference:
|
||||
parent_object = none();
|
||||
writeable = !std::is_const<C>::value;
|
||||
break;
|
||||
|
||||
case return_value_policy::reference_internal:
|
||||
// Default should do the right thing
|
||||
if (!parent) {
|
||||
pybind11_fail("Cannot use reference internal when there is no parent");
|
||||
}
|
||||
parent_object = reinterpret_borrow<object>(parent);
|
||||
writeable = !std::is_const<C>::value;
|
||||
break;
|
||||
|
||||
default:
|
||||
pybind11_fail("pybind11 bug in eigen.h, please file a bug report");
|
||||
}
|
||||
|
||||
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
|
||||
convert_dsizes_to_vector(Helper::get_shape(*src)), src->data(), parent_object);
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return result.release();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename StoragePointerType,
|
||||
bool needs_writeable,
|
||||
enable_if_t<!needs_writeable, bool> = true>
|
||||
StoragePointerType get_array_data_for_type(array &arr) {
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
||||
return reinterpret_cast<StoragePointerType>(arr.data());
|
||||
#else
|
||||
// Handle Eigen bug
|
||||
return reinterpret_cast<StoragePointerType>(const_cast<void *>(arr.data()));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename StoragePointerType,
|
||||
bool needs_writeable,
|
||||
enable_if_t<needs_writeable, bool> = true>
|
||||
StoragePointerType get_array_data_for_type(array &arr) {
|
||||
return reinterpret_cast<StoragePointerType>(arr.mutable_data());
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct get_storage_pointer_type;
|
||||
|
||||
template <typename MapType>
|
||||
struct get_storage_pointer_type<MapType, void_t<typename MapType::StoragePointerType>> {
|
||||
using SPT = typename MapType::StoragePointerType;
|
||||
};
|
||||
|
||||
template <typename MapType>
|
||||
struct get_storage_pointer_type<MapType, void_t<typename MapType::PointerArgType>> {
|
||||
using SPT = typename MapType::PointerArgType;
|
||||
};
|
||||
|
||||
template <typename Type, int Options>
|
||||
struct type_caster<Eigen::TensorMap<Type, Options>,
|
||||
typename eigen_tensor_helper<remove_cv_t<Type>>::ValidType> {
|
||||
using MapType = Eigen::TensorMap<Type, Options>;
|
||||
using Helper = eigen_tensor_helper<remove_cv_t<Type>>;
|
||||
|
||||
bool load(handle src, bool /*convert*/) {
|
||||
// Note that we have a lot more checks here as we want to make sure to avoid copies
|
||||
if (!isinstance<array>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto arr = reinterpret_borrow<array>(src);
|
||||
if ((arr.flags() & compute_array_flag_from_tensor<Type>()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!arr.dtype().is(dtype::of<typename Type::Scalar>())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arr.ndim() != Type::NumIndices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_aligned = (Options & Eigen::Aligned) != 0;
|
||||
|
||||
if (is_aligned && !is_tensor_aligned(arr.data())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto shape = get_shape_for_array<typename Type::Index, Type::NumIndices>(arr);
|
||||
|
||||
if (!Helper::is_correct_shape(shape)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (needs_writeable && !arr.writeable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto result = get_array_data_for_type<typename get_storage_pointer_type<MapType>::SPT,
|
||||
needs_writeable>(arr);
|
||||
|
||||
value.reset(new MapType(std::move(result), std::move(shape)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static handle cast(MapType &&src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const MapType &&src, return_value_policy policy, handle parent) {
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(MapType &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast_impl(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const MapType &src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic
|
||||
|| policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::copy;
|
||||
}
|
||||
return cast(&src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(MapType *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
static handle cast(const MapType *src, return_value_policy policy, handle parent) {
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::take_ownership;
|
||||
} else if (policy == return_value_policy::automatic_reference) {
|
||||
policy = return_value_policy::reference;
|
||||
}
|
||||
return cast_impl(src, policy, parent);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static handle cast_impl(C *src, return_value_policy policy, handle parent) {
|
||||
object parent_object;
|
||||
constexpr bool writeable = !std::is_const<C>::value;
|
||||
switch (policy) {
|
||||
case return_value_policy::reference:
|
||||
parent_object = none();
|
||||
break;
|
||||
|
||||
case return_value_policy::reference_internal:
|
||||
// Default should do the right thing
|
||||
if (!parent) {
|
||||
pybind11_fail("Cannot use reference internal when there is no parent");
|
||||
}
|
||||
parent_object = reinterpret_borrow<object>(parent);
|
||||
break;
|
||||
|
||||
case return_value_policy::take_ownership:
|
||||
delete src;
|
||||
// fallthrough
|
||||
default:
|
||||
// move, take_ownership don't make any sense for a ref/map:
|
||||
pybind11_fail("Invalid return_value_policy for Eigen Map type, must be either "
|
||||
"reference or reference_internal");
|
||||
}
|
||||
|
||||
auto result = array_t<typename Type::Scalar, compute_array_flag_from_tensor<Type>()>(
|
||||
convert_dsizes_to_vector(Helper::get_shape(*src)),
|
||||
src->data(),
|
||||
std::move(parent_object));
|
||||
|
||||
if (!writeable) {
|
||||
array_proxy(result.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
|
||||
}
|
||||
|
||||
return result.release();
|
||||
}
|
||||
|
||||
#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
|
||||
|
||||
static constexpr bool needs_writeable = !std::is_const<typename std::remove_pointer<
|
||||
typename get_storage_pointer_type<MapType>::SPT>::type>::value;
|
||||
#else
|
||||
// Handle Eigen bug
|
||||
static constexpr bool needs_writeable = !std::is_const<Type>::value;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// TODO: Move to std::optional once std::optional has more support
|
||||
std::unique_ptr<MapType> value;
|
||||
|
||||
public:
|
||||
static constexpr auto name = get_tensor_descriptor<Type, true, needs_writeable>::value;
|
||||
explicit operator MapType *() { return value.get(); }
|
||||
explicit operator MapType &() { return *value; }
|
||||
explicit operator MapType &&() && { return std::move(*value); }
|
||||
|
||||
template <typename T_>
|
||||
using cast_op_type = ::pybind11::detail::movable_cast_op_type<T_>;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
@ -19,9 +19,15 @@
|
|||
# error Embedding the interpreter is not supported with PyPy
|
||||
#endif
|
||||
|
||||
#define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" PyObject *pybind11_init_impl_##name(); \
|
||||
extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); }
|
||||
#else
|
||||
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||
extern "C" void pybind11_init_impl_##name(); \
|
||||
extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); }
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
Add a new module to the table of builtins for the interpreter. Must be
|
||||
|
@ -61,7 +67,11 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
|
||||
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
|
||||
struct embedded_module {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
using init_t = PyObject *(*) ();
|
||||
#else
|
||||
using init_t = void (*)();
|
||||
#endif
|
||||
embedded_module(const char *name, init_t init) {
|
||||
if (Py_IsInitialized() != 0) {
|
||||
pybind11_fail("Can't add new modules after the interpreter has been initialized");
|
||||
|
@ -76,37 +86,47 @@ 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;
|
||||
|
||||
// warning C4996: 'mbstowcs': This function or variable may be unsafe.
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4996)
|
||||
# endif
|
||||
|
||||
# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS
|
||||
size_t count = std::strlen(safe_arg);
|
||||
# else
|
||||
size_t count = std::mbstowcs(nullptr, safe_arg, 0);
|
||||
# endif
|
||||
if (count != static_cast<size_t>(-1)) {
|
||||
widened_arg = new wchar_t[count + 1];
|
||||
std::mbstowcs(widened_arg, safe_arg, count + 1);
|
||||
}
|
||||
|
||||
# if defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
return widened_arg;
|
||||
}
|
||||
|
||||
inline void precheck_interpreter() {
|
||||
if (Py_IsInitialized() != 0) {
|
||||
pybind11_fail("The interpreter is already running");
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX)
|
||||
# define PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX (0x03080000)
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
|
||||
int argc,
|
||||
const char *const *argv,
|
||||
bool add_program_dir_to_path) {
|
||||
detail::precheck_interpreter();
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
|
||||
PyEval_InitThreads();
|
||||
# endif
|
||||
|
||||
/// 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);
|
||||
|
@ -118,12 +138,13 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
|
|||
}
|
||||
|
||||
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[], detail::wide_char_arg_deleter>> widened_argv_entries;
|
||||
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(detail::widen_chars(safe_argv[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.
|
||||
|
@ -133,43 +154,20 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
|
|||
}
|
||||
|
||||
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));
|
||||
}
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
inline void initialize_interpreter(PyConfig *config,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
detail::precheck_interpreter();
|
||||
PyStatus status = PyConfig_SetBytesArgv(config, argc, const_cast<char *const *>(argv));
|
||||
if (PyStatus_Exception(status) != 0) {
|
||||
// A failure here indicates a character-encoding failure or the python
|
||||
// interpreter out of memory. Give up.
|
||||
PyConfig_Clear(config);
|
||||
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
|
||||
: "Failed to prepare CPython");
|
||||
}
|
||||
status = Py_InitializeFromConfig(config);
|
||||
if (PyStatus_Exception(status) != 0) {
|
||||
PyConfig_Clear(config);
|
||||
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
|
||||
: "Failed to init CPython");
|
||||
}
|
||||
if (add_program_dir_to_path) {
|
||||
PyRun_SimpleString("import sys, os.path; "
|
||||
"sys.path.insert(0, "
|
||||
"os.path.abspath(os.path.dirname(sys.argv[0])) "
|
||||
"if sys.argv and os.path.exists(sys.argv[0]) else '')");
|
||||
}
|
||||
PyConfig_Clear(config);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \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
|
||||
|
@ -193,17 +191,13 @@ 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_VERSION_HEX < PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
detail::initialize_interpreter_pre_pyconfig(
|
||||
init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||
#else
|
||||
PyConfig config;
|
||||
PyConfig_InitIsolatedConfig(&config);
|
||||
config.isolated = 0;
|
||||
config.use_environment = 1;
|
||||
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
|
||||
#endif
|
||||
if (Py_IsInitialized() != 0) {
|
||||
pybind11_fail("The interpreter is already running");
|
||||
}
|
||||
|
||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||
|
||||
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
/** \rst
|
||||
|
@ -290,15 +284,6 @@ public:
|
|||
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||
explicit scoped_interpreter(PyConfig *config,
|
||||
int argc = 0,
|
||||
const char *const *argv = nullptr,
|
||||
bool add_program_dir_to_path = true) {
|
||||
initialize_interpreter(config, argc, argv, add_program_dir_to_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
scoped_interpreter(const scoped_interpreter &) = delete;
|
||||
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
|
||||
scoped_interpreter &operator=(const scoped_interpreter &) = delete;
|
||||
|
|
|
@ -20,10 +20,10 @@ PYBIND11_NAMESPACE_BEGIN(detail)
|
|||
|
||||
inline void ensure_builtins_in_globals(object &global) {
|
||||
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
|
||||
// Running exec and eval adds `builtins` module under `__builtins__` key to
|
||||
// globals if not yet present. Python 3.8 made PyRun_String behave
|
||||
// similarly. Let's also do that for older versions, for consistency. This
|
||||
// was missing from PyPy3.8 7.3.7.
|
||||
// Running exec and eval on Python 2 and 3 adds `builtins` module under
|
||||
// `__builtins__` key to globals if not yet present.
|
||||
// Python 3.8 made PyRun_String behave similarly. Let's also do that for
|
||||
// older versions, for consistency. This was missing from PyPy3.8 7.3.7.
|
||||
if (!global.contains("__builtins__"))
|
||||
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||
#else
|
||||
|
@ -94,7 +94,7 @@ void exec(const char (&s)[N], object global = globals(), object local = object()
|
|||
eval<eval_statements>(s, std::move(global), std::move(local));
|
||||
}
|
||||
|
||||
#if defined(PYPY_VERSION)
|
||||
#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000
|
||||
template <eval_mode mode = eval_statements>
|
||||
object eval_file(str, object, object) {
|
||||
pybind11_fail("eval_file not supported in PyPy3. Use eval");
|
||||
|
@ -133,18 +133,40 @@ object eval_file(str fname, object global = globals(), object local = object())
|
|||
|
||||
int closeFile = 1;
|
||||
std::string fname_str = (std::string) fname;
|
||||
# if PY_VERSION_HEX >= 0x03040000
|
||||
FILE *f = _Py_fopen_obj(fname.ptr(), "r");
|
||||
# elif PY_VERSION_HEX >= 0x03000000
|
||||
FILE *f = _Py_fopen(fname.ptr(), "r");
|
||||
# else
|
||||
/* No unicode support in open() :( */
|
||||
auto fobj = reinterpret_steal<object>(
|
||||
PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
|
||||
FILE *f = nullptr;
|
||||
if (fobj)
|
||||
f = PyFile_AsFile(fobj.ptr());
|
||||
closeFile = 0;
|
||||
# endif
|
||||
if (!f) {
|
||||
PyErr_Clear();
|
||||
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());
|
||||
(void) closeFile;
|
||||
# else
|
||||
PyObject *result
|
||||
= PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
|
||||
# endif
|
||||
|
||||
if (!result) {
|
||||
throw error_already_set();
|
||||
|
|
|
@ -48,16 +48,9 @@ public:
|
|||
*/
|
||||
if (auto cfunc = func.cpp_function()) {
|
||||
auto *cfunc_self = PyCFunction_GET_SELF(cfunc.ptr());
|
||||
if (cfunc_self == nullptr) {
|
||||
PyErr_Clear();
|
||||
} else if (isinstance<capsule>(cfunc_self)) {
|
||||
if (isinstance<capsule>(cfunc_self)) {
|
||||
auto c = reinterpret_borrow<capsule>(cfunc_self);
|
||||
|
||||
function_record *rec = nullptr;
|
||||
// Check that we can safely reinterpret the capsule into a function_record
|
||||
if (detail::is_function_record_capsule(c)) {
|
||||
rec = c.get_pointer<function_record>();
|
||||
}
|
||||
auto *rec = (function_record *) c;
|
||||
|
||||
while (rec != nullptr) {
|
||||
if (rec->is_stateless
|
||||
|
@ -105,8 +98,9 @@ public:
|
|||
explicit func_wrapper(func_handle &&hf) noexcept : hfunc(std::move(hf)) {}
|
||||
Return operator()(Args... args) const {
|
||||
gil_scoped_acquire acq;
|
||||
// casts the returned object as a rvalue to the return type
|
||||
return hfunc.f(std::forward<Args>(args)...).template cast<Return>();
|
||||
object retval(hfunc.f(std::forward<Args>(args)...));
|
||||
/* Visual studio 2015 parser issue: need parentheses around this expression */
|
||||
return (retval.template cast<Return>());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -117,7 +111,7 @@ public:
|
|||
template <typename Func>
|
||||
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
|
||||
if (!f_) {
|
||||
return none().release();
|
||||
return none().inc_ref();
|
||||
}
|
||||
|
||||
auto result = f_.template target<function_type>();
|
||||
|
|
|
@ -10,10 +10,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
|
||||
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
# include "detail/internals.h"
|
||||
#endif
|
||||
#include "detail/internals.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
|
@ -24,9 +21,7 @@ PyThreadState *get_thread_state_unchecked();
|
|||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
#if defined(WITH_THREAD)
|
||||
|
||||
# if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
|
||||
|
||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||
* pattern, but there are a few important differences:
|
||||
|
@ -67,11 +62,11 @@ public:
|
|||
|
||||
if (!tstate) {
|
||||
tstate = PyThreadState_New(internals.istate);
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
# if !defined(NDEBUG)
|
||||
if (!tstate) {
|
||||
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
tstate->gilstate_counter = 0;
|
||||
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||
} else {
|
||||
|
@ -85,27 +80,24 @@ public:
|
|||
inc_ref();
|
||||
}
|
||||
|
||||
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
|
||||
|
||||
void inc_ref() { ++tstate->gilstate_counter; }
|
||||
|
||||
PYBIND11_NOINLINE void dec_ref() {
|
||||
--tstate->gilstate_counter;
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
# if !defined(NDEBUG)
|
||||
if (detail::get_thread_state_unchecked() != tstate) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||
}
|
||||
if (tstate->gilstate_counter < 0) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
if (tstate->gilstate_counter == 0) {
|
||||
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
# if !defined(NDEBUG)
|
||||
if (!release) {
|
||||
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
PyThreadState_Clear(tstate);
|
||||
if (active) {
|
||||
PyThreadState_DeleteCurrent();
|
||||
|
@ -152,9 +144,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
gil_scoped_release(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
|
||||
|
||||
/// This method will disable the PyThreadState_DeleteCurrent call and the
|
||||
/// GIL won't be acquired. This method should be used if the interpreter
|
||||
/// could be shutting down when this is called, as thread deletion is not
|
||||
|
@ -183,16 +172,12 @@ private:
|
|||
bool disassoc;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
# else // PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
|
||||
#elif defined(PYPY_VERSION)
|
||||
class gil_scoped_acquire {
|
||||
PyGILState_STATE state;
|
||||
|
||||
public:
|
||||
gil_scoped_acquire() : state{PyGILState_Ensure()} {}
|
||||
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
||||
~gil_scoped_acquire() { PyGILState_Release(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
|
@ -201,39 +186,17 @@ class gil_scoped_release {
|
|||
PyThreadState *state;
|
||||
|
||||
public:
|
||||
gil_scoped_release() : state{PyEval_SaveThread()} {}
|
||||
gil_scoped_release(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release() { state = PyEval_SaveThread(); }
|
||||
~gil_scoped_release() { PyEval_RestoreThread(state); }
|
||||
void disarm() {}
|
||||
};
|
||||
|
||||
# endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||
|
||||
#else // WITH_THREAD
|
||||
|
||||
#else
|
||||
class gil_scoped_acquire {
|
||||
public:
|
||||
gil_scoped_acquire() {
|
||||
// Trick to suppress `unused variable` error messages (at call sites).
|
||||
(void) (this != (this + 1));
|
||||
}
|
||||
gil_scoped_acquire(const gil_scoped_acquire &) = delete;
|
||||
gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
|
||||
void disarm() {}
|
||||
};
|
||||
|
||||
class gil_scoped_release {
|
||||
public:
|
||||
gil_scoped_release() {
|
||||
// Trick to suppress `unused variable` error messages (at call sites).
|
||||
(void) (this != (this + 1));
|
||||
}
|
||||
gil_scoped_release(const gil_scoped_release &) = delete;
|
||||
gil_scoped_release &operator=(const gil_scoped_release &) = delete;
|
||||
void disarm() {}
|
||||
};
|
||||
|
||||
#endif // WITH_THREAD
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -100,7 +100,7 @@ private:
|
|||
|
||||
if (size > remainder) {
|
||||
str line(pbase(), size - remainder);
|
||||
pywrite(std::move(line));
|
||||
pywrite(line);
|
||||
pyflush();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,6 @@ static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
|
|||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
class array; // Forward declaration
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
@ -265,7 +263,11 @@ private:
|
|||
static npy_api lookup() {
|
||||
module_ m = module_::import("numpy.core.multiarray");
|
||||
auto c = m.attr("_ARRAY_API");
|
||||
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL);
|
||||
#else
|
||||
void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr());
|
||||
#endif
|
||||
npy_api api;
|
||||
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
|
||||
DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
|
||||
|
@ -539,21 +541,21 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
|
||||
class dtype : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_)
|
||||
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_);
|
||||
|
||||
explicit dtype(const buffer_info &info) {
|
||||
dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
|
||||
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
|
||||
// If info.itemsize == 0, use the value calculated from the format string
|
||||
m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
|
||||
.release()
|
||||
.ptr();
|
||||
}
|
||||
|
||||
explicit dtype(const pybind11::str &format) : dtype(from_args(format)) {}
|
||||
explicit dtype(const std::string &format) {
|
||||
m_ptr = from_args(pybind11::str(format)).release().ptr();
|
||||
}
|
||||
|
||||
explicit dtype(const std::string &format) : dtype(pybind11::str(format)) {}
|
||||
|
||||
explicit dtype(const char *format) : dtype(pybind11::str(format)) {}
|
||||
explicit dtype(const char *format) : dtype(std::string(format)) {}
|
||||
|
||||
dtype(list names, list formats, list offsets, ssize_t itemsize) {
|
||||
dict args;
|
||||
|
@ -561,18 +563,11 @@ public:
|
|||
args["formats"] = std::move(formats);
|
||||
args["offsets"] = std::move(offsets);
|
||||
args["itemsize"] = pybind11::int_(itemsize);
|
||||
m_ptr = from_args(args).release().ptr();
|
||||
}
|
||||
|
||||
explicit dtype(int typenum)
|
||||
: object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
|
||||
if (m_ptr == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
m_ptr = from_args(std::move(args)).release().ptr();
|
||||
}
|
||||
|
||||
/// This is essentially the same as calling numpy.dtype(args) in Python.
|
||||
static dtype from_args(const object &args) {
|
||||
static dtype from_args(object args) {
|
||||
PyObject *ptr = nullptr;
|
||||
if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr) {
|
||||
throw error_already_set();
|
||||
|
@ -605,23 +600,6 @@ public:
|
|||
return detail::array_descriptor_proxy(m_ptr)->type;
|
||||
}
|
||||
|
||||
/// type number of dtype.
|
||||
int num() const {
|
||||
// Note: The signature, `dtype::num` follows the naming of NumPy's public
|
||||
// Python API (i.e., ``dtype.num``), rather than its internal
|
||||
// C API (``PyArray_Descr::type_num``).
|
||||
return detail::array_descriptor_proxy(m_ptr)->type_num;
|
||||
}
|
||||
|
||||
/// Single character for byteorder
|
||||
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
|
||||
|
||||
/// Alignment of the data type
|
||||
int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; }
|
||||
|
||||
/// Flags for the array descriptor
|
||||
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
|
||||
|
||||
private:
|
||||
static object _dtype_from_pep3118() {
|
||||
static PyObject *obj = module_::import("numpy.core._internal")
|
||||
|
@ -640,27 +618,22 @@ private:
|
|||
}
|
||||
|
||||
struct field_descr {
|
||||
pybind11::str name;
|
||||
PYBIND11_STR_TYPE name;
|
||||
object format;
|
||||
pybind11::int_ offset;
|
||||
field_descr(pybind11::str &&name, object &&format, pybind11::int_ &&offset)
|
||||
: name{std::move(name)}, format{std::move(format)}, offset{std::move(offset)} {};
|
||||
};
|
||||
auto field_dict = attr("fields").cast<dict>();
|
||||
std::vector<field_descr> field_descriptors;
|
||||
field_descriptors.reserve(field_dict.size());
|
||||
|
||||
for (auto field : field_dict.attr("items")()) {
|
||||
for (auto field : attr("fields").attr("items")()) {
|
||||
auto spec = field.cast<tuple>();
|
||||
auto name = spec[0].cast<pybind11::str>();
|
||||
auto spec_fo = spec[1].cast<tuple>();
|
||||
auto format = spec_fo[0].cast<dtype>();
|
||||
auto offset = spec_fo[1].cast<pybind11::int_>();
|
||||
auto format = spec[1].cast<tuple>()[0].cast<dtype>();
|
||||
auto offset = spec[1].cast<tuple>()[1].cast<pybind11::int_>();
|
||||
if ((len(name) == 0u) && format.kind() == 'V') {
|
||||
continue;
|
||||
}
|
||||
field_descriptors.emplace_back(
|
||||
std::move(name), format.strip_padding(format.itemsize()), std::move(offset));
|
||||
field_descriptors.push_back(
|
||||
{(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset});
|
||||
}
|
||||
|
||||
std::sort(field_descriptors.begin(),
|
||||
|
@ -671,9 +644,9 @@ private:
|
|||
|
||||
list names, formats, offsets;
|
||||
for (auto &descr : field_descriptors) {
|
||||
names.append(std::move(descr.name));
|
||||
formats.append(std::move(descr.format));
|
||||
offsets.append(std::move(descr.offset));
|
||||
names.append(descr.name);
|
||||
formats.append(descr.format);
|
||||
offsets.append(descr.offset);
|
||||
}
|
||||
return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
|
||||
}
|
||||
|
@ -877,7 +850,7 @@ public:
|
|||
*/
|
||||
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));
|
||||
|
@ -895,7 +868,7 @@ public:
|
|||
*/
|
||||
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));
|
||||
|
@ -971,7 +944,7 @@ protected:
|
|||
|
||||
void fail_dim_check(ssize_t dim, const std::string &msg) const {
|
||||
throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim())
|
||||
+ ')');
|
||||
+ ")");
|
||||
}
|
||||
|
||||
template <typename... Ix>
|
||||
|
@ -1121,10 +1094,10 @@ public:
|
|||
|
||||
/**
|
||||
* Returns a proxy object that provides const access to the array's data without bounds or
|
||||
* dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the
|
||||
* underlying array have the `writable` flag. Use with care: the array must not be destroyed
|
||||
* or reshaped for the duration of the returned object, and the caller must take care not to
|
||||
* access invalid dimensions or dimension indices.
|
||||
* dimensionality checking. Unlike `unchecked()`, this does not require that the underlying
|
||||
* array have the `writable` flag. Use with care: the array must not be destroyed or reshaped
|
||||
* for the duration of the returned object, and the caller must take care not to access invalid
|
||||
* dimensions or dimension indices.
|
||||
*/
|
||||
template <ssize_t Dims = -1>
|
||||
detail::unchecked_reference<T, Dims> unchecked() const & {
|
||||
|
@ -1175,11 +1148,11 @@ struct format_descriptor<T, detail::enable_if_t<detail::is_pod_struct<T>::value>
|
|||
|
||||
template <size_t N>
|
||||
struct format_descriptor<char[N]> {
|
||||
static std::string format() { return std::to_string(N) + 's'; }
|
||||
static std::string format() { return std::to_string(N) + "s"; }
|
||||
};
|
||||
template <size_t N>
|
||||
struct format_descriptor<std::array<char, N>> {
|
||||
static std::string format() { return std::to_string(N) + 's'; }
|
||||
static std::string format() { return std::to_string(N) + "s"; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -1319,8 +1292,7 @@ public:
|
|||
static pybind11::dtype dtype() {
|
||||
list shape;
|
||||
array_info<T>::append_extents(shape);
|
||||
return pybind11::dtype::from_args(
|
||||
pybind11::make_tuple(base_descr::dtype(), std::move(shape)));
|
||||
return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1366,7 +1338,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
|||
pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ "
|
||||
+ tinfo.name());
|
||||
}
|
||||
names.append(pybind11::str(field.name));
|
||||
names.append(PYBIND11_STR_TYPE(field.name));
|
||||
formats.append(field.descr);
|
||||
offsets.append(pybind11::int_(field.offset));
|
||||
}
|
||||
|
@ -1403,7 +1375,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
|||
oss << '}';
|
||||
auto format_str = oss.str();
|
||||
|
||||
// Smoke test: verify that NumPy properly parses our buffer format string
|
||||
// Sanity check: verify that NumPy properly parses our buffer format string
|
||||
auto &api = npy_api::get();
|
||||
auto arr = array(buffer_info(nullptr, itemsize, format_str, 1));
|
||||
if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) {
|
||||
|
@ -1411,7 +1383,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
|
|||
}
|
||||
|
||||
auto tindex = std::type_index(tinfo);
|
||||
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
|
||||
numpy_internals.registered_dtypes[tindex] = {dtype_ptr, format_str};
|
||||
get_internals().direct_conversions[tindex].push_back(direct_converter);
|
||||
}
|
||||
|
||||
|
@ -1471,7 +1443,7 @@ private:
|
|||
}
|
||||
|
||||
// Extract name, offset and format descriptor for a struct field
|
||||
# define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field)
|
||||
# define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, # Field)
|
||||
|
||||
// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro
|
||||
// (C) William Swanson, Paul Fultz
|
||||
|
@ -1558,7 +1530,7 @@ public:
|
|||
void *data() const { return p_ptr; }
|
||||
|
||||
private:
|
||||
char *p_ptr{nullptr};
|
||||
char *p_ptr{0};
|
||||
container_type m_strides;
|
||||
};
|
||||
|
||||
|
@ -1868,13 +1840,8 @@ private:
|
|||
|
||||
auto result = returned_array::create(trivial, shape);
|
||||
|
||||
PYBIND11_WARNING_PUSH
|
||||
#ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
|
||||
#endif
|
||||
|
||||
if (size == 0) {
|
||||
return result;
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
/* Call the function */
|
||||
|
@ -1885,8 +1852,7 @@ private:
|
|||
apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq);
|
||||
}
|
||||
|
||||
return result;
|
||||
PYBIND11_WARNING_POP
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
template <size_t... Index, size_t... VIndex, size_t... BIndex>
|
||||
|
|
|
@ -84,7 +84,6 @@ struct op_impl {};
|
|||
/// Operator implementation generator
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
struct op_ {
|
||||
static constexpr bool op_enable_if_hook = true;
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class &cl, const Extra &...extra) const {
|
||||
using Base = typename Class::type;
|
||||
|
@ -92,6 +91,16 @@ struct op_ {
|
|||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
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 (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
|
||||
}
|
||||
template <typename Class, typename... Extra>
|
||||
void execute_cast(Class &cl, const Extra &...extra) const {
|
||||
|
@ -100,6 +109,15 @@ struct op_ {
|
|||
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute_cast, is_operator(), extra...);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (id == op_truediv || id == op_itruediv)
|
||||
cl.def(id == op_itruediv ? "__idiv__"
|
||||
: ot == op_l ? "__div__"
|
||||
: "__rdiv__",
|
||||
&op::execute,
|
||||
is_operator(),
|
||||
extra...);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -47,16 +47,6 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
options &disable_enum_members_docstring() & {
|
||||
global_state().show_enum_members_docstring = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
options &enable_enum_members_docstring() & {
|
||||
global_state().show_enum_members_docstring = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Getter methods (return the global state):
|
||||
|
||||
static bool show_user_defined_docstrings() {
|
||||
|
@ -65,10 +55,6 @@ public:
|
|||
|
||||
static bool show_function_signatures() { return global_state().show_function_signatures; }
|
||||
|
||||
static bool show_enum_members_docstring() {
|
||||
return global_state().show_enum_members_docstring;
|
||||
}
|
||||
|
||||
// This type is not meant to be allocated on the heap.
|
||||
void *operator new(size_t) = delete;
|
||||
|
||||
|
@ -77,8 +63,6 @@ private:
|
|||
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
|
||||
bool show_function_signatures = true; //< Include auto-generated function signatures
|
||||
// in docstrings.
|
||||
bool show_enum_members_docstring = true; //< Include auto-generated member list in enum
|
||||
// docstrings.
|
||||
};
|
||||
|
||||
static state &global_state() {
|
||||
|
|
|
@ -35,8 +35,6 @@
|
|||
# include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
/* https://stackoverflow.com/questions/46798456/handling-gccs-noexcept-type-warning
|
||||
This warning is about ABI compatibility, not code health.
|
||||
It is only actually needed in a couple places, but apparently GCC 7 "generates this warning if
|
||||
|
@ -45,10 +43,11 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|||
No other GCC version generates this warning.
|
||||
*/
|
||||
#if defined(__GNUC__) && __GNUC__ == 7
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wnoexcept-type")
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnoexcept-type"
|
||||
#endif
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
|
@ -178,22 +177,22 @@ 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. */
|
||||
PYBIND11_WARNING_PUSH
|
||||
|
||||
#if defined(__GNUG__) && __GNUC__ >= 6
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wplacement-new")
|
||||
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wplacement-new"
|
||||
#endif
|
||||
|
||||
new ((capture *) &rec->data) capture{std::forward<Func>(f)};
|
||||
|
||||
#if !PYBIND11_HAS_STD_LAUNDER
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
|
||||
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
// UB without std::launder, but without breaking ABI and/or
|
||||
// a significant refactoring it's "impossible" to solve.
|
||||
if (!std::is_trivially_destructible<capture>::value) {
|
||||
|
@ -203,7 +202,9 @@ protected:
|
|||
data->~capture();
|
||||
};
|
||||
}
|
||||
PYBIND11_WARNING_POP
|
||||
#if defined(__GNUG__) && !PYBIND11_HAS_STD_LAUNDER && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
} else {
|
||||
rec->data[0] = new capture{std::forward<Func>(f)};
|
||||
rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); };
|
||||
|
@ -311,10 +312,6 @@ protected:
|
|||
// along the way.
|
||||
class strdup_guard {
|
||||
public:
|
||||
strdup_guard() = default;
|
||||
strdup_guard(const strdup_guard &) = delete;
|
||||
strdup_guard &operator=(const strdup_guard &) = delete;
|
||||
|
||||
~strdup_guard() {
|
||||
for (auto *s : strings) {
|
||||
std::free(s);
|
||||
|
@ -369,7 +366,7 @@ protected:
|
|||
rec->is_constructor = (std::strcmp(rec->name, "__init__") == 0)
|
||||
|| (std::strcmp(rec->name, "__setstate__") == 0);
|
||||
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
|
||||
#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING)
|
||||
if (rec->is_constructor && !rec->is_new_style_constructor) {
|
||||
const auto class_name
|
||||
= detail::get_fully_qualified_tp_name((PyTypeObject *) rec->scope.ptr());
|
||||
|
@ -434,8 +431,9 @@ protected:
|
|||
}
|
||||
if (auto *tinfo = detail::get_type_info(*t)) {
|
||||
handle th((PyObject *) tinfo->type);
|
||||
signature += th.attr("__module__").cast<std::string>() + "."
|
||||
+ th.attr("__qualname__").cast<std::string>();
|
||||
signature += th.attr("__module__").cast<std::string>() + "." +
|
||||
// Python 3.3+, but we backport it to earlier versions
|
||||
th.attr("__qualname__").cast<std::string>();
|
||||
} else if (rec->is_new_style_constructor && arg_index == 0) {
|
||||
// A new-style `__init__` takes `self` as `value_and_holder`.
|
||||
// Rewrite it to the proper class type.
|
||||
|
@ -455,6 +453,15 @@ protected:
|
|||
pybind11_fail("Internal error while parsing type signature (2)");
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (std::strcmp(rec->name, "__next__") == 0) {
|
||||
std::free(rec->name);
|
||||
rec->name = guarded_strdup("next");
|
||||
} else if (std::strcmp(rec->name, "__bool__") == 0) {
|
||||
std::free(rec->name);
|
||||
rec->name = guarded_strdup("__nonzero__");
|
||||
}
|
||||
#endif
|
||||
rec->signature = guarded_strdup(signature.c_str());
|
||||
rec->args.shrink_to_fit();
|
||||
rec->nargs = (std::uint16_t) args;
|
||||
|
@ -467,20 +474,13 @@ protected:
|
|||
if (rec->sibling) {
|
||||
if (PyCFunction_Check(rec->sibling.ptr())) {
|
||||
auto *self = PyCFunction_GET_SELF(rec->sibling.ptr());
|
||||
if (!isinstance<capsule>(self)) {
|
||||
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 */
|
||||
if (!chain->scope.is(rec->scope)) {
|
||||
chain = nullptr;
|
||||
} else {
|
||||
auto rec_capsule = reinterpret_borrow<capsule>(self);
|
||||
if (detail::is_function_record_capsule(rec_capsule)) {
|
||||
chain = rec_capsule.get_pointer<detail::function_record>();
|
||||
/* Never append a method to an overload chain of a parent class;
|
||||
instead, hide the parent's overloads in this case */
|
||||
if (!chain->scope.is(rec->scope)) {
|
||||
chain = nullptr;
|
||||
}
|
||||
} else {
|
||||
chain = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't trigger for things like the default __init__, which are wrapper_descriptors
|
||||
|
@ -502,7 +502,6 @@ protected:
|
|||
|
||||
capsule rec_capsule(unique_rec.release(),
|
||||
[](void *ptr) { destruct((detail::function_record *) ptr); });
|
||||
rec_capsule.set_name(detail::get_function_record_capsule_name());
|
||||
guarded_strdup.release();
|
||||
|
||||
object scope_module;
|
||||
|
@ -525,9 +524,8 @@ protected:
|
|||
if (chain->is_method != rec->is_method) {
|
||||
pybind11_fail(
|
||||
"overloading a method with both static and instance methods is not supported; "
|
||||
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for more "
|
||||
"details"
|
||||
#if defined(NDEBUG)
|
||||
"compile in debug mode for more details"
|
||||
#else
|
||||
"error while attempting to bind "
|
||||
+ std::string(rec->is_method ? "instance" : "static") + " method "
|
||||
|
@ -573,14 +571,14 @@ protected:
|
|||
for (auto *it = chain_start; it != nullptr; it = it->next) {
|
||||
if (options::show_function_signatures()) {
|
||||
if (index > 0) {
|
||||
signatures += '\n';
|
||||
signatures += "\n";
|
||||
}
|
||||
if (chain) {
|
||||
signatures += std::to_string(++index) + ". ";
|
||||
}
|
||||
signatures += rec->name;
|
||||
signatures += it->signature;
|
||||
signatures += '\n';
|
||||
signatures += "\n";
|
||||
}
|
||||
if (it->doc && it->doc[0] != '\0' && options::show_user_defined_docstrings()) {
|
||||
// If we're appending another docstring, and aren't printing function signatures,
|
||||
|
@ -589,15 +587,15 @@ protected:
|
|||
if (first_user_def) {
|
||||
first_user_def = false;
|
||||
} else {
|
||||
signatures += '\n';
|
||||
signatures += "\n";
|
||||
}
|
||||
}
|
||||
if (options::show_function_signatures()) {
|
||||
signatures += '\n';
|
||||
signatures += "\n";
|
||||
}
|
||||
signatures += it->doc;
|
||||
if (options::show_function_signatures()) {
|
||||
signatures += '\n';
|
||||
signatures += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -668,13 +666,10 @@ 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;
|
||||
assert(isinstance<capsule>(self));
|
||||
|
||||
/* Iterator over the list of potentially admissible overloads */
|
||||
const function_record *overloads = reinterpret_cast<function_record *>(
|
||||
PyCapsule_GetPointer(self, get_function_record_capsule_name())),
|
||||
const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr),
|
||||
*it = overloads;
|
||||
assert(overloads != nullptr);
|
||||
|
||||
/* Need to know how many arguments + keyword arguments there are to pick the right
|
||||
overload */
|
||||
|
@ -917,7 +912,7 @@ protected:
|
|||
|
||||
// 5. Put everything in a vector. Not technically step 5, we've been building it
|
||||
// in `call.args` all along.
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
#if !defined(NDEBUG)
|
||||
if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) {
|
||||
pybind11_fail("Internal error: function call dispatcher inserted wrong number "
|
||||
"of arguments!");
|
||||
|
@ -1070,7 +1065,7 @@ protected:
|
|||
msg += it2->signature;
|
||||
}
|
||||
|
||||
msg += '\n';
|
||||
msg += "\n";
|
||||
}
|
||||
msg += "\nInvoked with: ";
|
||||
auto args_ = reinterpret_borrow<tuple>(args_in);
|
||||
|
@ -1112,12 +1107,14 @@ protected:
|
|||
}
|
||||
|
||||
append_note_if_missing_header_is_suspected(msg);
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// Attach additional error info to the exception if supported
|
||||
if (PyErr_Occurred()) {
|
||||
// #HelpAppreciated: unit test coverage for this branch.
|
||||
raise_from(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1126,11 +1123,13 @@ protected:
|
|||
"Python type! The signature was\n\t";
|
||||
msg += it->signature;
|
||||
append_note_if_missing_header_is_suspected(msg);
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
// Attach additional error info to the exception if supported
|
||||
if (PyErr_Occurred()) {
|
||||
raise_from(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1150,7 +1149,11 @@ public:
|
|||
/// Create a new top-level Python module with the given name and docstring
|
||||
PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead")
|
||||
explicit module_(const char *name, const char *doc = nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
*this = create_extension_module(name, doc, new PyModuleDef());
|
||||
#else
|
||||
*this = create_extension_module(name, doc, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \rst
|
||||
|
@ -1183,16 +1186,9 @@ public:
|
|||
py::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'");
|
||||
\endrst */
|
||||
module_ def_submodule(const char *name, const char *doc = nullptr) {
|
||||
const char *this_name = PyModule_GetName(m_ptr);
|
||||
if (this_name == nullptr) {
|
||||
throw error_already_set();
|
||||
}
|
||||
std::string full_name = std::string(this_name) + '.' + name;
|
||||
handle submodule = PyImport_AddModule(full_name.c_str());
|
||||
if (!submodule) {
|
||||
throw error_already_set();
|
||||
}
|
||||
auto result = reinterpret_borrow<module_>(submodule);
|
||||
std::string full_name
|
||||
= std::string(PyModule_GetName(m_ptr)) + std::string(".") + std::string(name);
|
||||
auto result = reinterpret_borrow<module_>(PyImport_AddModule(full_name.c_str()));
|
||||
if (doc && options::show_user_defined_docstrings()) {
|
||||
result.attr("__doc__") = pybind11::str(doc);
|
||||
}
|
||||
|
@ -1235,14 +1231,20 @@ public:
|
|||
PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */);
|
||||
}
|
||||
|
||||
using module_def = PyModuleDef; // TODO: Can this be removed (it was needed only for Python 2)?
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
using module_def = PyModuleDef;
|
||||
#else
|
||||
struct module_def {};
|
||||
#endif
|
||||
|
||||
/** \rst
|
||||
Create a new top-level module that can be used as the main module of a C extension.
|
||||
|
||||
``def`` should point to a statically allocated module_def.
|
||||
For Python 3, ``def`` should point to a statically allocated module_def.
|
||||
For Python 2, ``def`` can be a nullptr and is completely ignored.
|
||||
\endrst */
|
||||
static module_ create_extension_module(const char *name, const char *doc, module_def *def) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// module_def is PyModuleDef
|
||||
// Placement new (not an allocation).
|
||||
def = new (def)
|
||||
|
@ -1256,6 +1258,12 @@ public:
|
|||
/* m_clear */ nullptr,
|
||||
/* m_free */ nullptr};
|
||||
auto *m = PyModule_Create(def);
|
||||
#else
|
||||
// Ignore module_def *def; only necessary for Python 3
|
||||
(void) def;
|
||||
auto m = Py_InitModule3(
|
||||
name, nullptr, options::show_user_defined_docstrings() ? doc : nullptr);
|
||||
#endif
|
||||
if (m == nullptr) {
|
||||
if (PyErr_Occurred()) {
|
||||
throw error_already_set();
|
||||
|
@ -1263,8 +1271,8 @@ public:
|
|||
pybind11_fail("Internal error in module_::create_extension_module()");
|
||||
}
|
||||
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
|
||||
// returned from PyInit_...
|
||||
// For Python 2, reinterpret_borrow was correct.
|
||||
// returned from PyInit_...
|
||||
// For Python 2, reinterpret_borrow is correct.
|
||||
return reinterpret_borrow<module_>(m);
|
||||
}
|
||||
};
|
||||
|
@ -1282,12 +1290,14 @@ inline dict globals() {
|
|||
return reinterpret_borrow<dict>(p ? p : module_::import("__main__").attr("__dict__").ptr());
|
||||
}
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
template <typename... Args, typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
|
||||
PYBIND11_DEPRECATED("make_simple_namespace should be replaced with "
|
||||
"py::module_::import(\"types\").attr(\"SimpleNamespace\") ")
|
||||
object make_simple_namespace(Args &&...args_) {
|
||||
return module_::import("types").attr("SimpleNamespace")(std::forward<Args>(args_)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
/// Generic support for creating new Python heap types
|
||||
|
@ -1426,9 +1436,9 @@ template <typename T, enable_if_t<has_operator_delete<T>::value, int> = 0>
|
|||
void call_operator_delete(T *p, size_t, size_t) {
|
||||
T::operator delete(p);
|
||||
}
|
||||
template <typename T,
|
||||
enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int>
|
||||
= 0>
|
||||
template <
|
||||
typename T,
|
||||
enable_if_t<!has_operator_delete<T>::value && has_operator_delete_size<T>::value, int> = 0>
|
||||
void call_operator_delete(T *p, size_t s, size_t) {
|
||||
T::operator delete(p, s);
|
||||
}
|
||||
|
@ -1583,19 +1593,18 @@ public:
|
|||
scope(*this),
|
||||
sibling(getattr(*this, name_, none())),
|
||||
extra...);
|
||||
auto cf_name = cf.name();
|
||||
attr(std::move(cf_name)) = staticmethod(std::move(cf));
|
||||
attr(cf.name()) = staticmethod(cf);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename... Extra, detail::enable_if_t<T::op_enable_if_hook, int> = 0>
|
||||
class_ &def(const T &op, const Extra &...extra) {
|
||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
op.execute(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename... Extra, detail::enable_if_t<T::op_enable_if_hook, int> = 0>
|
||||
class_ &def_cast(const T &op, const Extra &...extra) {
|
||||
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
|
||||
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
|
||||
op.execute_cast(*this, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
@ -1638,7 +1647,7 @@ public:
|
|||
if (!caster.load(obj, false)) {
|
||||
return nullptr;
|
||||
}
|
||||
return new buffer_info(((capture *) ptr)->func(std::move(caster)));
|
||||
return new buffer_info(((capture *) ptr)->func(caster));
|
||||
},
|
||||
ptr);
|
||||
weakref(m_ptr, cpp_function([ptr](handle wr) {
|
||||
|
@ -1840,7 +1849,7 @@ private:
|
|||
if (holder_ptr) {
|
||||
init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible<holder_type>());
|
||||
v_h.set_holder_constructed();
|
||||
} else if (detail::always_construct_holder<holder_type>::value || inst->owned) {
|
||||
} else if (inst->owned || detail::always_construct_holder<holder_type>::value) {
|
||||
new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());
|
||||
v_h.set_holder_constructed();
|
||||
}
|
||||
|
@ -1880,22 +1889,9 @@ private:
|
|||
|
||||
static detail::function_record *get_function_record(handle h) {
|
||||
h = detail::get_function(h);
|
||||
if (!h) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
handle func_self = PyCFunction_GET_SELF(h.ptr());
|
||||
if (!func_self) {
|
||||
throw error_already_set();
|
||||
}
|
||||
if (!isinstance<capsule>(func_self)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto cap = reinterpret_borrow<capsule>(func_self);
|
||||
if (!detail::is_function_record_capsule(cap)) {
|
||||
return nullptr;
|
||||
}
|
||||
return cap.get_pointer<detail::function_record>();
|
||||
return h ? (detail::function_record *) reinterpret_borrow<capsule>(
|
||||
PyCFunction_GET_SELF(h.ptr()))
|
||||
: nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1956,8 +1952,7 @@ struct enum_base {
|
|||
[](const object &arg) -> str {
|
||||
handle type = type::handle_of(arg);
|
||||
object type_name = type.attr("__name__");
|
||||
return pybind11::str("<{}.{}: {}>")
|
||||
.format(std::move(type_name), enum_name(arg), int_(arg));
|
||||
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
|
||||
},
|
||||
name("__repr__"),
|
||||
is_method(m_base));
|
||||
|
@ -1967,40 +1962,34 @@ struct enum_base {
|
|||
m_base.attr("__str__") = cpp_function(
|
||||
[](handle arg) -> str {
|
||||
object type_name = type::handle_of(arg).attr("__name__");
|
||||
return pybind11::str("{}.{}").format(std::move(type_name), enum_name(arg));
|
||||
return pybind11::str("{}.{}").format(type_name, enum_name(arg));
|
||||
},
|
||||
name("name"),
|
||||
is_method(m_base));
|
||||
|
||||
if (options::show_enum_members_docstring()) {
|
||||
m_base.attr("__doc__") = static_property(
|
||||
cpp_function(
|
||||
[](handle arg) -> std::string {
|
||||
std::string docstring;
|
||||
dict entries = arg.attr("__entries");
|
||||
if (((PyTypeObject *) arg.ptr())->tp_doc) {
|
||||
docstring += std::string(
|
||||
reinterpret_cast<PyTypeObject *>(arg.ptr())->tp_doc);
|
||||
docstring += "\n\n";
|
||||
m_base.attr("__doc__") = static_property(
|
||||
cpp_function(
|
||||
[](handle arg) -> std::string {
|
||||
std::string docstring;
|
||||
dict entries = arg.attr("__entries");
|
||||
if (((PyTypeObject *) arg.ptr())->tp_doc) {
|
||||
docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n";
|
||||
}
|
||||
docstring += "Members:";
|
||||
for (auto kv : entries) {
|
||||
auto key = std::string(pybind11::str(kv.first));
|
||||
auto comment = kv.second[int_(1)];
|
||||
docstring += "\n\n " + key;
|
||||
if (!comment.is_none()) {
|
||||
docstring += " : " + (std::string) pybind11::str(comment);
|
||||
}
|
||||
docstring += "Members:";
|
||||
for (auto kv : entries) {
|
||||
auto key = std::string(pybind11::str(kv.first));
|
||||
auto comment = kv.second[int_(1)];
|
||||
docstring += "\n\n ";
|
||||
docstring += key;
|
||||
if (!comment.is_none()) {
|
||||
docstring += " : ";
|
||||
docstring += pybind11::str(comment).cast<std::string>();
|
||||
}
|
||||
}
|
||||
return docstring;
|
||||
},
|
||||
name("__doc__")),
|
||||
none(),
|
||||
none(),
|
||||
"");
|
||||
}
|
||||
}
|
||||
return docstring;
|
||||
},
|
||||
name("__doc__")),
|
||||
none(),
|
||||
none(),
|
||||
"");
|
||||
|
||||
m_base.attr("__members__") = static_property(cpp_function(
|
||||
[](handle arg) -> dict {
|
||||
|
@ -2097,12 +2086,12 @@ struct enum_base {
|
|||
str name(name_);
|
||||
if (entries.contains(name)) {
|
||||
std::string type_name = (std::string) str(m_base.attr("__name__"));
|
||||
throw value_error(std::move(type_name) + ": element \"" + std::string(name_)
|
||||
throw value_error(type_name + ": element \"" + std::string(name_)
|
||||
+ "\" already exists!");
|
||||
}
|
||||
|
||||
entries[name] = pybind11::make_tuple(value, doc);
|
||||
m_base.attr(std::move(name)) = std::move(value);
|
||||
entries[name] = std::make_pair(value, doc);
|
||||
m_base.attr(name) = value;
|
||||
}
|
||||
|
||||
PYBIND11_NOINLINE void export_values() {
|
||||
|
@ -2184,6 +2173,9 @@ public:
|
|||
def_property_readonly("value", [](Type value) { return (Scalar) value; });
|
||||
def("__int__", [](Type value) { return (Scalar) value; });
|
||||
def("__index__", [](Type value) { return (Scalar) value; });
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
def("__long__", [](Type value) { return (Scalar) value; });
|
||||
#endif
|
||||
attr("__setstate__") = cpp_function(
|
||||
[](detail::value_and_holder &v_h, Scalar arg) {
|
||||
detail::initimpl::setstate<Base>(
|
||||
|
@ -2445,8 +2437,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
|||
typename Type,
|
||||
typename... Extra>
|
||||
iterator make_iterator(Type &value, Extra &&...extra) {
|
||||
return make_iterator<Policy>(
|
||||
std::begin(value), std::end(value), std::forward<Extra>(extra)...);
|
||||
return make_iterator<Policy>(std::begin(value), std::end(value), extra...);
|
||||
}
|
||||
|
||||
/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting
|
||||
|
@ -2455,8 +2446,7 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
|
|||
typename Type,
|
||||
typename... Extra>
|
||||
iterator make_key_iterator(Type &value, Extra &&...extra) {
|
||||
return make_key_iterator<Policy>(
|
||||
std::begin(value), std::end(value), std::forward<Extra>(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
|
||||
|
@ -2465,8 +2455,7 @@ 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), std::forward<Extra>(extra)...);
|
||||
return make_value_iterator<Policy>(std::begin(value), std::end(value), extra...);
|
||||
}
|
||||
|
||||
template <typename InputType, typename OutputType>
|
||||
|
@ -2495,7 +2484,7 @@ void implicitly_convertible() {
|
|||
};
|
||||
|
||||
if (auto *tinfo = detail::get_type_info(typeid(OutputType))) {
|
||||
tinfo->implicit_conversions.emplace_back(std::move(implicit_caster));
|
||||
tinfo->implicit_conversions.push_back(implicit_caster);
|
||||
} else {
|
||||
pybind11_fail("implicitly_convertible: Unable to find type " + type_id<OutputType>());
|
||||
}
|
||||
|
@ -2531,7 +2520,7 @@ public:
|
|||
exception(handle scope, const char *name, handle base = PyExc_Exception) {
|
||||
std::string full_name
|
||||
= scope.attr("__name__").cast<std::string>() + std::string(".") + name;
|
||||
m_ptr = PyErr_NewException(const_cast<char *>(full_name.c_str()), base.ptr(), nullptr);
|
||||
m_ptr = PyErr_NewException(const_cast<char *>(full_name.c_str()), base.ptr(), NULL);
|
||||
if (hasattr(scope, "__dict__") && scope.attr("__dict__").contains(name)) {
|
||||
pybind11_fail("Error during initialization: multiple incompatible "
|
||||
"definitions with name \""
|
||||
|
@ -2613,8 +2602,8 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
|
|||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
strings[i] = str(args[i]);
|
||||
}
|
||||
auto sep = kwargs.contains("sep") ? kwargs["sep"] : str(" ");
|
||||
auto line = sep.attr("join")(std::move(strings));
|
||||
auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" ");
|
||||
auto line = sep.attr("join")(strings);
|
||||
|
||||
object file;
|
||||
if (kwargs.contains("file")) {
|
||||
|
@ -2632,8 +2621,8 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
|
|||
}
|
||||
|
||||
auto write = file.attr("write");
|
||||
write(std::move(line));
|
||||
write(kwargs.contains("end") ? kwargs["end"] : str("\n"));
|
||||
write(line);
|
||||
write(kwargs.contains("end") ? kwargs["end"] : cast("\n"));
|
||||
|
||||
if (kwargs.contains("flush") && kwargs["flush"].cast<bool>()) {
|
||||
file.attr("flush")();
|
||||
|
@ -2647,21 +2636,17 @@ void print(Args &&...args) {
|
|||
detail::print(c.args(), c.kwargs());
|
||||
}
|
||||
|
||||
inline void
|
||||
error_already_set::m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr) {
|
||||
gil_scoped_acquire gil;
|
||||
error_scope scope;
|
||||
delete raw_ptr;
|
||||
}
|
||||
|
||||
inline const char *error_already_set::what() const noexcept {
|
||||
gil_scoped_acquire gil;
|
||||
error_scope scope;
|
||||
return m_fetched_error->error_string().c_str();
|
||||
error_already_set::~error_already_set() {
|
||||
if (m_type) {
|
||||
gil_scoped_acquire gil;
|
||||
error_scope scope;
|
||||
m_type.release().dec_ref();
|
||||
m_value.release().dec_ref();
|
||||
m_trace.release().dec_ref();
|
||||
}
|
||||
}
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
inline function
|
||||
get_type_override(const void *this_ptr, const type_info *this_type, const char *name) {
|
||||
handle self = get_object_handle(this_ptr, this_type);
|
||||
|
@ -2680,7 +2665,7 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
|
|||
|
||||
function override = getattr(self, name, function());
|
||||
if (override.is_cpp_function()) {
|
||||
cache.insert(std::move(key));
|
||||
cache.insert(key);
|
||||
return function();
|
||||
}
|
||||
|
||||
|
@ -2880,3 +2865,7 @@ inline function get_overload(const T *this_ptr, const char *name) {
|
|||
PYBIND11_OVERRIDE_PURE(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), fn, __VA_ARGS__);
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ == 7
|
||||
# pragma GCC diagnostic pop // -Wnoexcept-type
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,9 +13,9 @@
|
|||
#include "detail/common.h"
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
@ -45,35 +45,21 @@ using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
|
|||
/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically
|
||||
/// used for forwarding a container's elements.
|
||||
template <typename T, typename U>
|
||||
constexpr forwarded_type<T, U> forward_like(U &&u) {
|
||||
forwarded_type<T, U> forward_like(U &&u) {
|
||||
return std::forward<detail::forwarded_type<T, U>>(std::forward<U>(u));
|
||||
}
|
||||
|
||||
// Checks if a container has a STL style reserve method.
|
||||
// This will only return true for a `reserve()` with a `void` return.
|
||||
template <typename C>
|
||||
using has_reserve_method = std::is_same<decltype(std::declval<C>().reserve(0)), void>;
|
||||
|
||||
template <typename Type, typename Key>
|
||||
struct set_caster {
|
||||
using type = Type;
|
||||
using key_conv = make_caster<Key>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||
void reserve_maybe(const anyset &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
}
|
||||
void reserve_maybe(const anyset &, void *) {}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<anyset>(src)) {
|
||||
if (!isinstance<pybind11::set>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto s = reinterpret_borrow<anyset>(src);
|
||||
auto s = reinterpret_borrow<pybind11::set>(src);
|
||||
value.clear();
|
||||
reserve_maybe(s, &value);
|
||||
for (auto entry : s) {
|
||||
key_conv conv;
|
||||
if (!conv.load(entry, convert)) {
|
||||
|
@ -92,8 +78,8 @@ public:
|
|||
pybind11::set s;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
key_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(std::move(value_))) {
|
||||
key_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_ || !s.add(value_)) {
|
||||
return handle();
|
||||
}
|
||||
}
|
||||
|
@ -108,21 +94,12 @@ struct map_caster {
|
|||
using key_conv = make_caster<Key>;
|
||||
using value_conv = make_caster<Value>;
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||
void reserve_maybe(const dict &d, Type *) {
|
||||
value.reserve(d.size());
|
||||
}
|
||||
void reserve_maybe(const dict &, void *) {}
|
||||
|
||||
public:
|
||||
bool load(handle src, bool convert) {
|
||||
if (!isinstance<dict>(src)) {
|
||||
return false;
|
||||
}
|
||||
auto d = reinterpret_borrow<dict>(src);
|
||||
value.clear();
|
||||
reserve_maybe(d, &value);
|
||||
for (auto it : d) {
|
||||
key_conv kconv;
|
||||
value_conv vconv;
|
||||
|
@ -145,13 +122,13 @@ public:
|
|||
}
|
||||
for (auto &&kv : src) {
|
||||
auto key = reinterpret_steal<object>(
|
||||
key_conv::cast(detail::forward_like<T>(kv.first), policy_key, parent));
|
||||
key_conv::cast(forward_like<T>(kv.first), policy_key, parent));
|
||||
auto value = reinterpret_steal<object>(
|
||||
value_conv::cast(detail::forward_like<T>(kv.second), policy_value, parent));
|
||||
value_conv::cast(forward_like<T>(kv.second), policy_value, parent));
|
||||
if (!key || !value) {
|
||||
return handle();
|
||||
}
|
||||
d[std::move(key)] = std::move(value);
|
||||
d[key] = value;
|
||||
}
|
||||
return d.release();
|
||||
}
|
||||
|
@ -183,7 +160,9 @@ struct list_caster {
|
|||
}
|
||||
|
||||
private:
|
||||
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||
template <
|
||||
typename T = Type,
|
||||
enable_if_t<std::is_same<decltype(std::declval<T>().reserve(0)), void>::value, int> = 0>
|
||||
void reserve_maybe(const sequence &s, Type *) {
|
||||
value.reserve(s.size());
|
||||
}
|
||||
|
@ -199,7 +178,7 @@ public:
|
|||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
return handle();
|
||||
}
|
||||
|
@ -263,7 +242,7 @@ public:
|
|||
ssize_t index = 0;
|
||||
for (auto &&value : src) {
|
||||
auto value_ = reinterpret_steal<object>(
|
||||
value_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||
value_conv::cast(forward_like<T>(value), policy, parent));
|
||||
if (!value_) {
|
||||
return handle();
|
||||
}
|
||||
|
@ -311,7 +290,7 @@ struct optional_caster {
|
|||
template <typename T>
|
||||
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
return none().release();
|
||||
return none().inc_ref();
|
||||
}
|
||||
if (!std::is_lvalue_reference<T>::value) {
|
||||
policy = return_value_policy_override<Value>::policy(policy);
|
||||
|
@ -393,7 +372,7 @@ struct variant_caster<V<Ts...>> {
|
|||
bool load_alternative(handle src, bool convert, type_list<U, Us...>) {
|
||||
auto caster = make_caster<U>();
|
||||
if (caster.load(src, convert)) {
|
||||
value = cast_op<U>(std::move(caster));
|
||||
value = cast_op<U>(caster);
|
||||
return true;
|
||||
}
|
||||
return load_alternative(src, convert, type_list<Us...>{});
|
||||
|
@ -427,9 +406,6 @@ struct variant_caster<V<Ts...>> {
|
|||
#if defined(PYBIND11_HAS_VARIANT)
|
||||
template <typename... Ts>
|
||||
struct type_caster<std::variant<Ts...>> : variant_caster<std::variant<Ts...>> {};
|
||||
|
||||
template <>
|
||||
struct type_caster<std::monostate> : public void_caster<std::monostate> {};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
|
|
@ -13,28 +13,22 @@
|
|||
#include <string>
|
||||
|
||||
#ifdef __has_include
|
||||
# if defined(PYBIND11_CPP17)
|
||||
# if __has_include(<filesystem>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# elif __has_include(<experimental/filesystem>)
|
||||
# include <experimental/filesystem>
|
||||
# define PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM 1
|
||||
# endif
|
||||
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \
|
||||
PY_VERSION_HEX >= 0x03060000
|
||||
# include <filesystem>
|
||||
# define PYBIND11_HAS_FILESYSTEM 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) \
|
||||
&& !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
|
||||
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_FILESYSTEM_IS_OPTIONAL)
|
||||
# error \
|
||||
"Neither #include <filesystem> nor #include <experimental/filesystem is available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
|
||||
"#include <filesystem> is not available. (Use -DPYBIND11_HAS_FILESYSTEM_IS_OPTIONAL to ignore.)"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
template <typename T>
|
||||
struct path_caster {
|
||||
|
||||
|
@ -101,16 +95,9 @@ public:
|
|||
PYBIND11_TYPE_CASTER(T, const_name("os.PathLike"));
|
||||
};
|
||||
|
||||
#endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
|
||||
#if defined(PYBIND11_HAS_FILESYSTEM)
|
||||
template <>
|
||||
struct type_caster<std::filesystem::path> : public path_caster<std::filesystem::path> {};
|
||||
#elif defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
template <>
|
||||
struct type_caster<std::experimental::filesystem::path>
|
||||
: public path_caster<std::experimental::filesystem::path> {};
|
||||
#endif
|
||||
#endif // PYBIND11_HAS_FILESYSTEM
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
|
|
@ -10,13 +10,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "detail/type_caster_base.h"
|
||||
#include "cast.h"
|
||||
#include "operators.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
@ -235,7 +232,7 @@ void vector_modifiers(
|
|||
/// Slicing protocol
|
||||
cl.def(
|
||||
"__getitem__",
|
||||
[](const Vector &v, const slice &slice) -> Vector * {
|
||||
[](const Vector &v, slice slice) -> Vector * {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||
|
@ -256,7 +253,7 @@ void vector_modifiers(
|
|||
|
||||
cl.def(
|
||||
"__setitem__",
|
||||
[](Vector &v, const slice &slice, const Vector &value) {
|
||||
[](Vector &v, slice slice, const Vector &value) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||
throw error_already_set();
|
||||
|
@ -284,7 +281,7 @@ void vector_modifiers(
|
|||
|
||||
cl.def(
|
||||
"__delitem__",
|
||||
[](Vector &v, const slice &slice) {
|
||||
[](Vector &v, slice slice) {
|
||||
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||
|
||||
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||
|
@ -639,52 +636,18 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
|||
"Return the canonical string representation of this map.");
|
||||
}
|
||||
|
||||
template <typename KeyType>
|
||||
template <typename Map>
|
||||
struct keys_view {
|
||||
virtual size_t len() = 0;
|
||||
virtual iterator iter() = 0;
|
||||
virtual bool contains(const KeyType &k) = 0;
|
||||
virtual bool contains(const object &k) = 0;
|
||||
virtual ~keys_view() = default;
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename MappedType>
|
||||
template <typename Map>
|
||||
struct values_view {
|
||||
virtual size_t len() = 0;
|
||||
virtual iterator iter() = 0;
|
||||
virtual ~values_view() = default;
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename KeyType, typename MappedType>
|
||||
template <typename Map>
|
||||
struct items_view {
|
||||
virtual size_t len() = 0;
|
||||
virtual iterator iter() = 0;
|
||||
virtual ~items_view() = default;
|
||||
};
|
||||
|
||||
template <typename Map, typename KeysView>
|
||||
struct KeysViewImpl : public KeysView {
|
||||
explicit KeysViewImpl(Map &map) : map(map) {}
|
||||
size_t len() override { return map.size(); }
|
||||
iterator iter() override { return make_key_iterator(map.begin(), map.end()); }
|
||||
bool contains(const typename Map::key_type &k) override { return map.find(k) != map.end(); }
|
||||
bool contains(const object &) override { return false; }
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename Map, typename ValuesView>
|
||||
struct ValuesViewImpl : public ValuesView {
|
||||
explicit ValuesViewImpl(Map &map) : map(map) {}
|
||||
size_t len() override { return map.size(); }
|
||||
iterator iter() override { return make_value_iterator(map.begin(), map.end()); }
|
||||
Map ↦
|
||||
};
|
||||
|
||||
template <typename Map, typename ItemsView>
|
||||
struct ItemsViewImpl : public ItemsView {
|
||||
explicit ItemsViewImpl(Map &map) : map(map) {}
|
||||
size_t len() override { return map.size(); }
|
||||
iterator iter() override { return make_iterator(map.begin(), map.end()); }
|
||||
Map ↦
|
||||
};
|
||||
|
||||
|
@ -694,11 +657,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 StrippedKeyType = detail::remove_cvref_t<KeyType>;
|
||||
using StrippedMappedType = detail::remove_cvref_t<MappedType>;
|
||||
using KeysView = detail::keys_view<StrippedKeyType>;
|
||||
using ValuesView = detail::values_view<StrippedMappedType>;
|
||||
using ItemsView = detail::items_view<StrippedKeyType, StrippedMappedType>;
|
||||
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;
|
||||
|
@ -712,57 +673,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)...);
|
||||
static constexpr auto key_type_descr = detail::make_caster<KeyType>::name;
|
||||
static constexpr auto mapped_type_descr = detail::make_caster<MappedType>::name;
|
||||
std::string key_type_name(key_type_descr.text), mapped_type_name(mapped_type_descr.text);
|
||||
|
||||
// If key type isn't properly wrapped, fall back to C++ names
|
||||
if (key_type_name == "%") {
|
||||
key_type_name = detail::type_info_description(typeid(KeyType));
|
||||
}
|
||||
// Similarly for value type:
|
||||
if (mapped_type_name == "%") {
|
||||
mapped_type_name = detail::type_info_description(typeid(MappedType));
|
||||
}
|
||||
|
||||
// Wrap KeysView[KeyType] if it wasn't already wrapped
|
||||
if (!detail::get_type_info(typeid(KeysView))) {
|
||||
class_<KeysView> keys_view(
|
||||
scope, ("KeysView[" + key_type_name + "]").c_str(), pybind11::module_local(local));
|
||||
keys_view.def("__len__", &KeysView::len);
|
||||
keys_view.def("__iter__",
|
||||
&KeysView::iter,
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
keys_view.def("__contains__",
|
||||
static_cast<bool (KeysView::*)(const KeyType &)>(&KeysView::contains));
|
||||
// Fallback for when the object is not of the key type
|
||||
keys_view.def("__contains__",
|
||||
static_cast<bool (KeysView::*)(const object &)>(&KeysView::contains));
|
||||
}
|
||||
// Similarly for ValuesView:
|
||||
if (!detail::get_type_info(typeid(ValuesView))) {
|
||||
class_<ValuesView> values_view(scope,
|
||||
("ValuesView[" + mapped_type_name + "]").c_str(),
|
||||
pybind11::module_local(local));
|
||||
values_view.def("__len__", &ValuesView::len);
|
||||
values_view.def("__iter__",
|
||||
&ValuesView::iter,
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
}
|
||||
// Similarly for ItemsView:
|
||||
if (!detail::get_type_info(typeid(ItemsView))) {
|
||||
class_<ItemsView> items_view(
|
||||
scope,
|
||||
("ItemsView[" + key_type_name + ", ").append(mapped_type_name + "]").c_str(),
|
||||
pybind11::module_local(local));
|
||||
items_view.def("__len__", &ItemsView::len);
|
||||
items_view.def("__iter__",
|
||||
&ItemsView::iter,
|
||||
keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */
|
||||
);
|
||||
}
|
||||
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<>());
|
||||
|
||||
|
@ -782,25 +698,19 @@ class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&
|
|||
|
||||
cl.def(
|
||||
"keys",
|
||||
[](Map &m) {
|
||||
return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map, KeysView>(m));
|
||||
},
|
||||
[](Map &m) { return KeysView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def(
|
||||
"values",
|
||||
[](Map &m) {
|
||||
return std::unique_ptr<ValuesView>(new detail::ValuesViewImpl<Map, ValuesView>(m));
|
||||
},
|
||||
[](Map &m) { return ValuesView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
cl.def(
|
||||
"items",
|
||||
[](Map &m) {
|
||||
return std::unique_ptr<ItemsView>(new detail::ItemsViewImpl<Map, ItemsView>(m));
|
||||
},
|
||||
[](Map &m) { return ItemsView{m}; },
|
||||
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||
);
|
||||
|
||||
|
@ -839,6 +749,36 @@ 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,9 @@
|
|||
import os
|
||||
|
||||
import nox
|
||||
|
||||
nox.needs_version = ">=2022.1.7"
|
||||
nox.options.sessions = ["lint", "tests", "tests_packaging"]
|
||||
|
||||
PYTHON_VERSIONS = [
|
||||
"3.6",
|
||||
"3.7",
|
||||
"3.8",
|
||||
"3.9",
|
||||
"3.10",
|
||||
"3.11",
|
||||
"pypy3.7",
|
||||
"pypy3.8",
|
||||
"pypy3.9",
|
||||
]
|
||||
|
||||
if os.environ.get("CI", None):
|
||||
nox.options.error_on_missing_interpreters = True
|
||||
PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
|
@ -27,7 +12,7 @@ def lint(session: nox.Session) -> None:
|
|||
Lint the codebase (except for clang-format/tidy).
|
||||
"""
|
||||
session.install("pre-commit")
|
||||
session.run("pre-commit", "run", "-a", *session.posargs)
|
||||
session.run("pre-commit", "run", "-a")
|
||||
|
||||
|
||||
@nox.session(python=PYTHON_VERSIONS)
|
||||
|
@ -58,7 +43,7 @@ def tests_packaging(session: nox.Session) -> None:
|
|||
"""
|
||||
|
||||
session.install("-r", "tests/requirements.txt", "--prefer-binary")
|
||||
session.run("pytest", "tests/extra_python_package", *session.posargs)
|
||||
session.run("pytest", "tests/extra_python_package")
|
||||
|
||||
|
||||
@nox.session(reuse_venv=True)
|
||||
|
@ -71,10 +56,10 @@ def docs(session: nox.Session) -> None:
|
|||
session.chdir("docs")
|
||||
|
||||
if "pdf" in session.posargs:
|
||||
session.run("sphinx-build", "-M", "latexpdf", ".", "_build")
|
||||
session.run("sphinx-build", "-b", "latexpdf", ".", "_build")
|
||||
return
|
||||
|
||||
session.run("sphinx-build", "-M", "html", ".", "_build")
|
||||
session.run("sphinx-build", "-b", "html", ".", "_build")
|
||||
|
||||
if "serve" in session.posargs:
|
||||
session.log("Launching docs at http://localhost:8000/ - use Ctrl-C to quit")
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
import sys
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
|
||||
raise ImportError(msg)
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ._version import __version__, version_info
|
||||
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
__all__ = (
|
||||
"version_info",
|
||||
"__version__",
|
||||
"get_include",
|
||||
"get_cmake_dir",
|
||||
"get_pkgconfig_dir",
|
||||
)
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# pylint: disable=missing-function-docstring
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
from ._version import __version__
|
||||
from .commands import get_cmake_dir, get_include, get_pkgconfig_dir
|
||||
from .commands import get_cmake_dir, get_include
|
||||
|
||||
|
||||
def print_includes() -> None:
|
||||
def print_includes():
|
||||
# type: () -> None
|
||||
dirs = [
|
||||
sysconfig.get_path("include"),
|
||||
sysconfig.get_path("platinclude"),
|
||||
|
@ -24,14 +25,10 @@ def print_includes() -> None:
|
|||
print(" ".join("-I" + d for d in unique_dirs))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
def main():
|
||||
# type: () -> None
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=__version__,
|
||||
help="Print the version and exit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--includes",
|
||||
action="store_true",
|
||||
|
@ -42,11 +39,6 @@ def main() -> None:
|
|||
action="store_true",
|
||||
help="Print the CMake module directory, ideal for setting -Dpybind11_ROOT in CMake.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pkgconfigdir",
|
||||
action="store_true",
|
||||
help="Print the pkgconfig directory, ideal for setting $PKG_CONFIG_PATH.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if not sys.argv[1:]:
|
||||
parser.print_help()
|
||||
|
@ -54,8 +46,6 @@ def main() -> None:
|
|||
print_includes()
|
||||
if args.cmakedir:
|
||||
print(get_cmake_dir())
|
||||
if args.pkgconfigdir:
|
||||
print(get_pkgconfig_dir())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from typing import Union
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
def _to_int(s: str) -> Union[int, str]:
|
||||
def _to_int(s):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
return s
|
||||
|
||||
|
||||
__version__ = "2.10.4"
|
||||
__version__ = "2.9.2"
|
||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from typing import Tuple, Union
|
||||
|
||||
def _to_int(s: str) -> Union[int, str]: ...
|
||||
|
||||
__version__: str
|
||||
version_info: Tuple[Union[int, str], ...]
|
|
@ -1,37 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def get_include(user: bool = False) -> str: # pylint: disable=unused-argument
|
||||
"""
|
||||
Return the path to the pybind11 include directory. The historical "user"
|
||||
argument is unused, and may be removed.
|
||||
"""
|
||||
def get_include(user=False):
|
||||
# type: (bool) -> str
|
||||
installed_path = os.path.join(DIR, "include")
|
||||
source_path = os.path.join(os.path.dirname(DIR), "include")
|
||||
return installed_path if os.path.exists(installed_path) else source_path
|
||||
|
||||
|
||||
def get_cmake_dir() -> str:
|
||||
"""
|
||||
Return the path to the pybind11 CMake module directory.
|
||||
"""
|
||||
def get_cmake_dir():
|
||||
# type: () -> str
|
||||
cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11")
|
||||
if os.path.exists(cmake_installed_path):
|
||||
return cmake_installed_path
|
||||
|
||||
msg = "pybind11 not installed, installation required to access the CMake files"
|
||||
raise ImportError(msg)
|
||||
|
||||
|
||||
def get_pkgconfig_dir() -> str:
|
||||
"""
|
||||
Return the path to the pybind11 pkgconfig directory.
|
||||
"""
|
||||
pkgconfig_installed_path = os.path.join(DIR, "share", "pkgconfig")
|
||||
if os.path.exists(pkgconfig_installed_path):
|
||||
return pkgconfig_installed_path
|
||||
|
||||
msg = "pybind11 not installed, installation required to access the pkgconfig files"
|
||||
raise ImportError(msg)
|
||||
else:
|
||||
msg = "pybind11 not installed, installation required to access the CMake files"
|
||||
raise ImportError(msg)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This module provides helpers for C++11+ projects using pybind11.
|
||||
|
||||
|
@ -47,20 +49,6 @@ import sysconfig
|
|||
import tempfile
|
||||
import threading
|
||||
import warnings
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
try:
|
||||
from setuptools import Extension as _Extension
|
||||
|
@ -73,6 +61,7 @@ import distutils.ccompiler
|
|||
import distutils.errors
|
||||
|
||||
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
|
||||
PY2 = sys.version_info[0] < 3
|
||||
MACOS = sys.platform.startswith("darwin")
|
||||
STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
|
||||
|
||||
|
@ -84,7 +73,7 @@ STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}"
|
|||
# directory into your path if it sits beside your setup.py.
|
||||
|
||||
|
||||
class Pybind11Extension(_Extension): # type: ignore[misc]
|
||||
class Pybind11Extension(_Extension):
|
||||
"""
|
||||
Build a C++11+ Extension module with pybind11. This automatically adds the
|
||||
recommended flags when you init the extension and assumes C++ sources - you
|
||||
|
@ -106,18 +95,22 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
|
||||
If you want to add pybind11 headers manually, for example for an exact
|
||||
git checkout, then set ``include_pybind11=False``.
|
||||
|
||||
Warning: do not use property-based access to the instance on Python 2 -
|
||||
this is an ugly old-style class due to Distutils.
|
||||
"""
|
||||
|
||||
# flags are prepended, so that they can be further overridden, e.g. by
|
||||
# ``extra_compile_args=["-g"]``.
|
||||
|
||||
def _add_cflags(self, flags: List[str]) -> None:
|
||||
def _add_cflags(self, flags):
|
||||
self.extra_compile_args[:0] = flags
|
||||
|
||||
def _add_ldflags(self, flags: List[str]) -> None:
|
||||
def _add_ldflags(self, flags):
|
||||
self.extra_link_args[:0] = flags
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
self._cxx_level = 0
|
||||
cxx_std = kwargs.pop("cxx_std", 0)
|
||||
|
||||
|
@ -126,7 +119,9 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
|
||||
include_pybind11 = kwargs.pop("include_pybind11", True)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
# Can't use super here because distutils has old-style classes in
|
||||
# Python 2!
|
||||
_Extension.__init__(self, *args, **kwargs)
|
||||
|
||||
# Include the installed package pybind11 headers
|
||||
if include_pybind11:
|
||||
|
@ -138,10 +133,11 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
|
||||
if pyinc not in self.include_dirs:
|
||||
self.include_dirs.append(pyinc)
|
||||
except ModuleNotFoundError:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
self.cxx_std = cxx_std
|
||||
# Have to use the accessor manually to support Python 2 distutils
|
||||
Pybind11Extension.cxx_std.__set__(self, cxx_std)
|
||||
|
||||
cflags = []
|
||||
ldflags = []
|
||||
|
@ -161,22 +157,21 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
self._add_ldflags(ldflags)
|
||||
|
||||
@property
|
||||
def cxx_std(self) -> int:
|
||||
def cxx_std(self):
|
||||
"""
|
||||
The CXX standard level. If set, will add the required flags. If left at
|
||||
0, it will trigger an automatic search when pybind11's build_ext is
|
||||
used. If None, will have no effect. Besides just the flags, this may
|
||||
add a macos-min 10.9 or 10.14 flag if MACOSX_DEPLOYMENT_TARGET is
|
||||
unset.
|
||||
The CXX standard level. If set, will add the required flags. If left
|
||||
at 0, it will trigger an automatic search when pybind11's build_ext
|
||||
is used. If None, will have no effect. Besides just the flags, this
|
||||
may add a register warning/error fix for Python 2 or macos-min 10.9
|
||||
or 10.14.
|
||||
"""
|
||||
return self._cxx_level
|
||||
|
||||
@cxx_std.setter
|
||||
def cxx_std(self, level: int) -> None:
|
||||
def cxx_std(self, level):
|
||||
|
||||
if self._cxx_level:
|
||||
warnings.warn(
|
||||
"You cannot safely change the cxx_level after setting it!", stacklevel=2
|
||||
)
|
||||
warnings.warn("You cannot safely change the cxx_level after setting it!")
|
||||
|
||||
# MSVC 2015 Update 3 and later only have 14 (and later 17) modes, so
|
||||
# force a valid flag here.
|
||||
|
@ -200,20 +195,31 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
|
|||
current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2])
|
||||
desired_macos = (10, 9) if level < 17 else (10, 14)
|
||||
macos_string = ".".join(str(x) for x in min(current_macos, desired_macos))
|
||||
macosx_min = f"-mmacosx-version-min={macos_string}"
|
||||
macosx_min = "-mmacosx-version-min=" + macos_string
|
||||
cflags += [macosx_min]
|
||||
ldflags += [macosx_min]
|
||||
|
||||
if PY2:
|
||||
if WIN:
|
||||
# Will be ignored on MSVC 2015, where C++17 is not supported so
|
||||
# this flag is not valid.
|
||||
cflags += ["/wd5033"]
|
||||
elif level >= 17:
|
||||
cflags += ["-Wno-register"]
|
||||
elif level >= 14:
|
||||
cflags += ["-Wno-deprecated-register"]
|
||||
|
||||
self._add_cflags(cflags)
|
||||
self._add_ldflags(ldflags)
|
||||
|
||||
|
||||
# Just in case someone clever tries to multithread
|
||||
tmp_chdir_lock = threading.Lock()
|
||||
cpp_cache_lock = threading.Lock()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tmp_chdir() -> Iterator[str]:
|
||||
def tmp_chdir():
|
||||
"Prepare and enter a temporary directory, cleanup when done"
|
||||
|
||||
# Threadsafe
|
||||
|
@ -229,7 +235,7 @@ def tmp_chdir() -> Iterator[str]:
|
|||
|
||||
|
||||
# cf http://bugs.python.org/issue26689
|
||||
def has_flag(compiler: Any, flag: str) -> bool:
|
||||
def has_flag(compiler, flag):
|
||||
"""
|
||||
Return the flag if a flag name is supported on the
|
||||
specified compiler, otherwise None (can be used as a boolean).
|
||||
|
@ -237,12 +243,13 @@ def has_flag(compiler: Any, flag: str) -> bool:
|
|||
"""
|
||||
|
||||
with tmp_chdir():
|
||||
fname = Path("flagcheck.cpp")
|
||||
# Don't trigger -Wunused-parameter.
|
||||
fname.write_text("int main (int, char **) { return 0; }", encoding="utf-8")
|
||||
fname = "flagcheck.cpp"
|
||||
with open(fname, "w") as f:
|
||||
# Don't trigger -Wunused-parameter.
|
||||
f.write("int main (int, char **) { return 0; }")
|
||||
|
||||
try:
|
||||
compiler.compile([str(fname)], extra_postargs=[flag])
|
||||
compiler.compile([fname], extra_postargs=[flag])
|
||||
except distutils.errors.CompileError:
|
||||
return False
|
||||
return True
|
||||
|
@ -252,8 +259,7 @@ def has_flag(compiler: Any, flag: str) -> bool:
|
|||
cpp_flag_cache = None
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def auto_cpp_level(compiler: Any) -> Union[str, int]:
|
||||
def auto_cpp_level(compiler):
|
||||
"""
|
||||
Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows.
|
||||
"""
|
||||
|
@ -261,38 +267,48 @@ def auto_cpp_level(compiler: Any) -> Union[str, int]:
|
|||
if WIN:
|
||||
return "latest"
|
||||
|
||||
global cpp_flag_cache
|
||||
|
||||
# If this has been previously calculated with the same args, return that
|
||||
with cpp_cache_lock:
|
||||
if cpp_flag_cache:
|
||||
return cpp_flag_cache
|
||||
|
||||
levels = [17, 14, 11]
|
||||
|
||||
for level in levels:
|
||||
if has_flag(compiler, STD_TMPL.format(level)):
|
||||
with cpp_cache_lock:
|
||||
cpp_flag_cache = level
|
||||
return level
|
||||
|
||||
msg = "Unsupported compiler -- at least C++11 support is needed!"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
class build_ext(_build_ext): # type: ignore[misc] # noqa: N801
|
||||
class build_ext(_build_ext): # noqa: N801
|
||||
"""
|
||||
Customized build_ext that allows an auto-search for the highest supported
|
||||
C++ level for Pybind11Extension. This is only needed for the auto-search
|
||||
for now, and is completely optional otherwise.
|
||||
"""
|
||||
|
||||
def build_extensions(self) -> None:
|
||||
def build_extensions(self):
|
||||
"""
|
||||
Build extensions, injecting C++ std for Pybind11Extension if needed.
|
||||
"""
|
||||
|
||||
for ext in self.extensions:
|
||||
if hasattr(ext, "_cxx_level") and ext._cxx_level == 0:
|
||||
ext.cxx_std = auto_cpp_level(self.compiler)
|
||||
# Python 2 syntax - old-style distutils class
|
||||
ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler))
|
||||
|
||||
super().build_extensions()
|
||||
# Python 2 doesn't allow super here, since distutils uses old-style
|
||||
# classes!
|
||||
_build_ext.build_extensions(self)
|
||||
|
||||
|
||||
def intree_extensions(
|
||||
paths: Iterable[str], package_dir: Optional[Dict[str, str]] = None
|
||||
) -> List[Pybind11Extension]:
|
||||
def intree_extensions(paths, package_dir=None):
|
||||
"""
|
||||
Generate Pybind11Extensions from source files directly located in a Python
|
||||
source tree.
|
||||
|
@ -302,37 +318,33 @@ def intree_extensions(
|
|||
not contain an ``__init__.py`` file.
|
||||
"""
|
||||
exts = []
|
||||
|
||||
if package_dir is None:
|
||||
for path in paths:
|
||||
for path in paths:
|
||||
if package_dir is None:
|
||||
parent, _ = os.path.split(path)
|
||||
while os.path.exists(os.path.join(parent, "__init__.py")):
|
||||
parent, _ = os.path.split(parent)
|
||||
relname, _ = os.path.splitext(os.path.relpath(path, parent))
|
||||
qualified_name = relname.replace(os.path.sep, ".")
|
||||
exts.append(Pybind11Extension(qualified_name, [path]))
|
||||
return exts
|
||||
|
||||
for path in paths:
|
||||
for prefix, parent in package_dir.items():
|
||||
if path.startswith(parent):
|
||||
relname, _ = os.path.splitext(os.path.relpath(path, parent))
|
||||
qualified_name = relname.replace(os.path.sep, ".")
|
||||
if prefix:
|
||||
qualified_name = prefix + "." + qualified_name
|
||||
exts.append(Pybind11Extension(qualified_name, [path]))
|
||||
break
|
||||
else:
|
||||
msg = (
|
||||
f"path {path} is not a child of any of the directories listed "
|
||||
f"in 'package_dir' ({package_dir})"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
found = False
|
||||
for prefix, parent in package_dir.items():
|
||||
if path.startswith(parent):
|
||||
found = True
|
||||
relname, _ = os.path.splitext(os.path.relpath(path, parent))
|
||||
qualified_name = relname.replace(os.path.sep, ".")
|
||||
if prefix:
|
||||
qualified_name = prefix + "." + qualified_name
|
||||
exts.append(Pybind11Extension(qualified_name, [path]))
|
||||
if not found:
|
||||
raise ValueError(
|
||||
"path {} is not a child of any of the directories listed "
|
||||
"in 'package_dir' ({})".format(path, package_dir)
|
||||
)
|
||||
return exts
|
||||
|
||||
|
||||
def naive_recompile(obj: str, src: str) -> bool:
|
||||
def naive_recompile(obj, src):
|
||||
"""
|
||||
This will recompile only if the source file changes. It does not check
|
||||
header files, so a more advanced function or Ccache is better if you have
|
||||
|
@ -341,7 +353,7 @@ def naive_recompile(obj: str, src: str) -> bool:
|
|||
return os.stat(obj).st_mtime < os.stat(src).st_mtime
|
||||
|
||||
|
||||
def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument
|
||||
def no_recompile(obg, src):
|
||||
"""
|
||||
This is the safest but slowest choice (and is the default) - will always
|
||||
recompile sources.
|
||||
|
@ -349,33 +361,15 @@ def no_recompile(obg: str, src: str) -> bool: # pylint: disable=unused-argument
|
|||
return True
|
||||
|
||||
|
||||
S = TypeVar("S", bound="ParallelCompile")
|
||||
|
||||
CCompilerMethod = Callable[
|
||||
[
|
||||
distutils.ccompiler.CCompiler,
|
||||
List[str],
|
||||
Optional[str],
|
||||
Optional[Union[Tuple[str], Tuple[str, Optional[str]]]],
|
||||
Optional[List[str]],
|
||||
bool,
|
||||
Optional[List[str]],
|
||||
Optional[List[str]],
|
||||
Optional[List[str]],
|
||||
],
|
||||
List[str],
|
||||
]
|
||||
|
||||
|
||||
# Optional parallel compile utility
|
||||
# inspired by: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils
|
||||
# and: https://github.com/tbenthompson/cppimport/blob/stable/cppimport/build_module.py
|
||||
# and NumPy's parallel distutils module:
|
||||
# https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py
|
||||
class ParallelCompile:
|
||||
class ParallelCompile(object):
|
||||
"""
|
||||
Make a parallel compile function. Inspired by
|
||||
numpy.distutils.ccompiler.CCompiler.compile and cppimport.
|
||||
numpy.distutils.ccompiler.CCompiler_compile and cppimport.
|
||||
|
||||
This takes several arguments that allow you to customize the compile
|
||||
function created:
|
||||
|
@ -410,40 +404,35 @@ class ParallelCompile:
|
|||
|
||||
__slots__ = ("envvar", "default", "max", "_old", "needs_recompile")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
envvar: Optional[str] = None,
|
||||
default: int = 0,
|
||||
max: int = 0, # pylint: disable=redefined-builtin
|
||||
needs_recompile: Callable[[str, str], bool] = no_recompile,
|
||||
) -> None:
|
||||
def __init__(self, envvar=None, default=0, max=0, needs_recompile=no_recompile):
|
||||
self.envvar = envvar
|
||||
self.default = default
|
||||
self.max = max
|
||||
self.needs_recompile = needs_recompile
|
||||
self._old: List[CCompilerMethod] = []
|
||||
self._old = []
|
||||
|
||||
def function(self) -> CCompilerMethod:
|
||||
def function(self):
|
||||
"""
|
||||
Builds a function object usable as distutils.ccompiler.CCompiler.compile.
|
||||
"""
|
||||
|
||||
def compile_function(
|
||||
compiler: distutils.ccompiler.CCompiler,
|
||||
sources: List[str],
|
||||
output_dir: Optional[str] = None,
|
||||
macros: Optional[Union[Tuple[str], Tuple[str, Optional[str]]]] = None,
|
||||
include_dirs: Optional[List[str]] = None,
|
||||
debug: bool = False,
|
||||
extra_preargs: Optional[List[str]] = None,
|
||||
extra_postargs: Optional[List[str]] = None,
|
||||
depends: Optional[List[str]] = None,
|
||||
) -> Any:
|
||||
compiler,
|
||||
sources,
|
||||
output_dir=None,
|
||||
macros=None,
|
||||
include_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None,
|
||||
depends=None,
|
||||
):
|
||||
|
||||
# These lines are directly from distutils.ccompiler.CCompiler
|
||||
macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined]
|
||||
macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile(
|
||||
output_dir, macros, include_dirs, sources, depends, extra_postargs
|
||||
)
|
||||
cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs) # type: ignore[attr-defined]
|
||||
cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs)
|
||||
|
||||
# The number of threads; start with default.
|
||||
threads = self.default
|
||||
|
@ -452,14 +441,14 @@ class ParallelCompile:
|
|||
if self.envvar is not None:
|
||||
threads = int(os.environ.get(self.envvar, self.default))
|
||||
|
||||
def _single_compile(obj: Any) -> None:
|
||||
def _single_compile(obj):
|
||||
try:
|
||||
src, ext = build[obj]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if not os.path.exists(obj) or self.needs_recompile(obj, src):
|
||||
compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) # type: ignore[attr-defined]
|
||||
compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
|
||||
|
||||
try:
|
||||
# Importing .synchronize checks for platforms that have some multiprocessing
|
||||
|
@ -477,9 +466,14 @@ class ParallelCompile:
|
|||
threads = 1
|
||||
|
||||
if threads > 1:
|
||||
with ThreadPool(threads) as pool:
|
||||
pool = ThreadPool(threads)
|
||||
# In Python 2, ThreadPool can't be used as a context manager.
|
||||
# Once we are no longer supporting it, this can be 'with pool:'
|
||||
try:
|
||||
for _ in pool.imap_unordered(_single_compile, objects):
|
||||
pass
|
||||
finally:
|
||||
pool.terminate()
|
||||
else:
|
||||
for ob in objects:
|
||||
_single_compile(ob)
|
||||
|
@ -488,16 +482,13 @@ class ParallelCompile:
|
|||
|
||||
return compile_function
|
||||
|
||||
def install(self: S) -> S:
|
||||
"""
|
||||
Installs the compile function into distutils.ccompiler.CCompiler.compile.
|
||||
"""
|
||||
distutils.ccompiler.CCompiler.compile = self.function() # type: ignore[assignment]
|
||||
def install(self):
|
||||
distutils.ccompiler.CCompiler.compile = self.function()
|
||||
return self
|
||||
|
||||
def __enter__(self: S) -> S:
|
||||
def __enter__(self):
|
||||
self._old.append(distutils.ccompiler.CCompiler.compile)
|
||||
return self.install()
|
||||
|
||||
def __exit__(self, *args: Any) -> None:
|
||||
distutils.ccompiler.CCompiler.compile = self._old.pop() # type: ignore[assignment]
|
||||
def __exit__(self, *args):
|
||||
distutils.ccompiler.CCompiler.compile = self._old.pop()
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI /
|
||||
# pre-commit).
|
||||
|
||||
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
|
||||
from types import TracebackType
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union
|
||||
|
||||
WIN: bool
|
||||
PY2: bool
|
||||
MACOS: bool
|
||||
STD_TMPL: str
|
||||
|
||||
class Pybind11Extension(_Extension):
|
||||
def _add_cflags(self, *flags: str) -> None: ...
|
||||
def _add_lflags(self, *flags: str) -> None: ...
|
||||
def __init__(
|
||||
self, *args: Any, cxx_std: int = 0, language: str = "c++", **kwargs: Any
|
||||
) -> None: ...
|
||||
@property
|
||||
def cxx_std(self) -> int: ...
|
||||
@cxx_std.setter
|
||||
def cxx_std(self, level: int) -> None: ...
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tmp_chdir() -> Iterator[str]: ...
|
||||
def has_flag(compiler: distutils.ccompiler.CCompiler, flag: str) -> bool: ...
|
||||
def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]: ...
|
||||
|
||||
class build_ext(_build_ext): # type: ignore
|
||||
def build_extensions(self) -> None: ...
|
||||
|
||||
def intree_extensions(
|
||||
paths: Iterator[str], package_dir: Optional[Dict[str, str]] = None
|
||||
) -> List[Pybind11Extension]: ...
|
||||
def no_recompile(obj: str, src: str) -> bool: ...
|
||||
def naive_recompile(obj: str, src: str) -> bool: ...
|
||||
|
||||
T = TypeVar("T", bound="ParallelCompile")
|
||||
|
||||
class ParallelCompile:
|
||||
envvar: Optional[str]
|
||||
default: int
|
||||
max: int
|
||||
needs_recompile: Callable[[str, str], bool]
|
||||
def __init__(
|
||||
self,
|
||||
envvar: Optional[str] = None,
|
||||
default: int = 0,
|
||||
max: int = 0,
|
||||
needs_recompile: Callable[[str, str], bool] = no_recompile,
|
||||
) -> None: ...
|
||||
def function(self) -> Any: ...
|
||||
def install(self: T) -> T: ...
|
||||
def __enter__(self: T) -> T: ...
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> None: ...
|
|
@ -1,5 +1,5 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=42", "cmake>=3.18", "ninja"]
|
||||
requires = ["setuptools>=42", "wheel", "cmake>=3.18", "ninja"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.check-manifest]
|
||||
|
@ -22,40 +22,20 @@ known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
|
|||
profile = "black"
|
||||
|
||||
[tool.mypy]
|
||||
files = ["pybind11"]
|
||||
python_version = "3.6"
|
||||
strict = true
|
||||
show_error_codes = true
|
||||
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
|
||||
warn_unreachable = true
|
||||
files = "pybind11"
|
||||
python_version = "2.7"
|
||||
warn_unused_configs = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["ghapi.*", "setuptools.*"]
|
||||
ignore_missing_imports = true
|
||||
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "6.0"
|
||||
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
|
||||
xfail_strict = true
|
||||
filterwarnings = ["error"]
|
||||
log_cli_level = "info"
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
timeout=300
|
||||
|
||||
|
||||
[tool.pylint]
|
||||
master.py-version = "3.6"
|
||||
reports.output-format = "colorized"
|
||||
messages_control.disable = [
|
||||
"design",
|
||||
"fixme",
|
||||
"imports",
|
||||
"line-too-long",
|
||||
"imports",
|
||||
"invalid-name",
|
||||
"protected-access",
|
||||
"missing-module-docstring",
|
||||
]
|
||||
disallow_any_generics = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
no_implicit_optional = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_return_any = true
|
||||
no_implicit_reexport = true
|
||||
strict_equality = true
|
||||
|
|
|
@ -13,13 +13,14 @@ classifiers =
|
|||
Topic :: Software Development :: Libraries :: Python Modules
|
||||
Topic :: Utilities
|
||||
Programming Language :: C++
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
License :: OSI Approved :: BSD License
|
||||
Programming Language :: Python :: Implementation :: PyPy
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
|
@ -38,13 +39,25 @@ project_urls =
|
|||
Chat = https://gitter.im/pybind/Lobby
|
||||
|
||||
[options]
|
||||
python_requires = >=3.6
|
||||
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
|
||||
zip_safe = False
|
||||
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
max-line-length = 99
|
||||
show_source = True
|
||||
exclude = .git, __pycache__, build, dist, docs, tools, venv
|
||||
extend-ignore = E203, E722
|
||||
extend-select = B902, B904
|
||||
ignore =
|
||||
# required for pretty matrix formatting: multiple spaces after `,` and `[`
|
||||
E201, E241, W504,
|
||||
# camelcase 'cPickle' imported as lowercase 'pickle'
|
||||
N813
|
||||
# Black conflict
|
||||
W503, E203
|
||||
|
||||
|
||||
[tool:pytest]
|
||||
timeout = 300
|
||||
|
|
|
@ -1,50 +1,56 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Setup script for PyPI; use CMakeFile.txt to build extension modules
|
||||
|
||||
import contextlib
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Dict, Iterator, List, Union
|
||||
import tempfile
|
||||
|
||||
import setuptools.command.sdist
|
||||
|
||||
DIR = Path(__file__).parent.absolute()
|
||||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
VERSION_REGEX = re.compile(
|
||||
r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
|
||||
)
|
||||
VERSION_FILE = Path("pybind11/_version.py")
|
||||
COMMON_FILE = Path("include/pybind11/detail/common.h")
|
||||
|
||||
|
||||
def build_expected_version_hex(matches: Dict[str, str]) -> str:
|
||||
def build_expected_version_hex(matches):
|
||||
patch_level_serial = matches["PATCH"]
|
||||
serial = None
|
||||
major = int(matches["MAJOR"])
|
||||
minor = int(matches["MINOR"])
|
||||
flds = patch_level_serial.split(".")
|
||||
if flds:
|
||||
patch = int(flds[0])
|
||||
if len(flds) == 1:
|
||||
level = "0"
|
||||
serial = 0
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
try:
|
||||
major = int(matches["MAJOR"])
|
||||
minor = int(matches["MINOR"])
|
||||
flds = patch_level_serial.split(".")
|
||||
if flds:
|
||||
patch = int(flds[0])
|
||||
level = None
|
||||
if len(flds) == 1:
|
||||
level = "0"
|
||||
serial = 0
|
||||
elif len(flds) == 2:
|
||||
level_serial = flds[1]
|
||||
for level in ("a", "b", "c", "dev"):
|
||||
if level_serial.startswith(level):
|
||||
serial = int(level_serial[len(level) :])
|
||||
break
|
||||
except ValueError:
|
||||
pass
|
||||
if serial is None:
|
||||
msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
|
||||
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
|
||||
raise RuntimeError(msg)
|
||||
version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
|
||||
return f"0x{version_hex_str.upper()}"
|
||||
return (
|
||||
"0x"
|
||||
+ "{:02x}{:02x}{:02x}{}{:x}".format(
|
||||
major, minor, patch, level[:1], serial
|
||||
).upper()
|
||||
)
|
||||
|
||||
|
||||
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
|
||||
|
@ -52,67 +58,82 @@ def build_expected_version_hex(matches: Dict[str, str]) -> str:
|
|||
|
||||
global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False)
|
||||
|
||||
setup_py = Path(
|
||||
"tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
|
||||
)
|
||||
setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
|
||||
extra_cmd = 'cmdclass["sdist"] = SDist\n'
|
||||
|
||||
to_src = (
|
||||
(Path("pyproject.toml"), Path("tools/pyproject.toml")),
|
||||
(Path("setup.py"), setup_py),
|
||||
("pyproject.toml", "tools/pyproject.toml"),
|
||||
("setup.py", setup_py),
|
||||
)
|
||||
|
||||
|
||||
# Read the listed version
|
||||
loc: Dict[str, str] = {}
|
||||
code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
|
||||
with open("pybind11/_version.py") as f:
|
||||
code = compile(f.read(), "pybind11/_version.py", "exec")
|
||||
loc = {}
|
||||
exec(code, loc)
|
||||
version = loc["__version__"]
|
||||
|
||||
# Verify that the version matches the one in C++
|
||||
matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
|
||||
with io.open("include/pybind11/detail/common.h", encoding="utf8") as f:
|
||||
matches = dict(VERSION_REGEX.findall(f.read()))
|
||||
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
|
||||
if version != cpp_version:
|
||||
msg = f"Python version {version} does not match C++ version {cpp_version}!"
|
||||
msg = "Python version {} does not match C++ version {}!".format(
|
||||
version, cpp_version
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
version_hex = matches.get("HEX", "MISSING")
|
||||
exp_version_hex = build_expected_version_hex(matches)
|
||||
if version_hex != exp_version_hex:
|
||||
msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
|
||||
expected_version_hex = build_expected_version_hex(matches)
|
||||
if version_hex != expected_version_hex:
|
||||
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
|
||||
version_hex,
|
||||
expected_version_hex,
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
# TODO: use literals & overload (typing extensions or Python 3.8)
|
||||
def get_and_replace(
|
||||
filename: Path, binary: bool = False, **opts: str
|
||||
) -> Union[bytes, str]:
|
||||
def get_and_replace(filename, binary=False, **opts):
|
||||
with open(filename, "rb" if binary else "r") as f:
|
||||
contents = f.read()
|
||||
# Replacement has to be done on text in Python 3 (both work in Python 2)
|
||||
if binary:
|
||||
contents = filename.read_bytes()
|
||||
return string.Template(contents.decode()).substitute(opts).encode()
|
||||
|
||||
return string.Template(filename.read_text()).substitute(opts)
|
||||
else:
|
||||
return string.Template(contents).substitute(opts)
|
||||
|
||||
|
||||
# Use our input files instead when making the SDist (and anything that depends
|
||||
# on it, like a wheel)
|
||||
class SDist(setuptools.command.sdist.sdist): # type: ignore[misc]
|
||||
def make_release_tree(self, base_dir: str, files: List[str]) -> None:
|
||||
super().make_release_tree(base_dir, files)
|
||||
class SDist(setuptools.command.sdist.sdist):
|
||||
def make_release_tree(self, base_dir, files):
|
||||
setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files)
|
||||
|
||||
for to, src in to_src:
|
||||
txt = get_and_replace(src, binary=True, version=version, extra_cmd="")
|
||||
|
||||
dest = Path(base_dir) / to
|
||||
dest = os.path.join(base_dir, to)
|
||||
|
||||
# This is normally linked, so unlink before writing!
|
||||
dest.unlink()
|
||||
dest.write_bytes(txt) # type: ignore[arg-type]
|
||||
os.unlink(dest)
|
||||
with open(dest, "wb") as f:
|
||||
f.write(txt)
|
||||
|
||||
|
||||
# Backport from Python 3
|
||||
@contextlib.contextmanager
|
||||
def TemporaryDirectory(): # noqa: N802
|
||||
"Prepare a temporary directory, cleanup when done"
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
yield tmpdir
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
# Remove the CMake install directory when done
|
||||
@contextlib.contextmanager
|
||||
def remove_output(*sources: str) -> Iterator[None]:
|
||||
def remove_output(*sources):
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
|
@ -127,7 +148,6 @@ with remove_output("pybind11/include", "pybind11/share"):
|
|||
"-DCMAKE_INSTALL_PREFIX=pybind11",
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DPYBIND11_NOPYTHON=ON",
|
||||
"-Dprefix_for_pc_file=${pcfiledir}/../../",
|
||||
]
|
||||
if "CMAKE_ARGS" in os.environ:
|
||||
fcommand = [
|
||||
|
@ -136,14 +156,9 @@ with remove_output("pybind11/include", "pybind11/share"):
|
|||
if "DCMAKE_INSTALL_PREFIX" not in c
|
||||
]
|
||||
cmd += fcommand
|
||||
subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
|
||||
subprocess.run(
|
||||
["cmake", "--install", tmpdir],
|
||||
check=True,
|
||||
cwd=DIR,
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
)
|
||||
cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
|
||||
subprocess.check_call(cmd, **cmake_opts)
|
||||
subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts)
|
||||
|
||||
txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd)
|
||||
code = compile(txt, setup_py, "exec")
|
||||
|
|
|
@ -128,8 +128,7 @@ set(PYBIND11_TEST_FILES
|
|||
test_custom_type_casters
|
||||
test_custom_type_setup
|
||||
test_docstring_options
|
||||
test_eigen_matrix
|
||||
test_eigen_tensor
|
||||
test_eigen
|
||||
test_enum
|
||||
test_eval
|
||||
test_exceptions
|
||||
|
@ -169,7 +168,7 @@ if(PYBIND11_TEST_OVERRIDE)
|
|||
# This allows the override to be done with extensions, preserving backwards compatibility.
|
||||
foreach(test_name ${TEST_FILES_NO_EXT})
|
||||
if(NOT ${test_name} IN_LIST TEST_OVERRIDE_NO_EXT
|
||||
)# If not in the allowlist, add to be filtered out.
|
||||
)# If not in the whitelist, add to be filtered out.
|
||||
list(APPEND PYBIND11_TEST_FILTER ${test_name})
|
||||
endif()
|
||||
endforeach()
|
||||
|
@ -180,6 +179,11 @@ if(PYBIND11_TEST_FILTER)
|
|||
pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER})
|
||||
endif()
|
||||
|
||||
if(PYTHON_VERSION VERSION_LESS 3.5)
|
||||
pybind11_filter_tests(PYBIND11_TEST_FILES test_async.cpp MESSAGE
|
||||
"Skipping test_async on Python 2")
|
||||
endif()
|
||||
|
||||
# Skip tests for CUDA check:
|
||||
# /pybind11/tests/test_constants_and_functions.cpp(125):
|
||||
# error: incompatible exception specifications
|
||||
|
@ -216,7 +220,6 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_
|
|||
"pybind11_cross_module_tests")
|
||||
|
||||
# And add additional targets for other tests.
|
||||
tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set")
|
||||
tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils")
|
||||
|
||||
set(PYBIND11_EIGEN_REPO
|
||||
|
@ -234,10 +237,7 @@ list(GET PYBIND11_EIGEN_VERSION_AND_HASH 1 PYBIND11_EIGEN_VERSION_HASH)
|
|||
# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but
|
||||
# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed"
|
||||
# skip message).
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_matrix.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I EQUAL -1)
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
endif()
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
# Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake).
|
||||
# Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also
|
||||
|
@ -292,34 +292,13 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
|||
set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING})
|
||||
endif()
|
||||
message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}")
|
||||
|
||||
if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
|
||||
tests_extra_targets("test_eigen_tensor.py" "eigen_tensor_avoid_stl_array")
|
||||
endif()
|
||||
|
||||
else()
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_matrix.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
message(
|
||||
STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN=ON on CMake 3.11+ to download")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Some code doesn't support gcc 4
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
list(FIND PYBIND11_TEST_FILES test_eigen_tensor.cpp PYBIND11_TEST_FILES_EIGEN_I)
|
||||
if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1)
|
||||
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Optional dependency for some tests (boost::variant is only supported with version >= 1.56)
|
||||
find_package(Boost 1.56)
|
||||
|
||||
|
@ -377,7 +356,7 @@ endif()
|
|||
# Compile with compiler warnings turned on
|
||||
function(pybind11_enable_warnings target_name)
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name} PRIVATE /W4 /wd4189)
|
||||
target_compile_options(${target_name} PRIVATE /W4)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS)
|
||||
target_compile_options(
|
||||
${target_name}
|
||||
|
@ -409,6 +388,17 @@ function(pybind11_enable_warnings target_name)
|
|||
-diag-disable 11074,11076)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Needs to be re-added since the ordering requires these to be after the ones above
|
||||
if(CMAKE_CXX_STANDARD
|
||||
AND CMAKE_CXX_COMPILER_ID MATCHES "Clang"
|
||||
AND PYTHON_VERSION VERSION_LESS 3.0)
|
||||
if(CMAKE_CXX_STANDARD LESS 17)
|
||||
target_compile_options(${target_name} PUBLIC -Wno-deprecated-register)
|
||||
else()
|
||||
target_compile_options(${target_name} PUBLIC -Wno-register)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
set(test_targets pybind11_tests)
|
||||
|
|
|
@ -1,49 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""pytest configuration
|
||||
|
||||
Extends output capture as needed by pybind11: ignore constructors, optional unordered lines.
|
||||
Adds docstring and exceptions message sanitizers.
|
||||
Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import difflib
|
||||
import gc
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
|
||||
# Early diagnostic for failed imports
|
||||
try:
|
||||
import pybind11_tests
|
||||
except Exception:
|
||||
# pytest does not show the traceback without this.
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def always_forkserver_on_unix():
|
||||
if os.name == "nt":
|
||||
return
|
||||
|
||||
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
|
||||
# In a nutshell: fork() after starting threads == flakiness in the form of deadlocks.
|
||||
# It is actually a well-known pitfall, unfortunately without guard rails.
|
||||
# "forkserver" is more performant than "spawn" (~9s vs ~13s for tests/test_gil_scoped.py,
|
||||
# visit the issuecomment link above for details).
|
||||
# Windows does not have fork() and the associated pitfall, therefore it is best left
|
||||
# running with defaults.
|
||||
multiprocessing.set_start_method("forkserver")
|
||||
|
||||
import pybind11_tests # noqa: F401
|
||||
|
||||
_unicode_marker = re.compile(r"u(\'[^\']*\')")
|
||||
_long_marker = re.compile(r"([0-9])L")
|
||||
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
|
||||
|
||||
# Avoid collecting Python3 only files
|
||||
collect_ignore = []
|
||||
if env.PY2:
|
||||
collect_ignore.append("test_async.py")
|
||||
|
||||
|
||||
def _strip_and_dedent(s):
|
||||
|
@ -63,7 +45,7 @@ def _make_explanation(a, b):
|
|||
]
|
||||
|
||||
|
||||
class Output:
|
||||
class Output(object):
|
||||
"""Basic output post-processing and comparison"""
|
||||
|
||||
def __init__(self, string):
|
||||
|
@ -101,7 +83,7 @@ class Unordered(Output):
|
|||
return False
|
||||
|
||||
|
||||
class Capture:
|
||||
class Capture(object):
|
||||
def __init__(self, capfd):
|
||||
self.capfd = capfd
|
||||
self.out = ""
|
||||
|
@ -144,7 +126,7 @@ def capture(capsys):
|
|||
return Capture(capsys)
|
||||
|
||||
|
||||
class SanitizedString:
|
||||
class SanitizedString(object):
|
||||
def __init__(self, sanitizer):
|
||||
self.sanitizer = sanitizer
|
||||
self.string = ""
|
||||
|
@ -167,7 +149,9 @@ class SanitizedString:
|
|||
def _sanitize_general(s):
|
||||
s = s.strip()
|
||||
s = s.replace("pybind11_tests.", "m.")
|
||||
s = s.replace("unicode", "str")
|
||||
s = _long_marker.sub(r"\1", s)
|
||||
s = _unicode_marker.sub(r"\1", s)
|
||||
return s
|
||||
|
||||
|
||||
|
@ -222,17 +206,3 @@ def gc_collect():
|
|||
def pytest_configure():
|
||||
pytest.suppress = suppress
|
||||
pytest.gc_collect = gc_collect
|
||||
|
||||
|
||||
def pytest_report_header(config):
|
||||
del config # Unused.
|
||||
assert (
|
||||
pybind11_tests.compiler_info is not None
|
||||
), "Please update pybind11_tests.cpp if this assert fails."
|
||||
return (
|
||||
"C++ Info:"
|
||||
f" {pybind11_tests.compiler_info}"
|
||||
f" {pybind11_tests.cpp_std}"
|
||||
f" {pybind11_tests.PYBIND11_INTERNALS_ID}"
|
||||
f" PYBIND11_SIMPLE_GIL_MANAGEMENT={pybind11_tests.PYBIND11_SIMPLE_GIL_MANAGEMENT}"
|
||||
)
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
#if defined(PYPY_VERSION)
|
||||
PyObject *globals = PyEval_GetGlobals();
|
||||
PyObject *result = PyRun_String("import gc\n"
|
||||
"for i in range(2):\n"
|
||||
"for i in range(2):"
|
||||
" gc.collect()\n",
|
||||
Py_file_input,
|
||||
globals,
|
||||
|
|
|
@ -6,15 +6,9 @@
|
|||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
#if defined(PYBIND11_INTERNALS_VERSION)
|
||||
# undef PYBIND11_INTERNALS_VERSION
|
||||
#endif
|
||||
#define PYBIND11_INTERNALS_VERSION 21814642 // Ensure this module has its own `internals` instance.
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a
|
||||
// PYBIND11_MODULE. The purpose is to test that such a DSO can create a
|
||||
|
@ -27,82 +21,44 @@
|
|||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
void gil_acquire() { py::gil_scoped_acquire gil; }
|
||||
|
||||
std::string gil_multi_acquire_release(unsigned bits) {
|
||||
if ((bits & 0x1u) != 0u) {
|
||||
py::gil_scoped_acquire gil;
|
||||
}
|
||||
if ((bits & 0x2u) != 0u) {
|
||||
py::gil_scoped_release gil;
|
||||
}
|
||||
if ((bits & 0x4u) != 0u) {
|
||||
py::gil_scoped_acquire gil;
|
||||
}
|
||||
if ((bits & 0x8u) != 0u) {
|
||||
py::gil_scoped_release gil;
|
||||
}
|
||||
return PYBIND11_INTERNALS_ID;
|
||||
}
|
||||
|
||||
struct CustomAutoGIL {
|
||||
CustomAutoGIL() : gstate(PyGILState_Ensure()) {}
|
||||
~CustomAutoGIL() { PyGILState_Release(gstate); }
|
||||
|
||||
PyGILState_STATE gstate;
|
||||
};
|
||||
struct CustomAutoNoGIL {
|
||||
CustomAutoNoGIL() : save(PyEval_SaveThread()) {}
|
||||
~CustomAutoNoGIL() { PyEval_RestoreThread(save); }
|
||||
|
||||
PyThreadState *save;
|
||||
};
|
||||
|
||||
template <typename Acquire, typename Release>
|
||||
void gil_acquire_inner() {
|
||||
Acquire acquire_outer;
|
||||
Acquire acquire_inner;
|
||||
Release release;
|
||||
}
|
||||
|
||||
template <typename Acquire, typename Release>
|
||||
void gil_acquire_nested() {
|
||||
Acquire acquire_outer;
|
||||
Acquire acquire_inner;
|
||||
Release release;
|
||||
auto thread = std::thread(&gil_acquire_inner<Acquire, Release>);
|
||||
thread.join();
|
||||
}
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_gil_utils";
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
struct PyModuleDef moduledef
|
||||
= {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
|
||||
#else
|
||||
PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
#define ADD_FUNCTION(Name, ...) \
|
||||
PyModule_AddObject(m, Name, PyLong_FromVoidPtr(reinterpret_cast<void *>(&__VA_ARGS__)));
|
||||
extern "C" PYBIND11_EXPORT
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyObject *
|
||||
PyInit_cross_module_gil_utils()
|
||||
#else
|
||||
void
|
||||
initcross_module_gil_utils()
|
||||
#endif
|
||||
{
|
||||
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
|
||||
PyObject *m =
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyModule_Create(&moduledef);
|
||||
#else
|
||||
Py_InitModule(kModuleName, module_methods);
|
||||
#endif
|
||||
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
|
||||
if (m != nullptr) {
|
||||
if (m != NULL) {
|
||||
static_assert(sizeof(&gil_acquire) == sizeof(void *),
|
||||
"Function pointer must have the same size as void*");
|
||||
ADD_FUNCTION("gil_acquire_funcaddr", gil_acquire)
|
||||
ADD_FUNCTION("gil_multi_acquire_release_funcaddr", gil_multi_acquire_release)
|
||||
ADD_FUNCTION("gil_acquire_inner_custom_funcaddr",
|
||||
gil_acquire_inner<CustomAutoGIL, CustomAutoNoGIL>)
|
||||
ADD_FUNCTION("gil_acquire_nested_custom_funcaddr",
|
||||
gil_acquire_nested<CustomAutoGIL, CustomAutoNoGIL>)
|
||||
ADD_FUNCTION("gil_acquire_inner_pybind11_funcaddr",
|
||||
gil_acquire_inner<py::gil_scoped_acquire, py::gil_scoped_release>)
|
||||
ADD_FUNCTION("gil_acquire_nested_pybind11_funcaddr",
|
||||
gil_acquire_nested<py::gil_scoped_acquire, py::gil_scoped_release>)
|
||||
PyModule_AddObject(
|
||||
m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2022 Google LLC
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
// This file mimics a DSO that makes pybind11 calls but does not define a PYBIND11_MODULE,
|
||||
// so that the first call of cross_module_error_already_set() triggers the first call of
|
||||
// pybind11::detail::get_internals().
|
||||
|
||||
namespace {
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
void interleaved_error_already_set() {
|
||||
PyErr_SetString(PyExc_RuntimeError, "1st error.");
|
||||
try {
|
||||
throw py::error_already_set();
|
||||
} catch (const py::error_already_set &) {
|
||||
// The 2nd error could be conditional in a real application.
|
||||
PyErr_SetString(PyExc_RuntimeError, "2nd error.");
|
||||
} // Here the 1st error is destroyed before the 2nd error is fetched.
|
||||
// The error_already_set dtor triggers a pybind11::detail::get_internals()
|
||||
// call via pybind11::gil_scoped_acquire.
|
||||
if (PyErr_Occurred()) {
|
||||
throw py::error_already_set();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr char kModuleName[] = "cross_module_interleaved_error_already_set";
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT, kModuleName, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_interleaved_error_already_set() {
|
||||
PyObject *m = PyModule_Create(&moduledef);
|
||||
if (m != nullptr) {
|
||||
static_assert(sizeof(&interleaved_error_already_set) == sizeof(void *),
|
||||
"Function pointer must have the same size as void *");
|
||||
PyModule_AddObject(
|
||||
m,
|
||||
"funcaddr",
|
||||
PyLong_FromVoidPtr(reinterpret_cast<void *>(&interleaved_error_already_set)));
|
||||
}
|
||||
return m;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
tests/eigen_tensor.cpp -- automatic conversion of Eigen Tensor
|
||||
|
||||
All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef EIGEN_AVOID_STL_ARRAY
|
||||
# define EIGEN_AVOID_STL_ARRAY
|
||||
#endif
|
||||
|
||||
#include "test_eigen_tensor.inl"
|
||||
|
||||
PYBIND11_MODULE(eigen_tensor_avoid_stl_array, m) { eigen_tensor_test::test_module(m); }
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import platform
|
||||
import sys
|
||||
|
||||
|
@ -10,6 +11,10 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
|
|||
CPYTHON = platform.python_implementation() == "CPython"
|
||||
PYPY = platform.python_implementation() == "PyPy"
|
||||
|
||||
PY2 = sys.version_info.major == 2
|
||||
|
||||
PY = sys.version_info
|
||||
|
||||
|
||||
def deprecated_call():
|
||||
"""
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import contextlib
|
||||
import os
|
||||
import string
|
||||
|
@ -12,16 +13,6 @@ import zipfile
|
|||
DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
|
||||
|
||||
PKGCONFIG = """\
|
||||
prefix=${{pcfiledir}}/../../
|
||||
includedir=${{prefix}}/include
|
||||
|
||||
Name: pybind11
|
||||
Description: Seamless operability between C++11 and Python
|
||||
Version: {VERSION}
|
||||
Cflags: -I${{includedir}}
|
||||
"""
|
||||
|
||||
|
||||
main_headers = {
|
||||
"include/pybind11/attr.h",
|
||||
|
@ -55,11 +46,6 @@ detail_headers = {
|
|||
"include/pybind11/detail/typeid.h",
|
||||
}
|
||||
|
||||
eigen_headers = {
|
||||
"include/pybind11/eigen/matrix.h",
|
||||
"include/pybind11/eigen/tensor.h",
|
||||
}
|
||||
|
||||
stl_headers = {
|
||||
"include/pybind11/stl/filesystem.h",
|
||||
}
|
||||
|
@ -74,21 +60,19 @@ cmake_files = {
|
|||
"share/cmake/pybind11/pybind11Tools.cmake",
|
||||
}
|
||||
|
||||
pkgconfig_files = {
|
||||
"share/pkgconfig/pybind11.pc",
|
||||
}
|
||||
|
||||
py_files = {
|
||||
"__init__.py",
|
||||
"__main__.py",
|
||||
"_version.py",
|
||||
"_version.pyi",
|
||||
"commands.py",
|
||||
"py.typed",
|
||||
"setup_helpers.py",
|
||||
"setup_helpers.pyi",
|
||||
}
|
||||
|
||||
headers = main_headers | detail_headers | eigen_headers | stl_headers
|
||||
src_files = headers | cmake_files | pkgconfig_files
|
||||
headers = main_headers | detail_headers | stl_headers
|
||||
src_files = headers | cmake_files
|
||||
all_files = src_files | py_files
|
||||
|
||||
|
||||
|
@ -97,12 +81,10 @@ sdist_files = {
|
|||
"pybind11/include",
|
||||
"pybind11/include/pybind11",
|
||||
"pybind11/include/pybind11/detail",
|
||||
"pybind11/include/pybind11/eigen",
|
||||
"pybind11/include/pybind11/stl",
|
||||
"pybind11/share",
|
||||
"pybind11/share/cmake",
|
||||
"pybind11/share/cmake/pybind11",
|
||||
"pybind11/share/pkgconfig",
|
||||
"pyproject.toml",
|
||||
"setup.cfg",
|
||||
"setup.py",
|
||||
|
@ -122,56 +104,62 @@ local_sdist_files = {
|
|||
}
|
||||
|
||||
|
||||
def read_tz_file(tar: tarfile.TarFile, name: str) -> bytes:
|
||||
start = tar.getnames()[0] + "/"
|
||||
inner_file = tar.extractfile(tar.getmember(f"{start}{name}"))
|
||||
assert inner_file
|
||||
with contextlib.closing(inner_file) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def normalize_line_endings(value: bytes) -> bytes:
|
||||
return value.replace(os.linesep.encode("utf-8"), b"\n")
|
||||
|
||||
|
||||
def test_build_sdist(monkeypatch, tmpdir):
|
||||
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "build", "--sdist", f"--outdir={tmpdir}"], check=True
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"setup.py",
|
||||
"sdist",
|
||||
"--formats=tar",
|
||||
"--dist-dir",
|
||||
str(tmpdir),
|
||||
]
|
||||
)
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist,) = tmpdir.visit("*.tar.gz")
|
||||
(sdist,) = tmpdir.visit("*.tar")
|
||||
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
with tarfile.open(str(sdist)) as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
version = start[9:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
|
||||
setup_py = read_tz_file(tar, "setup.py")
|
||||
pyproject_toml = read_tz_file(tar, "pyproject.toml")
|
||||
pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc")
|
||||
cmake_cfg = read_tz_file(
|
||||
tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake"
|
||||
)
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))
|
||||
) as f:
|
||||
setup_py = f.read()
|
||||
|
||||
assert (
|
||||
'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")'
|
||||
in cmake_cfg.decode("utf-8")
|
||||
)
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "pyproject.toml"))
|
||||
) as f:
|
||||
pyproject_toml = f.read()
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
with contextlib.closing(
|
||||
tar.extractfile(
|
||||
tar.getmember(
|
||||
start + "pybind11/share/cmake/pybind11/pybind11Config.cmake"
|
||||
)
|
||||
)
|
||||
) as f:
|
||||
contents = f.read().decode("utf8")
|
||||
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
|
||||
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files |= sdist_files
|
||||
files |= {f"pybind11{n}" for n in local_sdist_files}
|
||||
files |= {"pybind11{}".format(n) for n in local_sdist_files}
|
||||
files.add("pybind11.egg-info/entry_points.txt")
|
||||
files.add("pybind11.egg-info/requires.txt")
|
||||
assert simpler == files
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f:
|
||||
contents = (
|
||||
string.Template(f.read().decode("utf-8"))
|
||||
string.Template(f.read().decode())
|
||||
.substitute(version=version, extra_cmd="")
|
||||
.encode("utf-8")
|
||||
.encode()
|
||||
)
|
||||
assert setup_py == contents
|
||||
|
||||
|
@ -179,47 +167,52 @@ def test_build_sdist(monkeypatch, tmpdir):
|
|||
contents = f.read()
|
||||
assert pyproject_toml == contents
|
||||
|
||||
simple_version = ".".join(version.split(".")[:3])
|
||||
pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8")
|
||||
assert normalize_line_endings(pkgconfig) == pkgconfig_expected
|
||||
|
||||
|
||||
def test_build_global_dist(monkeypatch, tmpdir):
|
||||
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir)], check=True
|
||||
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
sys.executable,
|
||||
"setup.py",
|
||||
"sdist",
|
||||
"--formats=tar",
|
||||
"--dist-dir",
|
||||
str(tmpdir),
|
||||
]
|
||||
)
|
||||
if hasattr(out, "decode"):
|
||||
out = out.decode()
|
||||
|
||||
(sdist,) = tmpdir.visit("*.tar.gz")
|
||||
(sdist,) = tmpdir.visit("*.tar")
|
||||
|
||||
with tarfile.open(str(sdist), "r:gz") as tar:
|
||||
with tarfile.open(str(sdist)) as tar:
|
||||
start = tar.getnames()[0] + "/"
|
||||
version = start[16:-1]
|
||||
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
|
||||
|
||||
setup_py = read_tz_file(tar, "setup.py")
|
||||
pyproject_toml = read_tz_file(tar, "pyproject.toml")
|
||||
pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc")
|
||||
cmake_cfg = read_tz_file(
|
||||
tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake"
|
||||
)
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "setup.py"))
|
||||
) as f:
|
||||
setup_py = f.read()
|
||||
|
||||
assert (
|
||||
'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")'
|
||||
in cmake_cfg.decode("utf-8")
|
||||
)
|
||||
with contextlib.closing(
|
||||
tar.extractfile(tar.getmember(start + "pyproject.toml"))
|
||||
) as f:
|
||||
pyproject_toml = f.read()
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files |= sdist_files
|
||||
files |= {f"pybind11_global{n}" for n in local_sdist_files}
|
||||
files |= {"pybind11_global{}".format(n) for n in local_sdist_files}
|
||||
assert simpler == files
|
||||
|
||||
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
|
||||
contents = (
|
||||
string.Template(f.read().decode())
|
||||
.substitute(version=version, extra_cmd="")
|
||||
.encode("utf-8")
|
||||
.encode()
|
||||
)
|
||||
assert setup_py == contents
|
||||
|
||||
|
@ -227,21 +220,17 @@ def test_build_global_dist(monkeypatch, tmpdir):
|
|||
contents = f.read()
|
||||
assert pyproject_toml == contents
|
||||
|
||||
simple_version = ".".join(version.split(".")[:3])
|
||||
pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8")
|
||||
assert normalize_line_endings(pkgconfig) == pkgconfig_expected
|
||||
|
||||
|
||||
def tests_build_wheel(monkeypatch, tmpdir):
|
||||
monkeypatch.chdir(MAIN_DIR)
|
||||
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True
|
||||
subprocess.check_output(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)]
|
||||
)
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
||||
files = {f"pybind11/{n}" for n in all_files}
|
||||
files = {"pybind11/{}".format(n) for n in all_files}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
|
@ -255,7 +244,9 @@ def tests_build_wheel(monkeypatch, tmpdir):
|
|||
names = z.namelist()
|
||||
|
||||
trimmed = {n for n in names if "dist-info" not in n}
|
||||
trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n}
|
||||
trimmed |= {
|
||||
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
|
||||
}
|
||||
assert files == trimmed
|
||||
|
||||
|
||||
|
@ -263,14 +254,14 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
|
|||
monkeypatch.chdir(MAIN_DIR)
|
||||
monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
|
||||
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True
|
||||
subprocess.check_output(
|
||||
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)]
|
||||
)
|
||||
|
||||
(wheel,) = tmpdir.visit("*.whl")
|
||||
|
||||
files = {f"data/data/{n}" for n in src_files}
|
||||
files |= {f"data/headers/{n[8:]}" for n in headers}
|
||||
files = {"data/data/{}".format(n) for n in src_files}
|
||||
files |= {"data/headers/{}".format(n[8:]) for n in headers}
|
||||
files |= {
|
||||
"dist-info/LICENSE",
|
||||
"dist-info/METADATA",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
@ -18,7 +19,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
|||
|
||||
(tmpdir / "setup.py").write_text(
|
||||
dedent(
|
||||
f"""\
|
||||
u"""\
|
||||
import sys
|
||||
sys.path.append({MAIN_DIR!r})
|
||||
|
||||
|
@ -51,13 +52,13 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
|||
ext_modules=ext_modules,
|
||||
)
|
||||
"""
|
||||
),
|
||||
).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel),
|
||||
encoding="ascii",
|
||||
)
|
||||
|
||||
(tmpdir / "main.cpp").write_text(
|
||||
dedent(
|
||||
"""\
|
||||
u"""\
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
int f(int x) {
|
||||
|
@ -95,7 +96,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
|
|||
|
||||
(tmpdir / "test.py").write_text(
|
||||
dedent(
|
||||
"""\
|
||||
u"""\
|
||||
import simple_setup
|
||||
assert simple_setup.f(3) == 9
|
||||
"""
|
||||
|
@ -120,11 +121,10 @@ def test_intree_extensions(monkeypatch, tmpdir):
|
|||
subdir.ensure_dir()
|
||||
src = subdir / "ext.cpp"
|
||||
src.ensure()
|
||||
relpath = src.relto(tmpdir)
|
||||
(ext,) = intree_extensions([relpath])
|
||||
(ext,) = intree_extensions([src.relto(tmpdir)])
|
||||
assert ext.name == "ext"
|
||||
subdir.ensure("__init__.py")
|
||||
(ext,) = intree_extensions([relpath])
|
||||
(ext,) = intree_extensions([src.relto(tmpdir)])
|
||||
assert ext.name == "dir.ext"
|
||||
|
||||
|
||||
|
|
|
@ -62,46 +62,15 @@ void bind_ConstructorStats(py::module_ &m) {
|
|||
});
|
||||
}
|
||||
|
||||
const char *cpp_std() {
|
||||
return
|
||||
#if defined(PYBIND11_CPP20)
|
||||
"C++20";
|
||||
#elif defined(PYBIND11_CPP17)
|
||||
"C++17";
|
||||
#elif defined(PYBIND11_CPP14)
|
||||
"C++14";
|
||||
#else
|
||||
"C++11";
|
||||
#endif
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(pybind11_tests, m) {
|
||||
m.doc() = "pybind11 test module";
|
||||
|
||||
// Intentionally kept minimal to not create a maintenance chore
|
||||
// ("just enough" to be conclusive).
|
||||
#if defined(_MSC_FULL_VER)
|
||||
m.attr("compiler_info") = "MSVC " PYBIND11_TOSTRING(_MSC_FULL_VER);
|
||||
#elif defined(__VERSION__)
|
||||
m.attr("compiler_info") = __VERSION__;
|
||||
#else
|
||||
m.attr("compiler_info") = py::none();
|
||||
#endif
|
||||
m.attr("cpp_std") = cpp_std();
|
||||
m.attr("PYBIND11_INTERNALS_ID") = PYBIND11_INTERNALS_ID;
|
||||
m.attr("PYBIND11_SIMPLE_GIL_MANAGEMENT") =
|
||||
#if defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
bind_ConstructorStats(m);
|
||||
|
||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
m.attr("detailed_error_messages_enabled") = true;
|
||||
#if !defined(NDEBUG)
|
||||
m.attr("debug_enabled") = true;
|
||||
#else
|
||||
m.attr("detailed_error_messages_enabled") = false;
|
||||
m.attr("debug_enabled") = false;
|
||||
#endif
|
||||
|
||||
py::class_<UserType>(m, "UserType", "A `py::class_` type for testing")
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
#include <pybind11/eval.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
// We get some really long type names here which causes MSVC 2015 to emit warnings
|
||||
# pragma warning( \
|
||||
disable : 4503) // NOLINT: warning C4503: decorated name length exceeded, name was truncated
|
||||
#endif
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11::literals;
|
||||
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
[pytest]
|
||||
minversion = 3.10
|
||||
minversion = 3.1
|
||||
norecursedirs = test_* extra_*
|
||||
xfail_strict = True
|
||||
addopts =
|
||||
# show summary of tests
|
||||
-ra
|
||||
# show summary of skipped tests
|
||||
-rs
|
||||
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
|
||||
--capture=sys
|
||||
# Show local info when a failure occurs
|
||||
--showlocals
|
||||
log_cli_level = info
|
||||
filterwarnings =
|
||||
# make warnings into errors but ignore certain third-party extension issues
|
||||
error
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
build==0.8.0
|
||||
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy"
|
||||
numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
|
||||
numpy==1.20.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
|
||||
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
|
||||
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
|
||||
pytest==7.0.0
|
||||
numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11"
|
||||
py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
|
||||
pytest==4.6.9; python_version<"3.5"
|
||||
pytest==6.1.2; python_version=="3.5"
|
||||
pytest==6.2.4; python_version>="3.6"
|
||||
pytest-timeout
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
|
||||
scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10"
|
||||
scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6"
|
||||
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
asyncio = pytest.importorskip("asyncio")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import ctypes
|
||||
import io
|
||||
import struct
|
||||
|
@ -92,16 +93,16 @@ def test_pointer_to_member_fn():
|
|||
def test_readonly_buffer():
|
||||
buf = m.BufferReadOnly(0x64)
|
||||
view = memoryview(buf)
|
||||
assert view[0] == 0x64
|
||||
assert view[0] == b"d" if env.PY2 else 0x64
|
||||
assert view.readonly
|
||||
with pytest.raises(TypeError):
|
||||
view[0] = 0
|
||||
view[0] = b"\0" if env.PY2 else 0
|
||||
|
||||
|
||||
def test_selective_readonly_buffer():
|
||||
buf = m.BufferReadOnlySelect()
|
||||
|
||||
memoryview(buf)[0] = 0x64
|
||||
memoryview(buf)[0] = b"d" if env.PY2 else 0x64
|
||||
assert buf.value == 0x64
|
||||
|
||||
io.BytesIO(b"A").readinto(buf)
|
||||
|
@ -109,7 +110,7 @@ def test_selective_readonly_buffer():
|
|||
|
||||
buf.readonly = True
|
||||
with pytest.raises(TypeError):
|
||||
memoryview(buf)[0] = 0
|
||||
memoryview(buf)[0] = b"\0" if env.PY2 else 0
|
||||
with pytest.raises(TypeError):
|
||||
io.BytesIO(b"1").readinto(buf)
|
||||
|
||||
|
@ -144,6 +145,9 @@ def test_ctypes_array_2d():
|
|||
assert not info.readonly
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
"env.PYPY and env.PY2", reason="PyPy2 bytes buffer not reported as readonly"
|
||||
)
|
||||
def test_ctypes_from_buffer():
|
||||
test_pystr = b"0123456789"
|
||||
for pyarray in (test_pystr, bytearray(test_pystr)):
|
||||
|
|
|
@ -73,9 +73,6 @@ PYBIND11_NAMESPACE_END(detail)
|
|||
PYBIND11_NAMESPACE_END(pybind11)
|
||||
|
||||
TEST_SUBMODULE(builtin_casters, m) {
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
// test_simple_string
|
||||
m.def("string_roundtrip", [](const char *s) { return s; });
|
||||
|
||||
|
@ -89,7 +86,7 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
std::wstring wstr;
|
||||
wstr.push_back(0x61); // a
|
||||
wstr.push_back(0x2e18); // ⸘
|
||||
if (sizeof(wchar_t) == 2) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
wstr.push_back(mathbfA16_1);
|
||||
wstr.push_back(mathbfA16_2);
|
||||
} // 𝐀, utf16
|
||||
|
@ -113,14 +110,16 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
"def");
|
||||
});
|
||||
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); });
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger
|
||||
// UnicodeDecodeError
|
||||
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
|
||||
if (sizeof(wchar_t) == 2) {
|
||||
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) {
|
||||
m.def("bad_wchar_string", [=]() {
|
||||
return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
|
||||
});
|
||||
}
|
||||
#endif
|
||||
m.def("u8_Z", []() -> char { return 'Z'; });
|
||||
m.def("u8_eacute", []() -> char { return '\xe9'; });
|
||||
m.def("u16_ibang", [=]() -> char16_t { return ib16; });
|
||||
|
@ -199,10 +198,12 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
[]() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
|
||||
m.def("string_view_from_bytes",
|
||||
[](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
|
||||
# if PY_MAJOR_VERSION >= 3
|
||||
m.def("string_view_memoryview", []() {
|
||||
static constexpr auto val = "Have some \360\237\216\202"sv;
|
||||
return py::memoryview::from_memory(val);
|
||||
});
|
||||
# endif
|
||||
|
||||
# ifdef PYBIND11_HAS_U8STRING
|
||||
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });
|
||||
|
@ -269,14 +270,8 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
});
|
||||
m.def("lvalue_nested", []() -> const decltype(lvnested) & { return lvnested; });
|
||||
|
||||
m.def(
|
||||
"int_string_pair",
|
||||
[]() {
|
||||
// Using no-destructor idiom to side-step warnings from overzealous compilers.
|
||||
static auto *int_string_pair = new std::pair<int, std::string>{2, "items"};
|
||||
return int_string_pair;
|
||||
},
|
||||
py::return_value_policy::reference);
|
||||
static std::pair<int, std::string> int_string_pair{2, "items"};
|
||||
m.def("int_string_pair", []() { return &int_string_pair; });
|
||||
|
||||
// test_builtins_cast_return_none
|
||||
m.def("return_none_string", []() -> std::string * { return nullptr; });
|
||||
|
@ -387,6 +382,4 @@ TEST_SUBMODULE(builtin_casters, m) {
|
|||
m.def("takes_const_ref", [](const ConstRefCasted &x) { return x.tag; });
|
||||
m.def("takes_const_ref_wrap",
|
||||
[](std::reference_wrapper<const ConstRefCasted> x) { return x.get().tag; });
|
||||
|
||||
PYBIND11_WARNING_POP
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import sys
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env
|
||||
|
@ -13,12 +12,12 @@ def test_simple_string():
|
|||
|
||||
def test_unicode_conversion():
|
||||
"""Tests unicode conversion and error reporting."""
|
||||
assert m.good_utf8_string() == "Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf16_string() == "b‽🎂𝐀z"
|
||||
assert m.good_utf32_string() == "a𝐀🎂‽z"
|
||||
assert m.good_wchar_string() == "a⸘𝐀z"
|
||||
assert m.good_utf8_string() == u"Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf16_string() == u"b‽🎂𝐀z"
|
||||
assert m.good_utf32_string() == u"a𝐀🎂‽z"
|
||||
assert m.good_wchar_string() == u"a⸘𝐀z"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.good_utf8_u8string() == "Say utf8‽ 🎂 𝐀"
|
||||
assert m.good_utf8_u8string() == u"Say utf8‽ 🎂 𝐀"
|
||||
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf8_string()
|
||||
|
@ -26,7 +25,7 @@ def test_unicode_conversion():
|
|||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf16_string()
|
||||
|
||||
# These are provided only if they actually fail (they don't when 32-bit)
|
||||
# These are provided only if they actually fail (they don't when 32-bit and under Python 2.7)
|
||||
if hasattr(m, "bad_utf32_string"):
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
m.bad_utf32_string()
|
||||
|
@ -38,10 +37,10 @@ def test_unicode_conversion():
|
|||
m.bad_utf8_u8string()
|
||||
|
||||
assert m.u8_Z() == "Z"
|
||||
assert m.u8_eacute() == "é"
|
||||
assert m.u16_ibang() == "‽"
|
||||
assert m.u32_mathbfA() == "𝐀"
|
||||
assert m.wchar_heart() == "♥"
|
||||
assert m.u8_eacute() == u"é"
|
||||
assert m.u16_ibang() == u"‽"
|
||||
assert m.u32_mathbfA() == u"𝐀"
|
||||
assert m.wchar_heart() == u"♥"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.u8_char8_Z() == "Z"
|
||||
|
||||
|
@ -50,72 +49,72 @@ def test_single_char_arguments():
|
|||
"""Tests failures for passing invalid inputs to char-accepting functions"""
|
||||
|
||||
def toobig_message(r):
|
||||
return f"Character code point not in range({r:#x})"
|
||||
return "Character code point not in range({:#x})".format(r)
|
||||
|
||||
toolong_message = "Expected a character, but multi-character string found"
|
||||
|
||||
assert m.ord_char("a") == 0x61 # simple ASCII
|
||||
assert m.ord_char_lv("b") == 0x62
|
||||
assert m.ord_char(u"a") == 0x61 # simple ASCII
|
||||
assert m.ord_char_lv(u"b") == 0x62
|
||||
assert (
|
||||
m.ord_char("é") == 0xE9
|
||||
m.ord_char(u"é") == 0xE9
|
||||
) # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char("Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert m.ord_char(u"Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert str(excinfo.value) == toobig_message(0x100)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char("ab")
|
||||
assert m.ord_char(u"ab")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_char16("a") == 0x61
|
||||
assert m.ord_char16("é") == 0xE9
|
||||
assert m.ord_char16_lv("ê") == 0xEA
|
||||
assert m.ord_char16("Ā") == 0x100
|
||||
assert m.ord_char16("‽") == 0x203D
|
||||
assert m.ord_char16("♥") == 0x2665
|
||||
assert m.ord_char16_lv("♡") == 0x2661
|
||||
assert m.ord_char16(u"a") == 0x61
|
||||
assert m.ord_char16(u"é") == 0xE9
|
||||
assert m.ord_char16_lv(u"ê") == 0xEA
|
||||
assert m.ord_char16(u"Ā") == 0x100
|
||||
assert m.ord_char16(u"‽") == 0x203D
|
||||
assert m.ord_char16(u"♥") == 0x2665
|
||||
assert m.ord_char16_lv(u"♡") == 0x2661
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16("🎂") == 0x1F382 # requires surrogate pair
|
||||
assert m.ord_char16(u"🎂") == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char16("aa")
|
||||
assert m.ord_char16(u"aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_char32("a") == 0x61
|
||||
assert m.ord_char32("é") == 0xE9
|
||||
assert m.ord_char32("Ā") == 0x100
|
||||
assert m.ord_char32("‽") == 0x203D
|
||||
assert m.ord_char32("♥") == 0x2665
|
||||
assert m.ord_char32("🎂") == 0x1F382
|
||||
assert m.ord_char32(u"a") == 0x61
|
||||
assert m.ord_char32(u"é") == 0xE9
|
||||
assert m.ord_char32(u"Ā") == 0x100
|
||||
assert m.ord_char32(u"‽") == 0x203D
|
||||
assert m.ord_char32(u"♥") == 0x2665
|
||||
assert m.ord_char32(u"🎂") == 0x1F382
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char32("aa")
|
||||
assert m.ord_char32(u"aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
assert m.ord_wchar("a") == 0x61
|
||||
assert m.ord_wchar("é") == 0xE9
|
||||
assert m.ord_wchar("Ā") == 0x100
|
||||
assert m.ord_wchar("‽") == 0x203D
|
||||
assert m.ord_wchar("♥") == 0x2665
|
||||
assert m.ord_wchar(u"a") == 0x61
|
||||
assert m.ord_wchar(u"é") == 0xE9
|
||||
assert m.ord_wchar(u"Ā") == 0x100
|
||||
assert m.ord_wchar(u"‽") == 0x203D
|
||||
assert m.ord_wchar(u"♥") == 0x2665
|
||||
if m.wchar_size == 2:
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_wchar("🎂") == 0x1F382 # requires surrogate pair
|
||||
assert m.ord_wchar(u"🎂") == 0x1F382 # requires surrogate pair
|
||||
assert str(excinfo.value) == toobig_message(0x10000)
|
||||
else:
|
||||
assert m.ord_wchar("🎂") == 0x1F382
|
||||
assert m.ord_wchar(u"🎂") == 0x1F382
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_wchar("aa")
|
||||
assert m.ord_wchar(u"aa")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.ord_char8("a") == 0x61 # simple ASCII
|
||||
assert m.ord_char8_lv("b") == 0x62
|
||||
assert m.ord_char8(u"a") == 0x61 # simple ASCII
|
||||
assert m.ord_char8_lv(u"b") == 0x62
|
||||
assert (
|
||||
m.ord_char8("é") == 0xE9
|
||||
m.ord_char8(u"é") == 0xE9
|
||||
) # requires 2 bytes in utf-8, but can be stuffed in a char
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char8("Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert m.ord_char8(u"Ā") == 0x100 # requires 2 bytes, doesn't fit in a char
|
||||
assert str(excinfo.value) == toobig_message(0x100)
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
assert m.ord_char8("ab")
|
||||
assert m.ord_char8(u"ab")
|
||||
assert str(excinfo.value) == toolong_message
|
||||
|
||||
|
||||
|
@ -124,22 +123,18 @@ def test_bytes_to_string():
|
|||
one-way: the only way to return bytes to Python is via the pybind11::bytes class."""
|
||||
# Issue #816
|
||||
|
||||
assert m.strlen(b"hi") == 2
|
||||
assert m.string_length(b"world") == 5
|
||||
assert m.string_length("a\x00b".encode()) == 3
|
||||
assert m.strlen("a\x00b".encode()) == 1 # C-string limitation
|
||||
def to_bytes(s):
|
||||
b = s if env.PY2 else s.encode("utf8")
|
||||
assert isinstance(b, bytes)
|
||||
return b
|
||||
|
||||
assert m.strlen(to_bytes("hi")) == 2
|
||||
assert m.string_length(to_bytes("world")) == 5
|
||||
assert m.string_length(to_bytes("a\x00b")) == 3
|
||||
assert m.strlen(to_bytes("a\x00b")) == 1 # C-string limitation
|
||||
|
||||
# passing in a utf8 encoded string should work
|
||||
assert m.string_length("💩".encode()) == 4
|
||||
|
||||
|
||||
def test_bytearray_to_string():
|
||||
"""Tests the ability to pass bytearray to C++ string-accepting functions"""
|
||||
assert m.string_length(bytearray(b"Hi")) == 2
|
||||
assert m.strlen(bytearray(b"bytearray")) == 9
|
||||
assert m.string_length(bytearray()) == 0
|
||||
assert m.string_length(bytearray("🦜", "utf-8", "strict")) == 4
|
||||
assert m.string_length(bytearray(b"\x80")) == 1
|
||||
assert m.string_length(u"💩".encode("utf8")) == 4
|
||||
|
||||
|
||||
@pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no <string_view>")
|
||||
|
@ -147,26 +142,26 @@ def test_string_view(capture):
|
|||
"""Tests support for C++17 string_view arguments and return values"""
|
||||
assert m.string_view_chars("Hi") == [72, 105]
|
||||
assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82]
|
||||
assert m.string_view16_chars("Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82]
|
||||
assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874]
|
||||
assert m.string_view16_chars(u"Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82]
|
||||
assert m.string_view32_chars(u"Hi 🎂") == [72, 105, 32, 127874]
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_chars("Hi") == [72, 105]
|
||||
assert m.string_view8_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82]
|
||||
assert m.string_view8_chars(u"Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82]
|
||||
|
||||
assert m.string_view_return() == "utf8 secret 🎂"
|
||||
assert m.string_view16_return() == "utf16 secret 🎂"
|
||||
assert m.string_view32_return() == "utf32 secret 🎂"
|
||||
assert m.string_view_return() == u"utf8 secret 🎂"
|
||||
assert m.string_view16_return() == u"utf16 secret 🎂"
|
||||
assert m.string_view32_return() == u"utf32 secret 🎂"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_return() == "utf8 secret 🎂"
|
||||
assert m.string_view8_return() == u"utf8 secret 🎂"
|
||||
|
||||
with capture:
|
||||
m.string_view_print("Hi")
|
||||
m.string_view_print("utf8 🎂")
|
||||
m.string_view16_print("utf16 🎂")
|
||||
m.string_view32_print("utf32 🎂")
|
||||
m.string_view16_print(u"utf16 🎂")
|
||||
m.string_view32_print(u"utf32 🎂")
|
||||
assert (
|
||||
capture
|
||||
== """
|
||||
== u"""
|
||||
Hi 2
|
||||
utf8 🎂 9
|
||||
utf16 🎂 8
|
||||
|
@ -176,10 +171,10 @@ def test_string_view(capture):
|
|||
if hasattr(m, "has_u8string"):
|
||||
with capture:
|
||||
m.string_view8_print("Hi")
|
||||
m.string_view8_print("utf8 🎂")
|
||||
m.string_view8_print(u"utf8 🎂")
|
||||
assert (
|
||||
capture
|
||||
== """
|
||||
== u"""
|
||||
Hi 2
|
||||
utf8 🎂 9
|
||||
"""
|
||||
|
@ -188,11 +183,11 @@ def test_string_view(capture):
|
|||
with capture:
|
||||
m.string_view_print("Hi, ascii")
|
||||
m.string_view_print("Hi, utf8 🎂")
|
||||
m.string_view16_print("Hi, utf16 🎂")
|
||||
m.string_view32_print("Hi, utf32 🎂")
|
||||
m.string_view16_print(u"Hi, utf16 🎂")
|
||||
m.string_view32_print(u"Hi, utf32 🎂")
|
||||
assert (
|
||||
capture
|
||||
== """
|
||||
== u"""
|
||||
Hi, ascii 9
|
||||
Hi, utf8 🎂 13
|
||||
Hi, utf16 🎂 12
|
||||
|
@ -202,21 +197,22 @@ def test_string_view(capture):
|
|||
if hasattr(m, "has_u8string"):
|
||||
with capture:
|
||||
m.string_view8_print("Hi, ascii")
|
||||
m.string_view8_print("Hi, utf8 🎂")
|
||||
m.string_view8_print(u"Hi, utf8 🎂")
|
||||
assert (
|
||||
capture
|
||||
== """
|
||||
== u"""
|
||||
Hi, ascii 9
|
||||
Hi, utf8 🎂 13
|
||||
"""
|
||||
)
|
||||
|
||||
assert m.string_view_bytes() == b"abc \x80\x80 def"
|
||||
assert m.string_view_str() == "abc ‽ def"
|
||||
assert m.string_view_from_bytes("abc ‽ def".encode()) == "abc ‽ def"
|
||||
assert m.string_view_str() == u"abc ‽ def"
|
||||
assert m.string_view_from_bytes(u"abc ‽ def".encode("utf-8")) == u"abc ‽ def"
|
||||
if hasattr(m, "has_u8string"):
|
||||
assert m.string_view8_str() == "abc ‽ def"
|
||||
assert m.string_view_memoryview() == "Have some 🎂".encode()
|
||||
assert m.string_view8_str() == u"abc ‽ def"
|
||||
if not env.PY2:
|
||||
assert m.string_view_memoryview() == "Have some 🎂".encode()
|
||||
|
||||
assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success"
|
||||
assert m.str_from_type_with_both_operator_string_and_string_view() == "success"
|
||||
|
@ -228,8 +224,20 @@ def test_integer_casting():
|
|||
assert m.i64_str(-1) == "-1"
|
||||
assert m.i32_str(2000000000) == "2000000000"
|
||||
assert m.u32_str(2000000000) == "2000000000"
|
||||
assert m.i64_str(-999999999999) == "-999999999999"
|
||||
assert m.u64_str(999999999999) == "999999999999"
|
||||
if env.PY2:
|
||||
assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long'
|
||||
assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long'
|
||||
assert (
|
||||
m.i64_str(long(-999999999999)) # noqa: F821 undefined name 'long'
|
||||
== "-999999999999"
|
||||
)
|
||||
assert (
|
||||
m.u64_str(long(999999999999)) # noqa: F821 undefined name 'long'
|
||||
== "999999999999"
|
||||
)
|
||||
else:
|
||||
assert m.i64_str(-999999999999) == "-999999999999"
|
||||
assert m.u64_str(999999999999) == "999999999999"
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u32_str(-1)
|
||||
|
@ -244,38 +252,46 @@ def test_integer_casting():
|
|||
m.i32_str(3000000000)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
if env.PY2:
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u32_str(long(-1)) # noqa: F821 undefined name 'long'
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.u64_str(long(-1)) # noqa: F821 undefined name 'long'
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_int_convert():
|
||||
class Int:
|
||||
class Int(object):
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
class NotInt:
|
||||
class NotInt(object):
|
||||
pass
|
||||
|
||||
class Float:
|
||||
class Float(object):
|
||||
def __float__(self):
|
||||
return 41.99999
|
||||
|
||||
class Index:
|
||||
class Index(object):
|
||||
def __index__(self):
|
||||
return 42
|
||||
|
||||
class IntAndIndex:
|
||||
class IntAndIndex(object):
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
def __index__(self):
|
||||
return 0
|
||||
|
||||
class RaisingTypeErrorOnIndex:
|
||||
class RaisingTypeErrorOnIndex(object):
|
||||
def __index__(self):
|
||||
raise TypeError
|
||||
|
||||
def __int__(self):
|
||||
return 42
|
||||
|
||||
class RaisingValueErrorOnIndex:
|
||||
class RaisingValueErrorOnIndex(object):
|
||||
def __index__(self):
|
||||
raise ValueError
|
||||
|
||||
|
@ -295,7 +311,7 @@ def test_int_convert():
|
|||
cant_convert(3.14159)
|
||||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
|
||||
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
|
||||
if (3, 8) <= env.PY < (3, 10) and env.CPYTHON:
|
||||
with env.deprecated_call():
|
||||
assert convert(Int()) == 42
|
||||
else:
|
||||
|
@ -332,7 +348,7 @@ def test_numpy_int_convert():
|
|||
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
|
||||
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
|
||||
# https://github.com/pybind/pybind11/issues/3408
|
||||
if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
|
||||
if (3, 8) <= env.PY < (3, 10) and env.CPYTHON:
|
||||
with env.deprecated_call():
|
||||
assert convert(np.float32(3.14159)) == 3
|
||||
else:
|
||||
|
@ -459,7 +475,7 @@ def test_bool_caster():
|
|||
require_implicit(None)
|
||||
assert convert(None) is False
|
||||
|
||||
class A:
|
||||
class A(object):
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
|
@ -469,7 +485,7 @@ def test_bool_caster():
|
|||
def __bool__(self):
|
||||
return self.x
|
||||
|
||||
class B:
|
||||
class B(object):
|
||||
pass
|
||||
|
||||
# Arbitrary objects are not accepted
|
||||
|
@ -499,9 +515,17 @@ def test_numpy_bool():
|
|||
|
||||
|
||||
def test_int_long():
|
||||
"""In Python 2, a C++ int should return a Python int rather than long
|
||||
if possible: longs are not always accepted where ints are used (such
|
||||
as the argument to sys.exit()). A C++ long long is always a Python
|
||||
long."""
|
||||
|
||||
import sys
|
||||
|
||||
must_be_long = type(getattr(sys, "maxint", 1) + 1)
|
||||
assert isinstance(m.int_cast(), int)
|
||||
assert isinstance(m.long_cast(), int)
|
||||
assert isinstance(m.longlong_cast(), int)
|
||||
assert isinstance(m.longlong_cast(), must_be_long)
|
||||
|
||||
|
||||
def test_void_caster_2():
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env # noqa: F401
|
||||
|
|
|
@ -240,41 +240,4 @@ TEST_SUBMODULE(callbacks, m) {
|
|||
f();
|
||||
}
|
||||
});
|
||||
|
||||
auto *custom_def = []() {
|
||||
static PyMethodDef def;
|
||||
def.ml_name = "example_name";
|
||||
def.ml_doc = "Example doc";
|
||||
def.ml_meth = [](PyObject *, PyObject *args) -> PyObject * {
|
||||
if (PyTuple_Size(args) != 1) {
|
||||
throw std::runtime_error("Invalid number of arguments for example_name");
|
||||
}
|
||||
PyObject *first = PyTuple_GetItem(args, 0);
|
||||
if (!PyLong_Check(first)) {
|
||||
throw std::runtime_error("Invalid argument to example_name");
|
||||
}
|
||||
auto result = py::cast(PyLong_AsLong(first) * 9);
|
||||
return result.release().ptr();
|
||||
};
|
||||
def.ml_flags = METH_VARARGS;
|
||||
return &def;
|
||||
}();
|
||||
|
||||
// rec_capsule with name that has the same value (but not pointer) as our internal one
|
||||
// This capsule should be detected by our code as foreign and not inspected as the pointers
|
||||
// shouldn't match
|
||||
constexpr const char *rec_capsule_name
|
||||
= pybind11::detail::internals_function_record_capsule_name;
|
||||
py::capsule rec_capsule(std::malloc(1), [](void *data) { std::free(data); });
|
||||
rec_capsule.set_name(rec_capsule_name);
|
||||
m.add_object("custom_function", PyCFunction_New(custom_def, rec_capsule.ptr()));
|
||||
|
||||
// This test requires a new ABI version to pass
|
||||
#if PYBIND11_INTERNALS_VERSION > 4
|
||||
// rec_capsule with nullptr name
|
||||
py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); });
|
||||
m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr()));
|
||||
#else
|
||||
m.add_object("custom_function2", py::none());
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
from threading import Thread
|
||||
|
||||
|
@ -17,7 +18,7 @@ def test_callbacks():
|
|||
return "func2", a, b, c, d
|
||||
|
||||
def func3(a):
|
||||
return f"func3({a})"
|
||||
return "func3({})".format(a)
|
||||
|
||||
assert m.test_callback1(func1) == "func1"
|
||||
assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
|
||||
|
@ -188,21 +189,14 @@ def test_callback_num_times():
|
|||
if not rep:
|
||||
print()
|
||||
print(
|
||||
f"callback_num_times: {num_millions:d} million / {td:.3f} seconds = {rate:.3f} million / second"
|
||||
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format(
|
||||
num_millions, td, rate
|
||||
)
|
||||
)
|
||||
if len(rates) > 1:
|
||||
print("Min Mean Max")
|
||||
print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}")
|
||||
|
||||
|
||||
def test_custom_func():
|
||||
assert m.custom_function(4) == 36
|
||||
assert m.roundtrip(m.custom_function)(4) == 36
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
m.custom_function2 is None, reason="Current PYBIND11_INTERNALS_VERSION too low"
|
||||
)
|
||||
def test_custom_func2():
|
||||
assert m.custom_function2(3) == 27
|
||||
assert m.roundtrip(m.custom_function2)(3) == 27
|
||||
print(
|
||||
"{:6.3f} {:6.3f} {:6.3f}".format(
|
||||
min(rates), sum(rates) / len(rates), max(rates)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
@ -7,6 +8,7 @@ from pybind11_tests import chrono as m
|
|||
|
||||
|
||||
def test_chrono_system_clock():
|
||||
|
||||
# Get the time from both c++ and datetime
|
||||
date0 = datetime.datetime.today()
|
||||
date1 = m.test_chrono1()
|
||||
|
@ -99,7 +101,7 @@ SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif(
|
|||
)
|
||||
def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
|
||||
if tz is not None:
|
||||
monkeypatch.setenv("TZ", f"/usr/share/zoneinfo/{tz}")
|
||||
monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz))
|
||||
|
||||
# Roundtrip the time
|
||||
datetime2 = m.test_chrono2(time1)
|
||||
|
@ -121,6 +123,7 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
|
|||
|
||||
|
||||
def test_chrono_duration_roundtrip():
|
||||
|
||||
# Get the difference between two times (a timedelta)
|
||||
date1 = datetime.datetime.today()
|
||||
date2 = datetime.datetime.today()
|
||||
|
@ -141,6 +144,7 @@ def test_chrono_duration_roundtrip():
|
|||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence():
|
||||
|
||||
date1 = datetime.datetime.today()
|
||||
date2 = datetime.datetime.today()
|
||||
|
||||
|
@ -151,6 +155,7 @@ def test_chrono_duration_subtraction_equivalence():
|
|||
|
||||
|
||||
def test_chrono_duration_subtraction_equivalence_date():
|
||||
|
||||
date1 = datetime.date.today()
|
||||
date2 = datetime.date.today()
|
||||
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4324)
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable : 4324)
|
||||
// warning C4324: structure was padded due to alignment specifier
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
|
@ -34,29 +36,7 @@ struct NoBraceInitialization {
|
|||
std::vector<int> vec;
|
||||
};
|
||||
|
||||
namespace test_class {
|
||||
namespace pr4220_tripped_over_this { // PR #4227
|
||||
|
||||
template <int>
|
||||
struct SoEmpty {};
|
||||
|
||||
template <typename T>
|
||||
std::string get_msg(const T &) {
|
||||
return "This is really only meant to exercise successful compilation.";
|
||||
}
|
||||
|
||||
using Empty0 = SoEmpty<0x0>;
|
||||
|
||||
void bind_empty0(py::module_ &m) {
|
||||
py::class_<Empty0>(m, "Empty0").def(py::init<>()).def("get_msg", get_msg<Empty0>);
|
||||
}
|
||||
|
||||
} // namespace pr4220_tripped_over_this
|
||||
} // namespace test_class
|
||||
|
||||
TEST_SUBMODULE(class_, m) {
|
||||
m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); });
|
||||
|
||||
// test_instance
|
||||
struct NoConstructor {
|
||||
NoConstructor() = default;
|
||||
|
@ -374,7 +354,13 @@ TEST_SUBMODULE(class_, m) {
|
|||
using ProtectedA::foo;
|
||||
};
|
||||
|
||||
py::class_<ProtectedA>(m, "ProtectedA").def(py::init<>()).def("foo", &PublicistA::foo);
|
||||
py::class_<ProtectedA>(m, "ProtectedA")
|
||||
.def(py::init<>())
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
||||
.def("foo", &PublicistA::foo);
|
||||
#else
|
||||
.def("foo", static_cast<int (ProtectedA::*)() const>(&PublicistA::foo));
|
||||
#endif
|
||||
|
||||
class ProtectedB {
|
||||
public:
|
||||
|
@ -384,8 +370,6 @@ TEST_SUBMODULE(class_, m) {
|
|||
|
||||
protected:
|
||||
virtual int foo() const { return value; }
|
||||
virtual void *void_foo() { return static_cast<void *>(&value); }
|
||||
virtual void *get_self() { return static_cast<void *>(this); }
|
||||
|
||||
private:
|
||||
int value = 42;
|
||||
|
@ -394,8 +378,6 @@ TEST_SUBMODULE(class_, m) {
|
|||
class TrampolineB : public ProtectedB {
|
||||
public:
|
||||
int foo() const override { PYBIND11_OVERRIDE(int, ProtectedB, foo, ); }
|
||||
void *void_foo() override { PYBIND11_OVERRIDE(void *, ProtectedB, void_foo, ); }
|
||||
void *get_self() override { PYBIND11_OVERRIDE(void *, ProtectedB, get_self, ); }
|
||||
};
|
||||
|
||||
class PublicistB : public ProtectedB {
|
||||
|
@ -405,23 +387,15 @@ TEST_SUBMODULE(class_, m) {
|
|||
// (in Debug builds only, tested with icpc (ICC) 2021.1 Beta 20200827)
|
||||
~PublicistB() override{}; // NOLINT(modernize-use-equals-default)
|
||||
using ProtectedB::foo;
|
||||
using ProtectedB::get_self;
|
||||
using ProtectedB::void_foo;
|
||||
};
|
||||
|
||||
m.def("read_foo", [](const void *original) {
|
||||
const int *ptr = reinterpret_cast<const int *>(original);
|
||||
return *ptr;
|
||||
});
|
||||
|
||||
m.def("pointers_equal",
|
||||
[](const void *original, const void *comparison) { return original == comparison; });
|
||||
|
||||
py::class_<ProtectedB, TrampolineB>(m, "ProtectedB")
|
||||
.def(py::init<>())
|
||||
.def("foo", &PublicistB::foo)
|
||||
.def("void_foo", &PublicistB::void_foo)
|
||||
.def("get_self", &PublicistB::get_self);
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1910
|
||||
.def("foo", &PublicistB::foo);
|
||||
#else
|
||||
.def("foo", static_cast<int (ProtectedB::*)() const>(&PublicistB::foo));
|
||||
#endif
|
||||
|
||||
// test_brace_initialization
|
||||
struct BraceInitialization {
|
||||
|
@ -553,8 +527,6 @@ TEST_SUBMODULE(class_, m) {
|
|||
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
|
||||
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
|
||||
});
|
||||
|
||||
test_class::pr4220_tripped_over_this::bind_empty0(m);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import ConstructorStats, UserType
|
||||
from pybind11_tests import class_ as m
|
||||
|
||||
|
||||
def test_obj_class_name():
|
||||
if env.PYPY:
|
||||
expected_name = "UserType"
|
||||
else:
|
||||
expected_name = "pybind11_tests.UserType"
|
||||
assert m.obj_class_name(UserType(1)) == expected_name
|
||||
assert m.obj_class_name(UserType) == expected_name
|
||||
|
||||
|
||||
def test_repr():
|
||||
# In Python 3.3+, repr() accesses __qualname__
|
||||
assert "pybind11_type" in repr(type(UserType))
|
||||
assert "UserType" in repr(UserType)
|
||||
|
||||
|
@ -110,8 +103,8 @@ def test_docstrings(doc):
|
|||
|
||||
|
||||
def test_qualname(doc):
|
||||
"""Tests that a properly qualified name is set in __qualname__ and that
|
||||
generated docstrings properly use it and the module name"""
|
||||
"""Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we
|
||||
backport the attribute) and that generated docstrings properly use it and the module name"""
|
||||
assert m.NestBase.__qualname__ == "NestBase"
|
||||
assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
|
||||
|
||||
|
@ -137,13 +130,13 @@ def test_qualname(doc):
|
|||
doc(m.NestBase.Nested.fn)
|
||||
== """
|
||||
fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
|
||||
"""
|
||||
""" # noqa: E501 line too long
|
||||
)
|
||||
assert (
|
||||
doc(m.NestBase.Nested.fa)
|
||||
== """
|
||||
fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None
|
||||
"""
|
||||
""" # noqa: E501 line too long
|
||||
)
|
||||
assert m.NestBase.__module__ == "pybind11_tests.class_"
|
||||
assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"
|
||||
|
@ -185,6 +178,7 @@ def test_inheritance(msg):
|
|||
|
||||
|
||||
def test_inheritance_init(msg):
|
||||
|
||||
# Single base
|
||||
class Python(m.Pet):
|
||||
def __init__(self):
|
||||
|
@ -321,8 +315,6 @@ def test_bind_protected_functions():
|
|||
|
||||
b = m.ProtectedB()
|
||||
assert b.foo() == 42
|
||||
assert m.read_foo(b.void_foo()) == 42
|
||||
assert m.pointers_equal(b.get_self(), b)
|
||||
|
||||
class C(m.ProtectedB):
|
||||
def __init__(self):
|
||||
|
@ -479,10 +471,3 @@ def test_register_duplicate_class():
|
|||
m.register_duplicate_nested_class_type(ClassScope)
|
||||
expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!'
|
||||
assert str(exc_info.value) == expected
|
||||
|
||||
|
||||
def test_pr4220_tripped_over_this():
|
||||
assert (
|
||||
m.Empty0().get_msg()
|
||||
== "This is really only meant to exercise successful compilation."
|
||||
)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import test_cmake_build
|
||||
|
||||
assert isinstance(__file__, str) # Test this is properly set
|
||||
if str is not bytes: # If not Python2
|
||||
assert isinstance(__file__, str) # Test this is properly set
|
||||
|
||||
assert test_cmake_build.add(1, 2) == 3
|
||||
print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3")
|
||||
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
|
||||
|
|
|
@ -4,50 +4,65 @@
|
|||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
|
||||
// MSVC 2015 fails in bizarre ways.
|
||||
# define PYBIND11_SKIP_TEST_CONST_NAME
|
||||
|
||||
#else // Only test with MSVC 2017 or newer.
|
||||
|
||||
// IUT = Implementation Under Test
|
||||
#define CONST_NAME_TESTS(TEST_FUNC, IUT) \
|
||||
std::string TEST_FUNC(int selector) { \
|
||||
switch (selector) { \
|
||||
case 0: \
|
||||
return IUT("").text; \
|
||||
case 1: \
|
||||
return IUT("A").text; \
|
||||
case 2: \
|
||||
return IUT("Bd").text; \
|
||||
case 3: \
|
||||
return IUT("Cef").text; \
|
||||
case 4: \
|
||||
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 5: \
|
||||
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 6: \
|
||||
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 7: \
|
||||
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 8: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<true>(IUT("D1"), IUT("D2")).text; \
|
||||
case 9: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<false>(IUT("E1"), IUT("E2")).text; \
|
||||
case 10: \
|
||||
return IUT("KeepAtEnd").text; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
throw std::runtime_error("Invalid selector value."); \
|
||||
}
|
||||
# define CONST_NAME_TESTS(TEST_FUNC, IUT) \
|
||||
std::string TEST_FUNC(int selector) { \
|
||||
switch (selector) { \
|
||||
case 0: \
|
||||
return IUT("").text; \
|
||||
case 1: \
|
||||
return IUT("A").text; \
|
||||
case 2: \
|
||||
return IUT("Bd").text; \
|
||||
case 3: \
|
||||
return IUT("Cef").text; \
|
||||
case 4: \
|
||||
return IUT<int>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 5: \
|
||||
return IUT<std::string>().text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 6: \
|
||||
return IUT<true>("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 7: \
|
||||
return IUT<false>("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \
|
||||
case 8: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<true>(IUT("D1"), IUT("D2")).text; \
|
||||
case 9: \
|
||||
/*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \
|
||||
return IUT<false>(IUT("E1"), IUT("E2")).text; \
|
||||
case 10: \
|
||||
return IUT("KeepAtEnd").text; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
throw std::runtime_error("Invalid selector value."); \
|
||||
}
|
||||
|
||||
CONST_NAME_TESTS(const_name_tests, py::detail::const_name)
|
||||
|
||||
#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
# ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||
CONST_NAME_TESTS(underscore_tests, py::detail::_)
|
||||
#endif
|
||||
# endif
|
||||
|
||||
#endif // MSVC >= 2017
|
||||
|
||||
TEST_SUBMODULE(const_name, m) {
|
||||
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
|
||||
m.attr("const_name_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
|
||||
#else
|
||||
m.def("const_name_tests", const_name_tests);
|
||||
#endif
|
||||
|
||||
#if defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
|
||||
#ifdef PYBIND11_SKIP_TEST_CONST_NAME
|
||||
m.attr("underscore_tests") = "PYBIND11_SKIP_TEST_CONST_NAME";
|
||||
#elif defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY)
|
||||
m.def("underscore_tests", underscore_tests);
|
||||
#else
|
||||
m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.";
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
import env
|
||||
from pybind11_tests import const_name as m
|
||||
|
||||
|
||||
|
@ -23,7 +25,7 @@ from pybind11_tests import const_name as m
|
|||
),
|
||||
)
|
||||
def test_const_name(func, selector, expected):
|
||||
if isinstance(func, str):
|
||||
if isinstance(func, type(u"") if env.PY2 else str):
|
||||
pytest.skip(func)
|
||||
text = func(selector)
|
||||
assert text == expected
|
||||
|
|
|
@ -31,8 +31,8 @@ py::bytes return_bytes() {
|
|||
std::string print_bytes(const py::bytes &bytes) {
|
||||
std::string ret = "bytes[";
|
||||
const auto value = static_cast<std::string>(bytes);
|
||||
for (char c : value) {
|
||||
ret += std::to_string(static_cast<int>(c)) + ' ';
|
||||
for (size_t i = 0; i < value.length(); ++i) {
|
||||
ret += std::to_string(static_cast<int>(value[i])) + " ";
|
||||
}
|
||||
ret.back() = ']';
|
||||
return ret;
|
||||
|
@ -52,12 +52,15 @@ int f1(int x) noexcept { return x + 1; }
|
|||
#endif
|
||||
int f2(int x) noexcept(true) { return x + 2; }
|
||||
int f3(int x) noexcept(false) { return x + 3; }
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int f4(int x) throw() { return x + 4; } // Deprecated equivalent to noexcept(true)
|
||||
PYBIND11_WARNING_POP
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
struct C {
|
||||
int m1(int x) noexcept { return x - 1; }
|
||||
int m2(int x) const noexcept { return x - 2; }
|
||||
|
@ -65,14 +68,17 @@ struct C {
|
|||
int m4(int x) const noexcept(true) { return x - 4; }
|
||||
int m5(int x) noexcept(false) { return x - 5; }
|
||||
int m6(int x) const noexcept(false) { return x - 6; }
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wdeprecated")
|
||||
PYBIND11_WARNING_DISABLE_CLANG("-Wdeprecated")
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m7(int x) throw() { return x - 7; }
|
||||
// NOLINTNEXTLINE(modernize-use-noexcept)
|
||||
int m8(int x) const throw() { return x - 8; }
|
||||
PYBIND11_WARNING_POP
|
||||
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
} // namespace test_exc_sp
|
||||
|
||||
|
@ -120,24 +126,29 @@ TEST_SUBMODULE(constants_and_functions, m) {
|
|||
.def("m8", &C::m8);
|
||||
m.def("f1", f1);
|
||||
m.def("f2", f2);
|
||||
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_INTEL(878) // incompatible exception specifications
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning push
|
||||
# pragma warning disable 878 // incompatible exception specifications
|
||||
#endif
|
||||
m.def("f3", f3);
|
||||
PYBIND11_WARNING_POP
|
||||
|
||||
#if defined(__INTEL_COMPILER)
|
||||
# pragma warning pop
|
||||
#endif
|
||||
m.def("f4", f4);
|
||||
|
||||
// test_function_record_leaks
|
||||
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
|
||||
struct LargeCapture {
|
||||
// This should always be enough to trigger the alternative branch
|
||||
// where `sizeof(capture) > sizeof(rec->data)`
|
||||
uint64_t capture[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 // CentOS7
|
||||
py::detail::silence_unused_warnings(capture);
|
||||
#endif
|
||||
uint64_t zeros[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
};
|
||||
m.def("register_large_capture_with_invalid_arguments", [](py::module_ m) {
|
||||
LargeCapture capture; // VS 2015's MSVC is acting up if we create the array here
|
||||
m.def(
|
||||
"should_raise", [capture](int) { return capture[9] + 33; }, py::kw_only(), py::arg());
|
||||
"should_raise",
|
||||
[capture](int) { return capture.zeros[9] + 33; },
|
||||
py::kw_only(),
|
||||
py::arg());
|
||||
});
|
||||
m.def("register_with_raising_repr", [](py::module_ m, const py::object &default_value) {
|
||||
m.def(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
m = pytest.importorskip("pybind11_tests.constants_and_functions")
|
||||
|
|
|
@ -289,7 +289,4 @@ TEST_SUBMODULE(copy_move_policies, m) {
|
|||
py::return_value_policy::move);
|
||||
m.def(
|
||||
"get_moveissue2", [](int i) { return MoveIssue2(i); }, py::return_value_policy::move);
|
||||
|
||||
// Make sure that cast from pytype rvalue to other pytype works
|
||||
m.def("get_pytype_rvalue_castissue", [](double i) { return py::float_(i).cast<py::int_>(); });
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import copy_move_policies as m
|
||||
|
@ -123,10 +124,3 @@ def test_move_fallback():
|
|||
assert m1.value == 1
|
||||
m2 = m.get_moveissue2(2)
|
||||
assert m2.value == 2
|
||||
|
||||
|
||||
def test_pytype_rvalue_cast():
|
||||
"""Make sure that cast from pytype rvalue to other pytype works"""
|
||||
|
||||
value = m.get_pytype_rvalue_castissue(1.0)
|
||||
assert value == 1
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
};
|
||||
class ArgAlwaysConverts {};
|
||||
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<ArgInspector1> {
|
||||
|
@ -74,7 +74,7 @@ public:
|
|||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
} // namespace pybind11
|
||||
|
||||
// test_custom_caster_destruction
|
||||
class DestructionTester {
|
||||
|
@ -92,7 +92,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
};
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<DestructionTester> {
|
||||
|
@ -104,7 +104,7 @@ struct type_caster<DestructionTester> {
|
|||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
} // namespace pybind11
|
||||
|
||||
// Define type caster outside of `pybind11::detail` and then alias it.
|
||||
namespace other_lib {
|
||||
|
@ -112,7 +112,7 @@ struct MyType {};
|
|||
// Corrupt `py` shorthand alias for surrounding context.
|
||||
namespace py {}
|
||||
// Corrupt unqualified relative `pybind11` namespace.
|
||||
namespace PYBIND11_NAMESPACE {}
|
||||
namespace pybind11 {}
|
||||
// Correct alias.
|
||||
namespace py_ = ::pybind11;
|
||||
// Define caster. This is effectively no-op, we only ensure it compiles and we
|
||||
|
@ -127,12 +127,12 @@ struct my_caster {
|
|||
};
|
||||
} // namespace other_lib
|
||||
// Effectively "alias" it into correct namespace (via inheritance).
|
||||
namespace PYBIND11_NAMESPACE {
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
struct type_caster<other_lib::MyType> : public other_lib::my_caster {};
|
||||
} // namespace detail
|
||||
} // namespace PYBIND11_NAMESPACE
|
||||
} // namespace pybind11
|
||||
|
||||
TEST_SUBMODULE(custom_type_casters, m) {
|
||||
// test_custom_type_casters
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import custom_type_casters as m
|
||||
|
@ -18,7 +19,7 @@ def test_noconvert_args(msg):
|
|||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||
13
|
||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
||||
"""
|
||||
""" # noqa: E501 line too long
|
||||
)
|
||||
assert (
|
||||
msg(a.g("this is a", "this is b", 42))
|
||||
|
@ -27,7 +28,7 @@ def test_noconvert_args(msg):
|
|||
loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
|
||||
42
|
||||
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
|
||||
"""
|
||||
""" # noqa: E501 line too long
|
||||
)
|
||||
assert (
|
||||
msg(a.g("this is a", "this is b", 42, "this is d"))
|
||||
|
@ -75,7 +76,7 @@ def test_noconvert_args(msg):
|
|||
1. (i: int) -> int
|
||||
|
||||
Invoked with: 4.0
|
||||
"""
|
||||
""" # noqa: E501 line too long
|
||||
)
|
||||
|
||||
assert m.ints_only(4) == 2
|
||||
|
@ -94,8 +95,7 @@ def test_noconvert_args(msg):
|
|||
|
||||
def test_custom_caster_destruction():
|
||||
"""Tests that returning a pointer to a type that gets converted with a custom type caster gets
|
||||
destroyed when the function has py::return_value_policy::take_ownership policy applied.
|
||||
"""
|
||||
destroyed when the function has py::return_value_policy::take_ownership policy applied."""
|
||||
|
||||
cstats = m.destruction_tester_cstats()
|
||||
# This one *doesn't* have take_ownership: the pointer should be used but not destroyed:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import gc
|
||||
import weakref
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue