mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 22:52:01 +00:00
Fix: 3.11 beta support (#3923)
* Placeholder commit for 3.11 testing * Does this fix it? * Try suggestion * Placeholder commit for 3.11 testing * Does this fix it? * Try suggestion * fix: try using modern init for embedded interp Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * fix: error message changed in 3.11 * fix: apply logic in Python manually Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * fix autodetect dynamic attrs in 3.11 * fix: include error message if possible in error Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * ci: enable standard Python 3.11 testing Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * Make dynamic attrs condtiion exclusive to ver. Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
This commit is contained in:
parent
c42e3ab793
commit
2af163d9c7
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -30,6 +30,7 @@ jobs:
|
|||||||
- '3.6'
|
- '3.6'
|
||||||
- '3.9'
|
- '3.9'
|
||||||
- '3.10'
|
- '3.10'
|
||||||
|
- '3.11-dev'
|
||||||
- 'pypy-3.7'
|
- 'pypy-3.7'
|
||||||
- 'pypy-3.8'
|
- 'pypy-3.8'
|
||||||
- 'pypy-3.9'
|
- 'pypy-3.9'
|
||||||
@ -185,8 +186,8 @@ jobs:
|
|||||||
- python-version: "3.9"
|
- python-version: "3.9"
|
||||||
python-debug: true
|
python-debug: true
|
||||||
valgrind: true
|
valgrind: true
|
||||||
# - python-version: "3.11-dev"
|
- python-version: "3.11-dev"
|
||||||
# python-debug: false
|
python-debug: false
|
||||||
|
|
||||||
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
|
name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
2
.github/workflows/upstream.yml
vendored
2
.github/workflows/upstream.yml
vendored
@ -14,7 +14,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
standard:
|
standard:
|
||||||
name: "🐍 3.11 dev • ubuntu-latest • x64"
|
name: "🐍 3.11 latest internals • ubuntu-latest • x64"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
|
if: "contains(github.event.pull_request.labels.*.name, 'python dev')"
|
||||||
|
|
||||||
|
@ -345,9 +345,11 @@ struct type_record {
|
|||||||
|
|
||||||
bases.append((PyObject *) base_info->type);
|
bases.append((PyObject *) base_info->type);
|
||||||
|
|
||||||
if (base_info->type->tp_dictoffset != 0) {
|
#if PY_VERSION_HEX < 0x030B0000
|
||||||
dynamic_attr = true;
|
dynamic_attr |= base_info->type->tp_dictoffset != 0;
|
||||||
}
|
#else
|
||||||
|
dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (caster) {
|
if (caster) {
|
||||||
base_info->implicit_casts.emplace_back(type, caster);
|
base_info->implicit_casts.emplace_back(type, caster);
|
||||||
|
@ -545,8 +545,12 @@ extern "C" inline int pybind11_clear(PyObject *self) {
|
|||||||
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
|
||||||
auto *type = &heap_type->ht_type;
|
auto *type = &heap_type->ht_type;
|
||||||
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
|
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_dictoffset = type->tp_basicsize; // place dict at the end
|
||||||
type->tp_basicsize += (ssize_t) sizeof(PyObject *); // and allocate enough space for it
|
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_traverse = pybind11_traverse;
|
||||||
type->tp_clear = pybind11_clear;
|
type->tp_clear = pybind11_clear;
|
||||||
|
|
||||||
|
@ -86,37 +86,6 @@ inline wchar_t *widen_chars(const char *safe_arg) {
|
|||||||
return widened_arg;
|
return widened_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Python 2.x/3.x-compatible version of `PySys_SetArgv`
|
|
||||||
inline void set_interpreter_argv(int argc, const char *const *argv, bool add_program_dir_to_path) {
|
|
||||||
// Before it was special-cased in python 3.8, passing an empty or null argv
|
|
||||||
// caused a segfault, so we have to reimplement the special case ourselves.
|
|
||||||
bool special_case = (argv == nullptr || argc <= 0);
|
|
||||||
|
|
||||||
const char *const empty_argv[]{"\0"};
|
|
||||||
const char *const *safe_argv = special_case ? empty_argv : argv;
|
|
||||||
if (special_case) {
|
|
||||||
argc = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto argv_size = static_cast<size_t>(argc);
|
|
||||||
// SetArgv* on python 3 takes wchar_t, so we have to convert.
|
|
||||||
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
|
|
||||||
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries;
|
|
||||||
widened_argv_entries.reserve(argv_size);
|
|
||||||
for (size_t ii = 0; ii < argv_size; ++ii) {
|
|
||||||
widened_argv_entries.emplace_back(widen_chars(safe_argv[ii]));
|
|
||||||
if (!widened_argv_entries.back()) {
|
|
||||||
// A null here indicates a character-encoding failure or the python
|
|
||||||
// interpreter out of memory. Give up.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
widened_argv[ii] = widened_argv_entries.back().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *pysys_argv = widened_argv.get();
|
|
||||||
PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
PYBIND11_NAMESPACE_END(detail)
|
||||||
|
|
||||||
/** \rst
|
/** \rst
|
||||||
@ -146,9 +115,64 @@ inline void initialize_interpreter(bool init_signal_handlers = true,
|
|||||||
pybind11_fail("The interpreter is already running");
|
pybind11_fail("The interpreter is already running");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX < 0x030B0000
|
||||||
|
|
||||||
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
Py_InitializeEx(init_signal_handlers ? 1 : 0);
|
||||||
|
|
||||||
detail::set_interpreter_argv(argc, argv, add_program_dir_to_path);
|
// Before it was special-cased in python 3.8, passing an empty or null argv
|
||||||
|
// caused a segfault, so we have to reimplement the special case ourselves.
|
||||||
|
bool special_case = (argv == nullptr || argc <= 0);
|
||||||
|
|
||||||
|
const char *const empty_argv[]{"\0"};
|
||||||
|
const char *const *safe_argv = special_case ? empty_argv : argv;
|
||||||
|
if (special_case) {
|
||||||
|
argc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto argv_size = static_cast<size_t>(argc);
|
||||||
|
// 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));
|
||||||
|
#else
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitIsolatedConfig(&config);
|
||||||
|
config.install_signal_handlers = init_signal_handlers ? 1 : 0;
|
||||||
|
|
||||||
|
PyStatus status = PyConfig_SetBytesArgv(&config, argc, const_cast<char *const *>(argv));
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
// 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) ? status.err_msg
|
||||||
|
: "Failed to prepare CPython");
|
||||||
|
}
|
||||||
|
status = Py_InitializeFromConfig(&config);
|
||||||
|
PyConfig_Clear(&config);
|
||||||
|
if (PyStatus_Exception(status)) {
|
||||||
|
throw std::runtime_error(PyStatus_IsError(status) ? 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 '')");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \rst
|
/** \rst
|
||||||
|
@ -19,6 +19,7 @@ classifiers =
|
|||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
Programming Language :: Python :: 3.10
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
License :: OSI Approved :: BSD License
|
License :: OSI Approved :: BSD License
|
||||||
Programming Language :: Python :: Implementation :: PyPy
|
Programming Language :: Python :: Implementation :: PyPy
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
build==0.7.0
|
build==0.8.0
|
||||||
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
|
||||||
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
|
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
|
||||||
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
|
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import env # noqa: F401
|
import env # noqa: F401
|
||||||
from pybind11_tests import ConstructorStats
|
from pybind11_tests import ConstructorStats
|
||||||
from pybind11_tests import methods_and_attributes as m
|
from pybind11_tests import methods_and_attributes as m
|
||||||
|
|
||||||
|
NO_GETTER_MSG = (
|
||||||
|
"unreadable attribute" if sys.version_info < (3, 11) else "object has no getter"
|
||||||
|
)
|
||||||
|
NO_SETTER_MSG = (
|
||||||
|
"can't set attribute" if sys.version_info < (3, 11) else "object has no setter"
|
||||||
|
)
|
||||||
|
NO_DELETER_MSG = (
|
||||||
|
"can't delete attribute" if sys.version_info < (3, 11) else "object has no deleter"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_methods_and_attributes():
|
def test_methods_and_attributes():
|
||||||
instance1 = m.ExampleMandA()
|
instance1 = m.ExampleMandA()
|
||||||
@ -102,32 +114,32 @@ def test_properties():
|
|||||||
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
dummy = instance.def_property_writeonly # unused var
|
dummy = instance.def_property_writeonly # unused var
|
||||||
assert "unreadable attribute" in str(excinfo.value)
|
assert NO_GETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
instance.def_property_writeonly = 4
|
instance.def_property_writeonly = 4
|
||||||
assert instance.def_property_readonly == 4
|
assert instance.def_property_readonly == 4
|
||||||
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
dummy = instance.def_property_impossible # noqa: F841 unused var
|
dummy = instance.def_property_impossible # noqa: F841 unused var
|
||||||
assert "unreadable attribute" in str(excinfo.value)
|
assert NO_GETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
instance.def_property_impossible = 5
|
instance.def_property_impossible = 5
|
||||||
assert "can't set attribute" in str(excinfo.value)
|
assert NO_SETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_static_properties():
|
def test_static_properties():
|
||||||
assert m.TestProperties.def_readonly_static == 1
|
assert m.TestProperties.def_readonly_static == 1
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
m.TestProperties.def_readonly_static = 2
|
m.TestProperties.def_readonly_static = 2
|
||||||
assert "can't set attribute" in str(excinfo.value)
|
assert NO_SETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
m.TestProperties.def_readwrite_static = 2
|
m.TestProperties.def_readwrite_static = 2
|
||||||
assert m.TestProperties.def_readwrite_static == 2
|
assert m.TestProperties.def_readwrite_static == 2
|
||||||
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
dummy = m.TestProperties.def_writeonly_static # unused var
|
dummy = m.TestProperties.def_writeonly_static # unused var
|
||||||
assert "unreadable attribute" in str(excinfo.value)
|
assert NO_GETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
m.TestProperties.def_writeonly_static = 3
|
m.TestProperties.def_writeonly_static = 3
|
||||||
assert m.TestProperties.def_readonly_static == 3
|
assert m.TestProperties.def_readonly_static == 3
|
||||||
@ -135,14 +147,14 @@ def test_static_properties():
|
|||||||
assert m.TestProperties.def_property_readonly_static == 3
|
assert m.TestProperties.def_property_readonly_static == 3
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
m.TestProperties.def_property_readonly_static = 99
|
m.TestProperties.def_property_readonly_static = 99
|
||||||
assert "can't set attribute" in str(excinfo.value)
|
assert NO_SETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
m.TestProperties.def_property_static = 4
|
m.TestProperties.def_property_static = 4
|
||||||
assert m.TestProperties.def_property_static == 4
|
assert m.TestProperties.def_property_static == 4
|
||||||
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
dummy = m.TestProperties.def_property_writeonly_static
|
dummy = m.TestProperties.def_property_writeonly_static
|
||||||
assert "unreadable attribute" in str(excinfo.value)
|
assert NO_GETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
m.TestProperties.def_property_writeonly_static = 5
|
m.TestProperties.def_property_writeonly_static = 5
|
||||||
assert m.TestProperties.def_property_static == 5
|
assert m.TestProperties.def_property_static == 5
|
||||||
@ -160,7 +172,7 @@ def test_static_properties():
|
|||||||
|
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
dummy = instance.def_property_writeonly_static # noqa: F841 unused var
|
dummy = instance.def_property_writeonly_static # noqa: F841 unused var
|
||||||
assert "unreadable attribute" in str(excinfo.value)
|
assert NO_GETTER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
instance.def_property_writeonly_static = 4
|
instance.def_property_writeonly_static = 4
|
||||||
assert instance.def_property_static == 4
|
assert instance.def_property_static == 4
|
||||||
@ -180,7 +192,7 @@ def test_static_properties():
|
|||||||
properties_override = m.TestPropertiesOverride()
|
properties_override = m.TestPropertiesOverride()
|
||||||
with pytest.raises(AttributeError) as excinfo:
|
with pytest.raises(AttributeError) as excinfo:
|
||||||
del properties_override.def_readonly
|
del properties_override.def_readonly
|
||||||
assert "can't delete attribute" in str(excinfo.value)
|
assert NO_DELETER_MSG in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_static_cls():
|
def test_static_cls():
|
||||||
|
Loading…
Reference in New Issue
Block a user