mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-26 23:22:01 +00:00
Merge branch 'pybind:master' into master
This commit is contained in:
commit
f33b2e981f
4
.github/workflows/pip.yml
vendored
4
.github/workflows/pip.yml
vendored
@ -98,13 +98,13 @@ jobs:
|
|||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
|
|
||||||
- name: Publish standard package
|
- name: Publish standard package
|
||||||
uses: pypa/gh-action-pypi-publish@v1.5.1
|
uses: pypa/gh-action-pypi-publish@v1.5.2
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.pypi_password }}
|
password: ${{ secrets.pypi_password }}
|
||||||
packages_dir: standard/
|
packages_dir: standard/
|
||||||
|
|
||||||
- name: Publish global package
|
- name: Publish global package
|
||||||
uses: pypa/gh-action-pypi-publish@v1.5.1
|
uses: pypa/gh-action-pypi-publish@v1.5.2
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.pypi_password_global }}
|
password: ${{ secrets.pypi_password_global }}
|
||||||
packages_dir: global/
|
packages_dir: global/
|
||||||
|
@ -86,37 +86,22 @@ inline wchar_t *widen_chars(const char *safe_arg) {
|
|||||||
return widened_arg;
|
return widened_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
inline void precheck_interpreter() {
|
||||||
|
|
||||||
/** \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_IsInitialized() != 0) {
|
if (Py_IsInitialized() != 0) {
|
||||||
pybind11_fail("The interpreter is already running");
|
pybind11_fail("The interpreter is already running");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if PY_VERSION_HEX < 0x030B0000
|
#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);
|
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||||
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
|
# if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
|
||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
@ -150,26 +135,30 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||||||
auto *pysys_argv = widened_argv.get();
|
auto *pysys_argv = widened_argv.get();
|
||||||
|
|
||||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
||||||
#else
|
}
|
||||||
PyConfig config;
|
#endif
|
||||||
PyConfig_InitIsolatedConfig(&config);
|
|
||||||
config.isolated = 0;
|
|
||||||
config.use_environment = 1;
|
|
||||||
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
|
||||||
|
|
||||||
PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast<char *const *>(argv));
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
if (PyStatus_Exception(status)) {
|
|
||||||
|
#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
|
// A failure here indicates a character-encoding failure or the python
|
||||||
// interpreter out of memory. Give up.
|
// interpreter out of memory. Give up.
|
||||||
PyConfig_Clear(&config);
|
PyConfig_Clear(config);
|
||||||
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg
|
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
|
||||||
: "Failed to prepare CPython");
|
: "Failed to prepare CPython");
|
||||||
}
|
}
|
||||||
status = Py_InitializeFromConfig(&config);
|
status = Py_InitializeFromConfig(config);
|
||||||
PyConfig_Clear(&config);
|
if (PyStatus_Exception(status) != 0) {
|
||||||
if (PyStatus_Exception(status)) {
|
PyConfig_Clear(config);
|
||||||
throw std::runtime_error(PyStatus_IsError(status) ? status.err_msg
|
throw std::runtime_error(PyStatus_IsError(status) != 0 ? status.err_msg
|
||||||
: "Failed to init CPython");
|
: "Failed to init CPython");
|
||||||
}
|
}
|
||||||
if (add_program_dir_to_path) {
|
if (add_program_dir_to_path) {
|
||||||
PyRun_SimpleString("import sys, os.path; "
|
PyRun_SimpleString("import sys, os.path; "
|
||||||
@ -177,6 +166,43 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||||||
"os.path.abspath(os.path.dirname(sys.argv[0])) "
|
"os.path.abspath(os.path.dirname(sys.argv[0])) "
|
||||||
"if sys.argv and os.path.exists(sys.argv[0]) else '')");
|
"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_InitIsolatedConfig(&config);
|
||||||
|
config.isolated = 0;
|
||||||
|
config.use_environment = 1;
|
||||||
|
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||||
|
initialize_interpreter(&config, argc, argv, add_program_dir_to_path);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +290,15 @@ public:
|
|||||||
initialize_interpreter(init_signal_handlers, argc, argv, add_program_dir_to_path);
|
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(const scoped_interpreter &) = delete;
|
||||||
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
|
scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; }
|
||||||
scoped_interpreter &operator=(const scoped_interpreter &) = delete;
|
scoped_interpreter &operator=(const scoped_interpreter &) = delete;
|
||||||
|
@ -17,7 +17,12 @@ import pytest
|
|||||||
# Early diagnostic for failed imports
|
# Early diagnostic for failed imports
|
||||||
import pybind11_tests
|
import pybind11_tests
|
||||||
|
|
||||||
if os.name != "nt":
|
|
||||||
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
|
def always_forkserver_on_unix():
|
||||||
|
if os.name == "nt":
|
||||||
|
return
|
||||||
|
|
||||||
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
|
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592
|
||||||
# In a nutshell: fork() after starting threads == flakiness in the form of deadlocks.
|
# In a nutshell: fork() after starting threads == flakiness in the form of deadlocks.
|
||||||
# It is actually a well-known pitfall, unfortunately without guard rails.
|
# It is actually a well-known pitfall, unfortunately without guard rails.
|
||||||
@ -27,6 +32,7 @@ if os.name != "nt":
|
|||||||
# running with defaults.
|
# running with defaults.
|
||||||
multiprocessing.set_start_method("forkserver")
|
multiprocessing.set_start_method("forkserver")
|
||||||
|
|
||||||
|
|
||||||
_long_marker = re.compile(r"([0-9])L")
|
_long_marker = re.compile(r"([0-9])L")
|
||||||
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
|
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
|
||||||
|
|
||||||
|
@ -14,6 +14,11 @@ PYBIND11_WARNING_DISABLE_MSVC(4996)
|
|||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using namespace py::literals;
|
using namespace py::literals;
|
||||||
|
|
||||||
|
size_t get_sys_path_size() {
|
||||||
|
auto sys_path = py::module::import("sys").attr("path");
|
||||||
|
return py::len(sys_path);
|
||||||
|
}
|
||||||
|
|
||||||
class Widget {
|
class Widget {
|
||||||
public:
|
public:
|
||||||
explicit Widget(std::string message) : message(std::move(message)) {}
|
explicit Widget(std::string message) : message(std::move(message)) {}
|
||||||
@ -166,6 +171,70 @@ TEST_CASE("There can be only one interpreter") {
|
|||||||
py::initialize_interpreter();
|
py::initialize_interpreter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||||
|
TEST_CASE("Custom PyConfig") {
|
||||||
|
py::finalize_interpreter();
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
REQUIRE_NOTHROW(py::scoped_interpreter{&config});
|
||||||
|
{
|
||||||
|
py::scoped_interpreter p{&config};
|
||||||
|
REQUIRE(py::module_::import("widget_module").attr("add")(1, 41).cast<int>() == 42);
|
||||||
|
}
|
||||||
|
py::initialize_interpreter();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Custom PyConfig with argv") {
|
||||||
|
py::finalize_interpreter();
|
||||||
|
{
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitIsolatedConfig(&config);
|
||||||
|
char *argv[] = {strdup("a.out")};
|
||||||
|
py::scoped_interpreter argv_scope{&config, 1, argv};
|
||||||
|
std::free(argv[0]);
|
||||||
|
auto module = py::module::import("test_interpreter");
|
||||||
|
auto py_widget = module.attr("DerivedWidget")("The question");
|
||||||
|
const auto &cpp_widget = py_widget.cast<const Widget &>();
|
||||||
|
REQUIRE(cpp_widget.argv0() == "a.out");
|
||||||
|
}
|
||||||
|
py::initialize_interpreter();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("Add program dir to path pre-PyConfig") {
|
||||||
|
py::finalize_interpreter();
|
||||||
|
size_t path_size_add_program_dir_to_path_false = 0;
|
||||||
|
{
|
||||||
|
py::scoped_interpreter scoped_interp{true, 0, nullptr, false};
|
||||||
|
path_size_add_program_dir_to_path_false = get_sys_path_size();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
py::scoped_interpreter scoped_interp{};
|
||||||
|
REQUIRE(get_sys_path_size() == path_size_add_program_dir_to_path_false + 1);
|
||||||
|
}
|
||||||
|
py::initialize_interpreter();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX
|
||||||
|
TEST_CASE("Add program dir to path using PyConfig") {
|
||||||
|
py::finalize_interpreter();
|
||||||
|
size_t path_size_add_program_dir_to_path_false = 0;
|
||||||
|
{
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
py::scoped_interpreter scoped_interp{&config, 0, nullptr, false};
|
||||||
|
path_size_add_program_dir_to_path_false = get_sys_path_size();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
py::scoped_interpreter scoped_interp{&config};
|
||||||
|
REQUIRE(get_sys_path_size() == path_size_add_program_dir_to_path_false + 1);
|
||||||
|
}
|
||||||
|
py::initialize_interpreter();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool has_pybind11_internals_builtin() {
|
bool has_pybind11_internals_builtin() {
|
||||||
auto builtins = py::handle(PyEval_GetBuiltins());
|
auto builtins = py::handle(PyEval_GetBuiltins());
|
||||||
return builtins.contains(PYBIND11_INTERNALS_ID);
|
return builtins.contains(PYBIND11_INTERNALS_ID);
|
||||||
|
Loading…
Reference in New Issue
Block a user