Merge branch 'work/adridg/remove-boost' into calamares
This commit is contained in:
commit
dcce7d8a57
16
3rdparty/pybind11/CMakeLists.txt
vendored
Normal file
16
3rdparty/pybind11/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
#
|
||||||
|
# SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
#
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# This is a very-stripped-down way of getting the bundled pybind11
|
||||||
|
|
||||||
|
add_library(pybind11_headers INTERFACE)
|
||||||
|
add_library(pybind11::headers ALIAS pybind11_headers)
|
||||||
|
|
||||||
|
target_include_directories(pybind11_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
target_link_libraries(pybind11_headers INTERFACE Python::Python) # Was searched-for at top-level
|
||||||
|
|
||||||
|
|
29
3rdparty/pybind11/LICENSE
vendored
Normal file
29
3rdparty/pybind11/LICENSE
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of
|
||||||
|
external contributions to this project including patches, pull requests, etc.
|
6
3rdparty/pybind11/MANIFEST.in
vendored
Normal file
6
3rdparty/pybind11/MANIFEST.in
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
prune tests
|
||||||
|
recursive-include pybind11/include/pybind11 *.h
|
||||||
|
recursive-include pybind11 *.py
|
||||||
|
recursive-include pybind11 py.typed
|
||||||
|
include pybind11/share/cmake/pybind11/*.cmake
|
||||||
|
include LICENSE README.rst SECURITY.md pyproject.toml setup.py setup.cfg
|
180
3rdparty/pybind11/README.rst
vendored
Normal file
180
3rdparty/pybind11/README.rst
vendored
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
.. figure:: https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png
|
||||||
|
:alt: pybind11 logo
|
||||||
|
|
||||||
|
**pybind11 — Seamless operability between C++11 and Python**
|
||||||
|
|
||||||
|
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| |CI| |Build status|
|
||||||
|
|
||||||
|
|Repology| |PyPI package| |Conda-forge| |Python Versions|
|
||||||
|
|
||||||
|
`Setuptools example <https://github.com/pybind/python_example>`_
|
||||||
|
• `Scikit-build example <https://github.com/pybind/scikit_build_example>`_
|
||||||
|
• `CMake example <https://github.com/pybind/cmake_example>`_
|
||||||
|
|
||||||
|
.. start
|
||||||
|
|
||||||
|
|
||||||
|
**pybind11** is a lightweight header-only library that exposes C++ types
|
||||||
|
in Python and vice versa, mainly to create Python bindings of existing
|
||||||
|
C++ code. Its goals and syntax are similar to the excellent
|
||||||
|
`Boost.Python <http://www.boost.org/doc/libs/1_58_0/libs/python/doc/>`_
|
||||||
|
library by David Abrahams: to minimize boilerplate code in traditional
|
||||||
|
extension modules by inferring type information using compile-time
|
||||||
|
introspection.
|
||||||
|
|
||||||
|
The main issue with Boost.Python—and the reason for creating such a
|
||||||
|
similar project—is Boost. Boost is an enormously large and complex suite
|
||||||
|
of utility libraries that works with almost every C++ compiler in
|
||||||
|
existence. This compatibility has its cost: arcane template tricks and
|
||||||
|
workarounds are necessary to support the oldest and buggiest of compiler
|
||||||
|
specimens. Now that C++11-compatible compilers are widely available,
|
||||||
|
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
|
||||||
|
generation. Without comments, the core header files only require ~4K
|
||||||
|
lines of code and depend on Python (3.6+, 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
|
||||||
|
grown beyond Boost.Python in many ways, leading to dramatically simpler
|
||||||
|
binding code in many common situations.
|
||||||
|
|
||||||
|
Tutorial and reference documentation is provided at
|
||||||
|
`pybind11.readthedocs.io <https://pybind11.readthedocs.io/en/latest>`_.
|
||||||
|
A PDF version of the manual is available
|
||||||
|
`here <https://pybind11.readthedocs.io/_/downloads/en/latest/pdf/>`_.
|
||||||
|
And the source code is always available at
|
||||||
|
`github.com/pybind/pybind11 <https://github.com/pybind/pybind11>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Core features
|
||||||
|
-------------
|
||||||
|
|
||||||
|
|
||||||
|
pybind11 can map the following core C++ features to Python:
|
||||||
|
|
||||||
|
- Functions accepting and returning custom data structures per value,
|
||||||
|
reference, or pointer
|
||||||
|
- Instance methods and static methods
|
||||||
|
- Overloaded functions
|
||||||
|
- Instance attributes and static attributes
|
||||||
|
- Arbitrary exception types
|
||||||
|
- Enumerations
|
||||||
|
- Callbacks
|
||||||
|
- Iterators and ranges
|
||||||
|
- Custom operators
|
||||||
|
- Single and multiple inheritance
|
||||||
|
- STL data structures
|
||||||
|
- Smart pointers with reference counting like ``std::shared_ptr``
|
||||||
|
- Internal references with correct reference counting
|
||||||
|
- C++ classes with virtual (and pure virtual) methods can be extended
|
||||||
|
in Python
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
- It is possible to bind C++11 lambda functions with captured
|
||||||
|
variables. The lambda capture data is stored inside the resulting
|
||||||
|
Python function object.
|
||||||
|
|
||||||
|
- 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
|
||||||
|
between C++ matrix classes like Eigen and NumPy without expensive
|
||||||
|
copy operations.
|
||||||
|
|
||||||
|
- pybind11 can automatically vectorize functions so that they are
|
||||||
|
transparently applied to all entries of one or more NumPy array
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
- Python's slice-based access and assignment operations can be
|
||||||
|
supported with just a few lines of code.
|
||||||
|
|
||||||
|
- Everything is contained in just a few header files; there is no need
|
||||||
|
to link against any additional libraries.
|
||||||
|
|
||||||
|
- Binaries are generally smaller by a factor of at least 2 compared to
|
||||||
|
equivalent bindings generated by Boost.Python. A recent pybind11
|
||||||
|
conversion of PyRosetta, an enormous Boost.Python binding project,
|
||||||
|
`reported <https://graylab.jhu.edu/Sergey/2016.RosettaCon/PyRosetta-4.pdf>`_
|
||||||
|
a binary size reduction of **5.4x** and compile time reduction by
|
||||||
|
**5.8x**.
|
||||||
|
|
||||||
|
- Function signatures are precomputed at compile time (using
|
||||||
|
``constexpr``), leading to smaller binaries.
|
||||||
|
|
||||||
|
- With little extra effort, C++ types can be pickled and unpickled
|
||||||
|
similar to regular Python objects.
|
||||||
|
|
||||||
|
Supported compilers
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
7. NVIDIA PGI (20.9 tested in CI)
|
||||||
|
|
||||||
|
About
|
||||||
|
-----
|
||||||
|
|
||||||
|
This project was created by `Wenzel
|
||||||
|
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
|
||||||
|
improvements to the code were contributed by Jonas Adler, Lori A. Burns,
|
||||||
|
Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel
|
||||||
|
Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov, Johan Mabille, Tomasz Miąsko,
|
||||||
|
Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim
|
||||||
|
Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart.
|
||||||
|
|
||||||
|
We thank Google for a generous financial contribution to the continuous
|
||||||
|
integration infrastructure used by this project.
|
||||||
|
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
See the `contributing
|
||||||
|
guide <https://github.com/pybind/pybind11/blob/master/.github/CONTRIBUTING.md>`_
|
||||||
|
for information on building and contributing to pybind11.
|
||||||
|
|
||||||
|
License
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
pybind11 is provided under a BSD-style license that can be found in the
|
||||||
|
`LICENSE <https://github.com/pybind/pybind11/blob/master/LICENSE>`_
|
||||||
|
file. By using, distributing, or contributing to this project, you agree
|
||||||
|
to the terms and conditions of this license.
|
||||||
|
|
||||||
|
.. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest
|
||||||
|
:target: http://pybind11.readthedocs.org/en/latest
|
||||||
|
.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue.svg
|
||||||
|
:target: http://pybind11.readthedocs.org/en/stable
|
||||||
|
.. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
|
||||||
|
:target: https://gitter.im/pybind/Lobby
|
||||||
|
.. |CI| image:: https://github.com/pybind/pybind11/workflows/CI/badge.svg
|
||||||
|
:target: https://github.com/pybind/pybind11/actions
|
||||||
|
.. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true
|
||||||
|
:target: https://ci.appveyor.com/project/wjakob/pybind11
|
||||||
|
.. |PyPI package| image:: https://img.shields.io/pypi/v/pybind11.svg
|
||||||
|
:target: https://pypi.org/project/pybind11/
|
||||||
|
.. |Conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pybind11.svg
|
||||||
|
:target: https://github.com/conda-forge/pybind11-feedstock
|
||||||
|
.. |Repology| image:: https://repology.org/badge/latest-versions/python:pybind11.svg
|
||||||
|
:target: https://repology.org/project/python:pybind11/versions
|
||||||
|
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
|
||||||
|
:target: https://pypi.org/project/pybind11/
|
||||||
|
.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
|
||||||
|
:target: https://github.com/pybind/pybind11/discussions
|
13
3rdparty/pybind11/SECURITY.md
vendored
Normal file
13
3rdparty/pybind11/SECURITY.md
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Security updates are applied only to the latest release.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
||||||
|
|
||||||
|
Please disclose it at [security advisory](https://github.com/pybind/pybind11/security/advisories/new).
|
||||||
|
|
||||||
|
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
|
690
3rdparty/pybind11/include/pybind11/attr.h
vendored
Normal file
690
3rdparty/pybind11/include/pybind11/attr.h
vendored
Normal file
@ -0,0 +1,690 @@
|
|||||||
|
/*
|
||||||
|
pybind11/attr.h: Infrastructure for processing custom
|
||||||
|
type and function attributes
|
||||||
|
|
||||||
|
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 "detail/common.h"
|
||||||
|
#include "cast.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
/// \addtogroup annotations
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Annotation for methods
|
||||||
|
struct is_method {
|
||||||
|
handle class_;
|
||||||
|
explicit is_method(const handle &c) : class_(c) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation for setters
|
||||||
|
struct is_setter {};
|
||||||
|
|
||||||
|
/// Annotation for operators
|
||||||
|
struct is_operator {};
|
||||||
|
|
||||||
|
/// Annotation for classes that cannot be subclassed
|
||||||
|
struct is_final {};
|
||||||
|
|
||||||
|
/// Annotation for parent scope
|
||||||
|
struct scope {
|
||||||
|
handle value;
|
||||||
|
explicit scope(const handle &s) : value(s) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation for documentation
|
||||||
|
struct doc {
|
||||||
|
const char *value;
|
||||||
|
explicit doc(const char *value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation for function names
|
||||||
|
struct name {
|
||||||
|
const char *value;
|
||||||
|
explicit name(const char *value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation indicating that a function is an overload associated with a given "sibling"
|
||||||
|
struct sibling {
|
||||||
|
handle value;
|
||||||
|
explicit sibling(const handle &value) : value(value.ptr()) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation indicating that a class derives from another given type
|
||||||
|
template <typename T>
|
||||||
|
struct base {
|
||||||
|
|
||||||
|
PYBIND11_DEPRECATED(
|
||||||
|
"base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
||||||
|
base() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Keep patient alive while nurse lives
|
||||||
|
template <size_t Nurse, size_t Patient>
|
||||||
|
struct keep_alive {};
|
||||||
|
|
||||||
|
/// Annotation indicating that a class is involved in a multiple inheritance relationship
|
||||||
|
struct multiple_inheritance {};
|
||||||
|
|
||||||
|
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
|
||||||
|
struct dynamic_attr {};
|
||||||
|
|
||||||
|
/// Annotation which enables the buffer protocol for a type
|
||||||
|
struct buffer_protocol {};
|
||||||
|
|
||||||
|
/// Annotation which requests that a special metaclass is created for a type
|
||||||
|
struct metaclass {
|
||||||
|
handle value;
|
||||||
|
|
||||||
|
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
|
||||||
|
metaclass() = default;
|
||||||
|
|
||||||
|
/// Override pybind11's default metaclass
|
||||||
|
explicit metaclass(handle value) : value(value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
|
||||||
|
/// may be used to customize the Python type.
|
||||||
|
///
|
||||||
|
/// The callback is invoked immediately before `PyType_Ready`.
|
||||||
|
///
|
||||||
|
/// Note: This is an advanced interface, and uses of it may require changes to
|
||||||
|
/// work with later versions of pybind11. You may wish to consult the
|
||||||
|
/// implementation of `make_new_python_type` in `detail/classes.h` to understand
|
||||||
|
/// the context in which the callback will be run.
|
||||||
|
struct custom_type_setup {
|
||||||
|
using callback = std::function<void(PyHeapTypeObject *heap_type)>;
|
||||||
|
|
||||||
|
explicit custom_type_setup(callback value) : value(std::move(value)) {}
|
||||||
|
|
||||||
|
callback value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation that marks a class as local to the module:
|
||||||
|
struct module_local {
|
||||||
|
const bool value;
|
||||||
|
constexpr explicit module_local(bool v = true) : value(v) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Annotation to mark enums as an arithmetic type
|
||||||
|
struct arithmetic {};
|
||||||
|
|
||||||
|
/// Mark a function for addition at the beginning of the existing overload chain instead of the end
|
||||||
|
struct prepend {};
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
A call policy which places one or more guard variables (``Ts...``) around the function call.
|
||||||
|
|
||||||
|
For example, this definition:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
m.def("foo", foo, py::call_guard<T>());
|
||||||
|
|
||||||
|
is equivalent to the following pseudocode:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
m.def("foo", [](args...) {
|
||||||
|
T scope_guard;
|
||||||
|
return foo(args...); // forwarded arguments
|
||||||
|
});
|
||||||
|
\endrst */
|
||||||
|
template <typename... Ts>
|
||||||
|
struct call_guard;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct call_guard<> {
|
||||||
|
using type = detail::void_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct call_guard<T> {
|
||||||
|
static_assert(std::is_default_constructible<T>::value,
|
||||||
|
"The guard type must be default constructible");
|
||||||
|
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
struct call_guard<T, Ts...> {
|
||||||
|
struct type {
|
||||||
|
T guard{}; // Compose multiple guard types with left-to-right default-constructor order
|
||||||
|
typename call_guard<Ts...>::type next{};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @} annotations
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
/* Forward declarations */
|
||||||
|
enum op_id : int;
|
||||||
|
enum op_type : int;
|
||||||
|
struct undefined_t;
|
||||||
|
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
|
||||||
|
struct op_;
|
||||||
|
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
||||||
|
|
||||||
|
/// Internal data structure which holds metadata about a keyword argument
|
||||||
|
struct argument_record {
|
||||||
|
const char *name; ///< Argument name
|
||||||
|
const char *descr; ///< Human-readable version of the argument value
|
||||||
|
handle value; ///< Associated Python object
|
||||||
|
bool convert : 1; ///< True if the argument is allowed to convert when loading
|
||||||
|
bool none : 1; ///< True if None is allowed when loading
|
||||||
|
|
||||||
|
argument_record(const char *name, const char *descr, handle value, bool convert, bool none)
|
||||||
|
: name(name), descr(descr), value(value), convert(convert), none(none) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Internal data structure which holds metadata about a bound function (signature, overloads,
|
||||||
|
/// etc.)
|
||||||
|
struct function_record {
|
||||||
|
function_record()
|
||||||
|
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
||||||
|
is_operator(false), is_method(false), is_setter(false), has_args(false),
|
||||||
|
has_kwargs(false), prepend(false) {}
|
||||||
|
|
||||||
|
/// Function name
|
||||||
|
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
||||||
|
|
||||||
|
// User-specified documentation string
|
||||||
|
char *doc = nullptr;
|
||||||
|
|
||||||
|
/// Human-readable version of the function signature
|
||||||
|
char *signature = nullptr;
|
||||||
|
|
||||||
|
/// List of registered keyword arguments
|
||||||
|
std::vector<argument_record> args;
|
||||||
|
|
||||||
|
/// Pointer to lambda function which converts arguments and performs the actual call
|
||||||
|
handle (*impl)(function_call &) = nullptr;
|
||||||
|
|
||||||
|
/// Storage for the wrapped function pointer and captured data, if any
|
||||||
|
void *data[3] = {};
|
||||||
|
|
||||||
|
/// Pointer to custom destructor for 'data' (if needed)
|
||||||
|
void (*free_data)(function_record *ptr) = nullptr;
|
||||||
|
|
||||||
|
/// Return value policy associated with this function
|
||||||
|
return_value_policy policy = return_value_policy::automatic;
|
||||||
|
|
||||||
|
/// True if name == '__init__'
|
||||||
|
bool is_constructor : 1;
|
||||||
|
|
||||||
|
/// True if this is a new-style `__init__` defined in `detail/init.h`
|
||||||
|
bool is_new_style_constructor : 1;
|
||||||
|
|
||||||
|
/// True if this is a stateless function pointer
|
||||||
|
bool is_stateless : 1;
|
||||||
|
|
||||||
|
/// True if this is an operator (__add__), etc.
|
||||||
|
bool is_operator : 1;
|
||||||
|
|
||||||
|
/// True if this is a method
|
||||||
|
bool is_method : 1;
|
||||||
|
|
||||||
|
/// True if this is a setter
|
||||||
|
bool is_setter : 1;
|
||||||
|
|
||||||
|
/// True if the function has a '*args' argument
|
||||||
|
bool has_args : 1;
|
||||||
|
|
||||||
|
/// True if the function has a '**kwargs' argument
|
||||||
|
bool has_kwargs : 1;
|
||||||
|
|
||||||
|
/// True if this function is to be inserted at the beginning of the overload resolution chain
|
||||||
|
bool prepend : 1;
|
||||||
|
|
||||||
|
/// Number of arguments (including py::args and/or py::kwargs, if present)
|
||||||
|
std::uint16_t nargs;
|
||||||
|
|
||||||
|
/// Number of leading positional arguments, which are terminated by a py::args or py::kwargs
|
||||||
|
/// argument or by a py::kw_only annotation.
|
||||||
|
std::uint16_t nargs_pos = 0;
|
||||||
|
|
||||||
|
/// Number of leading arguments (counted in `nargs`) that are positional-only
|
||||||
|
std::uint16_t nargs_pos_only = 0;
|
||||||
|
|
||||||
|
/// Python method object
|
||||||
|
PyMethodDef *def = nullptr;
|
||||||
|
|
||||||
|
/// Python handle to the parent scope (a class or a module)
|
||||||
|
handle scope;
|
||||||
|
|
||||||
|
/// Python handle to the sibling function representing an overload chain
|
||||||
|
handle sibling;
|
||||||
|
|
||||||
|
/// Pointer to next overload
|
||||||
|
function_record *next = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Special data structure which (temporarily) holds metadata about a bound class
|
||||||
|
struct type_record {
|
||||||
|
PYBIND11_NOINLINE type_record()
|
||||||
|
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
||||||
|
default_holder(true), module_local(false), is_final(false) {}
|
||||||
|
|
||||||
|
/// Handle to the parent scope
|
||||||
|
handle scope;
|
||||||
|
|
||||||
|
/// Name of the class
|
||||||
|
const char *name = nullptr;
|
||||||
|
|
||||||
|
// Pointer to RTTI type_info data structure
|
||||||
|
const std::type_info *type = nullptr;
|
||||||
|
|
||||||
|
/// How large is the underlying C++ type?
|
||||||
|
size_t type_size = 0;
|
||||||
|
|
||||||
|
/// What is the alignment of the underlying C++ type?
|
||||||
|
size_t type_align = 0;
|
||||||
|
|
||||||
|
/// How large is the type's holder?
|
||||||
|
size_t holder_size = 0;
|
||||||
|
|
||||||
|
/// The global operator new can be overridden with a class-specific variant
|
||||||
|
void *(*operator_new)(size_t) = nullptr;
|
||||||
|
|
||||||
|
/// Function pointer to class_<..>::init_instance
|
||||||
|
void (*init_instance)(instance *, const void *) = nullptr;
|
||||||
|
|
||||||
|
/// Function pointer to class_<..>::dealloc
|
||||||
|
void (*dealloc)(detail::value_and_holder &) = nullptr;
|
||||||
|
|
||||||
|
/// List of base classes of the newly created type
|
||||||
|
list bases;
|
||||||
|
|
||||||
|
/// Optional docstring
|
||||||
|
const char *doc = nullptr;
|
||||||
|
|
||||||
|
/// Custom metaclass (optional)
|
||||||
|
handle metaclass;
|
||||||
|
|
||||||
|
/// Custom type setup.
|
||||||
|
custom_type_setup::callback custom_type_setup_callback;
|
||||||
|
|
||||||
|
/// Multiple inheritance marker
|
||||||
|
bool multiple_inheritance : 1;
|
||||||
|
|
||||||
|
/// Does the class manage a __dict__?
|
||||||
|
bool dynamic_attr : 1;
|
||||||
|
|
||||||
|
/// Does the class implement the buffer protocol?
|
||||||
|
bool buffer_protocol : 1;
|
||||||
|
|
||||||
|
/// Is the default (unique_ptr) holder type used?
|
||||||
|
bool default_holder : 1;
|
||||||
|
|
||||||
|
/// Is the class definition local to the module shared object?
|
||||||
|
bool module_local : 1;
|
||||||
|
|
||||||
|
/// Is the class inheritable from python classes?
|
||||||
|
bool is_final : 1;
|
||||||
|
|
||||||
|
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
|
||||||
|
auto *base_info = detail::get_type_info(base, false);
|
||||||
|
if (!base_info) {
|
||||||
|
std::string tname(base.name());
|
||||||
|
detail::clean_type_id(tname);
|
||||||
|
pybind11_fail("generic_type: type \"" + std::string(name)
|
||||||
|
+ "\" referenced unknown base type \"" + tname + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default_holder != base_info->default_holder) {
|
||||||
|
std::string tname(base.name());
|
||||||
|
detail::clean_type_id(tname);
|
||||||
|
pybind11_fail("generic_type: type \"" + std::string(name) + "\" "
|
||||||
|
+ (default_holder ? "does not have" : "has")
|
||||||
|
+ " a non-default holder type while its base \"" + tname + "\" "
|
||||||
|
+ (base_info->default_holder ? "does not" : "does"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (caster) {
|
||||||
|
base_info->implicit_casts.emplace_back(type, caster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) {
|
||||||
|
args.reserve(f.nargs);
|
||||||
|
args_convert.reserve(f.nargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tag for a new-style `__init__` defined in `detail/init.h`
|
||||||
|
struct is_new_style_constructor {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial template specializations to process custom attributes provided to
|
||||||
|
* cpp_function_ and class_. These are either used to initialize the respective
|
||||||
|
* fields in the type_record and function_record data structures or executed at
|
||||||
|
* runtime to deal with custom call policies (e.g. keep_alive).
|
||||||
|
*/
|
||||||
|
template <typename T, typename SFINAE = void>
|
||||||
|
struct process_attribute;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct process_attribute_default {
|
||||||
|
/// Default implementation: do nothing
|
||||||
|
static void init(const T &, function_record *) {}
|
||||||
|
static void init(const T &, type_record *) {}
|
||||||
|
static void precall(function_call &) {}
|
||||||
|
static void postcall(function_call &, handle) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute specifying the function's name
|
||||||
|
template <>
|
||||||
|
struct process_attribute<name> : process_attribute_default<name> {
|
||||||
|
static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute specifying the function's docstring
|
||||||
|
template <>
|
||||||
|
struct process_attribute<doc> : process_attribute_default<doc> {
|
||||||
|
static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute specifying the function's docstring (provided as a C-style string)
|
||||||
|
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; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct process_attribute<char *> : process_attribute<const char *> {};
|
||||||
|
|
||||||
|
/// Process an attribute indicating the function's return value policy
|
||||||
|
template <>
|
||||||
|
struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
|
||||||
|
static void init(const return_value_policy &p, function_record *r) { r->policy = p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute which indicates that this is an overloaded function associated with a
|
||||||
|
/// given sibling
|
||||||
|
template <>
|
||||||
|
struct process_attribute<sibling> : process_attribute_default<sibling> {
|
||||||
|
static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute which indicates that this function is a method
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_method> : process_attribute_default<is_method> {
|
||||||
|
static void init(const is_method &s, function_record *r) {
|
||||||
|
r->is_method = true;
|
||||||
|
r->scope = s.class_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute which indicates that this function is a setter
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_setter> : process_attribute_default<is_setter> {
|
||||||
|
static void init(const is_setter &, function_record *r) { r->is_setter = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute which indicates the parent scope of a method
|
||||||
|
template <>
|
||||||
|
struct process_attribute<scope> : process_attribute_default<scope> {
|
||||||
|
static void init(const scope &s, function_record *r) { r->scope = s.value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an attribute which indicates that this function is an operator
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_operator> : process_attribute_default<is_operator> {
|
||||||
|
static void init(const is_operator &, function_record *r) { r->is_operator = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_new_style_constructor>
|
||||||
|
: process_attribute_default<is_new_style_constructor> {
|
||||||
|
static void init(const is_new_style_constructor &, function_record *r) {
|
||||||
|
r->is_new_style_constructor = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void check_kw_only_arg(const arg &a, function_record *r) {
|
||||||
|
if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) {
|
||||||
|
pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or "
|
||||||
|
"args() argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void append_self_arg_if_needed(function_record *r) {
|
||||||
|
if (r->is_method && r->args.empty()) {
|
||||||
|
r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a keyword argument attribute (*without* a default value)
|
||||||
|
template <>
|
||||||
|
struct process_attribute<arg> : process_attribute_default<arg> {
|
||||||
|
static void init(const arg &a, function_record *r) {
|
||||||
|
append_self_arg_if_needed(r);
|
||||||
|
r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none);
|
||||||
|
|
||||||
|
check_kw_only_arg(a, r);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a keyword argument attribute (*with* a default value)
|
||||||
|
template <>
|
||||||
|
struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
||||||
|
static void init(const arg_v &a, function_record *r) {
|
||||||
|
if (r->is_method && r->args.empty()) {
|
||||||
|
r->args.emplace_back(
|
||||||
|
"self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a.value) {
|
||||||
|
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
|
std::string descr("'");
|
||||||
|
if (a.name) {
|
||||||
|
descr += std::string(a.name) + ": ";
|
||||||
|
}
|
||||||
|
descr += a.type + "'";
|
||||||
|
if (r->is_method) {
|
||||||
|
if (r->name) {
|
||||||
|
descr += " in method '" + (std::string) str(r->scope) + "."
|
||||||
|
+ (std::string) r->name + "'";
|
||||||
|
} else {
|
||||||
|
descr += " in method of '" + (std::string) str(r->scope) + "'";
|
||||||
|
}
|
||||||
|
} else if (r->name) {
|
||||||
|
descr += " in function '" + (std::string) r->name + "'";
|
||||||
|
}
|
||||||
|
pybind11_fail("arg(): could not convert default argument " + descr
|
||||||
|
+ " into a Python object (type not registered yet?)");
|
||||||
|
#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.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
|
||||||
|
|
||||||
|
check_kw_only_arg(a, r);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a keyword-only-arguments-follow pseudo argument
|
||||||
|
template <>
|
||||||
|
struct process_attribute<kw_only> : process_attribute_default<kw_only> {
|
||||||
|
static void init(const kw_only &, function_record *r) {
|
||||||
|
append_self_arg_if_needed(r);
|
||||||
|
if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) {
|
||||||
|
pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative "
|
||||||
|
"argument location (or omit kw_only() entirely)");
|
||||||
|
}
|
||||||
|
r->nargs_pos = static_cast<std::uint16_t>(r->args.size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a positional-only-argument maker
|
||||||
|
template <>
|
||||||
|
struct process_attribute<pos_only> : process_attribute_default<pos_only> {
|
||||||
|
static void init(const pos_only &, function_record *r) {
|
||||||
|
append_self_arg_if_needed(r);
|
||||||
|
r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size());
|
||||||
|
if (r->nargs_pos_only > r->nargs_pos) {
|
||||||
|
pybind11_fail("pos_only(): cannot follow a py::args() argument");
|
||||||
|
}
|
||||||
|
// It also can't follow a kw_only, but a static_assert in pybind11.h checks that
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees
|
||||||
|
/// that)
|
||||||
|
template <typename T>
|
||||||
|
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>>
|
||||||
|
: process_attribute_default<handle> {
|
||||||
|
static void init(const handle &h, type_record *r) { r->bases.append(h); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
|
||||||
|
template <typename T>
|
||||||
|
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
||||||
|
static void init(const base<T> &, type_record *r) { r->add_base(typeid(T), nullptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a multiple inheritance attribute
|
||||||
|
template <>
|
||||||
|
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
|
||||||
|
static void init(const multiple_inheritance &, type_record *r) {
|
||||||
|
r->multiple_inheritance = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr> {
|
||||||
|
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<custom_type_setup> {
|
||||||
|
static void init(const custom_type_setup &value, type_record *r) {
|
||||||
|
r->custom_type_setup_callback = value.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_final> : process_attribute_default<is_final> {
|
||||||
|
static void init(const is_final &, type_record *r) { r->is_final = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
|
||||||
|
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
|
||||||
|
static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<module_local> : process_attribute_default<module_local> {
|
||||||
|
static void init(const module_local &l, type_record *r) { r->module_local = l.value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process a 'prepend' attribute, putting this at the beginning of the overload chain
|
||||||
|
template <>
|
||||||
|
struct process_attribute<prepend> : process_attribute_default<prepend> {
|
||||||
|
static void init(const prepend &, function_record *r) { r->prepend = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Process an 'arithmetic' attribute for enums (does nothing here)
|
||||||
|
template <>
|
||||||
|
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a keep_alive call policy -- invokes keep_alive_impl during the
|
||||||
|
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
template <size_t Nurse, size_t Patient>
|
||||||
|
struct process_attribute<keep_alive<Nurse, Patient>>
|
||||||
|
: public process_attribute_default<keep_alive<Nurse, Patient>> {
|
||||||
|
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
||||||
|
static void precall(function_call &call) {
|
||||||
|
keep_alive_impl(Nurse, Patient, call, handle());
|
||||||
|
}
|
||||||
|
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
||||||
|
static void postcall(function_call &, handle) {}
|
||||||
|
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||||
|
static void precall(function_call &) {}
|
||||||
|
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
||||||
|
static void postcall(function_call &call, handle ret) {
|
||||||
|
keep_alive_impl(Nurse, Patient, call, ret);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Recursively iterate over variadic template arguments
|
||||||
|
template <typename... Args>
|
||||||
|
struct process_attributes {
|
||||||
|
static void init(const Args &...args, function_record *r) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||||
|
using expander = int[];
|
||||||
|
(void) expander{
|
||||||
|
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||||
|
}
|
||||||
|
static void init(const Args &...args, type_record *r) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
||||||
|
using expander = int[];
|
||||||
|
(void) expander{0,
|
||||||
|
(process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
||||||
|
}
|
||||||
|
static void precall(function_call &call) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
|
||||||
|
using expander = int[];
|
||||||
|
(void) expander{0,
|
||||||
|
(process_attribute<typename std::decay<Args>::type>::precall(call), 0)...};
|
||||||
|
}
|
||||||
|
static void postcall(function_call &call, handle fn_ret) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret);
|
||||||
|
using expander = int[];
|
||||||
|
(void) expander{
|
||||||
|
0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0)...};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_call_guard = is_instantiation<call_guard, T>;
|
||||||
|
|
||||||
|
/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found)
|
||||||
|
template <typename... Extra>
|
||||||
|
using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extra...>::type;
|
||||||
|
|
||||||
|
/// Check the number of named arguments at compile time
|
||||||
|
template <typename... Extra,
|
||||||
|
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
|
||||||
|
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
||||||
|
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
||||||
|
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
208
3rdparty/pybind11/include/pybind11/buffer_info.h
vendored
Normal file
208
3rdparty/pybind11/include/pybind11/buffer_info.h
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
pybind11/buffer_info.h: Python buffer object interface
|
||||||
|
|
||||||
|
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 "detail/common.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
// Default, C-style strides
|
||||||
|
inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
|
||||||
|
auto ndim = shape.size();
|
||||||
|
std::vector<ssize_t> strides(ndim, itemsize);
|
||||||
|
if (ndim > 0) {
|
||||||
|
for (size_t i = ndim - 1; i > 0; --i) {
|
||||||
|
strides[i - 1] = strides[i] * shape[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strides;
|
||||||
|
}
|
||||||
|
|
||||||
|
// F-style strides; default when constructing an array_t with `ExtraFlags & f_style`
|
||||||
|
inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
|
||||||
|
auto ndim = shape.size();
|
||||||
|
std::vector<ssize_t> strides(ndim, itemsize);
|
||||||
|
for (size_t i = 1; i < ndim; ++i) {
|
||||||
|
strides[i] = strides[i - 1] * shape[i - 1];
|
||||||
|
}
|
||||||
|
return strides;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename SFINAE = void>
|
||||||
|
struct compare_buffer_info;
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
/// Information record describing a Python buffer object
|
||||||
|
struct buffer_info {
|
||||||
|
void *ptr = nullptr; // Pointer to the underlying storage
|
||||||
|
ssize_t itemsize = 0; // Size of individual items in bytes
|
||||||
|
ssize_t size = 0; // Total number of entries
|
||||||
|
std::string format; // For homogeneous buffers, this should be set to
|
||||||
|
// format_descriptor<T>::format()
|
||||||
|
ssize_t ndim = 0; // Number of dimensions
|
||||||
|
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
|
||||||
|
std::vector<ssize_t> strides; // Number of bytes between adjacent entries
|
||||||
|
// (for each per dimension)
|
||||||
|
bool readonly = false; // flag to indicate if the underlying storage may be written to
|
||||||
|
|
||||||
|
buffer_info() = default;
|
||||||
|
|
||||||
|
buffer_info(void *ptr,
|
||||||
|
ssize_t itemsize,
|
||||||
|
const std::string &format,
|
||||||
|
ssize_t ndim,
|
||||||
|
detail::any_container<ssize_t> shape_in,
|
||||||
|
detail::any_container<ssize_t> strides_in,
|
||||||
|
bool readonly = false)
|
||||||
|
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
||||||
|
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
|
||||||
|
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
|
||||||
|
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < (size_t) ndim; ++i) {
|
||||||
|
size *= shape[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
buffer_info(T *ptr,
|
||||||
|
detail::any_container<ssize_t> shape_in,
|
||||||
|
detail::any_container<ssize_t> strides_in,
|
||||||
|
bool readonly = false)
|
||||||
|
: buffer_info(private_ctr_tag(),
|
||||||
|
ptr,
|
||||||
|
sizeof(T),
|
||||||
|
format_descriptor<T>::format(),
|
||||||
|
static_cast<ssize_t>(shape_in->size()),
|
||||||
|
std::move(shape_in),
|
||||||
|
std::move(strides_in),
|
||||||
|
readonly) {}
|
||||||
|
|
||||||
|
buffer_info(void *ptr,
|
||||||
|
ssize_t itemsize,
|
||||||
|
const std::string &format,
|
||||||
|
ssize_t size,
|
||||||
|
bool readonly = false)
|
||||||
|
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
buffer_info(T *ptr, ssize_t size, bool readonly = false)
|
||||||
|
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
buffer_info(const T *ptr, ssize_t size, bool readonly = true)
|
||||||
|
: buffer_info(
|
||||||
|
const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
||||||
|
|
||||||
|
explicit buffer_info(Py_buffer *view, bool ownview = true)
|
||||||
|
: buffer_info(
|
||||||
|
view->buf,
|
||||||
|
view->itemsize,
|
||||||
|
view->format,
|
||||||
|
view->ndim,
|
||||||
|
{view->shape, view->shape + view->ndim},
|
||||||
|
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
|
||||||
|
* ignore this flag and return a view with NULL strides.
|
||||||
|
* When strides are NULL, build them manually. */
|
||||||
|
view->strides
|
||||||
|
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
||||||
|
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
||||||
|
(view->readonly != 0)) {
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||||
|
this->m_view = view;
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||||
|
this->ownview = ownview;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_info(const buffer_info &) = delete;
|
||||||
|
buffer_info &operator=(const buffer_info &) = delete;
|
||||||
|
|
||||||
|
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
|
||||||
|
|
||||||
|
buffer_info &operator=(buffer_info &&rhs) noexcept {
|
||||||
|
ptr = rhs.ptr;
|
||||||
|
itemsize = rhs.itemsize;
|
||||||
|
size = rhs.size;
|
||||||
|
format = std::move(rhs.format);
|
||||||
|
ndim = rhs.ndim;
|
||||||
|
shape = std::move(rhs.shape);
|
||||||
|
strides = std::move(rhs.strides);
|
||||||
|
std::swap(m_view, rhs.m_view);
|
||||||
|
std::swap(ownview, rhs.ownview);
|
||||||
|
readonly = rhs.readonly;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~buffer_info() {
|
||||||
|
if (m_view && ownview) {
|
||||||
|
PyBuffer_Release(m_view);
|
||||||
|
delete m_view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_buffer *view() const { return m_view; }
|
||||||
|
Py_buffer *&view() { return m_view; }
|
||||||
|
|
||||||
|
/* True if the buffer item type is equivalent to `T`. */
|
||||||
|
// To define "equivalent" by example:
|
||||||
|
// `buffer_info::item_type_is_equivalent_to<int>(b)` and
|
||||||
|
// `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
|
||||||
|
// on some platforms, but `int` and `unsigned` will never be equivalent.
|
||||||
|
// For the ground truth, please inspect `detail::compare_buffer_info<>`.
|
||||||
|
template <typename T>
|
||||||
|
bool item_type_is_equivalent_to() const {
|
||||||
|
return detail::compare_buffer_info<T>::compare(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct private_ctr_tag {};
|
||||||
|
|
||||||
|
buffer_info(private_ctr_tag,
|
||||||
|
void *ptr,
|
||||||
|
ssize_t itemsize,
|
||||||
|
const std::string &format,
|
||||||
|
ssize_t ndim,
|
||||||
|
detail::any_container<ssize_t> &&shape_in,
|
||||||
|
detail::any_container<ssize_t> &&strides_in,
|
||||||
|
bool readonly)
|
||||||
|
: buffer_info(
|
||||||
|
ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {}
|
||||||
|
|
||||||
|
Py_buffer *m_view = nullptr;
|
||||||
|
bool ownview = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <typename T, typename SFINAE>
|
||||||
|
struct compare_buffer_info {
|
||||||
|
static bool compare(const buffer_info &b) {
|
||||||
|
// NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
|
||||||
|
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
|
||||||
|
static bool compare(const buffer_info &b) {
|
||||||
|
return (size_t) b.itemsize == sizeof(T)
|
||||||
|
&& (b.format == format_descriptor<T>::value
|
||||||
|
|| ((sizeof(T) == sizeof(long))
|
||||||
|
&& b.format == (std::is_unsigned<T>::value ? "L" : "l"))
|
||||||
|
|| ((sizeof(T) == sizeof(size_t))
|
||||||
|
&& b.format == (std::is_unsigned<T>::value ? "N" : "n")));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
1704
3rdparty/pybind11/include/pybind11/cast.h
vendored
Normal file
1704
3rdparty/pybind11/include/pybind11/cast.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
225
3rdparty/pybind11/include/pybind11/chrono.h
vendored
Normal file
225
3rdparty/pybind11/include/pybind11/chrono.h
vendored
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
|
||||||
|
|
||||||
|
Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
|
||||||
|
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 "pybind11.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <ctime>
|
||||||
|
#include <datetime.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <typename type>
|
||||||
|
class duration_caster {
|
||||||
|
public:
|
||||||
|
using rep = typename type::rep;
|
||||||
|
using period = typename type::period;
|
||||||
|
|
||||||
|
// signed 25 bits required by the standard.
|
||||||
|
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>;
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// Lazy initialise the PyDateTime import
|
||||||
|
if (!PyDateTimeAPI) {
|
||||||
|
PyDateTime_IMPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If invoked with datetime.delta object
|
||||||
|
if (PyDelta_Check(src.ptr())) {
|
||||||
|
value = type(duration_cast<duration<rep, period>>(
|
||||||
|
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
||||||
|
+ seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
|
||||||
|
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If invoked with a float we assume it is seconds and convert
|
||||||
|
if (PyFloat_Check(src.ptr())) {
|
||||||
|
value = type(duration_cast<duration<rep, period>>(
|
||||||
|
duration<double>(PyFloat_AsDouble(src.ptr()))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a duration just return it back
|
||||||
|
static const std::chrono::duration<rep, period> &
|
||||||
|
get_duration(const std::chrono::duration<rep, period> &src) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a time_point get the time_since_epoch
|
||||||
|
template <typename Clock>
|
||||||
|
static std::chrono::duration<rep, period>
|
||||||
|
get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
|
||||||
|
return src.time_since_epoch();
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// Use overloaded function to get our duration from our source
|
||||||
|
// Works out if it is a duration or time_point and get the duration
|
||||||
|
auto d = get_duration(src);
|
||||||
|
|
||||||
|
// Lazy initialise the PyDateTime import
|
||||||
|
if (!PyDateTimeAPI) {
|
||||||
|
PyDateTime_IMPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare these special duration types so the conversions happen with the correct
|
||||||
|
// primitive types (int)
|
||||||
|
using dd_t = duration<int, std::ratio<86400>>;
|
||||||
|
using ss_t = duration<int, std::ratio<1>>;
|
||||||
|
using us_t = duration<int, std::micro>;
|
||||||
|
|
||||||
|
auto dd = duration_cast<dd_t>(d);
|
||||||
|
auto subd = d - dd;
|
||||||
|
auto ss = duration_cast<ss_t>(subd);
|
||||||
|
auto us = duration_cast<us_t>(subd - ss);
|
||||||
|
return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta"));
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
|
||||||
|
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER)
|
||||||
|
if (localtime_s(buf, time))
|
||||||
|
return nullptr;
|
||||||
|
return buf;
|
||||||
|
#else
|
||||||
|
static std::mutex mtx;
|
||||||
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
std::tm *tm_ptr = std::localtime(time);
|
||||||
|
if (tm_ptr != nullptr) {
|
||||||
|
*buf = *tm_ptr;
|
||||||
|
}
|
||||||
|
return tm_ptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is for casting times on the system clock into datetime.datetime instances
|
||||||
|
template <typename Duration>
|
||||||
|
class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||||
|
public:
|
||||||
|
using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// Lazy initialise the PyDateTime import
|
||||||
|
if (!PyDateTimeAPI) {
|
||||||
|
PyDateTime_IMPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tm cal;
|
||||||
|
microseconds msecs;
|
||||||
|
|
||||||
|
if (PyDateTime_Check(src.ptr())) {
|
||||||
|
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
|
||||||
|
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
|
||||||
|
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
|
||||||
|
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||||
|
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||||
|
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||||
|
cal.tm_isdst = -1;
|
||||||
|
msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
||||||
|
} else if (PyDate_Check(src.ptr())) {
|
||||||
|
cal.tm_sec = 0;
|
||||||
|
cal.tm_min = 0;
|
||||||
|
cal.tm_hour = 0;
|
||||||
|
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
||||||
|
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
||||||
|
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
||||||
|
cal.tm_isdst = -1;
|
||||||
|
msecs = microseconds(0);
|
||||||
|
} else if (PyTime_Check(src.ptr())) {
|
||||||
|
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
|
||||||
|
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
|
||||||
|
cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
|
||||||
|
cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
|
||||||
|
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
|
||||||
|
cal.tm_year = 70; // earliest available date for Python's datetime
|
||||||
|
cal.tm_isdst = -1;
|
||||||
|
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src,
|
||||||
|
return_value_policy /* policy */,
|
||||||
|
handle /* parent */) {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// Lazy initialise the PyDateTime import
|
||||||
|
if (!PyDateTimeAPI) {
|
||||||
|
PyDateTime_IMPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get out microseconds, and make sure they are positive, to avoid bug in eastern
|
||||||
|
// hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
|
||||||
|
using us_t = duration<int, std::micro>;
|
||||||
|
auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
|
||||||
|
if (us.count() < 0) {
|
||||||
|
us += seconds(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract microseconds BEFORE `system_clock::to_time_t`, because:
|
||||||
|
// > If std::time_t has lower precision, it is implementation-defined whether the value is
|
||||||
|
// rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
|
||||||
|
std::time_t tt
|
||||||
|
= system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
|
||||||
|
|
||||||
|
std::tm localtime;
|
||||||
|
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
|
||||||
|
if (!localtime_ptr) {
|
||||||
|
throw cast_error("Unable to represent system_clock in local time");
|
||||||
|
}
|
||||||
|
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
|
||||||
|
localtime.tm_mon + 1,
|
||||||
|
localtime.tm_mday,
|
||||||
|
localtime.tm_hour,
|
||||||
|
localtime.tm_min,
|
||||||
|
localtime.tm_sec,
|
||||||
|
us.count());
|
||||||
|
}
|
||||||
|
PYBIND11_TYPE_CASTER(type, const_name("datetime.datetime"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Other clocks that are not the system clock are not measured as datetime.datetime objects
|
||||||
|
// since they are not measured on calendar time. So instead we just make them timedeltas
|
||||||
|
// Or if they have passed us a time as a float we convert that
|
||||||
|
template <typename Clock, typename Duration>
|
||||||
|
class type_caster<std::chrono::time_point<Clock, Duration>>
|
||||||
|
: public duration_caster<std::chrono::time_point<Clock, Duration>> {};
|
||||||
|
|
||||||
|
template <typename Rep, typename Period>
|
||||||
|
class type_caster<std::chrono::duration<Rep, Period>>
|
||||||
|
: public duration_caster<std::chrono::duration<Rep, Period>> {};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
2
3rdparty/pybind11/include/pybind11/common.h
vendored
Normal file
2
3rdparty/pybind11/include/pybind11/common.h
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#include "detail/common.h"
|
||||||
|
#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'."
|
74
3rdparty/pybind11/include/pybind11/complex.h
vendored
Normal file
74
3rdparty/pybind11/include/pybind11/complex.h
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
pybind11/complex.h: Complex number support
|
||||||
|
|
||||||
|
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 "pybind11.h"
|
||||||
|
|
||||||
|
#include <complex>
|
||||||
|
|
||||||
|
/// glibc defines I as a macro which breaks things, e.g., boost template names
|
||||||
|
#ifdef I
|
||||||
|
# undef I
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||||
|
static constexpr const char c = format_descriptor<T>::c;
|
||||||
|
static constexpr const char value[3] = {'Z', c, '\0'};
|
||||||
|
static std::string format() { return std::string(value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef PYBIND11_CPP17
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr const char
|
||||||
|
format_descriptor<std::complex<T>,
|
||||||
|
detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
||||||
|
static constexpr bool value = true;
|
||||||
|
static constexpr int index = is_fmt_numeric<T>::index + 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class type_caster<std::complex<T>> {
|
||||||
|
public:
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!src) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!convert && !PyComplex_Check(src.ptr())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Py_complex result = PyComplex_AsCComplex(src.ptr());
|
||||||
|
if (result.real == -1.0 && PyErr_Occurred()) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = std::complex<T>((T) result.real, (T) result.imag);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle
|
||||||
|
cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) {
|
||||||
|
return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(std::complex<T>, const_name("complex"));
|
||||||
|
};
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
743
3rdparty/pybind11/include/pybind11/detail/class.h
vendored
Normal file
743
3rdparty/pybind11/include/pybind11/detail/class.h
vendored
Normal file
@ -0,0 +1,743 @@
|
|||||||
|
/*
|
||||||
|
pybind11/detail/class.h: Python C API implementation details for py::class_
|
||||||
|
|
||||||
|
Copyright (c) 2017 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 "../attr.h"
|
||||||
|
#include "../options.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
#if !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:
|
||||||
|
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \
|
||||||
|
setattr((PyObject *) obj, "__qualname__", nameobj)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline std::string get_fully_qualified_tp_name(PyTypeObject *type) {
|
||||||
|
#if !defined(PYPY_VERSION)
|
||||||
|
return type->tp_name;
|
||||||
|
#else
|
||||||
|
auto module_name = handle((PyObject *) type).attr("__module__").cast<std::string>();
|
||||||
|
if (module_name == PYBIND11_BUILTINS_MODULE)
|
||||||
|
return type->tp_name;
|
||||||
|
else
|
||||||
|
return std::move(module_name) + "." + type->tp_name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PyTypeObject *type_incref(PyTypeObject *type) {
|
||||||
|
Py_INCREF(type);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(PYPY_VERSION)
|
||||||
|
|
||||||
|
/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance.
|
||||||
|
extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) {
|
||||||
|
return PyProperty_Type.tp_descr_get(self, cls, cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `pybind11_static_property.__set__()`: Just like the above `__get__()`.
|
||||||
|
extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) {
|
||||||
|
PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj);
|
||||||
|
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. */
|
||||||
|
inline PyTypeObject *make_static_property_type() {
|
||||||
|
constexpr auto *name = "pybind11_static_property";
|
||||||
|
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||||
|
|
||||||
|
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||||
|
issue no Python C API calls which could potentially invoke the
|
||||||
|
garbage collector (the GC will call type_traverse(), which will in
|
||||||
|
turn find the newly constructed type in an invalid state) */
|
||||||
|
auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||||
|
if (!heap_type) {
|
||||||
|
pybind11_fail("make_static_property_type(): error allocating type!");
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||||
|
# ifdef PYBIND11_BUILTIN_QUALNAME
|
||||||
|
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||||
|
# endif
|
||||||
|
|
||||||
|
auto *type = &heap_type->ht_type;
|
||||||
|
type->tp_name = name;
|
||||||
|
type->tp_base = type_incref(&PyProperty_Type);
|
||||||
|
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
type->tp_descr_get = pybind11_static_get;
|
||||||
|
type->tp_descr_set = pybind11_static_set;
|
||||||
|
|
||||||
|
if (PyType_Ready(type) < 0) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // PYPY
|
||||||
|
|
||||||
|
/** PyPy has some issues with the above C API, so we evaluate Python code instead.
|
||||||
|
This function will only be called once so performance isn't really a concern.
|
||||||
|
Return value: New reference. */
|
||||||
|
inline PyTypeObject *make_static_property_type() {
|
||||||
|
auto d = dict();
|
||||||
|
PyObject *result = PyRun_String(R"(\
|
||||||
|
class pybind11_static_property(property):
|
||||||
|
def __get__(self, obj, cls):
|
||||||
|
return property.__get__(self, cls, cls)
|
||||||
|
|
||||||
|
def __set__(self, obj, value):
|
||||||
|
cls = obj if isinstance(obj, type) else type(obj)
|
||||||
|
property.__set__(self, cls, value)
|
||||||
|
)",
|
||||||
|
Py_file_input,
|
||||||
|
d.ptr(),
|
||||||
|
d.ptr());
|
||||||
|
if (result == nullptr)
|
||||||
|
throw error_already_set();
|
||||||
|
Py_DECREF(result);
|
||||||
|
return (PyTypeObject *) d["pybind11_static_property"].cast<object>().release().ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PYPY
|
||||||
|
|
||||||
|
/** Types with static properties need to handle `Type.static_prop = x` in a specific way.
|
||||||
|
By default, Python replaces the `static_property` itself, but for wrapped C++ types
|
||||||
|
we need to call `static_property.__set__()` in order to propagate the new value to
|
||||||
|
the underlying C++ data structure. */
|
||||||
|
extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyObject *value) {
|
||||||
|
// Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
|
||||||
|
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
|
||||||
|
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||||
|
|
||||||
|
// The following assignment combinations are possible:
|
||||||
|
// 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)`
|
||||||
|
// 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop`
|
||||||
|
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
|
||||||
|
auto *const static_prop = (PyObject *) get_internals().static_property_type;
|
||||||
|
const auto call_descr_set = (descr != nullptr) && (value != nullptr)
|
||||||
|
&& (PyObject_IsInstance(descr, static_prop) != 0)
|
||||||
|
&& (PyObject_IsInstance(value, static_prop) == 0);
|
||||||
|
if (call_descr_set) {
|
||||||
|
// Call `static_property.__set__()` instead of replacing the `static_property`.
|
||||||
|
#if !defined(PYPY_VERSION)
|
||||||
|
return Py_TYPE(descr)->tp_descr_set(descr, obj, value);
|
||||||
|
#else
|
||||||
|
if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
// Replace existing attribute.
|
||||||
|
return PyType_Type.tp_setattro(obj, name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
* when called on a class, or a PyMethod, when called on an instance. Override that behaviour here
|
||||||
|
* to do a special case bypass for PyInstanceMethod_Types.
|
||||||
|
*/
|
||||||
|
extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) {
|
||||||
|
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||||
|
if (descr && PyInstanceMethod_Check(descr)) {
|
||||||
|
Py_INCREF(descr);
|
||||||
|
return descr;
|
||||||
|
}
|
||||||
|
return PyType_Type.tp_getattro(obj, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// metaclass `__call__` function that is used to create all pybind11 objects.
|
||||||
|
extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) {
|
||||||
|
|
||||||
|
// use the default metaclass call to create/initialize the object
|
||||||
|
PyObject *self = PyType_Type.tp_call(type, args, kwargs);
|
||||||
|
if (self == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must be a pybind11 instance
|
||||||
|
auto *instance = reinterpret_cast<detail::instance *>(self);
|
||||||
|
|
||||||
|
// Ensure that the base __init__ function(s) were called
|
||||||
|
for (const auto &vh : values_and_holders(instance)) {
|
||||||
|
if (!vh.holder_constructed()) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"%.200s.__init__() must be called when overriding __init__",
|
||||||
|
get_fully_qualified_tp_name(vh.type->type).c_str());
|
||||||
|
Py_DECREF(self);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cleanup the type-info for a pybind11-registered type.
|
||||||
|
extern "C" inline void pybind11_meta_dealloc(PyObject *obj) {
|
||||||
|
auto *type = (PyTypeObject *) obj;
|
||||||
|
auto &internals = get_internals();
|
||||||
|
|
||||||
|
// A pybind11-registered type will:
|
||||||
|
// 1) be found in internals.registered_types_py
|
||||||
|
// 2) have exactly one associated `detail::type_info`
|
||||||
|
auto found_type = internals.registered_types_py.find(type);
|
||||||
|
if (found_type != internals.registered_types_py.end() && found_type->second.size() == 1
|
||||||
|
&& found_type->second[0]->type == type) {
|
||||||
|
|
||||||
|
auto *tinfo = found_type->second[0];
|
||||||
|
auto tindex = std::type_index(*tinfo->cpptype);
|
||||||
|
internals.direct_conversions.erase(tindex);
|
||||||
|
|
||||||
|
if (tinfo->module_local) {
|
||||||
|
get_local_internals().registered_types_cpp.erase(tindex);
|
||||||
|
} else {
|
||||||
|
internals.registered_types_cpp.erase(tindex);
|
||||||
|
}
|
||||||
|
internals.registered_types_py.erase(tinfo->type);
|
||||||
|
|
||||||
|
// Actually just `std::erase_if`, but that's only available in C++20
|
||||||
|
auto &cache = internals.inactive_override_cache;
|
||||||
|
for (auto it = cache.begin(), last = cache.end(); it != last;) {
|
||||||
|
if (it->first == (PyObject *) tinfo->type) {
|
||||||
|
it = cache.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete tinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyType_Type.tp_dealloc(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This metaclass is assigned by default to all pybind11 types and is required in order
|
||||||
|
for static properties to function correctly. Users may override this using `py::metaclass`.
|
||||||
|
Return value: New reference. */
|
||||||
|
inline PyTypeObject *make_default_metaclass() {
|
||||||
|
constexpr auto *name = "pybind11_type";
|
||||||
|
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||||
|
|
||||||
|
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||||
|
issue no Python C API calls which could potentially invoke the
|
||||||
|
garbage collector (the GC will call type_traverse(), which will in
|
||||||
|
turn find the newly constructed type in an invalid state) */
|
||||||
|
auto *heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
|
||||||
|
if (!heap_type) {
|
||||||
|
pybind11_fail("make_default_metaclass(): error allocating metaclass!");
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||||
|
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||||
|
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto *type = &heap_type->ht_type;
|
||||||
|
type->tp_name = name;
|
||||||
|
type->tp_base = type_incref(&PyType_Type);
|
||||||
|
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
|
||||||
|
type->tp_call = pybind11_meta_call;
|
||||||
|
|
||||||
|
type->tp_setattro = pybind11_meta_setattro;
|
||||||
|
type->tp_getattro = pybind11_meta_getattro;
|
||||||
|
|
||||||
|
type->tp_dealloc = pybind11_meta_dealloc;
|
||||||
|
|
||||||
|
if (PyType_Ready(type) < 0) {
|
||||||
|
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
|
||||||
|
}
|
||||||
|
|
||||||
|
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||||
|
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For multiple inheritance types we need to recursively register/deregister base pointers for any
|
||||||
|
/// base classes with pointers that are difference from the instance value pointer so that we can
|
||||||
|
/// correctly recognize an offset base class pointer. This calls a function with any offset base
|
||||||
|
/// ptrs.
|
||||||
|
inline void traverse_offset_bases(void *valueptr,
|
||||||
|
const detail::type_info *tinfo,
|
||||||
|
instance *self,
|
||||||
|
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
|
||||||
|
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
|
||||||
|
if (auto *parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
|
||||||
|
for (auto &c : parent_tinfo->implicit_casts) {
|
||||||
|
if (c.first == tinfo->cpptype) {
|
||||||
|
auto *parentptr = c.second(valueptr);
|
||||||
|
if (parentptr != valueptr) {
|
||||||
|
f(parentptr, self);
|
||||||
|
}
|
||||||
|
traverse_offset_bases(parentptr, parent_tinfo, self, f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool register_instance_impl(void *ptr, instance *self) {
|
||||||
|
get_internals().registered_instances.emplace(ptr, self);
|
||||||
|
return true; // unused, but gives the same signature as the deregister func
|
||||||
|
}
|
||||||
|
inline bool deregister_instance_impl(void *ptr, instance *self) {
|
||||||
|
auto ®istered_instances = get_internals().registered_instances;
|
||||||
|
auto range = registered_instances.equal_range(ptr);
|
||||||
|
for (auto it = range.first; it != range.second; ++it) {
|
||||||
|
if (self == it->second) {
|
||||||
|
registered_instances.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||||
|
register_instance_impl(valptr, self);
|
||||||
|
if (!tinfo->simple_ancestors) {
|
||||||
|
traverse_offset_bases(valptr, tinfo, self, register_instance_impl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||||
|
bool ret = deregister_instance_impl(valptr, self);
|
||||||
|
if (!tinfo->simple_ancestors) {
|
||||||
|
traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instance creation function for all pybind11 types. It allocates the internal instance layout
|
||||||
|
/// for holding C++ objects and holders. Allocation is done lazily (the first time the instance is
|
||||||
|
/// cast to a reference or pointer), and initialization is done by an `__init__` function.
|
||||||
|
inline PyObject *make_new_instance(PyTypeObject *type) {
|
||||||
|
#if defined(PYPY_VERSION)
|
||||||
|
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first
|
||||||
|
// inherited object is a plain Python type (i.e. not derived from an extension type). Fix it.
|
||||||
|
ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
|
||||||
|
if (type->tp_basicsize < instance_size) {
|
||||||
|
type->tp_basicsize = instance_size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
PyObject *self = type->tp_alloc(type, 0);
|
||||||
|
auto *inst = reinterpret_cast<instance *>(self);
|
||||||
|
// Allocate the value/holder internals:
|
||||||
|
inst->allocate_layout();
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Instance creation function for all pybind11 types. It only allocates space for the
|
||||||
|
/// C++ object, but doesn't call the constructor -- an `__init__` function must do that.
|
||||||
|
extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) {
|
||||||
|
return make_new_instance(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An `__init__` function constructs the C++ object. Users should provide at least one
|
||||||
|
/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the
|
||||||
|
/// following default function will be used which simply throws an exception.
|
||||||
|
extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) {
|
||||||
|
PyTypeObject *type = Py_TYPE(self);
|
||||||
|
std::string msg = get_fully_qualified_tp_name(type) + ": No constructor defined!";
|
||||||
|
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void add_patient(PyObject *nurse, PyObject *patient) {
|
||||||
|
auto &internals = get_internals();
|
||||||
|
auto *instance = reinterpret_cast<detail::instance *>(nurse);
|
||||||
|
instance->has_patients = true;
|
||||||
|
Py_INCREF(patient);
|
||||||
|
internals.patients[nurse].push_back(patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear_patients(PyObject *self) {
|
||||||
|
auto *instance = reinterpret_cast<detail::instance *>(self);
|
||||||
|
auto &internals = get_internals();
|
||||||
|
auto pos = internals.patients.find(self);
|
||||||
|
assert(pos != internals.patients.end());
|
||||||
|
// Clearing the patients can cause more Python code to run, which
|
||||||
|
// can invalidate the iterator. Extract the vector of patients
|
||||||
|
// from the unordered_map first.
|
||||||
|
auto patients = std::move(pos->second);
|
||||||
|
internals.patients.erase(pos);
|
||||||
|
instance->has_patients = false;
|
||||||
|
for (PyObject *&patient : patients) {
|
||||||
|
Py_CLEAR(patient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears all internal data from the instance and removes it from registered instances in
|
||||||
|
/// preparation for deallocation.
|
||||||
|
inline void clear_instance(PyObject *self) {
|
||||||
|
auto *instance = reinterpret_cast<detail::instance *>(self);
|
||||||
|
|
||||||
|
// Deallocate any values/holders, if present:
|
||||||
|
for (auto &v_h : values_and_holders(instance)) {
|
||||||
|
if (v_h) {
|
||||||
|
|
||||||
|
// We have to deregister before we call dealloc because, for virtual MI types, we still
|
||||||
|
// need to be able to get the parent pointers.
|
||||||
|
if (v_h.instance_registered()
|
||||||
|
&& !deregister_instance(instance, v_h.value_ptr(), v_h.type)) {
|
||||||
|
pybind11_fail(
|
||||||
|
"pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance->owned || v_h.holder_constructed()) {
|
||||||
|
v_h.type->dealloc(v_h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Deallocate the value/holder layout internals:
|
||||||
|
instance->deallocate_layout();
|
||||||
|
|
||||||
|
if (instance->weakrefs) {
|
||||||
|
PyObject_ClearWeakRefs(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject **dict_ptr = _PyObject_GetDictPtr(self);
|
||||||
|
if (dict_ptr) {
|
||||||
|
Py_CLEAR(*dict_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance->has_patients) {
|
||||||
|
clear_patients(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);
|
||||||
|
|
||||||
|
type->tp_free(self);
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX < 0x03080000
|
||||||
|
// `type->tp_dealloc != pybind11_object_dealloc` means that we're being called
|
||||||
|
// as part of a derived type's dealloc, in which case we're not allowed to decref
|
||||||
|
// the type here. For cross-module compatibility, we shouldn't compare directly
|
||||||
|
// with `pybind11_object_dealloc`, but with the common one stashed in internals.
|
||||||
|
auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base;
|
||||||
|
if (type->tp_dealloc == pybind11_object_type->tp_dealloc)
|
||||||
|
Py_DECREF(type);
|
||||||
|
#else
|
||||||
|
// This was not needed before Python 3.8 (Python issue 35810)
|
||||||
|
// https://github.com/pybind/pybind11/issues/1946
|
||||||
|
Py_DECREF(type);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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. */
|
||||||
|
inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||||
|
constexpr auto *name = "pybind11_object";
|
||||||
|
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||||
|
|
||||||
|
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||||
|
issue no Python C API calls which could potentially invoke the
|
||||||
|
garbage collector (the GC will call type_traverse(), which will in
|
||||||
|
turn find the newly constructed type in an invalid state) */
|
||||||
|
auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||||
|
if (!heap_type) {
|
||||||
|
pybind11_fail("make_object_base_type(): error allocating type!");
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_type->ht_name = name_obj.inc_ref().ptr();
|
||||||
|
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||||
|
heap_type->ht_qualname = name_obj.inc_ref().ptr();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto *type = &heap_type->ht_type;
|
||||||
|
type->tp_name = name;
|
||||||
|
type->tp_base = type_incref(&PyBaseObject_Type);
|
||||||
|
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||||
|
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
|
||||||
|
type->tp_new = pybind11_object_new;
|
||||||
|
type->tp_init = pybind11_object_init;
|
||||||
|
type->tp_dealloc = pybind11_object_dealloc;
|
||||||
|
|
||||||
|
/* Support weak references (needed for the keep_alive feature) */
|
||||||
|
type->tp_weaklistoffset = offsetof(instance, weakrefs);
|
||||||
|
|
||||||
|
if (PyType_Ready(type) < 0) {
|
||||||
|
pybind11_fail("PyType_Ready failed in make_object_base_type(): " + error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
setattr((PyObject *) type, "__module__", str("pybind11_builtins"));
|
||||||
|
PYBIND11_SET_OLDPY_QUALNAME(type, name_obj);
|
||||||
|
|
||||||
|
assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||||
|
return (PyObject *) heap_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// dynamic_attr: Allow the GC to clear the dictionary.
|
||||||
|
extern "C" inline int pybind11_clear(PyObject *self) {
|
||||||
|
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||||
|
Py_CLEAR(dict);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Give instances of this type a `__dict__` and opt into garbage collection.
|
||||||
|
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}};
|
||||||
|
type->tp_getset = getset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// buffer_protocol: Fill in the view as specified by flags.
|
||||||
|
extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) {
|
||||||
|
// Look for a `get_buffer` implementation in this type's info or any bases (following MRO).
|
||||||
|
type_info *tinfo = nullptr;
|
||||||
|
for (auto type : reinterpret_borrow<tuple>(Py_TYPE(obj)->tp_mro)) {
|
||||||
|
tinfo = get_type_info((PyTypeObject *) type.ptr());
|
||||||
|
if (tinfo && tinfo->get_buffer) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (view == nullptr || !tinfo || !tinfo->get_buffer) {
|
||||||
|
if (view) {
|
||||||
|
view->obj = nullptr;
|
||||||
|
}
|
||||||
|
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::memset(view, 0, sizeof(Py_buffer));
|
||||||
|
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
|
||||||
|
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
|
||||||
|
delete info;
|
||||||
|
// view->obj = nullptr; // Was just memset to 0, so not necessary
|
||||||
|
PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
view->obj = obj;
|
||||||
|
view->ndim = 1;
|
||||||
|
view->internal = info;
|
||||||
|
view->buf = info->ptr;
|
||||||
|
view->itemsize = info->itemsize;
|
||||||
|
view->len = view->itemsize;
|
||||||
|
for (auto s : info->shape) {
|
||||||
|
view->len *= s;
|
||||||
|
}
|
||||||
|
view->readonly = static_cast<int>(info->readonly);
|
||||||
|
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) {
|
||||||
|
view->format = const_cast<char *>(info->format.c_str());
|
||||||
|
}
|
||||||
|
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||||
|
view->ndim = (int) info->ndim;
|
||||||
|
view->strides = info->strides.data();
|
||||||
|
view->shape = info->shape.data();
|
||||||
|
}
|
||||||
|
Py_INCREF(view->obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// buffer_protocol: Release the resources of the buffer.
|
||||||
|
extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
|
||||||
|
delete (buffer_info *) view->internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
|
||||||
|
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a brand new Python type according to the `type_record` specification.
|
||||||
|
Return value: New reference. */
|
||||||
|
inline PyObject *make_new_python_type(const type_record &rec) {
|
||||||
|
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
|
||||||
|
|
||||||
|
auto qualname = name;
|
||||||
|
if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) {
|
||||||
|
qualname = reinterpret_steal<object>(
|
||||||
|
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
object module_;
|
||||||
|
if (rec.scope) {
|
||||||
|
if (hasattr(rec.scope, "__module__")) {
|
||||||
|
module_ = rec.scope.attr("__module__");
|
||||||
|
} else if (hasattr(rec.scope, "__name__")) {
|
||||||
|
module_ = rec.scope.attr("__name__");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto *full_name = c_str(
|
||||||
|
#if !defined(PYPY_VERSION)
|
||||||
|
module_ ? str(module_).cast<std::string>() + "." + rec.name :
|
||||||
|
#endif
|
||||||
|
rec.name);
|
||||||
|
|
||||||
|
char *tp_doc = nullptr;
|
||||||
|
if (rec.doc && options::show_user_defined_docstrings()) {
|
||||||
|
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
||||||
|
Python will free this later on) */
|
||||||
|
size_t size = std::strlen(rec.doc) + 1;
|
||||||
|
tp_doc = (char *) PyObject_MALLOC(size);
|
||||||
|
std::memcpy((void *) tp_doc, rec.doc, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &internals = get_internals();
|
||||||
|
auto bases = tuple(rec.bases);
|
||||||
|
auto *base = (bases.empty()) ? internals.instance_base : bases[0].ptr();
|
||||||
|
|
||||||
|
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||||
|
issue no Python C API calls which could potentially invoke the
|
||||||
|
garbage collector (the GC will call type_traverse(), which will in
|
||||||
|
turn find the newly constructed type in an invalid state) */
|
||||||
|
auto *metaclass
|
||||||
|
= rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() : internals.default_metaclass;
|
||||||
|
|
||||||
|
auto *heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||||
|
if (!heap_type) {
|
||||||
|
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_type->ht_name = name.release().ptr();
|
||||||
|
#ifdef PYBIND11_BUILTIN_QUALNAME
|
||||||
|
heap_type->ht_qualname = qualname.inc_ref().ptr();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto *type = &heap_type->ht_type;
|
||||||
|
type->tp_name = full_name;
|
||||||
|
type->tp_doc = tp_doc;
|
||||||
|
type->tp_base = type_incref((PyTypeObject *) base);
|
||||||
|
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||||
|
if (!bases.empty()) {
|
||||||
|
type->tp_bases = bases.release().ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't inherit base __init__ */
|
||||||
|
type->tp_init = pybind11_object_init;
|
||||||
|
|
||||||
|
/* Supported protocols */
|
||||||
|
type->tp_as_number = &heap_type->as_number;
|
||||||
|
type->tp_as_sequence = &heap_type->as_sequence;
|
||||||
|
type->tp_as_mapping = &heap_type->as_mapping;
|
||||||
|
type->tp_as_async = &heap_type->as_async;
|
||||||
|
|
||||||
|
/* Flags */
|
||||||
|
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
if (!rec.is_final) {
|
||||||
|
type->tp_flags |= Py_TPFLAGS_BASETYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rec.dynamic_attr) {
|
||||||
|
enable_dynamic_attributes(heap_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rec.buffer_protocol) {
|
||||||
|
enable_buffer_protocol(heap_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rec.custom_type_setup_callback) {
|
||||||
|
rec.custom_type_setup_callback(heap_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyType_Ready(type) < 0) {
|
||||||
|
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed: " + error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!rec.dynamic_attr || PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
|
||||||
|
|
||||||
|
/* Register type with the parent scope */
|
||||||
|
if (rec.scope) {
|
||||||
|
setattr(rec.scope, rec.name, (PyObject *) type);
|
||||||
|
} else {
|
||||||
|
Py_INCREF(type); // Keep it alive forever (reference leak)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module_) { // Needed by pydoc
|
||||||
|
setattr((PyObject *) type, "__module__", module_);
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_SET_OLDPY_QUALNAME(type, qualname);
|
||||||
|
|
||||||
|
return (PyObject *) type;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
1255
3rdparty/pybind11/include/pybind11/detail/common.h
vendored
Normal file
1255
3rdparty/pybind11/include/pybind11/detail/common.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
171
3rdparty/pybind11/include/pybind11/detail/descr.h
vendored
Normal file
171
3rdparty/pybind11/include/pybind11/detail/descr.h
vendored
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time
|
||||||
|
|
||||||
|
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 "common.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER)
|
||||||
|
# define PYBIND11_DESCR_CONSTEXPR static constexpr
|
||||||
|
#else
|
||||||
|
# define PYBIND11_DESCR_CONSTEXPR const
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Concatenate type signatures at compile time */
|
||||||
|
template <size_t N, typename... Ts>
|
||||||
|
struct descr {
|
||||||
|
char text[N + 1]{'\0'};
|
||||||
|
|
||||||
|
constexpr descr() = default;
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
constexpr descr(char const (&s)[N + 1]) : descr(s, make_index_sequence<N>()) {}
|
||||||
|
|
||||||
|
template <size_t... Is>
|
||||||
|
constexpr descr(char const (&s)[N + 1], index_sequence<Is...>) : text{s[Is]..., '\0'} {}
|
||||||
|
|
||||||
|
template <typename... Chars>
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
constexpr descr(char c, Chars... cs) : text{c, static_cast<char>(cs)..., '\0'} {}
|
||||||
|
|
||||||
|
static constexpr std::array<const std::type_info *, sizeof...(Ts) + 1> types() {
|
||||||
|
return {{&typeid(Ts)..., nullptr}};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2, size_t... Is1, size_t... Is2>
|
||||||
|
constexpr descr<N1 + N2, Ts1..., Ts2...> plus_impl(const descr<N1, Ts1...> &a,
|
||||||
|
const descr<N2, Ts2...> &b,
|
||||||
|
index_sequence<Is1...>,
|
||||||
|
index_sequence<Is2...>) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(b);
|
||||||
|
return {a.text[Is1]..., b.text[Is2]...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
|
||||||
|
constexpr descr<N1 + N2, Ts1..., Ts2...> operator+(const descr<N1, Ts1...> &a,
|
||||||
|
const descr<N2, Ts2...> &b) {
|
||||||
|
return plus_impl(a, b, make_index_sequence<N1>(), make_index_sequence<N2>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
constexpr descr<N - 1> const_name(char const (&text)[N]) {
|
||||||
|
return descr<N - 1>(text);
|
||||||
|
}
|
||||||
|
constexpr descr<0> const_name(char const (&)[1]) { return {}; }
|
||||||
|
|
||||||
|
template <size_t Rem, size_t... Digits>
|
||||||
|
struct int_to_str : int_to_str<Rem / 10, Rem % 10, Digits...> {};
|
||||||
|
template <size_t... Digits>
|
||||||
|
struct int_to_str<0, Digits...> {
|
||||||
|
// WARNING: This only works with C++17 or higher.
|
||||||
|
static constexpr auto digits = descr<sizeof...(Digits)>(('0' + Digits)...);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ternary description (like std::conditional)
|
||||||
|
template <bool B, size_t N1, size_t N2>
|
||||||
|
constexpr enable_if_t<B, descr<N1 - 1>> const_name(char const (&text1)[N1], char const (&)[N2]) {
|
||||||
|
return const_name(text1);
|
||||||
|
}
|
||||||
|
template <bool B, size_t N1, size_t N2>
|
||||||
|
constexpr enable_if_t<!B, descr<N2 - 1>> const_name(char const (&)[N1], char const (&text2)[N2]) {
|
||||||
|
return const_name(text2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool B, typename T1, typename T2>
|
||||||
|
constexpr enable_if_t<B, T1> const_name(const T1 &d, const T2 &) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
template <bool B, typename T1, typename T2>
|
||||||
|
constexpr enable_if_t<!B, T2> const_name(const T1 &, const T2 &d) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
auto constexpr const_name() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
|
||||||
|
return int_to_str<Size / 10, Size % 10>::digits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
constexpr descr<1, Type> const_name() {
|
||||||
|
return {'%'};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If "_" is defined as a macro, py::detail::_ cannot be provided.
|
||||||
|
// It is therefore best to use py::detail::const_name universally.
|
||||||
|
// This block is for backward compatibility only.
|
||||||
|
// (The const_name code is repeated to avoid introducing a "_" #define ourselves.)
|
||||||
|
#ifndef _
|
||||||
|
# define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY
|
||||||
|
template <size_t N>
|
||||||
|
constexpr descr<N - 1> _(char const (&text)[N]) {
|
||||||
|
return const_name<N>(text);
|
||||||
|
}
|
||||||
|
template <bool B, size_t N1, size_t N2>
|
||||||
|
constexpr enable_if_t<B, descr<N1 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||||
|
return const_name<B, N1, N2>(text1, text2);
|
||||||
|
}
|
||||||
|
template <bool B, size_t N1, size_t N2>
|
||||||
|
constexpr enable_if_t<!B, descr<N2 - 1>> _(char const (&text1)[N1], char const (&text2)[N2]) {
|
||||||
|
return const_name<B, N1, N2>(text1, text2);
|
||||||
|
}
|
||||||
|
template <bool B, typename T1, typename T2>
|
||||||
|
constexpr enable_if_t<B, T1> _(const T1 &d1, const T2 &d2) {
|
||||||
|
return const_name<B, T1, T2>(d1, d2);
|
||||||
|
}
|
||||||
|
template <bool B, typename T1, typename T2>
|
||||||
|
constexpr enable_if_t<!B, T2> _(const T1 &d1, const T2 &d2) {
|
||||||
|
return const_name<B, T1, T2>(d1, d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
|
||||||
|
return const_name<Size>();
|
||||||
|
}
|
||||||
|
template <typename Type>
|
||||||
|
constexpr descr<1, Type> _() {
|
||||||
|
return const_name<Type>();
|
||||||
|
}
|
||||||
|
#endif // #ifndef _
|
||||||
|
|
||||||
|
constexpr descr<0> concat() { return {}; }
|
||||||
|
|
||||||
|
template <size_t N, typename... Ts>
|
||||||
|
constexpr descr<N, Ts...> concat(const descr<N, Ts...> &descr) {
|
||||||
|
return descr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_fold_expressions
|
||||||
|
template <size_t N1, size_t N2, typename... Ts1, typename... Ts2>
|
||||||
|
constexpr descr<N1 + N2 + 2, Ts1..., Ts2...> operator,(const descr<N1, Ts1...> &a,
|
||||||
|
const descr<N2, Ts2...> &b) {
|
||||||
|
return a + const_name(", ") + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N, typename... Ts, typename... Args>
|
||||||
|
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args) {
|
||||||
|
return (d, ..., args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <size_t N, typename... Ts, typename... Args>
|
||||||
|
constexpr auto concat(const descr<N, Ts...> &d, const Args &...args)
|
||||||
|
-> decltype(std::declval<descr<N + 2, Ts...>>() + concat(args...)) {
|
||||||
|
return d + const_name(", ") + concat(args...);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <size_t N, typename... Ts>
|
||||||
|
constexpr descr<N + 2, Ts...> type_descr(const descr<N, Ts...> &descr) {
|
||||||
|
return const_name("{") + descr + const_name("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
434
3rdparty/pybind11/include/pybind11/detail/init.h
vendored
Normal file
434
3rdparty/pybind11/include/pybind11/detail/init.h
vendored
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
/*
|
||||||
|
pybind11/detail/init.h: init factory function implementation and support code.
|
||||||
|
|
||||||
|
Copyright (c) 2017 Jason Rhinelander <jason@imaginary.ca>
|
||||||
|
|
||||||
|
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 "class.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class type_caster<value_and_holder> {
|
||||||
|
public:
|
||||||
|
bool load(handle h, bool) {
|
||||||
|
value = reinterpret_cast<value_and_holder *>(h.ptr());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
using cast_op_type = value_and_holder &;
|
||||||
|
explicit operator value_and_holder &() { return *value; }
|
||||||
|
static constexpr auto name = const_name<value_and_holder>();
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_and_holder *value = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(initimpl)
|
||||||
|
|
||||||
|
inline void no_nullptr(void *ptr) {
|
||||||
|
if (!ptr) {
|
||||||
|
throw type_error("pybind11::init(): factory function returned nullptr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementing functions for all forms of py::init<...> and py::init(...)
|
||||||
|
template <typename Class>
|
||||||
|
using Cpp = typename Class::type;
|
||||||
|
template <typename Class>
|
||||||
|
using Alias = typename Class::type_alias;
|
||||||
|
template <typename Class>
|
||||||
|
using Holder = typename Class::holder_type;
|
||||||
|
|
||||||
|
template <typename Class>
|
||||||
|
using is_alias_constructible = std::is_constructible<Alias<Class>, Cpp<Class> &&>;
|
||||||
|
|
||||||
|
// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance.
|
||||||
|
template <typename Class, enable_if_t<Class::has_alias, int> = 0>
|
||||||
|
bool is_alias(Cpp<Class> *ptr) {
|
||||||
|
return dynamic_cast<Alias<Class> *>(ptr) != nullptr;
|
||||||
|
}
|
||||||
|
// Failing fallback version of the above for a no-alias class (always returns false)
|
||||||
|
template <typename /*Class*/>
|
||||||
|
constexpr bool is_alias(void *) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall
|
||||||
|
// back to brace aggregate initiailization so that for aggregate initialization can be used with
|
||||||
|
// py::init, e.g. `py::init<int, int>` to initialize a `struct T { int a; int b; }`. For
|
||||||
|
// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually
|
||||||
|
// works, but will not do the expected thing when `T` has an `initializer_list<T>` constructor).
|
||||||
|
template <typename Class,
|
||||||
|
typename... Args,
|
||||||
|
detail::enable_if_t<std::is_constructible<Class, Args...>::value, int> = 0>
|
||||||
|
inline Class *construct_or_initialize(Args &&...args) {
|
||||||
|
return new Class(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
template <typename Class,
|
||||||
|
typename... Args,
|
||||||
|
detail::enable_if_t<!std::is_constructible<Class, Args...>::value, int> = 0>
|
||||||
|
inline Class *construct_or_initialize(Args &&...args) {
|
||||||
|
return new Class{std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with
|
||||||
|
// an alias to provide only a single Cpp factory function as long as the Alias can be
|
||||||
|
// constructed from an rvalue reference of the base Cpp type. This means that Alias classes
|
||||||
|
// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to
|
||||||
|
// inherit all the base class constructors.
|
||||||
|
template <typename Class>
|
||||||
|
void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/,
|
||||||
|
value_and_holder &v_h,
|
||||||
|
Cpp<Class> &&base) {
|
||||||
|
v_h.value_ptr() = new Alias<Class>(std::move(base));
|
||||||
|
}
|
||||||
|
template <typename Class>
|
||||||
|
[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/,
|
||||||
|
value_and_holder &,
|
||||||
|
Cpp<Class> &&) {
|
||||||
|
throw type_error("pybind11::init(): unable to convert returned instance to required "
|
||||||
|
"alias class: no `Alias<Class>(Class &&)` constructor available");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error-generating fallback for factories that don't match one of the below construction
|
||||||
|
// mechanisms.
|
||||||
|
template <typename Class>
|
||||||
|
void construct(...) {
|
||||||
|
static_assert(!std::is_same<Class, Class>::value /* always false */,
|
||||||
|
"pybind11::init(): init function must return a compatible pointer, "
|
||||||
|
"holder, or value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer return v1: the factory function returns a class pointer for a registered class.
|
||||||
|
// If we don't need an alias (because this class doesn't have one, or because the final type is
|
||||||
|
// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to
|
||||||
|
// construct an Alias from the returned base instance.
|
||||||
|
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)) {
|
||||||
|
// 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
|
||||||
|
// throw an error), but we can't just call `delete ptr`: it might have a special
|
||||||
|
// deleter, or might be shared_from_this. So we construct a holder around it as if
|
||||||
|
// it was a normal instance, then steal the holder away into a local variable; thus
|
||||||
|
// the holder and destruction happens when we leave the C++ scope, and the holder
|
||||||
|
// class gets to handle the destruction however it likes.
|
||||||
|
v_h.value_ptr() = ptr;
|
||||||
|
v_h.set_instance_registered(true); // To prevent init_instance from registering it
|
||||||
|
v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder
|
||||||
|
Holder<Class> temp_holder(std::move(v_h.holder<Holder<Class>>())); // Steal the holder
|
||||||
|
v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null
|
||||||
|
v_h.set_instance_registered(false);
|
||||||
|
|
||||||
|
construct_alias_from_cpp<Class>(is_alias_constructible<Class>{}, v_h, std::move(*ptr));
|
||||||
|
} else {
|
||||||
|
// Otherwise the type isn't inherited, so we don't need an Alias
|
||||||
|
v_h.value_ptr() = ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over
|
||||||
|
// ownership of the pointer.
|
||||||
|
template <typename Class, enable_if_t<Class::has_alias, int> = 0>
|
||||||
|
void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
|
||||||
|
no_nullptr(alias_ptr);
|
||||||
|
v_h.value_ptr() = static_cast<Cpp<Class> *>(alias_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holder return: copy its pointer, and move or copy the returned holder into the new instance's
|
||||||
|
// holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
|
||||||
|
// derived type (through those holder's implicit conversion from derived class holder
|
||||||
|
// constructors).
|
||||||
|
template <typename Class>
|
||||||
|
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(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)) {
|
||||||
|
throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance "
|
||||||
|
"is not an alias instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
v_h.value_ptr() = ptr;
|
||||||
|
v_h.type->init_instance(v_h.inst, &holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return-by-value version 1: returning a cpp class by value. If the class has an alias and an
|
||||||
|
// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct
|
||||||
|
// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't
|
||||||
|
// need it, we simply move-construct the cpp value into a new instance.
|
||||||
|
template <typename Class>
|
||||||
|
void construct(value_and_holder &v_h, Cpp<Class> &&result, bool need_alias) {
|
||||||
|
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||||
|
static_assert(is_move_constructible<Cpp<Class>>::value,
|
||||||
|
"pybind11::init() return-by-value factory function requires a movable class");
|
||||||
|
if (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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return-by-value version 2: returning a value of the alias type itself. We move-construct an
|
||||||
|
// Alias instance (even if no the python-side inheritance is involved). The is intended for
|
||||||
|
// cases where Alias initialization is always desired.
|
||||||
|
template <typename Class>
|
||||||
|
void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
||||||
|
static_assert(
|
||||||
|
is_move_constructible<Alias<Class>>::value,
|
||||||
|
"pybind11::init() return-by-alias-value factory function requires a movable alias class");
|
||||||
|
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementing class for py::init<...>()
|
||||||
|
template <typename... Args>
|
||||||
|
struct constructor {
|
||||||
|
template <typename Class, typename... Extra, enable_if_t<!Class::has_alias, int> = 0>
|
||||||
|
static void execute(Class &cl, const Extra &...extra) {
|
||||||
|
cl.def(
|
||||||
|
"__init__",
|
||||||
|
[](value_and_holder &v_h, Args... args) {
|
||||||
|
v_h.value_ptr() = construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
|
||||||
|
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__",
|
||||||
|
[](value_and_holder &v_h, Args... args) {
|
||||||
|
if (Py_TYPE(v_h.inst) == v_h.type->type) {
|
||||||
|
v_h.value_ptr()
|
||||||
|
= construct_or_initialize<Cpp<Class>>(std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
v_h.value_ptr()
|
||||||
|
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
|
||||||
|
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__",
|
||||||
|
[](value_and_holder &v_h, Args... args) {
|
||||||
|
v_h.value_ptr()
|
||||||
|
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
static void execute(Class &cl, const Extra &...extra) {
|
||||||
|
cl.def(
|
||||||
|
"__init__",
|
||||||
|
[](value_and_holder &v_h, Args... args) {
|
||||||
|
v_h.value_ptr()
|
||||||
|
= construct_or_initialize<Alias<Class>>(std::forward<Args>(args)...);
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implementation class for py::init(Func) and py::init(Func, AliasFunc)
|
||||||
|
template <typename CFunc,
|
||||||
|
typename AFunc = void_type (*)(),
|
||||||
|
typename = function_signature_t<CFunc>,
|
||||||
|
typename = function_signature_t<AFunc>>
|
||||||
|
struct factory;
|
||||||
|
|
||||||
|
// Specialization for py::init(Func)
|
||||||
|
template <typename Func, typename Return, typename... Args>
|
||||||
|
struct factory<Func, void_type (*)(), Return(Args...)> {
|
||||||
|
remove_reference_t<Func> class_factory;
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||||
|
factory(Func &&f) : class_factory(std::forward<Func>(f)) {}
|
||||||
|
|
||||||
|
// The given class either has no alias or has no separate alias factory;
|
||||||
|
// this always constructs the class itself. If the class is registered with an alias
|
||||||
|
// type and an alias instance is needed (i.e. because the final type is a Python class
|
||||||
|
// inheriting from the C++ type) the returned value needs to either already be an alias
|
||||||
|
// instance, or the alias needs to be constructible from a `Class &&` argument.
|
||||||
|
template <typename Class, typename... Extra>
|
||||||
|
void execute(Class &cl, const Extra &...extra) && {
|
||||||
|
#if defined(PYBIND11_CPP14)
|
||||||
|
cl.def(
|
||||||
|
"__init__",
|
||||||
|
[func = std::move(class_factory)]
|
||||||
|
#else
|
||||||
|
auto &func = class_factory;
|
||||||
|
cl.def(
|
||||||
|
"__init__",
|
||||||
|
[func]
|
||||||
|
#endif
|
||||||
|
(value_and_holder &v_h, Args... args) {
|
||||||
|
construct<Class>(
|
||||||
|
v_h, func(std::forward<Args>(args)...), Py_TYPE(v_h.inst) != v_h.type->type);
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization for py::init(Func, AliasFunc)
|
||||||
|
template <typename CFunc,
|
||||||
|
typename AFunc,
|
||||||
|
typename CReturn,
|
||||||
|
typename... CArgs,
|
||||||
|
typename AReturn,
|
||||||
|
typename... AArgs>
|
||||||
|
struct factory<CFunc, AFunc, CReturn(CArgs...), AReturn(AArgs...)> {
|
||||||
|
static_assert(sizeof...(CArgs) == sizeof...(AArgs),
|
||||||
|
"pybind11::init(class_factory, alias_factory): class and alias factories "
|
||||||
|
"must have identical argument signatures");
|
||||||
|
static_assert(all_of<std::is_same<CArgs, AArgs>...>::value,
|
||||||
|
"pybind11::init(class_factory, alias_factory): class and alias factories "
|
||||||
|
"must have identical argument signatures");
|
||||||
|
|
||||||
|
remove_reference_t<CFunc> class_factory;
|
||||||
|
remove_reference_t<AFunc> alias_factory;
|
||||||
|
|
||||||
|
factory(CFunc &&c, AFunc &&a)
|
||||||
|
: class_factory(std::forward<CFunc>(c)), alias_factory(std::forward<AFunc>(a)) {}
|
||||||
|
|
||||||
|
// The class factory is called when the `self` type passed to `__init__` is the direct
|
||||||
|
// class (i.e. not inherited), the alias factory when `self` is a Python-side subtype.
|
||||||
|
template <typename Class, typename... Extra>
|
||||||
|
void execute(Class &cl, const Extra &...extra) && {
|
||||||
|
static_assert(Class::has_alias,
|
||||||
|
"The two-argument version of `py::init()` can "
|
||||||
|
"only be used if the class has an alias");
|
||||||
|
#if defined(PYBIND11_CPP14)
|
||||||
|
cl.def(
|
||||||
|
"__init__",
|
||||||
|
[class_func = std::move(class_factory), alias_func = std::move(alias_factory)]
|
||||||
|
#else
|
||||||
|
auto &class_func = class_factory;
|
||||||
|
auto &alias_func = alias_factory;
|
||||||
|
cl.def(
|
||||||
|
"__init__",
|
||||||
|
[class_func, alias_func]
|
||||||
|
#endif
|
||||||
|
(value_and_holder &v_h, CArgs... args) {
|
||||||
|
if (Py_TYPE(v_h.inst) == v_h.type->type) {
|
||||||
|
// If the instance type equals the registered type we don't have inheritance,
|
||||||
|
// so don't need the alias and can construct using the class function:
|
||||||
|
construct<Class>(v_h, class_func(std::forward<CArgs>(args)...), false);
|
||||||
|
} else {
|
||||||
|
construct<Class>(v_h, alias_func(std::forward<CArgs>(args)...), true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Set just the C++ state. Same as `__init__`.
|
||||||
|
template <typename Class, typename T>
|
||||||
|
void setstate(value_and_holder &v_h, T &&result, bool need_alias) {
|
||||||
|
construct<Class>(v_h, std::forward<T>(result), need_alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set both the C++ and Python states
|
||||||
|
template <typename Class,
|
||||||
|
typename T,
|
||||||
|
typename O,
|
||||||
|
enable_if_t<std::is_convertible<O, handle>::value, int> = 0>
|
||||||
|
void setstate(value_and_holder &v_h, std::pair<T, O> &&result, bool need_alias) {
|
||||||
|
construct<Class>(v_h, std::move(result.first), need_alias);
|
||||||
|
auto d = handle(result.second);
|
||||||
|
if (PyDict_Check(d.ptr()) && PyDict_Size(d.ptr()) == 0) {
|
||||||
|
// Skipping setattr below, to not force use of py::dynamic_attr() for Class unnecessarily.
|
||||||
|
// See PR #2972 for details.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setattr((PyObject *) v_h.inst, "__dict__", d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation for py::pickle(GetState, SetState)
|
||||||
|
template <typename Get,
|
||||||
|
typename Set,
|
||||||
|
typename = function_signature_t<Get>,
|
||||||
|
typename = function_signature_t<Set>>
|
||||||
|
struct pickle_factory;
|
||||||
|
|
||||||
|
template <typename Get,
|
||||||
|
typename Set,
|
||||||
|
typename RetState,
|
||||||
|
typename Self,
|
||||||
|
typename NewInstance,
|
||||||
|
typename ArgState>
|
||||||
|
struct pickle_factory<Get, Set, RetState(Self), NewInstance(ArgState)> {
|
||||||
|
static_assert(std::is_same<intrinsic_t<RetState>, intrinsic_t<ArgState>>::value,
|
||||||
|
"The type returned by `__getstate__` must be the same "
|
||||||
|
"as the argument accepted by `__setstate__`");
|
||||||
|
|
||||||
|
remove_reference_t<Get> get;
|
||||||
|
remove_reference_t<Set> set;
|
||||||
|
|
||||||
|
pickle_factory(Get get, Set set) : get(std::forward<Get>(get)), set(std::forward<Set>(set)) {}
|
||||||
|
|
||||||
|
template <typename Class, typename... Extra>
|
||||||
|
void execute(Class &cl, const Extra &...extra) && {
|
||||||
|
cl.def("__getstate__", std::move(get));
|
||||||
|
|
||||||
|
#if defined(PYBIND11_CPP14)
|
||||||
|
cl.def(
|
||||||
|
"__setstate__",
|
||||||
|
[func = std::move(set)]
|
||||||
|
#else
|
||||||
|
auto &func = set;
|
||||||
|
cl.def(
|
||||||
|
"__setstate__",
|
||||||
|
[func]
|
||||||
|
#endif
|
||||||
|
(value_and_holder &v_h, ArgState state) {
|
||||||
|
setstate<Class>(
|
||||||
|
v_h, func(std::forward<ArgState>(state)), Py_TYPE(v_h.inst) != v_h.type->type);
|
||||||
|
},
|
||||||
|
is_new_style_constructor(),
|
||||||
|
extra...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(initimpl)
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
656
3rdparty/pybind11/include/pybind11/detail/internals.h
vendored
Normal file
656
3rdparty/pybind11/include/pybind11/detail/internals.h
vendored
Normal file
@ -0,0 +1,656 @@
|
|||||||
|
/*
|
||||||
|
pybind11/detail/internals.h: Internal data structure and related functions
|
||||||
|
|
||||||
|
Copyright (c) 2017 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 "common.h"
|
||||||
|
|
||||||
|
#if defined(WITH_THREAD) && defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||||
|
# include "../gil.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../pytypes.h"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
/// Tracks the `internals` and `type_info` ABI version independent of the main library version.
|
||||||
|
///
|
||||||
|
/// Some portions of the code use an ABI that is conditional depending on this
|
||||||
|
/// version number. That allows ABI-breaking changes to be "pre-implemented".
|
||||||
|
/// Once the default version number is incremented, the conditional logic that
|
||||||
|
/// no longer applies can be removed. Additionally, users that need not
|
||||||
|
/// maintain ABI compatibility can increase the version number in order to take
|
||||||
|
/// advantage of any functionality/efficiency improvements that depend on the
|
||||||
|
/// newer ABI.
|
||||||
|
///
|
||||||
|
/// WARNING: If you choose to manually increase the ABI version, note that
|
||||||
|
/// pybind11 may not be tested as thoroughly with a non-default ABI version, and
|
||||||
|
/// further ABI-incompatible changes may be made before the ABI is officially
|
||||||
|
/// changed to the new version.
|
||||||
|
#ifndef PYBIND11_INTERNALS_VERSION
|
||||||
|
# if PY_VERSION_HEX >= 0x030C0000
|
||||||
|
// Version bump for Python 3.12+, before first 3.12 beta release.
|
||||||
|
# define PYBIND11_INTERNALS_VERSION 5
|
||||||
|
# else
|
||||||
|
# define PYBIND11_INTERNALS_VERSION 4
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This requirement is mainly to reduce the support burden (see PR #4570).
|
||||||
|
static_assert(PY_VERSION_HEX < 0x030C0000 || PYBIND11_INTERNALS_VERSION >= 5,
|
||||||
|
"pybind11 ABI version 5 is the minimum for Python 3.12+");
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
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();
|
||||||
|
inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
||||||
|
|
||||||
|
// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new
|
||||||
|
// Thread Specific Storage (TSS) API.
|
||||||
|
#if PY_VERSION_HEX >= 0x03070000
|
||||||
|
// Avoid unnecessary allocation of `Py_tss_t`, since we cannot use
|
||||||
|
// `Py_LIMITED_API` anyway.
|
||||||
|
# if PYBIND11_INTERNALS_VERSION > 4
|
||||||
|
# define PYBIND11_TLS_KEY_REF Py_tss_t &
|
||||||
|
# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
|
||||||
|
// Clang on macOS warns due to `Py_tss_NEEDS_INIT` not specifying an initializer
|
||||||
|
// for every field.
|
||||||
|
# define PYBIND11_TLS_KEY_INIT(var) \
|
||||||
|
_Pragma("GCC diagnostic push") /**/ \
|
||||||
|
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") /**/ \
|
||||||
|
Py_tss_t var \
|
||||||
|
= Py_tss_NEEDS_INIT; \
|
||||||
|
_Pragma("GCC diagnostic pop")
|
||||||
|
# else
|
||||||
|
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t var = Py_tss_NEEDS_INIT;
|
||||||
|
# endif
|
||||||
|
# define PYBIND11_TLS_KEY_CREATE(var) (PyThread_tss_create(&(var)) == 0)
|
||||||
|
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get(&(key))
|
||||||
|
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set(&(key), (value))
|
||||||
|
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set(&(key), nullptr)
|
||||||
|
# define PYBIND11_TLS_FREE(key) PyThread_tss_delete(&(key))
|
||||||
|
# else
|
||||||
|
# define PYBIND11_TLS_KEY_REF Py_tss_t *
|
||||||
|
# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr;
|
||||||
|
# define PYBIND11_TLS_KEY_CREATE(var) \
|
||||||
|
(((var) = PyThread_tss_alloc()) != nullptr && (PyThread_tss_create((var)) == 0))
|
||||||
|
# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
|
||||||
|
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
|
||||||
|
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
|
||||||
|
# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
// Usually an int but a long on Cygwin64 with Python 3.x
|
||||||
|
# define PYBIND11_TLS_KEY_REF decltype(PyThread_create_key())
|
||||||
|
# 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)
|
||||||
|
// 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.
|
||||||
|
inline void tls_replace_value(PYBIND11_TLS_KEY_REF key, void *value) {
|
||||||
|
PyThread_delete_key_value(key);
|
||||||
|
PyThread_set_key_value(key, value);
|
||||||
|
}
|
||||||
|
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_delete_key_value(key)
|
||||||
|
# define PYBIND11_TLS_REPLACE_VALUE(key, value) \
|
||||||
|
::pybind11::detail::tls_replace_value((key), (value))
|
||||||
|
# else
|
||||||
|
# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr)
|
||||||
|
# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value))
|
||||||
|
# endif
|
||||||
|
# define PYBIND11_TLS_FREE(key) (void) key
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
|
||||||
|
// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module
|
||||||
|
// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under
|
||||||
|
// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name,
|
||||||
|
// which works. If not under a known-good stl, provide our own name-based hash and equality
|
||||||
|
// functions that use the type name.
|
||||||
|
#if (PYBIND11_INTERNALS_VERSION <= 4 && defined(__GLIBCXX__)) \
|
||||||
|
|| (PYBIND11_INTERNALS_VERSION >= 5 && !defined(_LIBCPP_VERSION))
|
||||||
|
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; }
|
||||||
|
using type_hash = std::hash<std::type_index>;
|
||||||
|
using type_equal_to = std::equal_to<std::type_index>;
|
||||||
|
#else
|
||||||
|
inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) {
|
||||||
|
return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct type_hash {
|
||||||
|
size_t operator()(const std::type_index &t) const {
|
||||||
|
size_t hash = 5381;
|
||||||
|
const char *ptr = t.name();
|
||||||
|
while (auto c = static_cast<unsigned char>(*ptr++)) {
|
||||||
|
hash = (hash * 33) ^ c;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct type_equal_to {
|
||||||
|
bool operator()(const std::type_index &lhs, const std::type_index &rhs) const {
|
||||||
|
return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename value_type>
|
||||||
|
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
|
||||||
|
|
||||||
|
struct override_hash {
|
||||||
|
inline size_t operator()(const std::pair<const PyObject *, const char *> &v) const {
|
||||||
|
size_t value = std::hash<const void *>()(v.first);
|
||||||
|
value ^= std::hash<const void *>()(v.second) + 0x9e3779b9 + (value << 6) + (value >> 2);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Internal data structure used to track registered instances and types.
|
||||||
|
/// Whenever binary incompatible changes are made to this structure,
|
||||||
|
/// `PYBIND11_INTERNALS_VERSION` must be incremented.
|
||||||
|
struct internals {
|
||||||
|
// std::type_index -> pybind11's type information
|
||||||
|
type_map<type_info *> registered_types_cpp;
|
||||||
|
// PyTypeObject* -> base type_info(s)
|
||||||
|
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py;
|
||||||
|
std::unordered_multimap<const void *, instance *> registered_instances; // void * -> instance*
|
||||||
|
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
|
||||||
|
inactive_override_cache;
|
||||||
|
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||||
|
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
|
||||||
|
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||||
|
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across
|
||||||
|
// extensions
|
||||||
|
#if PYBIND11_INTERNALS_VERSION == 4
|
||||||
|
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5;
|
||||||
|
#endif
|
||||||
|
std::forward_list<std::string> static_strings; // Stores the std::strings backing
|
||||||
|
// detail::c_str()
|
||||||
|
PyTypeObject *static_property_type;
|
||||||
|
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);
|
||||||
|
# endif // PYBIND11_INTERNALS_VERSION > 4
|
||||||
|
|
||||||
|
// This destructor is called *after* Py_Finalize() in finalize_interpreter().
|
||||||
|
// That *SHOULD BE* fine. The following details what happens when PyThread_tss_free is
|
||||||
|
// called. PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does
|
||||||
|
// nothing. PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
|
||||||
|
// PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX).
|
||||||
|
// Neither of those have anything to do with CPython internals. PyMem_RawFree *requires*
|
||||||
|
// that the `tstate` be allocated with the CPython allocator.
|
||||||
|
PYBIND11_TLS_FREE(tstate);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Additional type information which does not fit into the PyTypeObject.
|
||||||
|
/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`.
|
||||||
|
struct type_info {
|
||||||
|
PyTypeObject *type;
|
||||||
|
const std::type_info *cpptype;
|
||||||
|
size_t type_size, type_align, holder_size_in_ptrs;
|
||||||
|
void *(*operator_new)(size_t);
|
||||||
|
void (*init_instance)(instance *, const void *);
|
||||||
|
void (*dealloc)(value_and_holder &v_h);
|
||||||
|
std::vector<PyObject *(*) (PyObject *, PyTypeObject *)> implicit_conversions;
|
||||||
|
std::vector<std::pair<const std::type_info *, void *(*) (void *)>> implicit_casts;
|
||||||
|
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
|
||||||
|
buffer_info *(*get_buffer)(PyObject *, void *) = nullptr;
|
||||||
|
void *get_buffer_data = nullptr;
|
||||||
|
void *(*module_local_load)(PyObject *, const type_info *) = nullptr;
|
||||||
|
/* A simple type never occurs as a (direct or indirect) parent
|
||||||
|
* of a class that makes use of multiple inheritance.
|
||||||
|
* A type can be simple even if it has non-simple ancestors as long as it has no descendants.
|
||||||
|
*/
|
||||||
|
bool simple_type : 1;
|
||||||
|
/* True if there is no multiple inheritance in this type's inheritance tree */
|
||||||
|
bool simple_ancestors : 1;
|
||||||
|
/* for base vs derived holder_type checks */
|
||||||
|
bool default_holder : 1;
|
||||||
|
/* true if this is a type registered with py::module_local */
|
||||||
|
bool module_local : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||||
|
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||||
|
# define PYBIND11_BUILD_TYPE "_debug"
|
||||||
|
#else
|
||||||
|
# define PYBIND11_BUILD_TYPE ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Let's assume that different compilers are ABI-incompatible.
|
||||||
|
/// A user can manually set this string if they know their
|
||||||
|
/// compiler is compatible.
|
||||||
|
#ifndef PYBIND11_COMPILER_TYPE
|
||||||
|
# if defined(_MSC_VER)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_msvc"
|
||||||
|
# elif defined(__INTEL_COMPILER)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_icc"
|
||||||
|
# elif defined(__clang__)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_clang"
|
||||||
|
# elif defined(__PGI)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_pgi"
|
||||||
|
# elif defined(__MINGW32__)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_mingw"
|
||||||
|
# elif defined(__CYGWIN__)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
|
||||||
|
# elif defined(__GNUC__)
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_gcc"
|
||||||
|
# else
|
||||||
|
# define PYBIND11_COMPILER_TYPE "_unknown"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Also standard libs
|
||||||
|
#ifndef PYBIND11_STDLIB
|
||||||
|
# if defined(_LIBCPP_VERSION)
|
||||||
|
# define PYBIND11_STDLIB "_libcpp"
|
||||||
|
# elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||||
|
# define PYBIND11_STDLIB "_libstdcpp"
|
||||||
|
# else
|
||||||
|
# define PYBIND11_STDLIB ""
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
|
||||||
|
#ifndef PYBIND11_BUILD_ABI
|
||||||
|
# if defined(__GXX_ABI_VERSION)
|
||||||
|
# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
|
||||||
|
# else
|
||||||
|
# define PYBIND11_BUILD_ABI ""
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PYBIND11_INTERNALS_KIND
|
||||||
|
# if defined(WITH_THREAD)
|
||||||
|
# define PYBIND11_INTERNALS_KIND ""
|
||||||
|
# else
|
||||||
|
# define PYBIND11_INTERNALS_KIND "_without_thread"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PYBIND11_INTERNALS_ID \
|
||||||
|
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||||
|
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
|
||||||
|
PYBIND11_BUILD_TYPE "__"
|
||||||
|
|
||||||
|
#define PYBIND11_MODULE_LOCAL_ID \
|
||||||
|
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||||
|
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
|
||||||
|
PYBIND11_BUILD_TYPE "__"
|
||||||
|
|
||||||
|
/// Each module locally stores a pointer to the `internals` data. The data
|
||||||
|
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
||||||
|
inline internals **&get_internals_pp() {
|
||||||
|
static internals **internals_pp = nullptr;
|
||||||
|
return internals_pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward decl
|
||||||
|
inline void translate_exception(std::exception_ptr);
|
||||||
|
|
||||||
|
template <class T,
|
||||||
|
enable_if_t<std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
|
||||||
|
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
|
||||||
|
std::exception_ptr nested = exc.nested_ptr();
|
||||||
|
if (nested != nullptr && nested != p) {
|
||||||
|
translate_exception(nested);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T,
|
||||||
|
enable_if_t<!std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0>
|
||||||
|
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) {
|
||||||
|
if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(exc))) {
|
||||||
|
return handle_nested_exception(*nep, p);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool raise_err(PyObject *exc_type, const char *msg) {
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
raise_from(exc_type, msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PyErr_SetString(exc_type, msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void translate_exception(std::exception_ptr p) {
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(p);
|
||||||
|
} catch (error_already_set &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
e.restore();
|
||||||
|
return;
|
||||||
|
} catch (const builtin_exception &e) {
|
||||||
|
// Could not use template since it's an abstract class.
|
||||||
|
if (const auto *nep = dynamic_cast<const std::nested_exception *>(std::addressof(e))) {
|
||||||
|
handle_nested_exception(*nep, p);
|
||||||
|
}
|
||||||
|
e.set_error();
|
||||||
|
return;
|
||||||
|
} catch (const std::bad_alloc &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_MemoryError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::domain_error &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_ValueError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_ValueError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::length_error &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_ValueError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::out_of_range &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_IndexError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::range_error &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_ValueError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::overflow_error &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_OverflowError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_RuntimeError, e.what());
|
||||||
|
return;
|
||||||
|
} catch (const std::nested_exception &e) {
|
||||||
|
handle_nested_exception(e, p);
|
||||||
|
raise_err(PyExc_RuntimeError, "Caught an unknown nested exception!");
|
||||||
|
return;
|
||||||
|
} catch (...) {
|
||||||
|
raise_err(PyExc_RuntimeError, "Caught an unknown exception!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(__GLIBCXX__)
|
||||||
|
inline void translate_local_exception(std::exception_ptr p) {
|
||||||
|
try {
|
||||||
|
if (p) {
|
||||||
|
std::rethrow_exception(p);
|
||||||
|
}
|
||||||
|
} catch (error_already_set &e) {
|
||||||
|
e.restore();
|
||||||
|
return;
|
||||||
|
} catch (const builtin_exception &e) {
|
||||||
|
e.set_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline object get_python_state_dict() {
|
||||||
|
object state_dict;
|
||||||
|
#if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
|
||||||
|
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
|
||||||
|
#else
|
||||||
|
# if PY_VERSION_HEX < 0x03090000
|
||||||
|
PyInterpreterState *istate = _PyInterpreterState_Get();
|
||||||
|
# else
|
||||||
|
PyInterpreterState *istate = PyInterpreterState_Get();
|
||||||
|
# endif
|
||||||
|
if (istate) {
|
||||||
|
state_dict = reinterpret_borrow<object>(PyInterpreterState_GetDict(istate));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!state_dict) {
|
||||||
|
raise_from(PyExc_SystemError, "pybind11::detail::get_python_state_dict() FAILED");
|
||||||
|
}
|
||||||
|
return state_dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object get_internals_obj_from_state_dict(handle state_dict) {
|
||||||
|
return reinterpret_borrow<object>(dict_getitemstring(state_dict.ptr(), PYBIND11_INTERNALS_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline internals **get_internals_pp_from_capsule(handle obj) {
|
||||||
|
void *raw_ptr = PyCapsule_GetPointer(obj.ptr(), /*name=*/nullptr);
|
||||||
|
if (raw_ptr == nullptr) {
|
||||||
|
raise_from(PyExc_SystemError, "pybind11::detail::get_internals_pp_from_capsule() FAILED");
|
||||||
|
}
|
||||||
|
return static_cast<internals **>(raw_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the current `internals` data
|
||||||
|
PYBIND11_NOINLINE internals &get_internals() {
|
||||||
|
auto **&internals_pp = get_internals_pp();
|
||||||
|
if (internals_pp && *internals_pp) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
dict state_dict = get_python_state_dict();
|
||||||
|
if (object internals_obj = get_internals_obj_from_state_dict(state_dict)) {
|
||||||
|
internals_pp = get_internals_pp_from_capsule(internals_obj);
|
||||||
|
}
|
||||||
|
if (internals_pp && *internals_pp) {
|
||||||
|
// We loaded the internals through `state_dict`, which means that our `error_already_set`
|
||||||
|
// and `builtin_exception` may be different local classes than the ones set up in the
|
||||||
|
// initial exception translator, below, so add another for our local exception classes.
|
||||||
|
//
|
||||||
|
// libstdc++ doesn't require this (types there are identified only by name)
|
||||||
|
// libc++ with CPython doesn't require this (types are explicitly exported)
|
||||||
|
// libc++ with PyPy still need it, awaiting further investigation
|
||||||
|
#if !defined(__GLIBCXX__)
|
||||||
|
(*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
if (!internals_pp) {
|
||||||
|
internals_pp = new internals *();
|
||||||
|
}
|
||||||
|
auto *&internals_ptr = *internals_pp;
|
||||||
|
internals_ptr = new internals();
|
||||||
|
#if defined(WITH_THREAD)
|
||||||
|
|
||||||
|
PyThreadState *tstate = PyThreadState_Get();
|
||||||
|
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||||
|
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->tstate)) {
|
||||||
|
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
|
||||||
|
}
|
||||||
|
PYBIND11_TLS_REPLACE_VALUE(internals_ptr->tstate, tstate);
|
||||||
|
|
||||||
|
# if PYBIND11_INTERNALS_VERSION > 4
|
||||||
|
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||||
|
if (!PYBIND11_TLS_KEY_CREATE(internals_ptr->loader_life_support_tls_key)) {
|
||||||
|
pybind11_fail("get_internals: could not successfully initialize the "
|
||||||
|
"loader_life_support TSS key!");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
internals_ptr->istate = tstate->interp;
|
||||||
|
#endif
|
||||||
|
state_dict[PYBIND11_INTERNALS_ID] = capsule(internals_pp);
|
||||||
|
internals_ptr->registered_exception_translators.push_front(&translate_exception);
|
||||||
|
internals_ptr->static_property_type = make_static_property_type();
|
||||||
|
internals_ptr->default_metaclass = make_default_metaclass();
|
||||||
|
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
|
||||||
|
}
|
||||||
|
return **internals_pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the internals struct (above) is shared between all the modules. local_internals are only
|
||||||
|
// for a single module. Any changes made to internals may require an update to
|
||||||
|
// PYBIND11_INTERNALS_VERSION, breaking backwards compatibility. local_internals is, by design,
|
||||||
|
// restricted to a single module. Whether a module has local internals or not should not
|
||||||
|
// impact any other modules, because the only things accessing the local internals is the
|
||||||
|
// module that contains them.
|
||||||
|
struct local_internals {
|
||||||
|
type_map<type_info *> registered_types_cpp;
|
||||||
|
std::forward_list<ExceptionTranslator> registered_exception_translators;
|
||||||
|
#if defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
|
||||||
|
|
||||||
|
// For ABI compatibility, we can't store the loader_life_support TLS key in
|
||||||
|
// the `internals` struct directly. Instead, we store it in `shared_data` and
|
||||||
|
// cache a copy in `local_internals`. If we allocated a separate TLS key for
|
||||||
|
// each instance of `local_internals`, we could end up allocating hundreds of
|
||||||
|
// TLS keys if hundreds of different pybind11 modules are loaded (which is a
|
||||||
|
// plausible number).
|
||||||
|
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||||
|
|
||||||
|
// Holds the shared TLS key for the loader_life_support stack.
|
||||||
|
struct shared_loader_life_support_data {
|
||||||
|
PYBIND11_TLS_KEY_INIT(loader_life_support_tls_key)
|
||||||
|
shared_loader_life_support_data() {
|
||||||
|
// NOLINTNEXTLINE(bugprone-assignment-in-if-condition)
|
||||||
|
if (!PYBIND11_TLS_KEY_CREATE(loader_life_support_tls_key)) {
|
||||||
|
pybind11_fail("local_internals: could not successfully initialize the "
|
||||||
|
"loader_life_support TLS key!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We can't help but leak the TLS key, because Python never unloads extension modules.
|
||||||
|
};
|
||||||
|
|
||||||
|
local_internals() {
|
||||||
|
auto &internals = get_internals();
|
||||||
|
// Get or create the `loader_life_support_stack_key`.
|
||||||
|
auto &ptr = internals.shared_data["_life_support"];
|
||||||
|
if (!ptr) {
|
||||||
|
ptr = new shared_loader_life_support_data;
|
||||||
|
}
|
||||||
|
loader_life_support_tls_key
|
||||||
|
= static_cast<shared_loader_life_support_data *>(ptr)->loader_life_support_tls_key;
|
||||||
|
}
|
||||||
|
#endif // defined(WITH_THREAD) && PYBIND11_INTERNALS_VERSION == 4
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its
|
||||||
|
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only
|
||||||
|
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are
|
||||||
|
/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name).
|
||||||
|
template <typename... Args>
|
||||||
|
const char *c_str(Args &&...args) {
|
||||||
|
auto &strings = get_internals().static_strings;
|
||||||
|
strings.emplace_front(std::forward<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
|
||||||
|
/// pybind11 version) running in the current interpreter. Names starting with underscores
|
||||||
|
/// are reserved for internal usage. Returns `nullptr` if no matching entry was found.
|
||||||
|
PYBIND11_NOINLINE void *get_shared_data(const std::string &name) {
|
||||||
|
auto &internals = detail::get_internals();
|
||||||
|
auto it = internals.shared_data.find(name);
|
||||||
|
return it != internals.shared_data.end() ? it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the shared data that can be later recovered by `get_shared_data()`.
|
||||||
|
PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) {
|
||||||
|
detail::get_internals().shared_data[name] = data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if
|
||||||
|
/// such entry exists. Otherwise, a new object of default-constructible type `T` is
|
||||||
|
/// added to the shared data under the given name and a reference to it is returned.
|
||||||
|
template <typename T>
|
||||||
|
T &get_or_create_shared_data(const std::string &name) {
|
||||||
|
auto &internals = detail::get_internals();
|
||||||
|
auto it = internals.shared_data.find(name);
|
||||||
|
T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr);
|
||||||
|
if (!ptr) {
|
||||||
|
ptr = new T();
|
||||||
|
internals.shared_data[name] = ptr;
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
1177
3rdparty/pybind11/include/pybind11/detail/type_caster_base.h
vendored
Normal file
1177
3rdparty/pybind11/include/pybind11/detail/type_caster_base.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
65
3rdparty/pybind11/include/pybind11/detail/typeid.h
vendored
Normal file
65
3rdparty/pybind11/include/pybind11/detail/typeid.h
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
pybind11/detail/typeid.h: Compiler-independent access to type identifiers
|
||||||
|
|
||||||
|
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 <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
# include <cxxabi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
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;;) {
|
||||||
|
pos = string.find(search, pos);
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
string.erase(pos, search.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NOINLINE void clean_type_id(std::string &name) {
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
int status = 0;
|
||||||
|
std::unique_ptr<char, void (*)(void *)> res{
|
||||||
|
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};
|
||||||
|
if (status == 0) {
|
||||||
|
name = res.get();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
detail::erase_all(name, "class ");
|
||||||
|
detail::erase_all(name, "struct ");
|
||||||
|
detail::erase_all(name, "enum ");
|
||||||
|
#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());
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
12
3rdparty/pybind11/include/pybind11/eigen.h
vendored
Normal file
12
3rdparty/pybind11/include/pybind11/eigen.h
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
pybind11/eigen.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 "eigen/matrix.h"
|
9
3rdparty/pybind11/include/pybind11/eigen/common.h
vendored
Normal file
9
3rdparty/pybind11/include/pybind11/eigen/common.h
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) 2023 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Common message for `static_assert()`s, which are useful to easily
|
||||||
|
// preempt much less obvious errors.
|
||||||
|
#define PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED \
|
||||||
|
"Pointer types (in particular `PyObject *`) are not supported as scalar types for Eigen " \
|
||||||
|
"types."
|
714
3rdparty/pybind11/include/pybind11/eigen/matrix.h
vendored
Normal file
714
3rdparty/pybind11/include/pybind11/eigen/matrix.h
vendored
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
#include "common.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;
|
||||||
|
static_assert(!std::is_pointer<Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
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 {
|
||||||
|
static_assert(!std::is_pointer<typename MapType::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
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;
|
||||||
|
static_assert(!std::is_pointer<Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
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>> {
|
||||||
|
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
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;
|
||||||
|
static_assert(!std::is_pointer<Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
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)
|
516
3rdparty/pybind11/include/pybind11/eigen/tensor.h
vendored
Normal file
516
3rdparty/pybind11/include/pybind11/eigen/tensor.h
vendored
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
#include "common.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> {
|
||||||
|
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
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> {
|
||||||
|
static_assert(!std::is_pointer<typename Type::Scalar>::value,
|
||||||
|
PYBIND11_EIGEN_MESSAGE_POINTER_TYPES_ARE_NOT_SUPPORTED);
|
||||||
|
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)
|
316
3rdparty/pybind11/include/pybind11/embed.h
vendored
Normal file
316
3rdparty/pybind11/include/pybind11/embed.h
vendored
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
pybind11/embed.h: Support for embedding the interpreter
|
||||||
|
|
||||||
|
Copyright (c) 2017 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 "pybind11.h"
|
||||||
|
#include "eval.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(PYPY_VERSION)
|
||||||
|
# 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(); }
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Add a new module to the table of builtins for the interpreter. Must be
|
||||||
|
defined in global scope. The first macro parameter is the name of the
|
||||||
|
module (without quotes). The second parameter is the variable which will
|
||||||
|
be used as the interface to add functions and classes to the module.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
PYBIND11_EMBEDDED_MODULE(example, m) {
|
||||||
|
// ... initialize functions and classes here
|
||||||
|
m.def("foo", []() {
|
||||||
|
return "Hello, World!";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
\endrst */
|
||||||
|
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||||
|
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
|
||||||
|
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
|
||||||
|
static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \
|
||||||
|
auto m = ::pybind11::module_::create_extension_module( \
|
||||||
|
PYBIND11_TOSTRING(name), nullptr, &PYBIND11_CONCAT(pybind11_module_def_, name)); \
|
||||||
|
try { \
|
||||||
|
PYBIND11_CONCAT(pybind11_init_, name)(m); \
|
||||||
|
return m.ptr(); \
|
||||||
|
} \
|
||||||
|
PYBIND11_CATCH_INIT_EXCEPTIONS \
|
||||||
|
} \
|
||||||
|
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
|
||||||
|
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \
|
||||||
|
PYBIND11_TOSTRING(name), PYBIND11_CONCAT(pybind11_init_impl_, name)); \
|
||||||
|
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ \
|
||||||
|
& variable) // NOLINT(bugprone-macro-parentheses)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
|
||||||
|
struct embedded_module {
|
||||||
|
using init_t = PyObject *(*) ();
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = PyImport_AppendInittab(name, init);
|
||||||
|
if (result == -1) {
|
||||||
|
pybind11_fail("Insufficient memory to add a new module");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wide_char_arg_deleter {
|
||||||
|
void operator()(wchar_t *ptr) const {
|
||||||
|
// API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
|
||||||
|
PyMem_RawFree(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline wchar_t *widen_chars(const char *safe_arg) {
|
||||||
|
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr);
|
||||||
|
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
|
||||||
|
|
||||||
|
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||||
|
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||||
|
bool special_case = (argv == nullptr || argc <= 0);
|
||||||
|
|
||||||
|
const char *const empty_argv[]{"\0"};
|
||||||
|
const char *const *safe_argv = special_case ? empty_argv : argv;
|
||||||
|
if (special_case) {
|
||||||
|
argc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto argv_size = static_cast<size_t>(argc);
|
||||||
|
// 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;
|
||||||
|
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]));
|
||||||
|
if (!widened_argv_entries.back()) {
|
||||||
|
// A null here indicates a character-encoding failure or the python
|
||||||
|
// interpreter out of memory. Give up.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
widened_argv[ii] = widened_argv_entries.back().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *pysys_argv = widened_argv.get();
|
||||||
|
|
||||||
|
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
|
||||||
|
optional `init_signal_handlers` parameter can be used to skip the registration of
|
||||||
|
signal handlers (see the `Python documentation`_ for details). Calling this function
|
||||||
|
again after the interpreter has already been initialized is a fatal error.
|
||||||
|
|
||||||
|
If initializing the Python interpreter fails, then the program is terminated. (This
|
||||||
|
is controlled by the CPython runtime and is an exception to pybind11's normal behavior
|
||||||
|
of throwing exceptions on errors.)
|
||||||
|
|
||||||
|
The remaining optional parameters, `argc`, `argv`, and `add_program_dir_to_path` are
|
||||||
|
used to populate ``sys.argv`` and ``sys.path``.
|
||||||
|
See the |PySys_SetArgvEx documentation|_ for details.
|
||||||
|
|
||||||
|
.. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx
|
||||||
|
.. |PySys_SetArgvEx documentation| replace:: ``PySys_SetArgvEx`` documentation
|
||||||
|
.. _PySys_SetArgvEx documentation: https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx
|
||||||
|
\endrst */
|
||||||
|
inline void initialize_interpreter(bool init_signal_handlers = true,
|
||||||
|
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_InitPythonConfig(&config);
|
||||||
|
// See PR #4473 for background
|
||||||
|
config.parse_argv = 0;
|
||||||
|
|
||||||
|
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||||
|
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Shut down the Python interpreter. No pybind11 or CPython API functions can be called
|
||||||
|
after this. In addition, pybind11 objects must not outlive the interpreter:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
{ // BAD
|
||||||
|
py::initialize_interpreter();
|
||||||
|
auto hello = py::str("Hello, World!");
|
||||||
|
py::finalize_interpreter();
|
||||||
|
} // <-- BOOM, hello's destructor is called after interpreter shutdown
|
||||||
|
|
||||||
|
{ // GOOD
|
||||||
|
py::initialize_interpreter();
|
||||||
|
{ // scoped
|
||||||
|
auto hello = py::str("Hello, World!");
|
||||||
|
} // <-- OK, hello is cleaned up properly
|
||||||
|
py::finalize_interpreter();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // BETTER
|
||||||
|
py::scoped_interpreter guard{};
|
||||||
|
auto hello = py::str("Hello, World!");
|
||||||
|
}
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The interpreter can be restarted by calling `initialize_interpreter` again.
|
||||||
|
Modules created using pybind11 can be safely re-initialized. However, Python
|
||||||
|
itself cannot completely unload binary extension modules and there are several
|
||||||
|
caveats with regard to interpreter restarting. All the details can be found
|
||||||
|
in the CPython documentation. In short, not all interpreter memory may be
|
||||||
|
freed, either due to reference cycles or user-created global data.
|
||||||
|
|
||||||
|
\endrst */
|
||||||
|
inline void finalize_interpreter() {
|
||||||
|
// Get the internals pointer (without creating it if it doesn't exist). It's possible for the
|
||||||
|
// internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()`
|
||||||
|
// during destruction), so we get the pointer-pointer here and check it after Py_Finalize().
|
||||||
|
detail::internals **internals_ptr_ptr = detail::get_internals_pp();
|
||||||
|
// It could also be stashed in state_dict, so look there too:
|
||||||
|
if (object internals_obj
|
||||||
|
= get_internals_obj_from_state_dict(detail::get_python_state_dict())) {
|
||||||
|
internals_ptr_ptr = detail::get_internals_pp_from_capsule(internals_obj);
|
||||||
|
}
|
||||||
|
// Local internals contains data managed by the current interpreter, so we must clear them to
|
||||||
|
// avoid undefined behaviors when initializing another interpreter
|
||||||
|
detail::get_local_internals().registered_types_cpp.clear();
|
||||||
|
detail::get_local_internals().registered_exception_translators.clear();
|
||||||
|
|
||||||
|
Py_Finalize();
|
||||||
|
|
||||||
|
if (internals_ptr_ptr) {
|
||||||
|
delete *internals_ptr_ptr;
|
||||||
|
*internals_ptr_ptr = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Scope guard version of `initialize_interpreter` and `finalize_interpreter`.
|
||||||
|
This a move-only guard and only a single instance can exist.
|
||||||
|
|
||||||
|
See `initialize_interpreter` for a discussion of its constructor arguments.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <pybind11/embed.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
py::scoped_interpreter guard{};
|
||||||
|
py::print(Hello, World!);
|
||||||
|
} // <-- interpreter shutdown
|
||||||
|
\endrst */
|
||||||
|
class scoped_interpreter {
|
||||||
|
public:
|
||||||
|
explicit scoped_interpreter(bool init_signal_handlers = true,
|
||||||
|
int argc = 0,
|
||||||
|
const char *const *argv = nullptr,
|
||||||
|
bool add_program_dir_to_path = true) {
|
||||||
|
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
scoped_interpreter &operator=(scoped_interpreter &&) = delete;
|
||||||
|
|
||||||
|
~scoped_interpreter() {
|
||||||
|
if (is_valid) {
|
||||||
|
finalize_interpreter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_valid = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
156
3rdparty/pybind11/include/pybind11/eval.h
vendored
Normal file
156
3rdparty/pybind11/include/pybind11/eval.h
vendored
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
pybind11/eval.h: Support for evaluating Python expressions and statements
|
||||||
|
from strings and files
|
||||||
|
|
||||||
|
Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
|
||||||
|
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 "pybind11.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
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.
|
||||||
|
if (!global.contains("__builtins__"))
|
||||||
|
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
|
||||||
|
#else
|
||||||
|
(void) global;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
enum eval_mode {
|
||||||
|
/// Evaluate a string containing an isolated expression
|
||||||
|
eval_expr,
|
||||||
|
|
||||||
|
/// Evaluate a string containing a single statement. Returns \c none
|
||||||
|
eval_single_statement,
|
||||||
|
|
||||||
|
/// Evaluate a string containing a sequence of statement. Returns \c none
|
||||||
|
eval_statements
|
||||||
|
};
|
||||||
|
|
||||||
|
template <eval_mode mode = eval_expr>
|
||||||
|
object eval(const str &expr, object global = globals(), object local = object()) {
|
||||||
|
if (!local) {
|
||||||
|
local = global;
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::ensure_builtins_in_globals(global);
|
||||||
|
|
||||||
|
/* PyRun_String does not accept a PyObject / encoding specifier,
|
||||||
|
this seems to be the only alternative */
|
||||||
|
std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
switch (mode) {
|
||||||
|
case eval_expr:
|
||||||
|
start = Py_eval_input;
|
||||||
|
break;
|
||||||
|
case eval_single_statement:
|
||||||
|
start = Py_single_input;
|
||||||
|
break;
|
||||||
|
case eval_statements:
|
||||||
|
start = Py_file_input;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pybind11_fail("invalid evaluation mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
|
||||||
|
if (!result) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
return reinterpret_steal<object>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <eval_mode mode = eval_expr, size_t N>
|
||||||
|
object eval(const char (&s)[N], object global = globals(), object local = object()) {
|
||||||
|
/* Support raw string literals by removing common leading whitespace */
|
||||||
|
auto expr = (s[0] == '\n') ? str(module_::import("textwrap").attr("dedent")(s)) : str(s);
|
||||||
|
return eval<mode>(expr, std::move(global), std::move(local));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void exec(const str &expr, object global = globals(), object local = object()) {
|
||||||
|
eval<eval_statements>(expr, std::move(global), std::move(local));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
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)
|
||||||
|
template <eval_mode mode = eval_statements>
|
||||||
|
object eval_file(str, object, object) {
|
||||||
|
pybind11_fail("eval_file not supported in PyPy3. Use eval");
|
||||||
|
}
|
||||||
|
template <eval_mode mode = eval_statements>
|
||||||
|
object eval_file(str, object) {
|
||||||
|
pybind11_fail("eval_file not supported in PyPy3. Use eval");
|
||||||
|
}
|
||||||
|
template <eval_mode mode = eval_statements>
|
||||||
|
object eval_file(str) {
|
||||||
|
pybind11_fail("eval_file not supported in PyPy3. Use eval");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <eval_mode mode = eval_statements>
|
||||||
|
object eval_file(str fname, object global = globals(), object local = object()) {
|
||||||
|
if (!local) {
|
||||||
|
local = global;
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::ensure_builtins_in_globals(global);
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
switch (mode) {
|
||||||
|
case eval_expr:
|
||||||
|
start = Py_eval_input;
|
||||||
|
break;
|
||||||
|
case eval_single_statement:
|
||||||
|
start = Py_single_input;
|
||||||
|
break;
|
||||||
|
case eval_statements:
|
||||||
|
start = Py_file_input;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pybind11_fail("invalid evaluation mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
int closeFile = 1;
|
||||||
|
std::string fname_str = (std::string) fname;
|
||||||
|
FILE *f = _Py_fopen_obj(fname.ptr(), "r");
|
||||||
|
if (!f) {
|
||||||
|
PyErr_Clear();
|
||||||
|
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!global.contains("__file__")) {
|
||||||
|
global["__file__"] = std::move(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *result
|
||||||
|
= PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
return reinterpret_steal<object>(result);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
137
3rdparty/pybind11/include/pybind11/functional.h
vendored
Normal file
137
3rdparty/pybind11/include/pybind11/functional.h
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
pybind11/functional.h: std::function<> support
|
||||||
|
|
||||||
|
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 "pybind11.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <typename Return, typename... Args>
|
||||||
|
struct type_caster<std::function<Return(Args...)>> {
|
||||||
|
using type = std::function<Return(Args...)>;
|
||||||
|
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||||
|
using function_type = Return (*)(Args...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (src.is_none()) {
|
||||||
|
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||||
|
if (!convert) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isinstance<function>(src)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto func = reinterpret_borrow<function>(src);
|
||||||
|
|
||||||
|
/*
|
||||||
|
When passing a C++ function as an argument to another C++
|
||||||
|
function via Python, every function call would normally involve
|
||||||
|
a full C++ -> Python -> C++ roundtrip, which can be prohibitive.
|
||||||
|
Here, we try to at least detect the case where the function is
|
||||||
|
stateless (i.e. function pointer or lambda function without
|
||||||
|
captured variables), in which case the roundtrip can be avoided.
|
||||||
|
*/
|
||||||
|
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)) {
|
||||||
|
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>();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (rec != nullptr) {
|
||||||
|
if (rec->is_stateless
|
||||||
|
&& same_type(typeid(function_type),
|
||||||
|
*reinterpret_cast<const std::type_info *>(rec->data[1]))) {
|
||||||
|
struct capture {
|
||||||
|
function_type f;
|
||||||
|
};
|
||||||
|
value = ((capture *) &rec->data)->f;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
rec = rec->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PYPY segfaults here when passing builtin function like sum.
|
||||||
|
// Raising an fail exception here works to prevent the segfault, but only on gcc.
|
||||||
|
// See PR #1413 for full details
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure GIL is held during functor destruction
|
||||||
|
struct func_handle {
|
||||||
|
function f;
|
||||||
|
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
|
||||||
|
// This triggers a syntax error under very special conditions (very weird indeed).
|
||||||
|
explicit
|
||||||
|
#endif
|
||||||
|
func_handle(function &&f_) noexcept
|
||||||
|
: f(std::move(f_)) {
|
||||||
|
}
|
||||||
|
func_handle(const func_handle &f_) { operator=(f_); }
|
||||||
|
func_handle &operator=(const func_handle &f_) {
|
||||||
|
gil_scoped_acquire acq;
|
||||||
|
f = f_.f;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~func_handle() {
|
||||||
|
gil_scoped_acquire acq;
|
||||||
|
function kill_f(std::move(f));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// to emulate 'move initialization capture' in C++11
|
||||||
|
struct func_wrapper {
|
||||||
|
func_handle hfunc;
|
||||||
|
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>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
value = func_wrapper(func_handle(std::move(func)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
|
||||||
|
if (!f_) {
|
||||||
|
return none().release();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = f_.template target<function_type>();
|
||||||
|
if (result) {
|
||||||
|
return cpp_function(*result, policy).release();
|
||||||
|
}
|
||||||
|
return cpp_function(std::forward<Func>(f_), policy).release();
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(type,
|
||||||
|
const_name("Callable[[") + concat(make_caster<Args>::name...)
|
||||||
|
+ const_name("], ") + make_caster<retval_type>::name
|
||||||
|
+ const_name("]"));
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
239
3rdparty/pybind11/include/pybind11/gil.h
vendored
Normal file
239
3rdparty/pybind11/include/pybind11/gil.h
vendored
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
pybind11/gil.h: RAII helpers for managing the GIL
|
||||||
|
|
||||||
|
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 "detail/common.h"
|
||||||
|
|
||||||
|
#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||||
|
# include "detail/internals.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
|
PyThreadState *get_thread_state_unchecked();
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
#if defined(WITH_THREAD)
|
||||||
|
|
||||||
|
# if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
||||||
|
|
||||||
|
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||||
|
* pattern, but there are a few important differences:
|
||||||
|
*
|
||||||
|
* 1. When acquiring the GIL from an non-main thread during the finalization
|
||||||
|
* phase, the GILState API blindly terminates the calling thread, which
|
||||||
|
* is often not what is wanted. This API does not do this.
|
||||||
|
*
|
||||||
|
* 2. The gil_scoped_release function can optionally cut the relationship
|
||||||
|
* of a PyThreadState and its associated thread, which allows moving it to
|
||||||
|
* another thread (this is a fairly rare/advanced use case).
|
||||||
|
*
|
||||||
|
* 3. The reference count of an acquired thread state can be controlled. This
|
||||||
|
* can be handy to prevent cases where callbacks issued from an external
|
||||||
|
* thread would otherwise constantly construct and destroy thread state data
|
||||||
|
* structures.
|
||||||
|
*
|
||||||
|
* See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an
|
||||||
|
* example which uses features 2 and 3 to migrate the Python thread of
|
||||||
|
* execution to another thread (to run the event loop on the original thread,
|
||||||
|
* in this case).
|
||||||
|
*/
|
||||||
|
|
||||||
|
class gil_scoped_acquire {
|
||||||
|
public:
|
||||||
|
PYBIND11_NOINLINE gil_scoped_acquire() {
|
||||||
|
auto &internals = detail::get_internals();
|
||||||
|
tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
|
||||||
|
|
||||||
|
if (!tstate) {
|
||||||
|
/* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
|
||||||
|
calling from a Python thread). Since we use a different key, this ensures
|
||||||
|
we don't create a new thread state and deadlock in PyEval_AcquireThread
|
||||||
|
below. Note we don't save this state with internals.tstate, since we don't
|
||||||
|
create it we would fail to clear it (its reference count should be > 0). */
|
||||||
|
tstate = PyGILState_GetThisThreadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tstate) {
|
||||||
|
tstate = PyThreadState_New(internals.istate);
|
||||||
|
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
|
if (!tstate) {
|
||||||
|
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
tstate->gilstate_counter = 0;
|
||||||
|
PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
|
||||||
|
} else {
|
||||||
|
release = detail::get_thread_state_unchecked() != tstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release) {
|
||||||
|
PyEval_AcquireThread(tstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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
|
||||||
|
if (tstate->gilstate_counter == 0) {
|
||||||
|
# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||||
|
if (!release) {
|
||||||
|
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
PyThreadState_Clear(tstate);
|
||||||
|
if (active) {
|
||||||
|
PyThreadState_DeleteCurrent();
|
||||||
|
}
|
||||||
|
PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
|
||||||
|
release = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||||
|
/// protect subsequent code.
|
||||||
|
PYBIND11_NOINLINE void disarm() { active = false; }
|
||||||
|
|
||||||
|
PYBIND11_NOINLINE ~gil_scoped_acquire() {
|
||||||
|
dec_ref();
|
||||||
|
if (release) {
|
||||||
|
PyEval_SaveThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PyThreadState *tstate = nullptr;
|
||||||
|
bool release = true;
|
||||||
|
bool active = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class gil_scoped_release {
|
||||||
|
public:
|
||||||
|
explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
|
||||||
|
// `get_internals()` must be called here unconditionally in order to initialize
|
||||||
|
// `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
|
||||||
|
// initialization race could occur as multiple threads try `gil_scoped_acquire`.
|
||||||
|
auto &internals = detail::get_internals();
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
||||||
|
tstate = PyEval_SaveThread();
|
||||||
|
if (disassoc) {
|
||||||
|
// Python >= 3.7 can remove this, it's an int before 3.7
|
||||||
|
// NOLINTNEXTLINE(readability-qualified-auto)
|
||||||
|
auto key = internals.tstate;
|
||||||
|
PYBIND11_TLS_DELETE_VALUE(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
/// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
|
||||||
|
/// protect subsequent code.
|
||||||
|
PYBIND11_NOINLINE void disarm() { active = false; }
|
||||||
|
|
||||||
|
~gil_scoped_release() {
|
||||||
|
if (!tstate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// `PyEval_RestoreThread()` should not be called if runtime is finalizing
|
||||||
|
if (active) {
|
||||||
|
PyEval_RestoreThread(tstate);
|
||||||
|
}
|
||||||
|
if (disassoc) {
|
||||||
|
// Python >= 3.7 can remove this, it's an int before 3.7
|
||||||
|
// NOLINTNEXTLINE(readability-qualified-auto)
|
||||||
|
auto key = detail::get_internals().tstate;
|
||||||
|
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PyThreadState *tstate;
|
||||||
|
bool disassoc;
|
||||||
|
bool active = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# else // PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||||
|
|
||||||
|
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() { PyGILState_Release(state); }
|
||||||
|
void disarm() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
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() { PyEval_RestoreThread(state); }
|
||||||
|
void disarm() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
# endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
|
||||||
|
|
||||||
|
#else // WITH_THREAD
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
265
3rdparty/pybind11/include/pybind11/iostream.h
vendored
Normal file
265
3rdparty/pybind11/include/pybind11/iostream.h
vendored
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python
|
||||||
|
|
||||||
|
Copyright (c) 2017 Henry F. Schreiner
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
WARNING: The implementation in this file is NOT thread safe. Multiple
|
||||||
|
threads writing to a redirected ostream concurrently cause data races
|
||||||
|
and potentially buffer overflows. Therefore it is currently a requirement
|
||||||
|
that all (possibly) concurrent redirected ostream writes are protected by
|
||||||
|
a mutex.
|
||||||
|
#HelpAppreciated: Work on iostream.h thread safety.
|
||||||
|
For more background see the discussions under
|
||||||
|
https://github.com/pybind/pybind11/pull/2982 and
|
||||||
|
https://github.com/pybind/pybind11/pull/2995.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pybind11.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <streambuf>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
// Buffer that writes to Python instead of C++
|
||||||
|
class pythonbuf : public std::streambuf {
|
||||||
|
private:
|
||||||
|
using traits_type = std::streambuf::traits_type;
|
||||||
|
|
||||||
|
const size_t buf_size;
|
||||||
|
std::unique_ptr<char[]> d_buffer;
|
||||||
|
object pywrite;
|
||||||
|
object pyflush;
|
||||||
|
|
||||||
|
int overflow(int c) override {
|
||||||
|
if (!traits_type::eq_int_type(c, traits_type::eof())) {
|
||||||
|
*pptr() = traits_type::to_char_type(c);
|
||||||
|
pbump(1);
|
||||||
|
}
|
||||||
|
return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes how many bytes at the end of the buffer are part of an
|
||||||
|
// incomplete sequence of UTF-8 bytes.
|
||||||
|
// Precondition: pbase() < pptr()
|
||||||
|
size_t utf8_remainder() const {
|
||||||
|
const auto rbase = std::reverse_iterator<char *>(pbase());
|
||||||
|
const auto rpptr = std::reverse_iterator<char *>(pptr());
|
||||||
|
auto is_ascii = [](char c) { return (static_cast<unsigned char>(c) & 0x80) == 0x00; };
|
||||||
|
auto is_leading = [](char c) { return (static_cast<unsigned char>(c) & 0xC0) == 0xC0; };
|
||||||
|
auto is_leading_2b = [](char c) { return static_cast<unsigned char>(c) <= 0xDF; };
|
||||||
|
auto is_leading_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; };
|
||||||
|
// If the last character is ASCII, there are no incomplete code points
|
||||||
|
if (is_ascii(*rpptr)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Otherwise, work back from the end of the buffer and find the first
|
||||||
|
// UTF-8 leading byte
|
||||||
|
const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
|
||||||
|
const auto leading = std::find_if(rpptr, rpend, is_leading);
|
||||||
|
if (leading == rbase) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const auto dist = static_cast<size_t>(leading - rpptr);
|
||||||
|
size_t remainder = 0;
|
||||||
|
|
||||||
|
if (dist == 0) {
|
||||||
|
remainder = 1; // 1-byte code point is impossible
|
||||||
|
} else if (dist == 1) {
|
||||||
|
remainder = is_leading_2b(*leading) ? 0 : dist + 1;
|
||||||
|
} else if (dist == 2) {
|
||||||
|
remainder = is_leading_3b(*leading) ? 0 : dist + 1;
|
||||||
|
}
|
||||||
|
// else if (dist >= 3), at least 4 bytes before encountering an UTF-8
|
||||||
|
// leading byte, either no remainder or invalid UTF-8.
|
||||||
|
// Invalid UTF-8 will cause an exception later when converting
|
||||||
|
// to a Python string, so that's not handled here.
|
||||||
|
return remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function must be non-virtual to be called in a destructor.
|
||||||
|
int _sync() {
|
||||||
|
if (pbase() != pptr()) { // If buffer is not empty
|
||||||
|
gil_scoped_acquire tmp;
|
||||||
|
// This subtraction cannot be negative, so dropping the sign.
|
||||||
|
auto size = static_cast<size_t>(pptr() - pbase());
|
||||||
|
size_t remainder = utf8_remainder();
|
||||||
|
|
||||||
|
if (size > remainder) {
|
||||||
|
str line(pbase(), size - remainder);
|
||||||
|
pywrite(std::move(line));
|
||||||
|
pyflush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the remainder at the end of the buffer to the beginning:
|
||||||
|
if (remainder > 0) {
|
||||||
|
std::memmove(pbase(), pptr() - remainder, remainder);
|
||||||
|
}
|
||||||
|
setp(pbase(), epptr());
|
||||||
|
pbump(static_cast<int>(remainder));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sync() override { return _sync(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
|
||||||
|
: buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
|
||||||
|
pyflush(pyostream.attr("flush")) {
|
||||||
|
setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pythonbuf(pythonbuf &&) = default;
|
||||||
|
|
||||||
|
/// Sync before destroy
|
||||||
|
~pythonbuf() override { _sync(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
This a move-only guard that redirects output.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <pybind11/iostream.h>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
{
|
||||||
|
py::scoped_ostream_redirect output;
|
||||||
|
std::cout << "Hello, World!"; // Python stdout
|
||||||
|
} // <-- return std::cout to normal
|
||||||
|
|
||||||
|
You can explicitly pass the c++ stream and the python object,
|
||||||
|
for example to guard stderr instead.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
{
|
||||||
|
py::scoped_ostream_redirect output{
|
||||||
|
std::cerr, py::module::import("sys").attr("stderr")};
|
||||||
|
std::cout << "Hello, World!";
|
||||||
|
}
|
||||||
|
\endrst */
|
||||||
|
class scoped_ostream_redirect {
|
||||||
|
protected:
|
||||||
|
std::streambuf *old;
|
||||||
|
std::ostream &costream;
|
||||||
|
detail::pythonbuf buffer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
|
||||||
|
const object &pyostream
|
||||||
|
= module_::import("sys").attr("stdout"))
|
||||||
|
: costream(costream), buffer(pyostream) {
|
||||||
|
old = costream.rdbuf(&buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
~scoped_ostream_redirect() { costream.rdbuf(old); }
|
||||||
|
|
||||||
|
scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
|
||||||
|
scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
|
||||||
|
scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete;
|
||||||
|
scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
Like `scoped_ostream_redirect`, but redirects cerr by default. This class
|
||||||
|
is provided primary to make ``py::call_guard`` easier to make.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
m.def("noisy_func", &noisy_func,
|
||||||
|
py::call_guard<scoped_ostream_redirect,
|
||||||
|
scoped_estream_redirect>());
|
||||||
|
|
||||||
|
\endrst */
|
||||||
|
class scoped_estream_redirect : public scoped_ostream_redirect {
|
||||||
|
public:
|
||||||
|
explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
|
||||||
|
const object &pyostream
|
||||||
|
= module_::import("sys").attr("stderr"))
|
||||||
|
: scoped_ostream_redirect(costream, pyostream) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
// Class to redirect output as a context manager. C++ backend.
|
||||||
|
class OstreamRedirect {
|
||||||
|
bool do_stdout_;
|
||||||
|
bool do_stderr_;
|
||||||
|
std::unique_ptr<scoped_ostream_redirect> redirect_stdout;
|
||||||
|
std::unique_ptr<scoped_estream_redirect> redirect_stderr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
|
||||||
|
: do_stdout_(do_stdout), do_stderr_(do_stderr) {}
|
||||||
|
|
||||||
|
void enter() {
|
||||||
|
if (do_stdout_) {
|
||||||
|
redirect_stdout.reset(new scoped_ostream_redirect());
|
||||||
|
}
|
||||||
|
if (do_stderr_) {
|
||||||
|
redirect_stderr.reset(new scoped_estream_redirect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit() {
|
||||||
|
redirect_stdout.reset();
|
||||||
|
redirect_stderr.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
/** \rst
|
||||||
|
This is a helper function to add a C++ redirect context manager to Python
|
||||||
|
instead of using a C++ guard. To use it, add the following to your binding code:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
#include <pybind11/iostream.h>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
py::add_ostream_redirect(m, "ostream_redirect");
|
||||||
|
|
||||||
|
You now have a Python context manager that redirects your output:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with m.ostream_redirect():
|
||||||
|
m.print_to_cout_function()
|
||||||
|
|
||||||
|
This manager can optionally be told which streams to operate on:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
with m.ostream_redirect(stdout=true, stderr=true):
|
||||||
|
m.noisy_function_with_error_printing()
|
||||||
|
|
||||||
|
\endrst */
|
||||||
|
inline class_<detail::OstreamRedirect>
|
||||||
|
add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
|
||||||
|
return class_<detail::OstreamRedirect>(std::move(m), name.c_str(), module_local())
|
||||||
|
.def(init<bool, bool>(), arg("stdout") = true, arg("stderr") = true)
|
||||||
|
.def("__enter__", &detail::OstreamRedirect::enter)
|
||||||
|
.def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
1998
3rdparty/pybind11/include/pybind11/numpy.h
vendored
Normal file
1998
3rdparty/pybind11/include/pybind11/numpy.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
202
3rdparty/pybind11/include/pybind11/operators.h
vendored
Normal file
202
3rdparty/pybind11/include/pybind11/operators.h
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
pybind11/operator.h: Metatemplates for operator overloading
|
||||||
|
|
||||||
|
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 "pybind11.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
/// Enumeration with all supported operator types
|
||||||
|
enum op_id : int {
|
||||||
|
op_add,
|
||||||
|
op_sub,
|
||||||
|
op_mul,
|
||||||
|
op_div,
|
||||||
|
op_mod,
|
||||||
|
op_divmod,
|
||||||
|
op_pow,
|
||||||
|
op_lshift,
|
||||||
|
op_rshift,
|
||||||
|
op_and,
|
||||||
|
op_xor,
|
||||||
|
op_or,
|
||||||
|
op_neg,
|
||||||
|
op_pos,
|
||||||
|
op_abs,
|
||||||
|
op_invert,
|
||||||
|
op_int,
|
||||||
|
op_long,
|
||||||
|
op_float,
|
||||||
|
op_str,
|
||||||
|
op_cmp,
|
||||||
|
op_gt,
|
||||||
|
op_ge,
|
||||||
|
op_lt,
|
||||||
|
op_le,
|
||||||
|
op_eq,
|
||||||
|
op_ne,
|
||||||
|
op_iadd,
|
||||||
|
op_isub,
|
||||||
|
op_imul,
|
||||||
|
op_idiv,
|
||||||
|
op_imod,
|
||||||
|
op_ilshift,
|
||||||
|
op_irshift,
|
||||||
|
op_iand,
|
||||||
|
op_ixor,
|
||||||
|
op_ior,
|
||||||
|
op_complex,
|
||||||
|
op_bool,
|
||||||
|
op_nonzero,
|
||||||
|
op_repr,
|
||||||
|
op_truediv,
|
||||||
|
op_itruediv,
|
||||||
|
op_hash
|
||||||
|
};
|
||||||
|
|
||||||
|
enum op_type : int {
|
||||||
|
op_l, /* base type on left */
|
||||||
|
op_r, /* base type on right */
|
||||||
|
op_u /* unary operator */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct self_t {};
|
||||||
|
static const self_t self = self_t();
|
||||||
|
|
||||||
|
/// Type for an unused type slot
|
||||||
|
struct undefined_t {};
|
||||||
|
|
||||||
|
/// Don't warn about an unused variable
|
||||||
|
inline self_t __self() { return self; }
|
||||||
|
|
||||||
|
/// base template of operator implementations
|
||||||
|
template <op_id, op_type, typename B, typename L, typename R>
|
||||||
|
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;
|
||||||
|
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||||
|
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...);
|
||||||
|
}
|
||||||
|
template <typename Class, typename... Extra>
|
||||||
|
void execute_cast(Class &cl, const Extra &...extra) const {
|
||||||
|
using Base = typename Class::type;
|
||||||
|
using L_type = conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||||
|
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...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
|
||||||
|
template <typename B, typename L, typename R> \
|
||||||
|
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||||
|
static char const *name() { return "__" #id "__"; } \
|
||||||
|
static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \
|
||||||
|
static B execute_cast(const L &l, const R &r) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
template <typename B, typename L, typename R> \
|
||||||
|
struct op_impl<op_##id, op_r, B, L, R> { \
|
||||||
|
static char const *name() { return "__" #rid "__"; } \
|
||||||
|
static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \
|
||||||
|
static B execute_cast(const R &r, const L &l) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
inline op_<op_##id, op_l, self_t, self_t> op(const self_t &, const self_t &) { \
|
||||||
|
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||||
|
} \
|
||||||
|
template <typename T> \
|
||||||
|
op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||||
|
return op_<op_##id, op_l, self_t, T>(); \
|
||||||
|
} \
|
||||||
|
template <typename T> \
|
||||||
|
op_<op_##id, op_r, T, self_t> op(const T &, const self_t &) { \
|
||||||
|
return op_<op_##id, op_r, T, self_t>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
|
||||||
|
template <typename B, typename L, typename R> \
|
||||||
|
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||||
|
static char const *name() { return "__" #id "__"; } \
|
||||||
|
static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \
|
||||||
|
static B execute_cast(L &l, const R &r) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
template <typename T> \
|
||||||
|
op_<op_##id, op_l, self_t, T> op(const self_t &, const T &) { \
|
||||||
|
return op_<op_##id, op_l, self_t, T>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||||
|
template <typename B, typename L> \
|
||||||
|
struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||||
|
static char const *name() { return "__" #id "__"; } \
|
||||||
|
static auto execute(const L &l) -> decltype(expr) { return expr; } \
|
||||||
|
static B execute_cast(const L &l) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t &) { \
|
||||||
|
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l *r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l &r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||||
|
// PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||||
|
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||||
|
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||||
|
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||||
|
// WARNING: This usage of `abs` should only be done for existing STL overloads.
|
||||||
|
// Adding overloads directly in to the `std::` namespace is advised against:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/extending_std
|
||||||
|
PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||||
|
PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||||
|
PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||||
|
PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||||
|
PYBIND11_UNARY_OPERATOR(int, int_, (int) l)
|
||||||
|
PYBIND11_UNARY_OPERATOR(float, float_, (double) l)
|
||||||
|
|
||||||
|
#undef PYBIND11_BINARY_OPERATOR
|
||||||
|
#undef PYBIND11_INPLACE_OPERATOR
|
||||||
|
#undef PYBIND11_UNARY_OPERATOR
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
using detail::self;
|
||||||
|
// Add named operators so that they are accessible via `py::`.
|
||||||
|
using detail::hash;
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
92
3rdparty/pybind11/include/pybind11/options.h
vendored
Normal file
92
3rdparty/pybind11/include/pybind11/options.h
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
pybind11/options.h: global settings that are configurable at runtime.
|
||||||
|
|
||||||
|
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 "detail/common.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
|
||||||
|
class options {
|
||||||
|
public:
|
||||||
|
// Default RAII constructor, which leaves settings as they currently are.
|
||||||
|
options() : previous_state(global_state()) {}
|
||||||
|
|
||||||
|
// Class is non-copyable.
|
||||||
|
options(const options &) = delete;
|
||||||
|
options &operator=(const options &) = delete;
|
||||||
|
|
||||||
|
// Destructor, which restores settings that were in effect before.
|
||||||
|
~options() { global_state() = previous_state; }
|
||||||
|
|
||||||
|
// Setter methods (affect the global state):
|
||||||
|
|
||||||
|
options &disable_user_defined_docstrings() & {
|
||||||
|
global_state().show_user_defined_docstrings = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
options &enable_user_defined_docstrings() & {
|
||||||
|
global_state().show_user_defined_docstrings = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
options &disable_function_signatures() & {
|
||||||
|
global_state().show_function_signatures = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
options &enable_function_signatures() & {
|
||||||
|
global_state().show_function_signatures = true;
|
||||||
|
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() {
|
||||||
|
return global_state().show_user_defined_docstrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct state {
|
||||||
|
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() {
|
||||||
|
static state instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
state previous_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
2890
3rdparty/pybind11/include/pybind11/pybind11.h
vendored
Normal file
2890
3rdparty/pybind11/include/pybind11/pybind11.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2557
3rdparty/pybind11/include/pybind11/pytypes.h
vendored
Normal file
2557
3rdparty/pybind11/include/pybind11/pytypes.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
447
3rdparty/pybind11/include/pybind11/stl.h
vendored
Normal file
447
3rdparty/pybind11/include/pybind11/stl.h
vendored
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
pybind11/stl.h: Transparent conversion for STL data types
|
||||||
|
|
||||||
|
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 "pybind11.h"
|
||||||
|
#include "detail/common.h"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <ostream>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <valarray>
|
||||||
|
|
||||||
|
// See `detail/common.h` for implementation of these guards.
|
||||||
|
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||||
|
# include <optional>
|
||||||
|
#elif defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||||
|
# include <experimental/optional>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PYBIND11_HAS_VARIANT)
|
||||||
|
# include <variant>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for
|
||||||
|
/// forwarding a container element). Typically used indirect via forwarded_type(), below.
|
||||||
|
template <typename T, typename U>
|
||||||
|
using forwarded_type = conditional_t<std::is_lvalue_reference<T>::value,
|
||||||
|
remove_reference_t<U> &,
|
||||||
|
remove_reference_t<U> &&>;
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
|
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)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto s = reinterpret_borrow<anyset>(src);
|
||||||
|
value.clear();
|
||||||
|
reserve_maybe(s, &value);
|
||||||
|
for (auto entry : s) {
|
||||||
|
key_conv conv;
|
||||||
|
if (!conv.load(entry, convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value.insert(cast_op<Key &&>(std::move(conv)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
|
if (!std::is_lvalue_reference<T>::value) {
|
||||||
|
policy = return_value_policy_override<Key>::policy(policy);
|
||||||
|
}
|
||||||
|
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_))) {
|
||||||
|
return handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(type, const_name("Set[") + key_conv::name + const_name("]"));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Type, typename Key, typename Value>
|
||||||
|
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;
|
||||||
|
if (!kconv.load(it.first.ptr(), convert) || !vconv.load(it.second.ptr(), convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value.emplace(cast_op<Key &&>(std::move(kconv)), cast_op<Value &&>(std::move(vconv)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
|
dict d;
|
||||||
|
return_value_policy policy_key = policy;
|
||||||
|
return_value_policy policy_value = policy;
|
||||||
|
if (!std::is_lvalue_reference<T>::value) {
|
||||||
|
policy_key = return_value_policy_override<Key>::policy(policy_key);
|
||||||
|
policy_value = return_value_policy_override<Value>::policy(policy_value);
|
||||||
|
}
|
||||||
|
for (auto &&kv : src) {
|
||||||
|
auto key = reinterpret_steal<object>(
|
||||||
|
key_conv::cast(detail::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));
|
||||||
|
if (!key || !value) {
|
||||||
|
return handle();
|
||||||
|
}
|
||||||
|
d[std::move(key)] = std::move(value);
|
||||||
|
}
|
||||||
|
return d.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(Type,
|
||||||
|
const_name("Dict[") + key_conv::name + const_name(", ") + value_conv::name
|
||||||
|
+ const_name("]"));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Type, typename Value>
|
||||||
|
struct list_caster {
|
||||||
|
using value_conv = make_caster<Value>;
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!isinstance<sequence>(src) || isinstance<bytes>(src) || isinstance<str>(src)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto s = reinterpret_borrow<sequence>(src);
|
||||||
|
value.clear();
|
||||||
|
reserve_maybe(s, &value);
|
||||||
|
for (auto it : s) {
|
||||||
|
value_conv conv;
|
||||||
|
if (!conv.load(it, convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value.push_back(cast_op<Value &&>(std::move(conv)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T = Type, enable_if_t<has_reserve_method<T>::value, int> = 0>
|
||||||
|
void reserve_maybe(const sequence &s, Type *) {
|
||||||
|
value.reserve(s.size());
|
||||||
|
}
|
||||||
|
void reserve_maybe(const sequence &, void *) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
|
if (!std::is_lvalue_reference<T>::value) {
|
||||||
|
policy = return_value_policy_override<Value>::policy(policy);
|
||||||
|
}
|
||||||
|
list l(src.size());
|
||||||
|
ssize_t index = 0;
|
||||||
|
for (auto &&value : src) {
|
||||||
|
auto value_ = reinterpret_steal<object>(
|
||||||
|
value_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||||
|
if (!value_) {
|
||||||
|
return handle();
|
||||||
|
}
|
||||||
|
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
|
||||||
|
}
|
||||||
|
return l.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(Type, const_name("List[") + value_conv::name + const_name("]"));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Type, typename Alloc>
|
||||||
|
struct type_caster<std::vector<Type, Alloc>> : list_caster<std::vector<Type, Alloc>, Type> {};
|
||||||
|
|
||||||
|
template <typename Type, typename Alloc>
|
||||||
|
struct type_caster<std::deque<Type, Alloc>> : list_caster<std::deque<Type, Alloc>, Type> {};
|
||||||
|
|
||||||
|
template <typename Type, typename Alloc>
|
||||||
|
struct type_caster<std::list<Type, Alloc>> : list_caster<std::list<Type, Alloc>, Type> {};
|
||||||
|
|
||||||
|
template <typename ArrayType, typename Value, bool Resizable, size_t Size = 0>
|
||||||
|
struct array_caster {
|
||||||
|
using value_conv = make_caster<Value>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <bool R = Resizable>
|
||||||
|
bool require_size(enable_if_t<R, size_t> size) {
|
||||||
|
if (value.size() != size) {
|
||||||
|
value.resize(size);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template <bool R = Resizable>
|
||||||
|
bool require_size(enable_if_t<!R, size_t> size) {
|
||||||
|
return size == Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!isinstance<sequence>(src)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto l = reinterpret_borrow<sequence>(src);
|
||||||
|
if (!require_size(l.size())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size_t ctr = 0;
|
||||||
|
for (auto it : l) {
|
||||||
|
value_conv conv;
|
||||||
|
if (!conv.load(it, convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value[ctr++] = cast_op<Value &&>(std::move(conv));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
|
list l(src.size());
|
||||||
|
ssize_t index = 0;
|
||||||
|
for (auto &&value : src) {
|
||||||
|
auto value_ = reinterpret_steal<object>(
|
||||||
|
value_conv::cast(detail::forward_like<T>(value), policy, parent));
|
||||||
|
if (!value_) {
|
||||||
|
return handle();
|
||||||
|
}
|
||||||
|
PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
|
||||||
|
}
|
||||||
|
return l.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(ArrayType,
|
||||||
|
const_name<Resizable>(const_name(""), const_name("Annotated["))
|
||||||
|
+ const_name("List[") + value_conv::name + const_name("]")
|
||||||
|
+ const_name<Resizable>(const_name(""),
|
||||||
|
const_name(", FixedSize(")
|
||||||
|
+ const_name<Size>() + const_name(")]")));
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Type, size_t Size>
|
||||||
|
struct type_caster<std::array<Type, Size>>
|
||||||
|
: array_caster<std::array<Type, Size>, Type, false, Size> {};
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
struct type_caster<std::valarray<Type>> : array_caster<std::valarray<Type>, Type, true> {};
|
||||||
|
|
||||||
|
template <typename Key, typename Compare, typename Alloc>
|
||||||
|
struct type_caster<std::set<Key, Compare, Alloc>>
|
||||||
|
: set_caster<std::set<Key, Compare, Alloc>, Key> {};
|
||||||
|
|
||||||
|
template <typename Key, typename Hash, typename Equal, typename Alloc>
|
||||||
|
struct type_caster<std::unordered_set<Key, Hash, Equal, Alloc>>
|
||||||
|
: set_caster<std::unordered_set<Key, Hash, Equal, Alloc>, Key> {};
|
||||||
|
|
||||||
|
template <typename Key, typename Value, typename Compare, typename Alloc>
|
||||||
|
struct type_caster<std::map<Key, Value, Compare, Alloc>>
|
||||||
|
: map_caster<std::map<Key, Value, Compare, Alloc>, Key, Value> {};
|
||||||
|
|
||||||
|
template <typename Key, typename Value, typename Hash, typename Equal, typename Alloc>
|
||||||
|
struct type_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>>
|
||||||
|
: map_caster<std::unordered_map<Key, Value, Hash, Equal, Alloc>, Key, Value> {};
|
||||||
|
|
||||||
|
// This type caster is intended to be used for std::optional and std::experimental::optional
|
||||||
|
template <typename Type, typename Value = typename Type::value_type>
|
||||||
|
struct optional_caster {
|
||||||
|
using value_conv = make_caster<Value>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static handle cast(T &&src, return_value_policy policy, handle parent) {
|
||||||
|
if (!src) {
|
||||||
|
return none().release();
|
||||||
|
}
|
||||||
|
if (!std::is_lvalue_reference<T>::value) {
|
||||||
|
policy = return_value_policy_override<Value>::policy(policy);
|
||||||
|
}
|
||||||
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||||
|
return value_conv::cast(*std::forward<T>(src), policy, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if (!src) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (src.is_none()) {
|
||||||
|
return true; // default-constructed value is already empty
|
||||||
|
}
|
||||||
|
value_conv inner_caster;
|
||||||
|
if (!inner_caster.load(src, convert)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.emplace(cast_op<Value &&>(std::move(inner_caster)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_TYPE_CASTER(Type, const_name("Optional[") + value_conv::name + const_name("]"));
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(PYBIND11_HAS_OPTIONAL)
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<std::optional<T>> : public optional_caster<std::optional<T>> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct type_caster<std::nullopt_t> : public void_caster<std::nullopt_t> {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PYBIND11_HAS_EXP_OPTIONAL)
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<std::experimental::optional<T>>
|
||||||
|
: public optional_caster<std::experimental::optional<T>> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct type_caster<std::experimental::nullopt_t>
|
||||||
|
: public void_caster<std::experimental::nullopt_t> {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Visit a variant and cast any found type to Python
|
||||||
|
struct variant_caster_visitor {
|
||||||
|
return_value_policy policy;
|
||||||
|
handle parent;
|
||||||
|
|
||||||
|
using result_type = handle; // required by boost::variant in C++11
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
result_type operator()(T &&src) const {
|
||||||
|
return make_caster<T>::cast(std::forward<T>(src), policy, parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar
|
||||||
|
/// `namespace::variant` types which provide a `namespace::visit()` function are handled here
|
||||||
|
/// automatically using argument-dependent lookup. Users can provide specializations for other
|
||||||
|
/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`.
|
||||||
|
template <template <typename...> class Variant>
|
||||||
|
struct visit_helper {
|
||||||
|
template <typename... Args>
|
||||||
|
static auto call(Args &&...args) -> decltype(visit(std::forward<Args>(args)...)) {
|
||||||
|
return visit(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Generic variant caster
|
||||||
|
template <typename Variant>
|
||||||
|
struct variant_caster;
|
||||||
|
|
||||||
|
template <template <typename...> class V, typename... Ts>
|
||||||
|
struct variant_caster<V<Ts...>> {
|
||||||
|
static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative.");
|
||||||
|
|
||||||
|
template <typename U, typename... Us>
|
||||||
|
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));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return load_alternative(src, convert, type_list<Us...>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load_alternative(handle, bool, type_list<>) { return false; }
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
// Do a first pass without conversions to improve constructor resolution.
|
||||||
|
// E.g. `py::int_(1).cast<variant<double, int>>()` needs to fill the `int`
|
||||||
|
// slot of the variant. Without two-pass loading `double` would be filled
|
||||||
|
// because it appears first and a conversion is possible.
|
||||||
|
if (convert && load_alternative(src, false, type_list<Ts...>{})) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return load_alternative(src, convert, type_list<Ts...>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Variant>
|
||||||
|
static handle cast(Variant &&src, return_value_policy policy, handle parent) {
|
||||||
|
return visit_helper<V>::call(variant_caster_visitor{policy, parent},
|
||||||
|
std::forward<Variant>(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
using Type = V<Ts...>;
|
||||||
|
PYBIND11_TYPE_CASTER(Type,
|
||||||
|
const_name("Union[") + detail::concat(make_caster<Ts>::name...)
|
||||||
|
+ const_name("]"));
|
||||||
|
};
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
|
||||||
|
#ifdef PYBIND11_HAS_STRING_VIEW
|
||||||
|
os << str(obj).cast<std::string_view>();
|
||||||
|
#else
|
||||||
|
os << (std::string) str(obj);
|
||||||
|
#endif
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
116
3rdparty/pybind11/include/pybind11/stl/filesystem.h
vendored
Normal file
116
3rdparty/pybind11/include/pybind11/stl/filesystem.h
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright (c) 2021 The Pybind Development Team.
|
||||||
|
// 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 "../pybind11.h"
|
||||||
|
#include "../detail/common.h"
|
||||||
|
#include "../detail/descr.h"
|
||||||
|
#include "../cast.h"
|
||||||
|
#include "../pytypes.h"
|
||||||
|
|
||||||
|
#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
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(PYBIND11_HAS_FILESYSTEM) && !defined(PYBIND11_HAS_EXPERIMENTAL_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.)"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
#if defined(PYBIND11_HAS_FILESYSTEM) || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||||
|
template <typename T>
|
||||||
|
struct path_caster {
|
||||||
|
|
||||||
|
private:
|
||||||
|
static PyObject *unicode_from_fs_native(const std::string &w) {
|
||||||
|
# if !defined(PYPY_VERSION)
|
||||||
|
return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size()));
|
||||||
|
# else
|
||||||
|
// PyPy mistakenly declares the first parameter as non-const.
|
||||||
|
return PyUnicode_DecodeFSDefaultAndSize(const_cast<char *>(w.c_str()), ssize_t(w.size()));
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *unicode_from_fs_native(const std::wstring &w) {
|
||||||
|
return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static handle cast(const T &path, return_value_policy, handle) {
|
||||||
|
if (auto py_str = unicode_from_fs_native(path.native())) {
|
||||||
|
return module_::import("pathlib")
|
||||||
|
.attr("Path")(reinterpret_steal<object>(py_str))
|
||||||
|
.release();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle handle, bool) {
|
||||||
|
// PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of
|
||||||
|
// calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy
|
||||||
|
// issue #3168) so we do it ourselves instead.
|
||||||
|
PyObject *buf = PyOS_FSPath(handle.ptr());
|
||||||
|
if (!buf) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PyObject *native = nullptr;
|
||||||
|
if constexpr (std::is_same_v<typename T::value_type, char>) {
|
||||||
|
if (PyUnicode_FSConverter(buf, &native) != 0) {
|
||||||
|
if (auto *c_str = PyBytes_AsString(native)) {
|
||||||
|
// AsString returns a pointer to the internal buffer, which
|
||||||
|
// must not be free'd.
|
||||||
|
value = c_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if constexpr (std::is_same_v<typename T::value_type, wchar_t>) {
|
||||||
|
if (PyUnicode_FSDecoder(buf, &native) != 0) {
|
||||||
|
if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) {
|
||||||
|
// AsWideCharString returns a new string that must be free'd.
|
||||||
|
value = c_str; // Copies the string.
|
||||||
|
PyMem_Free(c_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_XDECREF(native);
|
||||||
|
Py_DECREF(buf);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
851
3rdparty/pybind11/include/pybind11/stl_bind.h
vendored
Normal file
851
3rdparty/pybind11/include/pybind11/stl_bind.h
vendored
Normal file
@ -0,0 +1,851 @@
|
|||||||
|
/*
|
||||||
|
pybind11/std_bind.h: Binding generators for STL data types
|
||||||
|
|
||||||
|
Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob
|
||||||
|
|
||||||
|
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 "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)
|
||||||
|
|
||||||
|
/* SFINAE helper class used by 'is_comparable */
|
||||||
|
template <typename T>
|
||||||
|
struct container_traits {
|
||||||
|
template <typename T2>
|
||||||
|
static std::true_type
|
||||||
|
test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *);
|
||||||
|
template <typename T2>
|
||||||
|
static std::false_type test_comparable(...);
|
||||||
|
template <typename T2>
|
||||||
|
static std::true_type test_value(typename T2::value_type *);
|
||||||
|
template <typename T2>
|
||||||
|
static std::false_type test_value(...);
|
||||||
|
template <typename T2>
|
||||||
|
static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *);
|
||||||
|
template <typename T2>
|
||||||
|
static std::false_type test_pair(...);
|
||||||
|
|
||||||
|
static constexpr const bool is_comparable
|
||||||
|
= std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value;
|
||||||
|
static constexpr const bool is_pair
|
||||||
|
= std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value;
|
||||||
|
static constexpr const bool is_vector
|
||||||
|
= std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value;
|
||||||
|
static constexpr const bool is_element = !is_pair && !is_vector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Default: is_comparable -> std::false_type */
|
||||||
|
template <typename T, typename SFINAE = void>
|
||||||
|
struct is_comparable : std::false_type {};
|
||||||
|
|
||||||
|
/* For non-map data structures, check whether operator== can be instantiated */
|
||||||
|
template <typename T>
|
||||||
|
struct is_comparable<
|
||||||
|
T,
|
||||||
|
enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
/* For a vector/map data structure, recursively check the value type
|
||||||
|
(which is std::pair for maps) */
|
||||||
|
template <typename T>
|
||||||
|
struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>>
|
||||||
|
: is_comparable<typename recursive_container_traits<T>::type_to_check_recursively> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct is_comparable<recursive_bottom> : std::true_type {};
|
||||||
|
|
||||||
|
/* For pairs, recursively check the two data types */
|
||||||
|
template <typename T>
|
||||||
|
struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
|
||||||
|
static constexpr const bool value = is_comparable<typename T::first_type>::value
|
||||||
|
&& is_comparable<typename T::second_type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Fallback functions */
|
||||||
|
template <typename, typename, typename... Args>
|
||||||
|
void vector_if_copy_constructible(const Args &...) {}
|
||||||
|
template <typename, typename, typename... Args>
|
||||||
|
void vector_if_equal_operator(const Args &...) {}
|
||||||
|
template <typename, typename, typename... Args>
|
||||||
|
void vector_if_insertion_operator(const Args &...) {}
|
||||||
|
template <typename, typename, typename... Args>
|
||||||
|
void vector_modifiers(const Args &...) {}
|
||||||
|
|
||||||
|
template <typename Vector, typename Class_>
|
||||||
|
void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) {
|
||||||
|
cl.def(init<const Vector &>(), "Copy constructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector, typename Class_>
|
||||||
|
void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) {
|
||||||
|
using T = typename Vector::value_type;
|
||||||
|
|
||||||
|
cl.def(self == self);
|
||||||
|
cl.def(self != self);
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"count",
|
||||||
|
[](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); },
|
||||||
|
arg("x"),
|
||||||
|
"Return the number of times ``x`` appears in the list");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"remove",
|
||||||
|
[](Vector &v, const T &x) {
|
||||||
|
auto p = std::find(v.begin(), v.end(), x);
|
||||||
|
if (p != v.end()) {
|
||||||
|
v.erase(p);
|
||||||
|
} else {
|
||||||
|
throw value_error();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
arg("x"),
|
||||||
|
"Remove the first item from the list whose value is x. "
|
||||||
|
"It is an error if there is no such item.");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__contains__",
|
||||||
|
[](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); },
|
||||||
|
arg("x"),
|
||||||
|
"Return true the container contains ``x``");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vector modifiers -- requires a copyable vector_type:
|
||||||
|
// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it
|
||||||
|
// seems silly to allow deletion but not insertion, so include them here too.)
|
||||||
|
template <typename Vector, typename Class_>
|
||||||
|
void vector_modifiers(
|
||||||
|
enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
|
||||||
|
using T = typename Vector::value_type;
|
||||||
|
using SizeType = typename Vector::size_type;
|
||||||
|
using DiffType = typename Vector::difference_type;
|
||||||
|
|
||||||
|
auto wrap_i = [](DiffType i, SizeType n) {
|
||||||
|
if (i < 0) {
|
||||||
|
i += n;
|
||||||
|
}
|
||||||
|
if (i < 0 || (SizeType) i >= n) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"append",
|
||||||
|
[](Vector &v, const T &value) { v.push_back(value); },
|
||||||
|
arg("x"),
|
||||||
|
"Add an item to the end of the list");
|
||||||
|
|
||||||
|
cl.def(init([](const iterable &it) {
|
||||||
|
auto v = std::unique_ptr<Vector>(new Vector());
|
||||||
|
v->reserve(len_hint(it));
|
||||||
|
for (handle h : it) {
|
||||||
|
v->push_back(h.cast<T>());
|
||||||
|
}
|
||||||
|
return v.release();
|
||||||
|
}));
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"clear", [](Vector &v) { v.clear(); }, "Clear the contents");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"extend",
|
||||||
|
[](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); },
|
||||||
|
arg("L"),
|
||||||
|
"Extend the list by appending all the items in the given list");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"extend",
|
||||||
|
[](Vector &v, const iterable &it) {
|
||||||
|
const size_t old_size = v.size();
|
||||||
|
v.reserve(old_size + len_hint(it));
|
||||||
|
try {
|
||||||
|
for (handle h : it) {
|
||||||
|
v.push_back(h.cast<T>());
|
||||||
|
}
|
||||||
|
} catch (const cast_error &) {
|
||||||
|
v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size),
|
||||||
|
v.end());
|
||||||
|
try {
|
||||||
|
v.shrink_to_fit();
|
||||||
|
} catch (const std::exception &) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
arg("L"),
|
||||||
|
"Extend the list by appending all the items in the given list");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"insert",
|
||||||
|
[](Vector &v, DiffType i, const T &x) {
|
||||||
|
// Can't use wrap_i; i == v.size() is OK
|
||||||
|
if (i < 0) {
|
||||||
|
i += v.size();
|
||||||
|
}
|
||||||
|
if (i < 0 || (SizeType) i > v.size()) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
v.insert(v.begin() + i, x);
|
||||||
|
},
|
||||||
|
arg("i"),
|
||||||
|
arg("x"),
|
||||||
|
"Insert an item at a given position.");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"pop",
|
||||||
|
[](Vector &v) {
|
||||||
|
if (v.empty()) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
T t = std::move(v.back());
|
||||||
|
v.pop_back();
|
||||||
|
return t;
|
||||||
|
},
|
||||||
|
"Remove and return the last item");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"pop",
|
||||||
|
[wrap_i](Vector &v, DiffType i) {
|
||||||
|
i = wrap_i(i, v.size());
|
||||||
|
T t = std::move(v[(SizeType) i]);
|
||||||
|
v.erase(std::next(v.begin(), i));
|
||||||
|
return t;
|
||||||
|
},
|
||||||
|
arg("i"),
|
||||||
|
"Remove and return the item at index ``i``");
|
||||||
|
|
||||||
|
cl.def("__setitem__", [wrap_i](Vector &v, DiffType i, const T &t) {
|
||||||
|
i = wrap_i(i, v.size());
|
||||||
|
v[(SizeType) i] = t;
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Slicing protocol
|
||||||
|
cl.def(
|
||||||
|
"__getitem__",
|
||||||
|
[](const Vector &v, const slice &slice) -> Vector * {
|
||||||
|
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||||
|
|
||||||
|
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *seq = new Vector();
|
||||||
|
seq->reserve((size_t) slicelength);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < slicelength; ++i) {
|
||||||
|
seq->push_back(v[start]);
|
||||||
|
start += step;
|
||||||
|
}
|
||||||
|
return seq;
|
||||||
|
},
|
||||||
|
arg("s"),
|
||||||
|
"Retrieve list elements using a slice object");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__setitem__",
|
||||||
|
[](Vector &v, const 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slicelength != value.size()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Left and right hand size of slice assignment have different sizes!");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < slicelength; ++i) {
|
||||||
|
v[start] = value[i];
|
||||||
|
start += step;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Assign list elements using a slice object");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__delitem__",
|
||||||
|
[wrap_i](Vector &v, DiffType i) {
|
||||||
|
i = wrap_i(i, v.size());
|
||||||
|
v.erase(v.begin() + i);
|
||||||
|
},
|
||||||
|
"Delete the list elements at index ``i``");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__delitem__",
|
||||||
|
[](Vector &v, const slice &slice) {
|
||||||
|
size_t start = 0, stop = 0, step = 0, slicelength = 0;
|
||||||
|
|
||||||
|
if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step == 1 && false) {
|
||||||
|
v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength));
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < slicelength; ++i) {
|
||||||
|
v.erase(v.begin() + DiffType(start));
|
||||||
|
start += step - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Delete list elements using a slice object");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>),
|
||||||
|
// we have to access by copying; otherwise we return by reference.
|
||||||
|
template <typename Vector>
|
||||||
|
using vector_needs_copy
|
||||||
|
= negation<std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]),
|
||||||
|
typename Vector::value_type &>>;
|
||||||
|
|
||||||
|
// The usual case: access and iterate by reference
|
||||||
|
template <typename Vector, typename Class_>
|
||||||
|
void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) {
|
||||||
|
using T = typename Vector::value_type;
|
||||||
|
using SizeType = typename Vector::size_type;
|
||||||
|
using DiffType = typename Vector::difference_type;
|
||||||
|
using ItType = typename Vector::iterator;
|
||||||
|
|
||||||
|
auto wrap_i = [](DiffType i, SizeType n) {
|
||||||
|
if (i < 0) {
|
||||||
|
i += n;
|
||||||
|
}
|
||||||
|
if (i < 0 || (SizeType) i >= n) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__getitem__",
|
||||||
|
[wrap_i](Vector &v, DiffType i) -> T & {
|
||||||
|
i = wrap_i(i, v.size());
|
||||||
|
return v[(SizeType) i];
|
||||||
|
},
|
||||||
|
return_value_policy::reference_internal // ref + keepalive
|
||||||
|
);
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__iter__",
|
||||||
|
[](Vector &v) {
|
||||||
|
return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>(
|
||||||
|
v.begin(), v.end());
|
||||||
|
},
|
||||||
|
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The case for special objects, like std::vector<bool>, that have to be returned-by-copy:
|
||||||
|
template <typename Vector, typename Class_>
|
||||||
|
void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl) {
|
||||||
|
using T = typename Vector::value_type;
|
||||||
|
using SizeType = typename Vector::size_type;
|
||||||
|
using DiffType = typename Vector::difference_type;
|
||||||
|
using ItType = typename Vector::iterator;
|
||||||
|
cl.def("__getitem__", [](const Vector &v, DiffType i) -> T {
|
||||||
|
if (i < 0) {
|
||||||
|
i += v.size();
|
||||||
|
if (i < 0) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto i_st = static_cast<SizeType>(i);
|
||||||
|
if (i_st >= v.size()) {
|
||||||
|
throw index_error();
|
||||||
|
}
|
||||||
|
return v[i_st];
|
||||||
|
});
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__iter__",
|
||||||
|
[](Vector &v) {
|
||||||
|
return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end());
|
||||||
|
},
|
||||||
|
keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector, typename Class_>
|
||||||
|
auto vector_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||||
|
-> decltype(std::declval<std::ostream &>() << std::declval<typename Vector::value_type>(),
|
||||||
|
void()) {
|
||||||
|
using size_type = typename Vector::size_type;
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__repr__",
|
||||||
|
[name](Vector &v) {
|
||||||
|
std::ostringstream s;
|
||||||
|
s << name << '[';
|
||||||
|
for (size_type i = 0; i < v.size(); ++i) {
|
||||||
|
s << v[i];
|
||||||
|
if (i != v.size() - 1) {
|
||||||
|
s << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s << ']';
|
||||||
|
return s.str();
|
||||||
|
},
|
||||||
|
"Return the canonical string representation of this list.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide the buffer interface for vectors if we have data() and we have a format for it
|
||||||
|
// GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data()
|
||||||
|
// is insufficient, we need to check it returns an appropriate pointer
|
||||||
|
template <typename Vector, typename = void>
|
||||||
|
struct vector_has_data_and_format : std::false_type {};
|
||||||
|
template <typename Vector>
|
||||||
|
struct vector_has_data_and_format<
|
||||||
|
Vector,
|
||||||
|
enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(),
|
||||||
|
std::declval<Vector>().data()),
|
||||||
|
typename Vector::value_type *>::value>> : std::true_type {};
|
||||||
|
|
||||||
|
// [workaround(intel)] Separate function required here
|
||||||
|
// Workaround as the Intel compiler does not compile the enable_if_t part below
|
||||||
|
// (tested with icc (ICC) 2021.1 Beta 20200827)
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr bool args_any_are_buffer() {
|
||||||
|
return detail::any_of<std::is_same<Args, buffer_protocol>...>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [workaround(intel)] Separate function required here
|
||||||
|
// [workaround(msvc)] Can't use constexpr bool in return type
|
||||||
|
|
||||||
|
// Add the buffer interface to a vector
|
||||||
|
template <typename Vector, typename Class_, typename... Args>
|
||||||
|
void vector_buffer_impl(Class_ &cl, std::true_type) {
|
||||||
|
using T = typename Vector::value_type;
|
||||||
|
|
||||||
|
static_assert(vector_has_data_and_format<Vector>::value,
|
||||||
|
"There is not an appropriate format descriptor for this vector");
|
||||||
|
|
||||||
|
// numpy.h declares this for arbitrary types, but it may raise an exception and crash hard
|
||||||
|
// at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here
|
||||||
|
format_descriptor<T>::format();
|
||||||
|
|
||||||
|
cl.def_buffer([](Vector &v) -> buffer_info {
|
||||||
|
return buffer_info(v.data(),
|
||||||
|
static_cast<ssize_t>(sizeof(T)),
|
||||||
|
format_descriptor<T>::format(),
|
||||||
|
1,
|
||||||
|
{v.size()},
|
||||||
|
{sizeof(T)});
|
||||||
|
});
|
||||||
|
|
||||||
|
cl.def(init([](const buffer &buf) {
|
||||||
|
auto info = buf.request();
|
||||||
|
if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) {
|
||||||
|
throw type_error("Only valid 1D buffers can be copied to a vector");
|
||||||
|
}
|
||||||
|
if (!detail::compare_buffer_info<T>::compare(info)
|
||||||
|
|| (ssize_t) sizeof(T) != info.itemsize) {
|
||||||
|
throw type_error("Format mismatch (Python: " + info.format
|
||||||
|
+ " C++: " + format_descriptor<T>::format() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
T *p = static_cast<T *>(info.ptr);
|
||||||
|
ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T));
|
||||||
|
T *end = p + info.shape[0] * step;
|
||||||
|
if (step == 1) {
|
||||||
|
return Vector(p, end);
|
||||||
|
}
|
||||||
|
Vector vec;
|
||||||
|
vec.reserve((size_t) info.shape[0]);
|
||||||
|
for (; p != end; p += step) {
|
||||||
|
vec.push_back(*p);
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vector, typename Class_, typename... Args>
|
||||||
|
void vector_buffer_impl(Class_ &, std::false_type) {}
|
||||||
|
|
||||||
|
template <typename Vector, typename Class_, typename... Args>
|
||||||
|
void vector_buffer(Class_ &cl) {
|
||||||
|
vector_buffer_impl<Vector, Class_, Args...>(
|
||||||
|
cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
//
|
||||||
|
// std::vector
|
||||||
|
//
|
||||||
|
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
|
||||||
|
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args &&...args) {
|
||||||
|
using Class_ = class_<Vector, holder_type>;
|
||||||
|
|
||||||
|
// If the value_type is unregistered (e.g. a converting type) or is itself registered
|
||||||
|
// module-local then make the vector binding module-local as well:
|
||||||
|
using vtype = typename Vector::value_type;
|
||||||
|
auto *vtype_info = detail::get_type_info(typeid(vtype));
|
||||||
|
bool local = !vtype_info || vtype_info->module_local;
|
||||||
|
|
||||||
|
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
// Declare the buffer interface if a buffer_protocol() is passed in
|
||||||
|
detail::vector_buffer<Vector, Class_, Args...>(cl);
|
||||||
|
|
||||||
|
cl.def(init<>());
|
||||||
|
|
||||||
|
// Register copy constructor (if possible)
|
||||||
|
detail::vector_if_copy_constructible<Vector, Class_>(cl);
|
||||||
|
|
||||||
|
// Register comparison-related operators and functions (if possible)
|
||||||
|
detail::vector_if_equal_operator<Vector, Class_>(cl);
|
||||||
|
|
||||||
|
// Register stream insertion operator (if possible)
|
||||||
|
detail::vector_if_insertion_operator<Vector, Class_>(cl, name);
|
||||||
|
|
||||||
|
// Modifiers require copyable vector value type
|
||||||
|
detail::vector_modifiers<Vector, Class_>(cl);
|
||||||
|
|
||||||
|
// Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
|
||||||
|
detail::vector_accessor<Vector, Class_>(cl);
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__bool__",
|
||||||
|
[](const Vector &v) -> bool { return !v.empty(); },
|
||||||
|
"Check whether the list is nonempty");
|
||||||
|
|
||||||
|
cl.def("__len__", &Vector::size);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// C++ style functions deprecated, leaving it here as an example
|
||||||
|
cl.def(init<size_type>());
|
||||||
|
|
||||||
|
cl.def("resize",
|
||||||
|
(void (Vector::*) (size_type count)) & Vector::resize,
|
||||||
|
"changes the number of elements stored");
|
||||||
|
|
||||||
|
cl.def("erase",
|
||||||
|
[](Vector &v, SizeType i) {
|
||||||
|
if (i >= v.size())
|
||||||
|
throw index_error();
|
||||||
|
v.erase(v.begin() + i);
|
||||||
|
}, "erases element at index ``i``");
|
||||||
|
|
||||||
|
cl.def("empty", &Vector::empty, "checks whether the container is empty");
|
||||||
|
cl.def("size", &Vector::size, "returns the number of elements");
|
||||||
|
cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end");
|
||||||
|
cl.def("pop_back", &Vector::pop_back, "removes the last element");
|
||||||
|
|
||||||
|
cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements");
|
||||||
|
cl.def("reserve", &Vector::reserve, "reserves storage");
|
||||||
|
cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage");
|
||||||
|
cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory");
|
||||||
|
|
||||||
|
cl.def("clear", &Vector::clear, "clears the contents");
|
||||||
|
cl.def("swap", &Vector::swap, "swaps the contents");
|
||||||
|
|
||||||
|
cl.def("front", [](Vector &v) {
|
||||||
|
if (v.size()) return v.front();
|
||||||
|
else throw index_error();
|
||||||
|
}, "access the first element");
|
||||||
|
|
||||||
|
cl.def("back", [](Vector &v) {
|
||||||
|
if (v.size()) return v.back();
|
||||||
|
else throw index_error();
|
||||||
|
}, "access the last element ");
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// std::map, std::unordered_map
|
||||||
|
//
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
/* Fallback functions */
|
||||||
|
template <typename, typename, typename... Args>
|
||||||
|
void map_if_insertion_operator(const Args &...) {}
|
||||||
|
template <typename, typename, typename... Args>
|
||||||
|
void map_assignment(const Args &...) {}
|
||||||
|
|
||||||
|
// Map assignment when copy-assignable: just copy the value
|
||||||
|
template <typename Map, typename Class_>
|
||||||
|
void map_assignment(
|
||||||
|
enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
|
||||||
|
using KeyType = typename Map::key_type;
|
||||||
|
using MappedType = typename Map::mapped_type;
|
||||||
|
|
||||||
|
cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
|
||||||
|
auto it = m.find(k);
|
||||||
|
if (it != m.end()) {
|
||||||
|
it->second = v;
|
||||||
|
} else {
|
||||||
|
m.emplace(k, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not copy-assignable, but still copy-constructible: we can update the value by erasing and
|
||||||
|
// reinserting
|
||||||
|
template <typename Map, typename Class_>
|
||||||
|
void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value
|
||||||
|
&& is_copy_constructible<typename Map::mapped_type>::value,
|
||||||
|
Class_> &cl) {
|
||||||
|
using KeyType = typename Map::key_type;
|
||||||
|
using MappedType = typename Map::mapped_type;
|
||||||
|
|
||||||
|
cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
|
||||||
|
// We can't use m[k] = v; because value type might not be default constructable
|
||||||
|
auto r = m.emplace(k, v);
|
||||||
|
if (!r.second) {
|
||||||
|
// value type is not copy assignable so the only way to insert it is to erase it
|
||||||
|
// first...
|
||||||
|
m.erase(r.first);
|
||||||
|
m.emplace(k, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Map, typename Class_>
|
||||||
|
auto map_if_insertion_operator(Class_ &cl, std::string const &name)
|
||||||
|
-> decltype(std::declval<std::ostream &>() << std::declval<typename Map::key_type>()
|
||||||
|
<< std::declval<typename Map::mapped_type>(),
|
||||||
|
void()) {
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__repr__",
|
||||||
|
[name](Map &m) {
|
||||||
|
std::ostringstream s;
|
||||||
|
s << name << '{';
|
||||||
|
bool f = false;
|
||||||
|
for (auto const &kv : m) {
|
||||||
|
if (f) {
|
||||||
|
s << ", ";
|
||||||
|
}
|
||||||
|
s << kv.first << ": " << kv.second;
|
||||||
|
f = true;
|
||||||
|
}
|
||||||
|
s << '}';
|
||||||
|
return s.str();
|
||||||
|
},
|
||||||
|
"Return the canonical string representation of this map.");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyType>
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename MappedType>
|
||||||
|
struct values_view {
|
||||||
|
virtual size_t len() = 0;
|
||||||
|
virtual iterator iter() = 0;
|
||||||
|
virtual ~values_view() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename KeyType, typename MappedType>
|
||||||
|
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 ↦
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
|
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
|
||||||
|
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 Class_ = class_<Map, holder_type>;
|
||||||
|
|
||||||
|
// If either type is a non-module-local bound type then make the map binding non-local as well;
|
||||||
|
// otherwise (e.g. both types are either module-local or converting) the map will be
|
||||||
|
// module-local.
|
||||||
|
auto *tinfo = detail::get_type_info(typeid(MappedType));
|
||||||
|
bool local = !tinfo || tinfo->module_local;
|
||||||
|
if (local) {
|
||||||
|
tinfo = detail::get_type_info(typeid(KeyType));
|
||||||
|
local = !tinfo || tinfo->module_local;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl.def(init<>());
|
||||||
|
|
||||||
|
// Register stream insertion operator (if possible)
|
||||||
|
detail::map_if_insertion_operator<Map, Class_>(cl, name);
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__bool__",
|
||||||
|
[](const Map &m) -> bool { return !m.empty(); },
|
||||||
|
"Check whether the map is nonempty");
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__iter__",
|
||||||
|
[](Map &m) { return make_key_iterator(m.begin(), m.end()); },
|
||||||
|
keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */
|
||||||
|
);
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"keys",
|
||||||
|
[](Map &m) {
|
||||||
|
return std::unique_ptr<KeysView>(new detail::KeysViewImpl<Map, 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));
|
||||||
|
},
|
||||||
|
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));
|
||||||
|
},
|
||||||
|
keep_alive<0, 1>() /* Essential: keep map alive while view exists */
|
||||||
|
);
|
||||||
|
|
||||||
|
cl.def(
|
||||||
|
"__getitem__",
|
||||||
|
[](Map &m, const KeyType &k) -> MappedType & {
|
||||||
|
auto it = m.find(k);
|
||||||
|
if (it == m.end()) {
|
||||||
|
throw key_error();
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
},
|
||||||
|
return_value_policy::reference_internal // ref + keepalive
|
||||||
|
);
|
||||||
|
|
||||||
|
cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
|
||||||
|
auto it = m.find(k);
|
||||||
|
if (it == m.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
// Fallback for when the object is not of the key type
|
||||||
|
cl.def("__contains__", [](Map &, const object &) -> bool { return false; });
|
||||||
|
|
||||||
|
// Assignment provided only if the type is copyable
|
||||||
|
detail::map_assignment<Map, Class_>(cl);
|
||||||
|
|
||||||
|
cl.def("__delitem__", [](Map &m, const KeyType &k) {
|
||||||
|
auto it = m.find(k);
|
||||||
|
if (it == m.end()) {
|
||||||
|
throw key_error();
|
||||||
|
}
|
||||||
|
m.erase(it);
|
||||||
|
});
|
||||||
|
|
||||||
|
cl.def("__len__", &Map::size);
|
||||||
|
|
||||||
|
return cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
61
3rdparty/pybind11/include/pybind11/type_caster_pyobject_ptr.h
vendored
Normal file
61
3rdparty/pybind11/include/pybind11/type_caster_pyobject_ptr.h
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2023 The pybind Community.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "detail/common.h"
|
||||||
|
#include "detail/descr.h"
|
||||||
|
#include "cast.h"
|
||||||
|
#include "pytypes.h"
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||||
|
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class type_caster<PyObject> {
|
||||||
|
public:
|
||||||
|
static constexpr auto name = const_name("object"); // See discussion under PR #4601.
|
||||||
|
|
||||||
|
// This overload is purely to guard against accidents.
|
||||||
|
template <typename T,
|
||||||
|
detail::enable_if_t<!is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
|
||||||
|
static handle cast(T &&, return_value_policy, handle /*parent*/) {
|
||||||
|
static_assert(is_same_ignoring_cvref<T, PyObject *>::value,
|
||||||
|
"Invalid C++ type T for to-Python conversion (type_caster<PyObject>).");
|
||||||
|
return nullptr; // Unreachable.
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(PyObject *src, return_value_policy policy, handle /*parent*/) {
|
||||||
|
if (src == nullptr) {
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
raise_from(PyExc_SystemError, "src != nullptr but PyErr_Occurred()");
|
||||||
|
throw error_already_set();
|
||||||
|
}
|
||||||
|
if (policy == return_value_policy::take_ownership) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
if (policy == return_value_policy::reference
|
||||||
|
|| policy == return_value_policy::automatic_reference) {
|
||||||
|
return handle(src).inc_ref();
|
||||||
|
}
|
||||||
|
pybind11_fail("type_caster<PyObject>::cast(): unsupported return_value_policy: "
|
||||||
|
+ std::to_string(static_cast<int>(policy)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
value = reinterpret_borrow<object>(src);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using cast_op_type = PyObject *;
|
||||||
|
|
||||||
|
explicit operator PyObject *() { return value.ptr(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
object value;
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
@ -15,7 +15,11 @@ This release contains contributions from (alphabetically by first name):
|
|||||||
- Anke Boersma
|
- Anke Boersma
|
||||||
|
|
||||||
## Core ##
|
## Core ##
|
||||||
- No changes yet
|
- Boost::Python is no longer a dependency, Calamares uses a bundled copy
|
||||||
|
of pybind11 instead. This speeds up compilation and reducese the
|
||||||
|
dependency tree a great deal. You can set `WITH_PYBIND11=OFF` in the
|
||||||
|
build to keep Boost::Python and all the binary-compatibility problems
|
||||||
|
it entails.
|
||||||
|
|
||||||
## Modules ##
|
## Modules ##
|
||||||
- All QML modules now have a Qt6-compatible set of QML files as well. (thanks Anke)
|
- All QML modules now have a Qt6-compatible set of QML files as well. (thanks Anke)
|
||||||
|
@ -23,8 +23,9 @@
|
|||||||
# WITH_<foo> : try to enable <foo> (these usually default to ON). For
|
# WITH_<foo> : try to enable <foo> (these usually default to ON). For
|
||||||
# a list of WITH_<foo> grep CMakeCache.txt after running
|
# a list of WITH_<foo> grep CMakeCache.txt after running
|
||||||
# CMake once. These affect the ABI offered by Calamares.
|
# CMake once. These affect the ABI offered by Calamares.
|
||||||
# - PYTHON (enable Python Job modules)
|
# - PYBIND11 (use bundled pybind11, default ON, needs WITH_PYTHON)
|
||||||
# - QML (enable QML UI View modules)
|
# - PYTHON (enable Python Job modules, default ON if Python is found)
|
||||||
|
# - QML (enable QML UI View modules, default ON)
|
||||||
# - QT6 (use Qt6 rather than Qt5, default to OFF)
|
# - QT6 (use Qt6 rather than Qt5, default to OFF)
|
||||||
# The WITH_* options affect the ABI of Calamares: you must
|
# The WITH_* options affect the ABI of Calamares: you must
|
||||||
# build (C++) modules for Calamares with the same WITH_*
|
# build (C++) modules for Calamares with the same WITH_*
|
||||||
@ -80,7 +81,8 @@ option(INSTALL_POLKIT "Install Polkit configuration" ON)
|
|||||||
option(INSTALL_COMPLETION "Install shell completions" OFF)
|
option(INSTALL_COMPLETION "Install shell completions" OFF)
|
||||||
# When adding WITH_* that affects the ABI offered by libcalamares,
|
# When adding WITH_* that affects the ABI offered by libcalamares,
|
||||||
# also update libcalamares/CalamaresConfig.h.in
|
# also update libcalamares/CalamaresConfig.h.in
|
||||||
option(WITH_PYTHON "Enable Python modules API (requires Boost.Python)." ON)
|
option(WITH_PYBIND11 "Use bundled pybind11 instead o Boost::Python" ON)
|
||||||
|
option(WITH_PYTHON "Enable Python modules API." ON)
|
||||||
option(WITH_QML "Enable QML UI options." ON)
|
option(WITH_QML "Enable QML UI options." ON)
|
||||||
option(WITH_QT6 "Use Qt6 instead of Qt5" OFF)
|
option(WITH_QT6 "Use Qt6 instead of Qt5" OFF)
|
||||||
#
|
#
|
||||||
@ -446,25 +448,30 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
add_feature_info(yaml-schema BUILD_SCHEMA_TESTING "Validate YAML (config files) with schema.${_schema_explanation}")
|
add_feature_info(yaml-schema BUILD_SCHEMA_TESTING "Validate YAML (config files) with schema.${_schema_explanation}")
|
||||||
|
|
||||||
if(Python_Development_FOUND)
|
if(NOT Python_Development_FOUND)
|
||||||
|
message(STATUS "Disabling Python modules")
|
||||||
|
set(WITH_PYTHON OFF)
|
||||||
|
set(WITH_PYBIND11 OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WITH_PYTHON AND NOT WITH_PYBIND11)
|
||||||
find_package(boost_python)
|
find_package(boost_python)
|
||||||
if(NOT TARGET Boost::python)
|
if(NOT TARGET Boost::python)
|
||||||
find_package(Boost ${BOOSTPYTHON_VERSION} COMPONENTS python)
|
find_package(Boost ${BOOSTPYTHON_VERSION} COMPONENTS python)
|
||||||
set_package_properties(Boost PROPERTIES PURPOSE "Boost.Python is used for Python job modules.")
|
set_package_properties(Boost PROPERTIES
|
||||||
|
PURPOSE "Boost.Python is used for Python job modules (because WITH_PYBIND11 is OFF)."
|
||||||
|
TYPE REQUIRED
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Found boost_python with target Boost::python")
|
message(STATUS "Found boost_python with target Boost::python")
|
||||||
set(Boost_FOUND ON)
|
set(Boost_FOUND ON)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT Python_Development_FOUND OR NOT Boost_FOUND)
|
|
||||||
message(STATUS "Disabling Boost::Python modules")
|
|
||||||
set(WITH_PYTHON OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Now we know the state of the ABI-options, copy them into "Calamares_"
|
# Now we know the state of the ABI-options, copy them into "Calamares_"
|
||||||
# prefixed variables, to match how the variables would-be-named
|
# prefixed variables, to match how the variables would-be-named
|
||||||
# when building out-of-tree.
|
# when building out-of-tree.
|
||||||
|
set(Calamares_WITH_PYBIND11 ${WITH_PYBIND11})
|
||||||
set(Calamares_WITH_PYTHON ${WITH_PYTHON})
|
set(Calamares_WITH_PYTHON ${WITH_PYTHON})
|
||||||
set(Calamares_WITH_QML ${WITH_QML})
|
set(Calamares_WITH_QML ${WITH_QML})
|
||||||
set(Calamares_WITH_QT6 ${WITH_QT6})
|
set(Calamares_WITH_QT6 ${WITH_QT6})
|
||||||
@ -595,9 +602,13 @@ include(GNUInstallDirs)
|
|||||||
set(Calamares_LIBRARIES calamares)
|
set(Calamares_LIBRARIES calamares)
|
||||||
|
|
||||||
add_subdirectory(3rdparty/kdsingleapplication)
|
add_subdirectory(3rdparty/kdsingleapplication)
|
||||||
|
if(WITH_PYBIND11)
|
||||||
|
add_subdirectory(3rdparty/pybind11)
|
||||||
|
endif()
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
add_feature_info(Python ${WITH_PYTHON} "Python job modules")
|
add_feature_info(Python ${WITH_PYTHON} "Python job modules")
|
||||||
|
add_feature_info(Pybind11 ${WITH_PYBIND11} "Python using bundled pybind11")
|
||||||
add_feature_info(Qml ${WITH_QML} "QML UI support")
|
add_feature_info(Qml ${WITH_QML} "QML UI support")
|
||||||
add_feature_info(Polkit ${INSTALL_POLKIT} "Install Polkit files")
|
add_feature_info(Polkit ${INSTALL_POLKIT} "Install Polkit files")
|
||||||
add_feature_info(KCrash ${BUILD_CRASH_REPORTING} "Crash dumps via KCrash")
|
add_feature_info(KCrash ${BUILD_CRASH_REPORTING} "Crash dumps via KCrash")
|
||||||
|
@ -131,7 +131,7 @@ Main:
|
|||||||
* KDE extra-cmake-modules >= 5.78 (recommended; required for some modules;
|
* KDE extra-cmake-modules >= 5.78 (recommended; required for some modules;
|
||||||
required for some tests)
|
required for some tests)
|
||||||
* Python >= 3.6 (required for some modules)
|
* Python >= 3.6 (required for some modules)
|
||||||
* Boost.Python >= 1.72.0 (required for some modules)
|
* Boost.Python >= 1.72.0 (required for some modules if WITH_PYBIND11 is OFF)
|
||||||
|
|
||||||
Individual modules may have their own requirements;
|
Individual modules may have their own requirements;
|
||||||
these are listed in CMake output.
|
these are listed in CMake output.
|
||||||
|
@ -11,7 +11,7 @@ zypper --non-interactive up
|
|||||||
zypper --non-interactive in git-core jq curl ninja
|
zypper --non-interactive in git-core jq curl ninja
|
||||||
# From deploycala.py
|
# From deploycala.py
|
||||||
zypper --non-interactive in bison flex git make cmake gcc-c++
|
zypper --non-interactive in bison flex git make cmake gcc-c++
|
||||||
zypper --non-interactive in yaml-cpp-devel libpwquality-devel parted-devel python-devel libboost_headers-devel libboost_python3-devel
|
zypper --non-interactive in yaml-cpp-devel libpwquality-devel parted-devel python3-devel
|
||||||
zypper --non-interactive in libicu-devel libatasmart-devel
|
zypper --non-interactive in libicu-devel libatasmart-devel
|
||||||
# Qt6/KF6 dependencies
|
# Qt6/KF6 dependencies
|
||||||
zypper --non-interactive in qt6-concurrent-devel qt6-gui-devel qt6-linguist-devel qt6-network-devel qt6-svg-devel qt6-declarative-devel
|
zypper --non-interactive in qt6-concurrent-devel qt6-gui-devel qt6-linguist-devel qt6-network-devel qt6-svg-devel qt6-declarative-devel
|
||||||
|
@ -64,4 +64,9 @@ if(BUILD_TESTING)
|
|||||||
|
|
||||||
add_executable(test_conf test_conf.cpp)
|
add_executable(test_conf test_conf.cpp)
|
||||||
target_link_libraries(test_conf PUBLIC yamlcpp::yamlcpp ${qtname}::Core)
|
target_link_libraries(test_conf PUBLIC yamlcpp::yamlcpp ${qtname}::Core)
|
||||||
|
|
||||||
|
if(WITH_PYBIND11)
|
||||||
|
target_compile_definitions(loadmodule PRIVATE WITH_PYBIND11=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -25,15 +25,21 @@
|
|||||||
#include "modulesystem/ViewModule.h"
|
#include "modulesystem/ViewModule.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Retranslator.h"
|
||||||
|
#include "utils/System.h"
|
||||||
#include "utils/Yaml.h"
|
#include "utils/Yaml.h"
|
||||||
#include "viewpages/ExecutionViewStep.h"
|
#include "viewpages/ExecutionViewStep.h"
|
||||||
|
|
||||||
// Optional features of Calamares
|
// Optional features of Calamares
|
||||||
// - Python support
|
// - Python support with pybind11
|
||||||
|
// - Python support with older Boost implementation
|
||||||
// - QML support
|
// - QML support
|
||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
|
#if WITH_PYBIND11
|
||||||
|
#include "python/PythonJob.h"
|
||||||
|
#else
|
||||||
#include "PythonJob.h"
|
#include "PythonJob.h"
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#ifdef WITH_QML
|
#ifdef WITH_QML
|
||||||
#include "utils/Qml.h"
|
#include "utils/Qml.h"
|
||||||
#endif
|
#endif
|
||||||
@ -59,6 +65,7 @@ struct ModuleConfig
|
|||||||
QString m_module;
|
QString m_module;
|
||||||
QString m_jobConfig;
|
QString m_jobConfig;
|
||||||
QString m_globalConfig;
|
QString m_globalConfig;
|
||||||
|
QString m_settingsConfig;
|
||||||
QString m_language;
|
QString m_language;
|
||||||
QString m_branding;
|
QString m_branding;
|
||||||
bool m_ui;
|
bool m_ui;
|
||||||
@ -70,8 +77,11 @@ handle_args( QCoreApplication& a )
|
|||||||
{
|
{
|
||||||
QCommandLineOption debugLevelOption(
|
QCommandLineOption debugLevelOption(
|
||||||
QStringLiteral( "D" ), "Verbose output for debugging purposes (0-8), ignored.", "level" );
|
QStringLiteral( "D" ), "Verbose output for debugging purposes (0-8), ignored.", "level" );
|
||||||
|
QCommandLineOption settingsOption( { QStringLiteral( "S" ), QStringLiteral( "settings" ) },
|
||||||
|
QStringLiteral( "Settings.conf document" ),
|
||||||
|
QString( "settings.conf" ) );
|
||||||
QCommandLineOption globalOption( { QStringLiteral( "g" ), QStringLiteral( "global" ) },
|
QCommandLineOption globalOption( { QStringLiteral( "g" ), QStringLiteral( "global" ) },
|
||||||
QStringLiteral( "Global settings document" ),
|
QStringLiteral( "Global storage settings document" ),
|
||||||
"global.yaml" );
|
"global.yaml" );
|
||||||
QCommandLineOption jobOption(
|
QCommandLineOption jobOption(
|
||||||
{ QStringLiteral( "j" ), QStringLiteral( "job" ) }, QStringLiteral( "Job settings document" ), "job.yaml" );
|
{ QStringLiteral( "j" ), QStringLiteral( "job" ) }, QStringLiteral( "Job settings document" ), "job.yaml" );
|
||||||
@ -91,6 +101,7 @@ handle_args( QCoreApplication& a )
|
|||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
parser.addOption( debugLevelOption );
|
parser.addOption( debugLevelOption );
|
||||||
|
parser.addOption( settingsOption );
|
||||||
parser.addOption( globalOption );
|
parser.addOption( globalOption );
|
||||||
parser.addOption( jobOption );
|
parser.addOption( jobOption );
|
||||||
parser.addOption( langOption );
|
parser.addOption( langOption );
|
||||||
@ -137,6 +148,7 @@ handle_args( QCoreApplication& a )
|
|||||||
return ModuleConfig { parser.isSet( slideshowOption ) ? QStringLiteral( "-" ) : args.first(),
|
return ModuleConfig { parser.isSet( slideshowOption ) ? QStringLiteral( "-" ) : args.first(),
|
||||||
jobSettings,
|
jobSettings,
|
||||||
parser.value( globalOption ),
|
parser.value( globalOption ),
|
||||||
|
parser.value( settingsOption ),
|
||||||
parser.value( langOption ),
|
parser.value( langOption ),
|
||||||
parser.value( brandOption ),
|
parser.value( brandOption ),
|
||||||
parser.isSet( slideshowOption ) || parser.isSet( uiOption ),
|
parser.isSet( slideshowOption ) || parser.isSet( uiOption ),
|
||||||
@ -453,8 +465,10 @@ main( int argc, char* argv[] )
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr< Calamares::Settings > settings_p( Calamares::Settings::init( QString() ) );
|
std::unique_ptr< Calamares::Settings > settings_p( Calamares::Settings::init( module.m_settingsConfig ) );
|
||||||
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
|
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
|
||||||
|
std::unique_ptr< Calamares::System > system_p( new Calamares::System( settings_p->doChroot() ) );
|
||||||
|
|
||||||
QMainWindow* mw = nullptr;
|
QMainWindow* mw = nullptr;
|
||||||
|
|
||||||
auto* gs = jobqueue_p->globalStorage();
|
auto* gs = jobqueue_p->globalStorage();
|
||||||
@ -472,7 +486,12 @@ main( int argc, char* argv[] )
|
|||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
if ( module.m_pythonInjection )
|
if ( module.m_pythonInjection )
|
||||||
{
|
{
|
||||||
|
#if WITH_PYBIND11
|
||||||
|
Calamares::Python::Job::setInjectedPreScript( pythonPreScript );
|
||||||
|
#else
|
||||||
|
// Old Boost approach
|
||||||
Calamares::PythonJob::setInjectedPreScript( pythonPreScript );
|
Calamares::PythonJob::setInjectedPreScript( pythonPreScript );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef WITH_QML
|
#ifdef WITH_QML
|
||||||
|
@ -115,8 +115,13 @@ endif()
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
if(WITH_PYTHON)
|
if(WITH_PYTHON)
|
||||||
target_sources(calamares PRIVATE PythonHelper.cpp PythonJob.cpp PythonJobApi.cpp)
|
if(WITH_PYBIND11)
|
||||||
target_link_libraries(calamares PRIVATE Python::Python Boost::python)
|
target_sources(calamares PRIVATE python/Api.cpp python/PythonJob.cpp)
|
||||||
|
target_link_libraries(calamares PRIVATE Python::Python pybind11::headers)
|
||||||
|
else()
|
||||||
|
target_sources(calamares PRIVATE PythonHelper.cpp PythonJob.cpp PythonJobApi.cpp)
|
||||||
|
target_link_libraries(calamares PRIVATE Python::Python Boost::python)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
### OPTIONAL GeoIP XML support
|
### OPTIONAL GeoIP XML support
|
||||||
|
647
src/libcalamares/python/Api.cpp
Normal file
647
src/libcalamares/python/Api.cpp
Normal file
@ -0,0 +1,647 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2017-2020, 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "python/Api.h"
|
||||||
|
|
||||||
|
#include "CalamaresVersion.h"
|
||||||
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
|
#include "compat/Variant.h"
|
||||||
|
#include "locale/Global.h"
|
||||||
|
#include "python/PythonJob.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/RAII.h"
|
||||||
|
#include "utils/Runner.h"
|
||||||
|
#include "utils/String.h"
|
||||||
|
#include "utils/System.h"
|
||||||
|
#include "utils/Yaml.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#undef slots
|
||||||
|
#include <pybind11/embed.h>
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
/** @namespace
|
||||||
|
*
|
||||||
|
* Helper functions for converting Python (pybind11) types to Qt types.
|
||||||
|
*/
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Forward declarations, since most of these are mutually recursive
|
||||||
|
Calamares::Python::List variantListToPyList( const QVariantList& variantList );
|
||||||
|
Calamares::Python::Dictionary variantMapToPyDict( const QVariantMap& variantMap );
|
||||||
|
Calamares::Python::Dictionary variantHashToPyDict( const QVariantHash& variantHash );
|
||||||
|
|
||||||
|
py::object
|
||||||
|
variantToPyObject( const QVariant& variant )
|
||||||
|
{
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wswitch-enum"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
const auto IntVariantType = QVariant::Int;
|
||||||
|
const auto UIntVariantType = QVariant::UInt;
|
||||||
|
#else
|
||||||
|
const auto IntVariantType = QMetaType::Type::Int;
|
||||||
|
const auto UIntVariantType = QMetaType::Type::UInt;
|
||||||
|
#endif
|
||||||
|
// 49 enumeration values not handled
|
||||||
|
switch ( Calamares::typeOf( variant ) )
|
||||||
|
{
|
||||||
|
case Calamares::MapVariantType:
|
||||||
|
return variantMapToPyDict( variant.toMap() );
|
||||||
|
|
||||||
|
case Calamares::HashVariantType:
|
||||||
|
return variantHashToPyDict( variant.toHash() );
|
||||||
|
|
||||||
|
case Calamares::ListVariantType:
|
||||||
|
case Calamares::StringListVariantType:
|
||||||
|
return variantListToPyList( variant.toList() );
|
||||||
|
|
||||||
|
case IntVariantType:
|
||||||
|
return py::int_( variant.toInt() );
|
||||||
|
case UIntVariantType:
|
||||||
|
return py::int_( variant.toUInt() );
|
||||||
|
|
||||||
|
case Calamares::LongLongVariantType:
|
||||||
|
return py::int_( variant.toLongLong() );
|
||||||
|
case Calamares::ULongLongVariantType:
|
||||||
|
return py::int_( variant.toULongLong() );
|
||||||
|
|
||||||
|
case Calamares::DoubleVariantType:
|
||||||
|
return py::float_( variant.toDouble() );
|
||||||
|
|
||||||
|
case Calamares::CharVariantType:
|
||||||
|
#if QT_VERSION > QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
case QMetaType::Type::QChar:
|
||||||
|
#endif
|
||||||
|
case Calamares::StringVariantType:
|
||||||
|
return Calamares::Python::String( variant.toString().toStdString() );
|
||||||
|
|
||||||
|
case Calamares::BoolVariantType:
|
||||||
|
return py::bool_( variant.toBool() );
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
case QVariant::Invalid:
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return py::object();
|
||||||
|
}
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Calamares::Python::List
|
||||||
|
variantListToPyList( const QVariantList& variantList )
|
||||||
|
{
|
||||||
|
Calamares::Python::List pyList;
|
||||||
|
for ( const QVariant& variant : variantList )
|
||||||
|
{
|
||||||
|
pyList.append( variantToPyObject( variant ) );
|
||||||
|
}
|
||||||
|
return pyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Calamares::Python::Dictionary
|
||||||
|
variantMapToPyDict( const QVariantMap& variantMap )
|
||||||
|
{
|
||||||
|
Calamares::Python::Dictionary pyDict;
|
||||||
|
for ( auto it = variantMap.constBegin(); it != variantMap.constEnd(); ++it )
|
||||||
|
{
|
||||||
|
pyDict[ Calamares::Python::String( it.key().toStdString() ) ] = variantToPyObject( it.value() );
|
||||||
|
}
|
||||||
|
return pyDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
Calamares::Python::Dictionary
|
||||||
|
variantHashToPyDict( const QVariantHash& variantHash )
|
||||||
|
{
|
||||||
|
Calamares::Python::Dictionary pyDict;
|
||||||
|
for ( auto it = variantHash.constBegin(); it != variantHash.constEnd(); ++it )
|
||||||
|
{
|
||||||
|
pyDict[ Calamares::Python::String( it.key().toStdString() ) ] = variantToPyObject( it.value() );
|
||||||
|
}
|
||||||
|
return pyDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList variantListFromPyList( const Calamares::Python::List& list );
|
||||||
|
QVariantMap variantMapFromPyDict( const Calamares::Python::Dictionary& dict );
|
||||||
|
|
||||||
|
QVariant
|
||||||
|
variantFromPyObject( const py::handle& o )
|
||||||
|
{
|
||||||
|
if ( py::isinstance< Calamares::Python::Dictionary >( o ) )
|
||||||
|
{
|
||||||
|
return variantMapFromPyDict( py::cast< Calamares::Python::Dictionary >( o ) );
|
||||||
|
}
|
||||||
|
else if ( py::isinstance< Calamares::Python::List >( o ) )
|
||||||
|
{
|
||||||
|
return variantListFromPyList( py::cast< Calamares::Python::List >( o ) );
|
||||||
|
}
|
||||||
|
else if ( py::isinstance< py::int_ >( o ) )
|
||||||
|
{
|
||||||
|
return QVariant( qlonglong( py::cast< py::int_ >( o ) ) );
|
||||||
|
}
|
||||||
|
else if ( py::isinstance< py::float_ >( o ) )
|
||||||
|
{
|
||||||
|
return QVariant( double( py::cast< py::float_ >( o ) ) );
|
||||||
|
}
|
||||||
|
else if ( py::isinstance< py::str >( o ) )
|
||||||
|
{
|
||||||
|
return QVariant( QString::fromStdString( std::string( py::str( o ) ) ) );
|
||||||
|
}
|
||||||
|
else if ( py::isinstance< py::bool_ >( o ) )
|
||||||
|
{
|
||||||
|
return QVariant( bool( py::cast< py::bool_ >( o ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList
|
||||||
|
variantListFromPyList( const Calamares::Python::List& list )
|
||||||
|
{
|
||||||
|
QVariantList l;
|
||||||
|
for ( const auto& h : list )
|
||||||
|
{
|
||||||
|
l.append( variantFromPyObject( h ) );
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap
|
||||||
|
variantMapFromPyDict( const Calamares::Python::Dictionary& dict )
|
||||||
|
{
|
||||||
|
QVariantMap m;
|
||||||
|
for ( const auto& item : dict )
|
||||||
|
{
|
||||||
|
m.insert( Calamares::Python::asQString( item.first ), variantFromPyObject( ( item.second ) ) );
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList
|
||||||
|
stringListFromPyList( const Calamares::Python::List& list )
|
||||||
|
{
|
||||||
|
QStringList l;
|
||||||
|
for ( const auto& h : list )
|
||||||
|
{
|
||||||
|
l.append( Calamares::Python::asQString( h ) );
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char output_prefix[] = "[PYTHON JOB]:";
|
||||||
|
inline void
|
||||||
|
log_action( unsigned int level, const std::string& s )
|
||||||
|
{
|
||||||
|
Logger::CDebug( level ) << output_prefix << QString::fromStdString( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Calamares::GlobalStorage*
|
||||||
|
_global_storage()
|
||||||
|
{
|
||||||
|
static Calamares::GlobalStorage* p = new Calamares::GlobalStorage;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList
|
||||||
|
_gettext_languages()
|
||||||
|
{
|
||||||
|
QStringList languages;
|
||||||
|
|
||||||
|
// There are two ways that Python jobs can be initialised:
|
||||||
|
// - through JobQueue, in which case that has an instance which holds
|
||||||
|
// a GlobalStorage object, or
|
||||||
|
// - through the Python test-script, which initialises its
|
||||||
|
// own GlobalStorageProxy, which then holds a
|
||||||
|
// GlobalStorage object for all of Python.
|
||||||
|
Calamares::JobQueue* jq = Calamares::JobQueue::instance();
|
||||||
|
Calamares::GlobalStorage* gs = jq ? jq->globalStorage() : _global_storage();
|
||||||
|
|
||||||
|
QString lang = Calamares::Locale::readGS( *gs, QStringLiteral( "LANG" ) );
|
||||||
|
if ( !lang.isEmpty() )
|
||||||
|
{
|
||||||
|
languages.append( lang );
|
||||||
|
if ( lang.indexOf( '.' ) > 0 )
|
||||||
|
{
|
||||||
|
lang.truncate( lang.indexOf( '.' ) );
|
||||||
|
languages.append( lang );
|
||||||
|
}
|
||||||
|
if ( lang.indexOf( '_' ) > 0 )
|
||||||
|
{
|
||||||
|
lang.truncate( lang.indexOf( '_' ) );
|
||||||
|
languages.append( lang );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_add_localedirs( QStringList& pathList, const QString& candidate )
|
||||||
|
{
|
||||||
|
if ( !candidate.isEmpty() && !pathList.contains( candidate ) )
|
||||||
|
{
|
||||||
|
pathList.prepend( candidate );
|
||||||
|
if ( QDir( candidate ).cd( "lang" ) )
|
||||||
|
{
|
||||||
|
pathList.prepend( candidate + "/lang" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
raise_on_error( const Calamares::ProcessResult& ec, const QStringList& commandList )
|
||||||
|
{
|
||||||
|
if ( ec.first == 0 )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString raise = QString( "import subprocess\n"
|
||||||
|
"e = subprocess.CalledProcessError(%1,\"%2\")\n" )
|
||||||
|
.arg( ec.first )
|
||||||
|
.arg( commandList.join( ' ' ) );
|
||||||
|
if ( !ec.second.isEmpty() )
|
||||||
|
{
|
||||||
|
raise.append( QStringLiteral( "e.output = \"\"\"%1\"\"\"\n" ).arg( ec.second ) );
|
||||||
|
}
|
||||||
|
raise.append( "raise e" );
|
||||||
|
py::exec( raise.toStdString() );
|
||||||
|
py::error_already_set();
|
||||||
|
return ec.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
process_output( Calamares::Utils::RunLocation location,
|
||||||
|
const QStringList& args,
|
||||||
|
const Calamares::Python::Object& callback,
|
||||||
|
const std::string& input,
|
||||||
|
int timeout )
|
||||||
|
{
|
||||||
|
Calamares::Utils::Runner r( args );
|
||||||
|
r.setLocation( location );
|
||||||
|
if ( !callback.is_none() )
|
||||||
|
{
|
||||||
|
if ( py::isinstance< Calamares::Python::List >( callback ) )
|
||||||
|
{
|
||||||
|
QObject::connect( &r,
|
||||||
|
&decltype( r )::output,
|
||||||
|
[ list_append = callback.attr( "append" ) ]( const QString& s )
|
||||||
|
{ list_append( s.toStdString() ); } );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QObject::connect(
|
||||||
|
&r, &decltype( r )::output, [ &callback ]( const QString& s ) { callback( s.toStdString() ); } );
|
||||||
|
}
|
||||||
|
r.enableOutputProcessing();
|
||||||
|
}
|
||||||
|
if ( !input.empty() )
|
||||||
|
{
|
||||||
|
r.setInput( QString::fromStdString( input ) );
|
||||||
|
}
|
||||||
|
if ( timeout > 0 )
|
||||||
|
{
|
||||||
|
r.setTimeout( std::chrono::seconds( timeout ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = r.run();
|
||||||
|
return raise_on_error( result, args );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
/** @namespace
|
||||||
|
*
|
||||||
|
* This is where the "public Python API" lives. It does not need to
|
||||||
|
* be a namespace, and it does not need to be public, but it's
|
||||||
|
* convenient to group things together.
|
||||||
|
*/
|
||||||
|
namespace Calamares
|
||||||
|
{
|
||||||
|
namespace Python
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string
|
||||||
|
obscure( const std::string& string )
|
||||||
|
{
|
||||||
|
return Calamares::String::obscure( QString::fromStdString( string ) ).toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
debug( const std::string& s )
|
||||||
|
{
|
||||||
|
log_action( Logger::LOGDEBUG, s );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
warning( const std::string& s )
|
||||||
|
{
|
||||||
|
log_action( Logger::LOGWARNING, s );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
error( const std::string& s )
|
||||||
|
{
|
||||||
|
log_action( Logger::LOGERROR, s );
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary
|
||||||
|
load_yaml( const std::string& path )
|
||||||
|
{
|
||||||
|
const QString filePath = QString::fromUtf8( path.c_str() );
|
||||||
|
bool ok = false;
|
||||||
|
auto map = Calamares::YAML::load( filePath, &ok );
|
||||||
|
if ( !ok )
|
||||||
|
{
|
||||||
|
cWarning() << "Loading YAML from" << filePath << "failed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return variantMapToPyDict( map );
|
||||||
|
}
|
||||||
|
|
||||||
|
py::list
|
||||||
|
gettext_languages()
|
||||||
|
{
|
||||||
|
py::list pyList;
|
||||||
|
for ( auto lang : _gettext_languages() )
|
||||||
|
{
|
||||||
|
pyList.append( lang.toStdString() );
|
||||||
|
}
|
||||||
|
return pyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
py::object
|
||||||
|
gettext_path()
|
||||||
|
{
|
||||||
|
// Going to log informatively just once
|
||||||
|
static bool first_time = true;
|
||||||
|
cScopedAssignment( &first_time, false );
|
||||||
|
|
||||||
|
// TODO: distinguish between -d runs and normal runs
|
||||||
|
// TODO: can we detect DESTDIR-installs?
|
||||||
|
QStringList candidatePaths
|
||||||
|
= QStandardPaths::locateAll( QStandardPaths::GenericDataLocation, "locale", QStandardPaths::LocateDirectory );
|
||||||
|
QString extra = QCoreApplication::applicationDirPath();
|
||||||
|
_add_localedirs( candidatePaths, extra ); // Often /usr/local/bin
|
||||||
|
if ( !extra.isEmpty() )
|
||||||
|
{
|
||||||
|
QDir d( extra );
|
||||||
|
if ( d.cd( "../share/locale" ) ) // Often /usr/local/bin/../share/locale -> /usr/local/share/locale
|
||||||
|
{
|
||||||
|
_add_localedirs( candidatePaths, d.canonicalPath() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_add_localedirs( candidatePaths, QDir().canonicalPath() ); // .
|
||||||
|
|
||||||
|
if ( first_time )
|
||||||
|
{
|
||||||
|
cDebug() << "Determining gettext path from" << candidatePaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList candidateLanguages = _gettext_languages();
|
||||||
|
for ( const auto& lang : candidateLanguages )
|
||||||
|
{
|
||||||
|
for ( auto localedir : candidatePaths )
|
||||||
|
{
|
||||||
|
QDir ldir( localedir );
|
||||||
|
if ( ldir.cd( lang ) )
|
||||||
|
{
|
||||||
|
Logger::CDebug( Logger::LOGDEBUG )
|
||||||
|
<< output_prefix << "Found gettext" << lang << "in" << ldir.canonicalPath();
|
||||||
|
return String( localedir.toStdString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cWarning() << "No translation found for languages" << candidateLanguages;
|
||||||
|
return py::none(); // None
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
target_env_call( const List& args, const std::string& input, int timeout )
|
||||||
|
{
|
||||||
|
return Calamares::System::instance()
|
||||||
|
->targetEnvCommand(
|
||||||
|
stringListFromPyList( args ), QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) )
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
check_target_env_call( const List& args, const std::string& input, int timeout )
|
||||||
|
{
|
||||||
|
const auto commandList = stringListFromPyList( args );
|
||||||
|
auto ec = Calamares::System::instance()->targetEnvCommand(
|
||||||
|
commandList, QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) );
|
||||||
|
return raise_on_error( ec, commandList );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
check_target_env_output( const List& args, const std::string& input, int timeout )
|
||||||
|
{
|
||||||
|
const auto commandList = stringListFromPyList( args );
|
||||||
|
auto ec = Calamares::System::instance()->targetEnvCommand(
|
||||||
|
commandList, QString(), QString::fromStdString( input ), std::chrono::seconds( timeout ) );
|
||||||
|
raise_on_error( ec, commandList );
|
||||||
|
return ec.second.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
target_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout )
|
||||||
|
{
|
||||||
|
return process_output(
|
||||||
|
Calamares::System::RunLocation::RunInTarget, stringListFromPyList( args ), callback, input, timeout );
|
||||||
|
}
|
||||||
|
int
|
||||||
|
host_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout )
|
||||||
|
{
|
||||||
|
return process_output(
|
||||||
|
Calamares::System::RunLocation::RunInHost, stringListFromPyList( args ), callback, input, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JobProxy::JobProxy( Calamares::Python::Job* parent )
|
||||||
|
: prettyName( parent->prettyName().toStdString() )
|
||||||
|
, workingPath( parent->workingPath().toStdString() )
|
||||||
|
, moduleName( QDir( parent->workingPath() ).dirName().toStdString() )
|
||||||
|
, configuration( variantMapToPyDict( parent->configuration() ) )
|
||||||
|
, m_parent( parent )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JobProxy::setprogress( qreal progress )
|
||||||
|
{
|
||||||
|
if ( progress >= 0.0 && progress <= 1.0 )
|
||||||
|
{
|
||||||
|
m_parent->emitProgress( progress );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Calamares::GlobalStorage* GlobalStorageProxy::s_gs_instance = nullptr;
|
||||||
|
|
||||||
|
// The special handling for nullptr is only for the testing
|
||||||
|
// script for the python bindings, which passes in None;
|
||||||
|
// normal use will have a GlobalStorage from JobQueue::instance()
|
||||||
|
// passed in. Testing use will leak the allocated GlobalStorage
|
||||||
|
// object, but that's OK for testing.
|
||||||
|
GlobalStorageProxy::GlobalStorageProxy( Calamares::GlobalStorage* gs )
|
||||||
|
: m_gs( gs ? gs : s_gs_instance )
|
||||||
|
{
|
||||||
|
if ( !m_gs )
|
||||||
|
{
|
||||||
|
s_gs_instance = new Calamares::GlobalStorage;
|
||||||
|
m_gs = s_gs_instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
GlobalStorageProxy::contains( const std::string& key ) const
|
||||||
|
{
|
||||||
|
return m_gs->contains( QString::fromStdString( key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
GlobalStorageProxy::count() const
|
||||||
|
{
|
||||||
|
return m_gs->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GlobalStorageProxy::insert( const std::string& key, const Object& value )
|
||||||
|
{
|
||||||
|
m_gs->insert( QString::fromStdString( key ), variantFromPyObject( value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
List
|
||||||
|
GlobalStorageProxy::keys() const
|
||||||
|
{
|
||||||
|
List pyList;
|
||||||
|
const auto keys = m_gs->keys();
|
||||||
|
for ( const QString& key : keys )
|
||||||
|
{
|
||||||
|
pyList.append( key.toStdString() );
|
||||||
|
}
|
||||||
|
return pyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
GlobalStorageProxy::remove( const std::string& key )
|
||||||
|
{
|
||||||
|
const QString gsKey( QString::fromStdString( key ) );
|
||||||
|
if ( !m_gs->contains( gsKey ) )
|
||||||
|
{
|
||||||
|
cWarning() << "Unknown GS key" << key.c_str();
|
||||||
|
}
|
||||||
|
return m_gs->remove( gsKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
Object
|
||||||
|
GlobalStorageProxy::value( const std::string& key ) const
|
||||||
|
{
|
||||||
|
const QString gsKey( QString::fromStdString( key ) );
|
||||||
|
if ( !m_gs->contains( gsKey ) )
|
||||||
|
{
|
||||||
|
cWarning() << "Unknown GS key" << key.c_str();
|
||||||
|
return py::none();
|
||||||
|
}
|
||||||
|
return variantToPyObject( m_gs->value( gsKey ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Python
|
||||||
|
} // namespace Calamares
|
||||||
|
|
||||||
|
PYBIND11_EMBEDDED_MODULE( utils, m )
|
||||||
|
{
|
||||||
|
m.doc() = "Calamares Utility API for Python";
|
||||||
|
|
||||||
|
m.def( "obscure", &Calamares::Python::obscure, "A function that obscures (encodes) a string" );
|
||||||
|
|
||||||
|
m.def( "debug", &Calamares::Python::debug, "Log a debug-message" );
|
||||||
|
m.def( "warn", &Calamares::Python::warning, "Log a warning-message" );
|
||||||
|
m.def( "warning", &Calamares::Python::warning, "Log a warning-message" );
|
||||||
|
m.def( "error", &Calamares::Python::error, "Log an error-message" );
|
||||||
|
|
||||||
|
m.def( "load_yaml", &Calamares::Python::load_yaml, "Loads YAML from a file." );
|
||||||
|
|
||||||
|
m.def( "target_env_call",
|
||||||
|
&Calamares::Python::target_env_call,
|
||||||
|
"Runs command in target, returns exit code.",
|
||||||
|
py::arg( "command_list" ),
|
||||||
|
py::arg( "input" ) = std::string(),
|
||||||
|
py::arg( "timeout" ) = 0 );
|
||||||
|
m.def( "check_target_env_call",
|
||||||
|
&Calamares::Python::check_target_env_call,
|
||||||
|
"Runs command in target, raises on error exit.",
|
||||||
|
py::arg( "command_list" ),
|
||||||
|
py::arg( "input" ) = std::string(),
|
||||||
|
py::arg( "timeout" ) = 0 );
|
||||||
|
m.def( "check_target_env_output",
|
||||||
|
&Calamares::Python::check_target_env_output,
|
||||||
|
"Runs command in target, returns standard output or raises on error.",
|
||||||
|
py::arg( "command_list" ),
|
||||||
|
py::arg( "input" ) = std::string(),
|
||||||
|
py::arg( "timeout" ) = 0 );
|
||||||
|
m.def( "target_env_process_output",
|
||||||
|
&Calamares::Python::target_env_process_output,
|
||||||
|
"Runs command in target, updating callback and returns standard output or raises on error.",
|
||||||
|
py::arg( "command_list" ),
|
||||||
|
py::arg( "callback" ) = pybind11::none(),
|
||||||
|
py::arg( "input" ) = std::string(),
|
||||||
|
py::arg( "timeout" ) = 0 );
|
||||||
|
m.def( "host_env_process_output",
|
||||||
|
&Calamares::Python::host_env_process_output,
|
||||||
|
"Runs command in target, updating callback and returns standard output or raises on error.",
|
||||||
|
py::arg( "command_list" ),
|
||||||
|
py::arg( "callback" ) = pybind11::none(),
|
||||||
|
py::arg( "input" ) = std::string(),
|
||||||
|
py::arg( "timeout" ) = 0 );
|
||||||
|
|
||||||
|
m.def( "gettext_languages",
|
||||||
|
&Calamares::Python::gettext_languages,
|
||||||
|
"Returns list of languages (most to least-specific) for gettext." );
|
||||||
|
m.def( "gettext_path", &Calamares::Python::gettext_path, "Returns path for gettext search." );
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_MODULE( libcalamares, m )
|
||||||
|
{
|
||||||
|
m.doc() = "Calamares API for Python";
|
||||||
|
|
||||||
|
m.add_object( "ORGANIZATION_NAME", Calamares::Python::String( CALAMARES_ORGANIZATION_NAME ) );
|
||||||
|
m.add_object( "ORGANIZATION_DOMAIN", Calamares::Python::String( CALAMARES_ORGANIZATION_DOMAIN ) );
|
||||||
|
m.add_object( "APPLICATION_NAME", Calamares::Python::String( CALAMARES_APPLICATION_NAME ) );
|
||||||
|
m.add_object( "VERSION", Calamares::Python::String( CALAMARES_VERSION ) );
|
||||||
|
m.add_object( "VERSION_SHORT", Calamares::Python::String( CALAMARES_VERSION_SHORT ) );
|
||||||
|
|
||||||
|
m.add_object( "utils", py::module::import( "utils" ) );
|
||||||
|
|
||||||
|
py::class_< Calamares::Python::JobProxy >( m, "Job" )
|
||||||
|
.def_readonly( "module_name", &Calamares::Python::JobProxy::moduleName )
|
||||||
|
.def_readonly( "pretty_name", &Calamares::Python::JobProxy::prettyName )
|
||||||
|
.def_readonly( "working_path", &Calamares::Python::JobProxy::workingPath )
|
||||||
|
.def_readonly( "configuration", &Calamares::Python::JobProxy::configuration )
|
||||||
|
.def( "setprogress", &Calamares::Python::JobProxy::setprogress );
|
||||||
|
|
||||||
|
py::class_< Calamares::Python::GlobalStorageProxy >( m, "GlobalStorage" )
|
||||||
|
.def( "contains", &Calamares::Python::GlobalStorageProxy::contains )
|
||||||
|
.def( "count", &Calamares::Python::GlobalStorageProxy::count )
|
||||||
|
.def( "insert", &Calamares::Python::GlobalStorageProxy::insert )
|
||||||
|
.def( "keys", &Calamares::Python::GlobalStorageProxy::keys )
|
||||||
|
.def( "remove", &Calamares::Python::GlobalStorageProxy::remove )
|
||||||
|
.def( "value", &Calamares::Python::GlobalStorageProxy::value );
|
||||||
|
}
|
100
src/libcalamares/python/Api.h
Normal file
100
src/libcalamares/python/Api.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALAMARES_PYTHON_API_H
|
||||||
|
#define CALAMARES_PYTHON_API_H
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* Contains the API that Python modules use from the Python code
|
||||||
|
* of that module. This is the C++ side that implements the functions
|
||||||
|
* imported by the Python code as `import libcalamares`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "python/Pybind11Helpers.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Calamares
|
||||||
|
{
|
||||||
|
|
||||||
|
class GlobalStorage;
|
||||||
|
class PythonJob;
|
||||||
|
|
||||||
|
namespace Python __attribute__( ( visibility( "hidden" ) ) )
|
||||||
|
{
|
||||||
|
std::string obscure( const std::string& string );
|
||||||
|
|
||||||
|
void debug( const std::string& s );
|
||||||
|
void warning( const std::string& s );
|
||||||
|
// void warn( const std::string& s) is an alias of warning()
|
||||||
|
void error( const std::string& s );
|
||||||
|
|
||||||
|
Dictionary load_yaml( const std::string& path );
|
||||||
|
|
||||||
|
List gettext_languages();
|
||||||
|
Object gettext_path();
|
||||||
|
|
||||||
|
int target_env_call( const List& args, const std::string& input, int timeout );
|
||||||
|
int check_target_env_call( const List& args, const std::string& input, int timeout );
|
||||||
|
std::string check_target_env_output( const List& args, const std::string& input, int timeout );
|
||||||
|
|
||||||
|
int target_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout );
|
||||||
|
int host_env_process_output( const List& args, const Object& callback, const std::string& input, int timeout );
|
||||||
|
|
||||||
|
|
||||||
|
class Job;
|
||||||
|
/** @brief Proxy class in Python for the Calamares Job class
|
||||||
|
*
|
||||||
|
* This is available as libcalamares.job in Python code.
|
||||||
|
*/
|
||||||
|
class JobProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit JobProxy( Calamares::Python::Job* parent );
|
||||||
|
|
||||||
|
std::string prettyName;
|
||||||
|
std::string workingPath;
|
||||||
|
std::string moduleName;
|
||||||
|
|
||||||
|
Dictionary configuration;
|
||||||
|
|
||||||
|
void setprogress( qreal progress );
|
||||||
|
|
||||||
|
private:
|
||||||
|
Calamares::Python::Job* m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GlobalStorageProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit GlobalStorageProxy( Calamares::GlobalStorage* gs );
|
||||||
|
|
||||||
|
bool contains( const std::string& key ) const;
|
||||||
|
int count() const;
|
||||||
|
void insert( const std::string& key, const Object& value );
|
||||||
|
List keys() const;
|
||||||
|
int remove( const std::string& key );
|
||||||
|
Object value( const std::string& key ) const;
|
||||||
|
|
||||||
|
// This is a helper for scripts that do not go through
|
||||||
|
// the JobQueue (i.e. the module testpython script),
|
||||||
|
// which allocate their own (singleton) GlobalStorage.
|
||||||
|
static Calamares::GlobalStorage* globalStorageInstance() { return s_gs_instance; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Calamares::GlobalStorage* m_gs;
|
||||||
|
static Calamares::GlobalStorage* s_gs_instance; // See globalStorageInstance()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace )
|
||||||
|
} // namespace Calamares
|
||||||
|
|
||||||
|
#endif
|
36
src/libcalamares/python/Logger.h
Normal file
36
src/libcalamares/python/Logger.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALAMARES_PYTHON_LOGGER_H
|
||||||
|
#define CALAMARES_PYTHON_LOGGER_H
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
*
|
||||||
|
* Additional logging helpers for pybind11 types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
|
#undef slots
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
inline QDebug&
|
||||||
|
operator<<( QDebug& s, const pybind11::handle& h )
|
||||||
|
{
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
return s << QString::fromUtf8( pybind11::str( h ).cast< std::string >().c_str() );
|
||||||
|
#else
|
||||||
|
return s << pybind11::str( h ).cast< std::string >();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
38
src/libcalamares/python/Pybind11Helpers.h
Normal file
38
src/libcalamares/python/Pybind11Helpers.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALAMARES_PYTHON_PYBIND11_HELPERS_H
|
||||||
|
#define CALAMARES_PYTHON_PYBIND11_HELPERS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#undef slots
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
namespace Calamares
|
||||||
|
{
|
||||||
|
namespace Python __attribute__( ( visibility( "hidden" ) ) )
|
||||||
|
{
|
||||||
|
using Dictionary = pybind11::dict;
|
||||||
|
using String = pybind11::str;
|
||||||
|
using List = pybind11::list;
|
||||||
|
using Object = pybind11::object;
|
||||||
|
|
||||||
|
using Float = double;
|
||||||
|
|
||||||
|
inline QString asQString( const pybind11::handle& o )
|
||||||
|
{
|
||||||
|
return QString::fromUtf8( pybind11::str( o ).cast< std::string >().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace )
|
||||||
|
} // namespace Calamares
|
||||||
|
#endif
|
261
src/libcalamares/python/PythonJob.cpp
Normal file
261
src/libcalamares/python/PythonJob.cpp
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "python/PythonJob.h"
|
||||||
|
|
||||||
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
|
#include "python/Api.h"
|
||||||
|
#include "python/Logger.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#undef slots
|
||||||
|
#include <pybind11/embed.h>
|
||||||
|
#include <pybind11/eval.h>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* s_preScript = nullptr;
|
||||||
|
|
||||||
|
QString
|
||||||
|
getPrettyNameFromScope( const py::dict& scope )
|
||||||
|
{
|
||||||
|
static constexpr char key_name[] = "pretty_name";
|
||||||
|
|
||||||
|
if ( scope.contains( key_name ) )
|
||||||
|
{
|
||||||
|
const py::object func = scope[ key_name ];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto s = func().cast< std::string >();
|
||||||
|
return QString::fromUtf8( s.c_str() );
|
||||||
|
}
|
||||||
|
catch ( const py::cast_error& e )
|
||||||
|
{
|
||||||
|
// Ignore, we will try __doc__ next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr char key_doc[] = "__doc__";
|
||||||
|
if ( scope.contains( key_doc ) )
|
||||||
|
{
|
||||||
|
const py::object doc = scope[ key_doc ];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto s = doc.cast< std::string >();
|
||||||
|
auto string = QString::fromUtf8( s.c_str() ).trimmed();
|
||||||
|
const auto newline_index = string.indexOf( '\n' );
|
||||||
|
if ( newline_index >= 0 )
|
||||||
|
{
|
||||||
|
string.truncate( newline_index );
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
// __doc__ is apparently empty, try next fallback
|
||||||
|
}
|
||||||
|
catch ( const py::cast_error& e )
|
||||||
|
{
|
||||||
|
// Ignore, try next fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No more fallbacks
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace Calamares
|
||||||
|
{
|
||||||
|
namespace Python
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Job::Private
|
||||||
|
{
|
||||||
|
Private( const QString& script, const QString& path, const QVariantMap& configuration )
|
||||||
|
: scriptFile( script )
|
||||||
|
, workingPath( path )
|
||||||
|
, configurationMap( configuration )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
QString scriptFile; // From the module descriptor
|
||||||
|
QString workingPath;
|
||||||
|
|
||||||
|
QVariantMap configurationMap; // The module configuration
|
||||||
|
|
||||||
|
QString description; // Obtained from the Python code
|
||||||
|
};
|
||||||
|
|
||||||
|
Job::Job( const QString& scriptFile,
|
||||||
|
const QString& workingPath,
|
||||||
|
const QVariantMap& moduleConfiguration,
|
||||||
|
QObject* parent )
|
||||||
|
: m_d( std::make_unique< Job::Private >( scriptFile, workingPath, moduleConfiguration ) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Job::~Job() {}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Job::prettyName() const
|
||||||
|
{
|
||||||
|
return QDir( m_d->workingPath ).dirName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Job::prettyStatusMessage() const
|
||||||
|
{
|
||||||
|
// The description is updated when progress is reported, see emitProgress()
|
||||||
|
if ( m_d->description.isEmpty() )
|
||||||
|
{
|
||||||
|
return tr( "Running %1 operation." ).arg( prettyName() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return m_d->description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JobResult
|
||||||
|
Job::exec()
|
||||||
|
{
|
||||||
|
// We assume m_scriptFile to be relative to m_workingPath.
|
||||||
|
QDir workingDir( m_d->workingPath );
|
||||||
|
if ( !workingDir.exists() || !workingDir.isReadable() )
|
||||||
|
{
|
||||||
|
return JobResult::error( tr( "Bad working directory path" ),
|
||||||
|
tr( "Working directory %1 for python job %2 is not readable." )
|
||||||
|
.arg( m_d->workingPath )
|
||||||
|
.arg( prettyName() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo scriptFI( workingDir.absoluteFilePath( m_d->scriptFile ) );
|
||||||
|
if ( !scriptFI.exists() || !scriptFI.isFile() || !scriptFI.isReadable() )
|
||||||
|
{
|
||||||
|
return JobResult::error( tr( "Bad main script file" ),
|
||||||
|
tr( "Main script file %1 for python job %2 is not readable." )
|
||||||
|
.arg( scriptFI.absoluteFilePath() )
|
||||||
|
.arg( prettyName() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
py::scoped_interpreter guard {};
|
||||||
|
auto scope = py::module_::import( "__main__" ).attr( "__dict__" );
|
||||||
|
auto calamaresModule = py::module::import( "libcalamares" );
|
||||||
|
calamaresModule.attr( "job" ) = Calamares::Python::JobProxy( this );
|
||||||
|
calamaresModule.attr( "globalstorage" )
|
||||||
|
= Calamares::Python::GlobalStorageProxy( JobQueue::instance()->globalStorage() );
|
||||||
|
|
||||||
|
if ( s_preScript )
|
||||||
|
{
|
||||||
|
py::exec( s_preScript );
|
||||||
|
}
|
||||||
|
py::eval_file( scriptFI.absoluteFilePath().toUtf8().constData(), scope );
|
||||||
|
|
||||||
|
m_d->description = getPrettyNameFromScope( scope );
|
||||||
|
|
||||||
|
Q_EMIT progress( 0 );
|
||||||
|
static constexpr char key_run[] = "run";
|
||||||
|
if ( scope.contains( key_run ) )
|
||||||
|
{
|
||||||
|
const py::object run = scope[ key_run ];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
py::object r;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
r = run();
|
||||||
|
}
|
||||||
|
catch ( const py::error_already_set& e )
|
||||||
|
{
|
||||||
|
// This is an error in the Python code itself
|
||||||
|
cError() << e.what();
|
||||||
|
return JobResult::internalError( tr( "Bad main script file" ),
|
||||||
|
tr( "Main script file %1 for python job %2 raised an exception." )
|
||||||
|
.arg( scriptFI.absoluteFilePath() )
|
||||||
|
.arg( prettyName() ),
|
||||||
|
JobResult::PythonUncaughtException );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( r.is( py::none() ) )
|
||||||
|
{
|
||||||
|
return JobResult::ok();
|
||||||
|
}
|
||||||
|
const py::tuple items = r;
|
||||||
|
return JobResult::error( asQString( items[ 0 ] ), asQString( items[ 1 ] ) );
|
||||||
|
}
|
||||||
|
catch ( const py::cast_error& e )
|
||||||
|
{
|
||||||
|
cError() << e.what();
|
||||||
|
return JobResult::error( tr( "Bad main script file" ),
|
||||||
|
tr( "Main script file %1 for python job %2 returned invalid results." )
|
||||||
|
.arg( scriptFI.absoluteFilePath() )
|
||||||
|
.arg( prettyName() ) );
|
||||||
|
}
|
||||||
|
catch ( const py::error_already_set& e )
|
||||||
|
{
|
||||||
|
cError() << e.what();
|
||||||
|
return JobResult::error( tr( "Bad main script file" ),
|
||||||
|
tr( "Main script file %1 for python job %2 returned invalid results." )
|
||||||
|
.arg( scriptFI.absoluteFilePath() )
|
||||||
|
.arg( prettyName() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return JobResult::error( tr( "Bad main script file" ),
|
||||||
|
tr( "Main script file %1 for python job %2 does not contain a run() function." )
|
||||||
|
.arg( scriptFI.absoluteFilePath() )
|
||||||
|
.arg( prettyName() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Job::workingPath() const
|
||||||
|
{
|
||||||
|
return m_d->workingPath;
|
||||||
|
}
|
||||||
|
QVariantMap
|
||||||
|
Job::configuration() const
|
||||||
|
{
|
||||||
|
return m_d->configurationMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Job::emitProgress( double progressValue )
|
||||||
|
{
|
||||||
|
// TODO: update prettyname
|
||||||
|
emit progress( progressValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Sets the pre-run Python code for all PythonJobs
|
||||||
|
*
|
||||||
|
* A PythonJob runs the code from the scriptFile parameter to
|
||||||
|
* the constructor; the pre-run code is **also** run, before
|
||||||
|
* even the scriptFile code. Use this in testing mode
|
||||||
|
* to modify Python internals.
|
||||||
|
*
|
||||||
|
* No ownership of @p script is taken: pass in a pointer to
|
||||||
|
* a character literal or something that lives longer than the
|
||||||
|
* job. Pass in @c nullptr to switch off pre-run code.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
Job::setInjectedPreScript( const char* script )
|
||||||
|
{
|
||||||
|
s_preScript = script;
|
||||||
|
cDebug() << "Python pre-script set to string" << Logger::Pointer( script ) << "length"
|
||||||
|
<< ( script ? strlen( script ) : 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Python
|
||||||
|
} // namespace Calamares
|
66
src/libcalamares/python/PythonJob.h
Normal file
66
src/libcalamares/python/PythonJob.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALAMARES_PYTHON_PYTHONJOB_H
|
||||||
|
#define CALAMARES_PYTHON_PYTHONJOB_H
|
||||||
|
|
||||||
|
// This file is called PythonJob.h because it would otherwise
|
||||||
|
// clashwith the Job.h in libcalamares proper.
|
||||||
|
|
||||||
|
#include "Job.h"
|
||||||
|
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Calamares
|
||||||
|
{
|
||||||
|
namespace Python
|
||||||
|
{
|
||||||
|
class Job : public ::Calamares::Job
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Job( const QString& scriptFile,
|
||||||
|
const QString& workingPath,
|
||||||
|
const QVariantMap& moduleConfiguration = QVariantMap(),
|
||||||
|
QObject* parent = nullptr );
|
||||||
|
~Job() override;
|
||||||
|
|
||||||
|
QString prettyName() const override;
|
||||||
|
QString prettyStatusMessage() const override;
|
||||||
|
::Calamares::JobResult exec() override;
|
||||||
|
|
||||||
|
/** @brief Sets the pre-run Python code for all PythonJobs
|
||||||
|
*
|
||||||
|
* A PythonJob runs the code from the scriptFile parameter to
|
||||||
|
* the constructor; the pre-run code is **also** run, before
|
||||||
|
* even the scriptFile code. Use this in testing mode
|
||||||
|
* to modify Python internals.
|
||||||
|
*
|
||||||
|
* No ownership of @p script is taken: pass in a pointer to
|
||||||
|
* a character literal or something that lives longer than the
|
||||||
|
* job. Pass in @c nullptr to switch off pre-run code.
|
||||||
|
*/
|
||||||
|
static void setInjectedPreScript( const char* script );
|
||||||
|
|
||||||
|
/** @brief Accessors for JobProxy */
|
||||||
|
QString workingPath() const;
|
||||||
|
QVariantMap configuration() const;
|
||||||
|
/** @brief Proxy functions */
|
||||||
|
void emitProgress( double progressValue );
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Private;
|
||||||
|
std::unique_ptr< Private > m_d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Python
|
||||||
|
} // namespace Calamares
|
||||||
|
#endif
|
@ -64,6 +64,10 @@ if(WITH_QML)
|
|||||||
target_link_libraries(calamaresui PUBLIC ${qtname}::QuickWidgets)
|
target_link_libraries(calamaresui PUBLIC ${qtname}::QuickWidgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WITH_PYBIND11)
|
||||||
|
target_compile_definitions(calamaresui PRIVATE WITH_PYBIND11=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(Calamares::calamaresui ALIAS calamaresui)
|
add_library(Calamares::calamaresui ALIAS calamaresui)
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2023 Adriaan de Groot <groot@kde.org>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
*
|
||||||
* Calamares is Free Software: see the License-Identifier above.
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
@ -9,7 +10,14 @@
|
|||||||
|
|
||||||
#include "PythonJobModule.h"
|
#include "PythonJobModule.h"
|
||||||
|
|
||||||
|
#if WITH_PYBIND11
|
||||||
|
#include "python/PythonJob.h"
|
||||||
|
using JobType = Calamares::Python::Job;
|
||||||
|
#else
|
||||||
|
// Old Boost::Python version
|
||||||
#include "PythonJob.h"
|
#include "PythonJob.h"
|
||||||
|
using JobType = Calamares::PythonJob;
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
@ -40,7 +48,7 @@ PythonJobModule::loadSelf()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_job = Calamares::job_ptr( new PythonJob( m_scriptFileName, m_workingPath, m_configurationMap ) );
|
m_job = Calamares::job_ptr( new JobType( m_scriptFileName, m_workingPath, m_configurationMap ) );
|
||||||
m_loaded = true;
|
m_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +334,8 @@ description if something went wrong.
|
|||||||
The interface from a Python module to Calamares internals is
|
The interface from a Python module to Calamares internals is
|
||||||
found in the *libcalamares* module. This is not a standard Python
|
found in the *libcalamares* module. This is not a standard Python
|
||||||
module, and is only available inside the Calamares "runtime" for
|
module, and is only available inside the Calamares "runtime" for
|
||||||
Python modules (it is implemented through Boost::Python in C++).
|
Python modules (it is implemented in C++ and injected into the Python
|
||||||
|
environment by Calamares).
|
||||||
|
|
||||||
A module should start by importing the Calamares internals:
|
A module should start by importing the Calamares internals:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user