diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c35f0ed48..8b092007c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,8 +31,8 @@ jobs: - '3.6' - '3.9' - '3.10' - - 'pypy-3.7-v7.3.7' - - 'pypy-3.8-v7.3.7' + - 'pypy-3.7' + - 'pypy-3.8' # Items in here will either be added to the build matrix (if not # present), or add new keys to an existing matrix element if all the @@ -756,6 +756,60 @@ jobs: - name: Python tests run: cmake --build build -t pytest + win32-debug: + strategy: + fail-fast: false + matrix: + python: + - 3.8 + - 3.9 + + include: + - python: 3.9 + args: -DCMAKE_CXX_STANDARD=20 + - python: 3.8 + args: -DCMAKE_CXX_STANDARD=17 + + name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}" + runs-on: windows-2019 + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + architecture: x86 + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.12 + + - name: Prepare MSVC + uses: ilammy/msvc-dev-cmd@v1.10.0 + with: + arch: x86 + + - name: Prepare env + run: | + python -m pip install -r tests/requirements.txt + + # First build - C++11 mode and inplace + - name: Configure ${{ matrix.args }} + run: > + cmake -S . -B build + -G "Visual Studio 16 2019" -A Win32 + -DCMAKE_BUILD_TYPE=Debug + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + ${{ matrix.args }} + - name: Build C++11 + run: cmake --build build --config Debug -j 2 + + - name: Python tests + run: cmake --build build --config Debug -t pytest + win32-msvc2017: name: "🐍 ${{ matrix.python }} • MSVC 2017 • x64" runs-on: windows-2016 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3ab1f4549..25b7eedb2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,7 +60,7 @@ repos: # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: "v1.1.12" + rev: "v1.1.13" hooks: - id: remove-tabs exclude: (^docs/.*|\.patch)?$ @@ -73,7 +73,7 @@ repos: # Autoremoves unused imports - repo: https://github.com/hadialqattan/pycln - rev: "v1.2.0" + rev: "v1.2.4" hooks: - id: pycln diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 008042eb2..3148e4a47 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -101,14 +101,14 @@ inline PyTypeObject *make_static_property_type() { 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) +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) - )", + 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()); diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index ff7d7cf8c..a4154136a 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -502,9 +502,10 @@ PYBIND11_NOINLINE std::string error_string() { } PyFrameObject *frame = trace->tb_frame; + Py_XINCREF(frame); errorString += "\n\nAt:\n"; while (frame) { -# if PY_VERSION_HEX >= 0x03090000 +# if PY_VERSION_HEX >= 0x030900B1 PyCodeObject *f_code = PyFrame_GetCode(frame); # else PyCodeObject *f_code = frame->f_code; @@ -514,8 +515,15 @@ PYBIND11_NOINLINE std::string error_string() { errorString += " " + handle(f_code->co_filename).cast() + "(" + std::to_string(lineno) + "): " + handle(f_code->co_name).cast() + "\n"; - frame = frame->f_back; Py_DECREF(f_code); +# if PY_VERSION_HEX >= 0x030900B1 + auto *b_frame = PyFrame_GetBack(frame); +# else + auto *b_frame = frame->f_back; + Py_XINCREF(b_frame); +# endif + Py_DECREF(frame); + frame = b_frame; } } #endif diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index c157a0095..bd5f981f5 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -82,7 +82,7 @@ template 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(expr, global, local); + return eval(expr, std::move(global), std::move(local)); } inline void exec(const str &expr, object global = globals(), object local = object()) { @@ -91,7 +91,7 @@ inline void exec(const str &expr, object global = globals(), object local = obje template void exec(const char (&s)[N], object global = globals(), object local = object()) { - eval(s, global, local); + eval(s, std::move(global), std::move(local)); } #if defined(PYPY_VERSION) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index fe36a2d31..7d62c6800 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -335,7 +335,7 @@ protected: const std::type_info *const *types, size_t args) { // Do NOT receive `unique_rec` by value. If this function fails to move out the unique_ptr, - // we do not want this to destuct the pointer. `initialize` (the caller) still relies on + // we do not want this to destruct the pointer. `initialize` (the caller) still relies on // the pointee being alive after this call. Only move out if a `capsule` is going to keep // it alive. auto *rec = unique_rec.get(); @@ -2739,9 +2739,7 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char * /* Don't call dispatch code if invoked from overridden function. Unfortunately this doesn't work on PyPy. */ -#if !defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000 - // TODO: Remove PyPy workaround for Python 3.11. - // Current API fails on 3.11 since co_varnames can be null. +#if !defined(PYPY_VERSION) # if PY_VERSION_HEX >= 0x03090000 PyFrameObject *frame = PyThreadState_GetFrame(PyThreadState_Get()); if (frame != nullptr) { @@ -2749,9 +2747,11 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char * // f_code is guaranteed to not be NULL if ((std::string) str(f_code->co_name) == name && f_code->co_argcount > 0) { PyObject *locals = PyEval_GetLocals(); - if (locals != nullptr && f_code->co_varnames != nullptr) { - PyObject *self_caller - = dict_getitem(locals, PyTuple_GET_ITEM(f_code->co_varnames, 0)); + if (locals != nullptr) { + PyObject *co_varnames = PyObject_GetAttrString((PyObject *) f_code, "co_varnames"); + PyObject *self_arg = PyTuple_GET_ITEM(co_varnames, 0); + Py_DECREF(co_varnames); + PyObject *self_caller = dict_getitem(locals, self_arg); if (self_caller == self.ptr()) { Py_DECREF(f_code); Py_DECREF(frame); @@ -2797,9 +2797,9 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char * d.ptr()); if (result == nullptr) throw error_already_set(); + Py_DECREF(result); if (d["self"].is_none()) return function(); - Py_DECREF(result); #endif return override; diff --git a/noxfile.py b/noxfile.py index 50b7e4ee0..341d964e9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,6 +2,7 @@ import os import nox +nox.needs_version = ">=2022.1.7" nox.options.sessions = ["lint", "tests", "tests_packaging"] PYTHON_VERISONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "pypy3.7", "pypy3.8"] @@ -29,14 +30,12 @@ def tests(session: nox.Session) -> None: session.install("-r", "tests/requirements.txt") session.run( "cmake", - "-S", - ".", - "-B", - tmpdir, + "-S.", + f"-B{tmpdir}", "-DPYBIND11_WERROR=ON", "-DDOWNLOAD_CATCH=ON", "-DDOWNLOAD_EIGEN=ON", - *session.posargs + *session.posargs, ) session.run("cmake", "--build", tmpdir) session.run("cmake", "--build", tmpdir, "--config=Release", "--target", "check") diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index 7aeffa441..6ad5fe157 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -112,11 +112,24 @@ endif() # VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. execute_process( COMMAND - "${PYTHON_EXECUTABLE}" "-c" "from distutils import sysconfig as s;import sys;import struct; + "${PYTHON_EXECUTABLE}" "-c" " +import sys;import struct; +import sysconfig as s +USE_SYSCONFIG = sys.version_info >= (3, 10) +if not USE_SYSCONFIG: + from distutils import sysconfig as ds print('.'.join(str(v) for v in sys.version_info)); print(sys.prefix); -print(s.get_python_inc(plat_specific=True)); -print(s.get_python_lib(plat_specific=True)); +if USE_SYSCONFIG: + scheme = s.get_default_scheme() + if scheme == 'posix_local': + # Debian's default scheme installs to /usr/local/ but we want to find headers in /usr/ + scheme = 'posix_prefix' + print(s.get_path('platinclude', scheme)) + print(s.get_path('platlib')) +else: + print(ds.get_python_inc(plat_specific=True)); + print(ds.get_python_lib(plat_specific=True)); print(s.get_config_var('EXT_SUFFIX') or s.get_config_var('SO')); print(hasattr(sys, 'gettotalrefcount')+0); print(struct.calcsize('@P'));