diff --git a/.appveyor.yml b/.appveyor.yml index 391cf1071c..8429d5ec57 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,6 +1,6 @@ version: 1.0.{build} image: -- Visual Studio 2017 +- Visual Studio 2019 test: off skip_branch_with_pr: true build: @@ -9,13 +9,20 @@ platform: - x86 environment: matrix: - - PYTHON: 38 + - PYTHON: 39 CONFIG: Debug install: - ps: | - $env:CMAKE_GENERATOR = "Visual Studio 15 2017" - if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } - $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" + $env:CMAKE_GENERATOR = "Visual Studio 16 2019" + # The 2019 generator defaults to x64, so name the architecture explicitly. + if ($env:PLATFORM -eq "x64") { + $env:PYTHON = "$env:PYTHON-x64" + $env:CMAKE_ARCH = "x64" + } else { + $env:CMAKE_ARCH = "Win32" + } + $env:PYTHON_ROOT = "C:\Python$env:PYTHON" + $env:PATH = "$env:PYTHON_ROOT\;$env:PYTHON_ROOT\Scripts\;$env:PATH" python -W ignore -m pip install --upgrade pip wheel python -W ignore -m pip install pytest numpy --no-warn-script-location pytest-timeout - ps: | @@ -23,7 +30,15 @@ install: 7z x eigen-3.3.7.zip -y > $null $env:CMAKE_INCLUDE_PATH = "eigen-3.3.7;$env:CMAKE_INCLUDE_PATH" build_script: +# FindPython looks at the registry before PATH, so it picks the newest Python on +# the image. Point it at the one that has the test packages. +# +# The image also has python39_d.lib, which FindPython uses for a Debug build. +# pybind11 undefines _DEBUG around Python.h, so pyconfig.h asks for python39.lib +# and the link fails. Take the release library in Debug too. - cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%" + -DPython_ROOT_DIR="%PYTHON_ROOT%" + -DCMAKE_MAP_IMPORTED_CONFIG_DEBUG=Release -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ad1178395..492239277b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: matrix: include: - runs-on: ubuntu-22.04 - python-version: '3.8' + python-version: '3.9' cmake-args: -DPYBIND11_FINDPYTHON=OFF -DPYBIND11_NUMPY_1_ONLY=ON - runs-on: ubuntu-latest python-version: '3.13' @@ -60,7 +60,7 @@ jobs: python-version: '3.14' cmake-args: -DCMAKE_CXX_STANDARD=14 - runs-on: windows-2022 - python-version: '3.8' + python-version: '3.9' cmake-args: -DPYBIND11_FINDPYTHON=OFF @@ -78,7 +78,7 @@ jobs: matrix: include: - runs-on: ubuntu-latest - python-version: '3.8' + python-version: '3.9' cmake-args: -DPYBIND11_FINDPYTHON=ON -DCMAKE_CXX_STANDARD=17 - runs-on: ubuntu-latest python-version: '3.10' @@ -95,7 +95,7 @@ jobs: # No SciPy for macOS ARM - runs-on: macos-15-intel - python-version: '3.8' + python-version: '3.9' cmake-args: -DCMAKE_CXX_STANDARD=14 - runs-on: macos-15-intel python-version: '3.13' @@ -118,7 +118,7 @@ jobs: python-version: '3.9' cmake-args: -DPYBIND11_TEST_SMART_HOLDER=ON - runs-on: windows-2022 - python-version: '3.8' + python-version: '3.9' cmake-args: -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DPYBIND11_NUMPY_1_ONLY=ON # This needs a python built with MTd # - runs-on: windows-2022 @@ -187,7 +187,7 @@ jobs: - name: Prepare env run: uv pip install --python=python --system -r tests/requirements.txt - # TODO Resolve Windows Ninja shared object issue on Python 3.8+ + # TODO Resolve Windows Ninja shared object issue on Python 3.9+ - name: Use Ninja except on Windows if: runner.os != 'Windows' run: echo "CMAKE_GENERATOR=Ninja" >> "$GITHUB_ENV" @@ -629,9 +629,9 @@ jobs: - name: Latest actions/checkout uses: actions/checkout@v7 - - name: Add Python 3.8 + - name: Add Python 3.9 if: matrix.container == 'almalinux:8' - run: dnf update -y && dnf install -y python38-devel gcc-c++ make git + run: dnf update -y && dnf install -y python39-devel gcc-c++ make git - name: Add Python 3 (default) if: matrix.container != 'almalinux:8' @@ -767,7 +767,7 @@ jobs: fail-fast: false matrix: include: - - python: '3.8' + - python: '3.9' args: -DCMAKE_CXX_STANDARD=17 - python: '3.13' diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 8d47802526..91279de771 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -19,16 +19,16 @@ jobs: # This builds the sdists and wheels and makes sure the files are exactly as # expected. test-packaging: - name: 🐍 3.8 β€’ πŸ“¦ tests β€’ windows-latest + name: 🐍 3.9 β€’ πŸ“¦ tests β€’ windows-latest runs-on: windows-latest steps: - uses: actions/checkout@v7 - - name: Setup 🐍 3.8 + - name: Setup 🐍 3.9 uses: actions/setup-python@v6 with: - python-version: 3.8 + python-version: 3.9 - name: Install uv uses: astral-sh/setup-uv@v8.2.0 @@ -43,16 +43,16 @@ jobs: # This runs the packaging tests and also builds and saves the packages as # artifacts. packaging: - name: 🐍 3.8 β€’ πŸ“¦ & πŸ“¦ tests β€’ ubuntu-latest + name: 🐍 3.9 β€’ πŸ“¦ & πŸ“¦ tests β€’ ubuntu-latest runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - name: Setup 🐍 3.8 + - name: Setup 🐍 3.9 uses: actions/setup-python@v6 with: - python-version: 3.8 + python-version: 3.9 - name: Install uv uses: astral-sh/setup-uv@v8.2.0 diff --git a/.github/workflows/reusable-standard.yml b/.github/workflows/reusable-standard.yml index 6081f801eb..1231bcf929 100644 --- a/.github/workflows/reusable-standard.yml +++ b/.github/workflows/reusable-standard.yml @@ -63,7 +63,7 @@ jobs: if: runner.os == 'Linux' run: uv pip install --python=python --system pytest-github-actions-annotate-failures - # TODO Resolve Windows Ninja shared object issue on Python 3.8+ + # TODO Resolve Windows Ninja shared object issue on Python 3.9+ - name: Use Ninja except on Windows if: runner.os != 'Windows' run: echo "CMAKE_GENERATOR=Ninja" >> "$GITHUB_ENV" diff --git a/AGENTS.md b/AGENTS.md index d3e09857ff..4108925520 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -95,5 +95,5 @@ Packaging produces two distributions: the normal `pybind11` (headers live inside - Any new functionality needs a test (add to or create the paired `.cpp`/`.py` in `tests/`, and register the test in `tests/CMakeLists.txt`). - Bug fixes should be paired with new tests that fail without the fix. - Try to add to an existing test file if possible (more files slow down tests) -- Default C++ standard follows the consumer's toolchain; CI exercises a wide matrix (CPython 3.8+, PyPy, GraalPy; multiple compilers and C++ standards). Keep changes portable across that matrix. +- Default C++ standard follows the consumer's toolchain; CI exercises a wide matrix (CPython 3.9+, PyPy, GraalPy; multiple compilers and C++ standards). Keep changes portable across that matrix. - PR descriptions follow a template, the key part is under "Suggested changelog entry", which is how we generate our changelog (with `nox -s make_changelog`) diff --git a/README.rst b/README.rst index 26d618f93a..77f88059b4 100644 --- a/README.rst +++ b/README.rst @@ -36,7 +36,7 @@ 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 (CPython 3.8+, PyPy, or GraalPy) and the C++ +lines of code and depend on Python (CPython 3.9+, PyPy, or GraalPy) and the C++ standard library. This compact implementation was possible thanks to some C++11 language features (specifically: tuples, lambda functions and variadic templates). Since its creation, this library has grown beyond Boost.Python in @@ -81,7 +81,7 @@ Goodies In addition to the core functionality, pybind11 provides some extra goodies: -- CPython 3.8+, PyPy3 7.3.17+, and GraalPy 24.1+ are supported with an +- CPython 3.9+, PyPy3 7.3.17+, and GraalPy 24.1+ are supported with an implementation-agnostic interface (see older versions for older CPython and PyPy versions). diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index bfbaea60ab..2954411d7b 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -1400,10 +1400,8 @@ You can do that using ``py::custom_type_setup``: auto *type = &heap_type->ht_type; type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) { - // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse - #if PY_VERSION_HEX >= 0x03090000 + // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse Py_VISIT(Py_TYPE(self_base)); - #endif if (py::detail::is_holder_constructed(self_base)) { auto &self = py::cast(py::handle(self_base)); for (auto &item : self.list) { diff --git a/docs/advanced/deprecated.rst b/docs/advanced/deprecated.rst index dc07a77b94..c9f4a2e666 100644 --- a/docs/advanced/deprecated.rst +++ b/docs/advanced/deprecated.rst @@ -3,8 +3,6 @@ Deprecated ########## -Support for Python 3.8 is deprecated and will be removed in 3.1. - Support for C++11 is deprecated and will be removed in a future version. Please use at least C++14. diff --git a/docs/basics.rst b/docs/basics.rst index 074d98850b..7f77f201fe 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -32,15 +32,14 @@ The last line will both compile and run the tests. Windows ------- -On Windows, only **Visual Studio 2017** and newer are supported. +On Windows, only **Visual Studio 2019** and newer are supported. .. Note:: - To use the C++17 in Visual Studio 2017 (MSVC 14.1), pybind11 requires the flag - ``/permissive-`` to be passed to the compiler `to enforce standard conformance`_. When - building with Visual Studio 2019, this is not strictly necessary, but still advised. + The ``/permissive-`` flag `to enforce standard conformance`_ is not necessary, but it is + still advised. -.. _`to enforce standard conformance`: https://docs.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance?view=vs-2017 +.. _`to enforce standard conformance`: https://docs.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance To compile and run the tests: diff --git a/docs/compiling.rst b/docs/compiling.rst index b693bd5878..fb21e16edf 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -429,7 +429,7 @@ with ``PYTHON_EXECUTABLE``. For example: .. code-block:: bash - cmake -DPYBIND11_PYTHON_VERSION=3.8 .. + cmake -DPYBIND11_PYTHON_VERSION=3.9 .. # Another method: cmake -DPYTHON_EXECUTABLE=/path/to/python .. @@ -495,7 +495,7 @@ FindPython, pybind11 will detect this and use the existing targets instead: cmake_minimum_required(VERSION 3.15...4.2) project(example LANGUAGES CXX) - find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED) + find_package(Python 3.9 COMPONENTS Interpreter Development REQUIRED) find_package(pybind11 CONFIG REQUIRED) # or add_subdirectory(pybind11) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 62ca45a09a..93dd1c458f 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -2251,13 +2251,8 @@ class unpacking_collector { if (m_names) { nargs -= m_names.size(); } - PyObject *result = -#if PY_VERSION_HEX >= 0x03090000 - PyObject_Vectorcall( -#else - _PyObject_Vectorcall( -#endif - ptr, m_args.data() + 1, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, m_names.ptr()); + PyObject *result = PyObject_Vectorcall( + ptr, m_args.data() + 1, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, m_names.ptr()); if (!result) { throw error_already_set(); } diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 8b9d0b8e9d..06a761de54 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -586,10 +586,8 @@ extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *a PyObject *&dict = *_PyObject_GetDictPtr(self); Py_VISIT(dict); #endif -// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse -#if PY_VERSION_HEX >= 0x03090000 + // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse Py_VISIT(Py_TYPE(self)); -#endif return 0; } diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index e22763c99e..7f182afb2c 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -10,8 +10,8 @@ #pragma once #include -#if PY_VERSION_HEX < 0x03080000 -# error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7." +#if PY_VERSION_HEX < 0x03090000 +# error "PYTHON < 3.9 IS UNSUPPORTED. pybind11 v3.0 was the last to support Python 3.8." #endif // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html @@ -87,7 +87,7 @@ # endif #endif -#if defined(__cpp_lib_launder) && !(defined(_MSC_VER) && (_MSC_VER < 1920)) // See PR #5968 +#if defined(__cpp_lib_launder) # define PYBIND11_STD_LAUNDER std::launder # define PYBIND11_HAS_STD_LAUNDER 1 #else @@ -132,8 +132,8 @@ # error pybind11 requires gcc 4.8 or newer # endif #elif defined(_MSC_VER) -# if _MSC_VER < 1910 -# error pybind11 2.10+ requires MSVC 2017 or newer +# if _MSC_VER < 1920 +# error pybind11 3.1+ requires MSVC 2019 or newer # endif #endif @@ -826,7 +826,7 @@ using void_t = typename void_t_impl::type; #endif /// Compile-time all/any/none of that check the boolean value of all template types -#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) +#if defined(__cpp_fold_expressions) template using all_of = bool_constant<(Ts::value && ...)>; template @@ -1357,7 +1357,7 @@ try_get_shared_from_this(std::enable_shared_from_this *holder_value_ptr) { // Use at your own risk! // See also https://en.cppreference.com/w/cpp/memory/enable_shared_from_this, and in particular // the `std::shared_ptr gp1 = not_so_good.getptr();` and `try`-`catch` parts of the example. -#if defined(__cpp_lib_enable_shared_from_this) && (!defined(_MSC_VER) || _MSC_VER >= 1912) +#if defined(__cpp_lib_enable_shared_from_this) return holder_value_ptr->weak_from_this().lock(); #else try { @@ -1370,19 +1370,10 @@ try_get_shared_from_this(std::enable_shared_from_this *holder_value_ptr) { // For silencing "unused" compiler warnings in special situations. template -#if defined(_MSC_VER) && _MSC_VER < 1920 // MSVC 2017 -constexpr -#endif - inline void silence_unused_warnings(Args &&...) { -} +inline void silence_unused_warnings(Args &&...) {} -// MSVC warning C4100: Unreferenced formal parameter -#if defined(_MSC_VER) && _MSC_VER <= 1916 -# define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...) \ - detail::silence_unused_warnings(__VA_ARGS__) -#else -# define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...) -#endif +// MSVC warning C4100: Unreferenced formal parameter (only incorrect before MSVC 2019) +#define PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(...) // GCC -Wunused-but-set-parameter All GCC versions (as of July 2021). #if defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER) diff --git a/include/pybind11/detail/function_record_pyobject.h b/include/pybind11/detail/function_record_pyobject.h index 94d27ad17b..42b0e96be7 100644 --- a/include/pybind11/detail/function_record_pyobject.h +++ b/include/pybind11/detail/function_record_pyobject.h @@ -48,8 +48,8 @@ static PyMethodDef tp_methods_impl[] // Python 3.12+ emits a DeprecationWarning for heap types whose tp_name does // not contain a dot ('.') and that lack a __module__ attribute. For pybind11's // internal function_record type, we do not have an actual module object to -// attach, so we cannot use PyType_FromModuleAndSpec (introduced in Python 3.9) -// to set __module__ automatically. +// attach, so we cannot use PyType_FromModuleAndSpec to set __module__ +// automatically. // // As a workaround, we define a "qualified" type name that includes a dummy // module name (PYBIND11_DUMMY_MODULE_NAME). This is non‑idiomatic but avoids @@ -59,8 +59,7 @@ static PyMethodDef tp_methods_impl[] // // even though no real pybind11_builtins module exists. If pybind11 gains an // actual module object in the future, this code should switch to -// PyType_FromModuleAndSpec for Python 3.9+ and drop the dummy module -// workaround. +// PyType_FromModuleAndSpec and drop the dummy module workaround. // // Note that this name is versioned. #define PYBIND11_DETAIL_FUNCTION_RECORD_TP_PLAINNAME \ diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 8fbf700e12..3b444ef600 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -1133,7 +1133,7 @@ class type_caster_generic { if (type->operator_new) { vptr = type->operator_new(type->type_size); } else { -#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) +#if defined(__cpp_aligned_new) if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { vptr = ::operator new(type->type_size, std::align_val_t(type->type_align)); } else { diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index c05887c33d..94be0d102d 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -12,9 +12,6 @@ #include "pybind11.h" #include "eval.h" -#include -#include - #if defined(PYPY_VERSION) # error Embedding the interpreter is not supported with PyPy #endif @@ -82,60 +79,14 @@ struct embedded_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); - - auto argv_size = static_cast(argc); - // SetArgv* on python 3 takes wchar_t, so we have to convert. - std::unique_ptr widened_argv(new wchar_t *[argv_size]); - std::vector> 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(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(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, @@ -163,7 +114,6 @@ inline void initialize_interpreter(PyConfig *config, } PyConfig_Clear(config); } -#endif /** \rst Initialize the Python interpreter. No other pybind11 or CPython API functions can be @@ -188,10 +138,6 @@ 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 @@ -199,7 +145,6 @@ inline void initialize_interpreter(bool init_signal_handlers = true, config.install_signal_handlers = init_signal_handlers ? 1 : 0; initialize_interpreter(&config, argc, argv, add_program_dir_to_path); -#endif // There is exactly one interpreter alive currently. detail::has_seen_non_main_interpreter() = false; @@ -293,14 +238,12 @@ class scoped_interpreter { 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; } diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index c7122795ad..c06fabe0d6 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -20,13 +20,7 @@ PYBIND11_NAMESPACE_BEGIN(type_caster_std_function_specializations) // 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_)) { - } + explicit 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; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index ccd7baee02..b62b6e79e2 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1958,7 +1958,7 @@ void call_operator_delete(T *p, size_t s, size_t) { inline void call_operator_delete(void *p, size_t s, size_t a) { (void) s; (void) a; -#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) +#if defined(__cpp_aligned_new) if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { # ifdef __cpp_sized_deallocation ::operator delete(p, s, std::align_val_t(a)); @@ -3837,24 +3837,23 @@ 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 and GraalPy. */ #if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) -# if PY_VERSION_HEX >= 0x03090000 PyFrameObject *frame = PyThreadState_GetFrame(PyThreadState_Get()); if (frame != nullptr) { PyCodeObject *f_code = PyFrame_GetCode(frame); // f_code is guaranteed to not be NULL if (std::string(str(f_code->co_name)) == name && f_code->co_argcount > 0) { -# if PY_VERSION_HEX >= 0x030d0000 +# if PY_VERSION_HEX >= 0x030d0000 PyObject *locals = PyEval_GetFrameLocals(); -# else +# else PyObject *locals = PyEval_GetLocals(); Py_XINCREF(locals); -# endif +# endif if (locals != nullptr) { -# if PY_VERSION_HEX >= 0x030b0000 +# if PY_VERSION_HEX >= 0x030b0000 PyObject *co_varnames = PyCode_GetVarnames(f_code); -# else +# else PyObject *co_varnames = PyObject_GetAttrString((PyObject *) f_code, "co_varnames"); -# endif +# endif PyObject *self_arg = PyTuple_GET_ITEM(co_varnames, 0); Py_DECREF(co_varnames); PyObject *self_caller = dict_getitem(locals, self_arg); @@ -3869,18 +3868,6 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char * Py_DECREF(f_code); Py_DECREF(frame); } -# else - PyFrameObject *frame = PyThreadState_Get()->frame; - if (frame != nullptr && (std::string) str(frame->f_code->co_name) == name - && frame->f_code->co_argcount > 0) { - PyFrame_FastToLocals(frame); - PyObject *self_caller - = dict_getitem(frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); - if (self_caller == self.ptr()) { - return function(); - } - } -# endif #else /* PyPy currently doesn't provide a detailed cpyext emulation of diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index ff4be6a500..13a6ebaa4d 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -670,12 +670,7 @@ struct error_fetch_and_normalize { Py_XINCREF(frame); result += "\n\nAt:\n"; while (frame) { -# if PY_VERSION_HEX >= 0x030900B1 PyCodeObject *f_code = PyFrame_GetCode(frame); -# else - PyCodeObject *f_code = frame->f_code; - Py_INCREF(f_code); -# endif int lineno = PyFrame_GetLineNumber(frame); result += " "; result += handle(f_code->co_filename).cast(); @@ -685,12 +680,7 @@ struct error_fetch_and_normalize { result += handle(f_code->co_name).cast(); result += '\n'; Py_DECREF(f_code); -# if PY_VERSION_HEX >= 0x030900B1 auto *b_frame = PyFrame_GetBack(frame); -# else - auto *b_frame = frame->f_back; - Py_XINCREF(b_frame); -# endif Py_DECREF(frame); frame = b_frame; } diff --git a/pybind11/__init__.py b/pybind11/__init__.py index e9d033c4b4..df5e8ee92a 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -2,8 +2,8 @@ import sys -if sys.version_info < (3, 8): # noqa: UP036 - msg = "pybind11 does not support Python < 3.8. v2.13 was the last release supporting Python 3.7." +if sys.version_info < (3, 9): # noqa: UP036 + msg = "pybind11 does not support Python < 3.9. v3.0 was the last release supporting Python 3.8." raise ImportError(msg) diff --git a/pybind11/setup_helpers.py b/pybind11/setup_helpers.py index 300688bf10..8f42605245 100644 --- a/pybind11/setup_helpers.py +++ b/pybind11/setup_helpers.py @@ -48,16 +48,13 @@ import tempfile import threading import warnings +from collections.abc import Iterable, Iterator from functools import lru_cache from pathlib import Path from typing import ( Any, Callable, - Iterable, - Iterator, - List, Optional, - Tuple, TypeVar, Union, ) @@ -175,8 +172,7 @@ def cxx_std(self, level: int) -> None: "You cannot safely change the cxx_level after setting it!", stacklevel=2 ) - # MSVC 2015 Update 3 and later only have 14 (and later 17) modes, so - # force a valid flag here. + # MSVC only has 14 and later modes, so force a valid flag here. if WIN and level == 11: level = 14 @@ -347,16 +343,16 @@ def no_recompile(obj: str, src: str) -> bool: # noqa: ARG001 CCompilerMethod = Callable[ [ distutils.ccompiler.CCompiler, - List[str], + list[str], Optional[str], - Optional[List[Union[Tuple[str], Tuple[str, Optional[str]]]]], - Optional[List[str]], + Optional[list[Union[tuple[str], tuple[str, Optional[str]]]]], + Optional[list[str]], bool, - Optional[List[str]], - Optional[List[str]], - Optional[List[str]], + Optional[list[str]], + Optional[list[str]], + Optional[list[str]], ], - List[str], + list[str], ] diff --git a/pyproject.toml b/pyproject.toml index c3395aa660..caf0a8b902 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ classifiers = [ "Topic :: Utilities", "Programming Language :: C++", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -34,7 +33,7 @@ keywords = [ "Python bindings", ] dynamic = ["version", "optional-dependencies"] -requires-python = ">=3.8" +requires-python = ">=3.9" [project.urls] Homepage = "https://github.com/pybind/pybind11" @@ -120,7 +119,7 @@ override-dependencies = ["pybind11-global"] [tool.mypy] files = ["pybind11"] -python_version = "3.8" +python_version = "3.9" strict = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = true @@ -131,7 +130,7 @@ ignore_missing_imports = true [tool.pylint] -master.py-version = "3.8" +master.py-version = "3.9" reports.output-format = "colorized" messages_control.disable = [ "design", diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index d96e9afc1f..e526899ec0 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -8,8 +8,8 @@ import sys import tarfile import zipfile +from collections.abc import Generator from pathlib import Path -from typing import Generator # These tests must be run explicitly diff --git a/tests/requirements.txt b/tests/requirements.txt index 1ba4026b46..e10fb40d29 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,11 +3,10 @@ build>=1 # No released numpy has cp315 wheels yet; drop the upper bound once it does. numpy>=2.4.0; platform_python_implementation=="CPython" and python_version>="3.14" and python_version<"3.15" -numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy" numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=="PyPy" numpy~=2.2.0; python_version=="3.10" and platform_python_implementation=="PyPy" numpy~=2.2.0; platform_python_implementation=="GraalVM" and sys_platform=="linux" -numpy~=1.21.5; platform_python_implementation=="CPython" and python_version>="3.8" and python_version<"3.10" +numpy~=1.21.5; platform_python_implementation=="CPython" and python_version=="3.9" numpy~=1.22.2; platform_python_implementation=="CPython" and python_version=="3.10" numpy~=1.26.0; platform_python_implementation=="CPython" and python_version>="3.11" and python_version<"3.13" and platform_machine!="ARM64" numpy>=2.3.0; platform_python_implementation=="CPython" and python_version>="3.11" and platform_machine=="ARM64" diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index b232c087e0..09203502d5 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -302,7 +302,6 @@ def cant_convert(v): assert noconvert(7) == 7 cant_convert(3.14159) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) - # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) if sys.version_info < (3, 10) and env.CPYTHON: with pytest.deprecated_call(): assert convert(Int()) == 42 @@ -312,8 +311,6 @@ def cant_convert(v): cant_convert(NotInt()) cant_convert(Float()) - # Before Python 3.8, `PyLong_AsLong` does not pick up on `obj.__index__`, - # but pybind11 "backports" this behavior. assert convert(Index()) == 42 assert isinstance(convert(Index()), int) assert noconvert(Index()) == 42 @@ -419,9 +416,8 @@ def require_implicit(v): # The implicit conversion from np.float32 is undesirable but currently accepted. # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) - # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) # https://github.com/pybind/pybind11/issues/3408 - if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: + if sys.version_info < (3, 10) and env.CPYTHON: with pytest.deprecated_call(): assert convert(np.float32(3.14159)) == 3 else: diff --git a/tests/test_custom_type_setup.cpp b/tests/test_custom_type_setup.cpp index 15516b21b0..5235729e40 100644 --- a/tests/test_custom_type_setup.cpp +++ b/tests/test_custom_type_setup.cpp @@ -71,10 +71,8 @@ TEST_SUBMODULE(custom_type_setup, m) { auto *type = &heap_type->ht_type; type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) { -// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse -#if PY_VERSION_HEX >= 0x03090000 + // https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse Py_VISIT(Py_TYPE(self_base)); -#endif if (py::detail::is_holder_constructed(self_base)) { auto &self = py::cast(py::handle(self_base)); for (auto &item : self.list) { diff --git a/tests/test_docs_advanced_cast_custom.py b/tests/test_docs_advanced_cast_custom.py index 5374cbceb2..63d608c446 100644 --- a/tests/test_docs_advanced_cast_custom.py +++ b/tests/test_docs_advanced_cast_custom.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING if TYPE_CHECKING: from conftest import SanitizedString diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 1ee13ba3f3..f811cbce12 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -94,7 +94,7 @@ def test_python_call_in_catch(): def ignore_pytest_unraisable_warning(f): unraisable = "PytestUnraisableExceptionWarning" - if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6 + if hasattr(pytest, unraisable): # pytest >= 6 dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}") return dec(f) return f diff --git a/tests/test_multiple_interpreters.py b/tests/test_multiple_interpreters.py index 65b2733191..6ca4fd8eee 100644 --- a/tests/test_multiple_interpreters.py +++ b/tests/test_multiple_interpreters.py @@ -157,9 +157,10 @@ def test_independent_subinterpreters_modern(): """ ).strip() - with contextlib.closing(interpreters.create()) as interp1, contextlib.closing( - interpreters.create() - ) as interp2: + with ( + contextlib.closing(interpreters.create()) as interp1, + contextlib.closing(interpreters.create()) as interp2, + ): with pytest.raises( interpreters.ExecutionFailed, match="does not support loading in subinterpreters", diff --git a/tests/test_with_catch/test_interpreter.cpp b/tests/test_with_catch/test_interpreter.cpp index 4103c0f5ff..709f66fc11 100644 --- a/tests/test_with_catch/test_interpreter.cpp +++ b/tests/test_with_catch/test_interpreter.cpp @@ -195,7 +195,6 @@ TEST_CASE("There can be only one interpreter") { py::initialize_interpreter(); } -#if PY_VERSION_HEX >= PYBIND11_PYCONFIG_SUPPORT_PY_VERSION_HEX TEST_CASE("Custom PyConfig") { py::finalize_interpreter(); PyConfig config; @@ -258,9 +257,8 @@ TEST_CASE("scoped_interpreter with PyConfig_InitPythonConfig and argv") { } py::initialize_interpreter(); } -#endif -TEST_CASE("Add program dir to path pre-PyConfig") { +TEST_CASE("Add program dir to path without PyConfig") { py::finalize_interpreter(); size_t path_size_add_program_dir_to_path_false = 0; { @@ -274,7 +272,6 @@ TEST_CASE("Add program dir to path pre-PyConfig") { 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; @@ -292,7 +289,6 @@ TEST_CASE("Add program dir to path using PyConfig") { } py::initialize_interpreter(); } -#endif TEST_CASE("Restart the interpreter") { // Verify pre-restart state. diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index 7351bcaa65..d72e21332b 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -92,7 +92,7 @@ endif() # Use the Python interpreter to find the libs. if(NOT PythonLibsNew_FIND_VERSION) - set(PythonLibsNew_FIND_VERSION "3.8") + set(PythonLibsNew_FIND_VERSION "3.9") endif() if(NOT CMAKE_VERSION VERSION_LESS "3.27") diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index 568b0b0595..b0fe20768d 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -52,7 +52,7 @@ if(NOT Python_FOUND AND NOT Python3_FOUND) endif() find_package( - Python 3.8 REQUIRED COMPONENTS ${_pybind11_interp_component} ${_pybind11_dev_component} + Python 3.9 REQUIRED COMPONENTS ${_pybind11_interp_component} ${_pybind11_dev_component} ${_pybind11_quiet} ${_pybind11_global_keyword}) # If we are in submodule mode, export the Python targets to global targets. @@ -192,8 +192,8 @@ else() endif() endif() -# Python debug libraries expose slightly different objects before 3.8 -# https://docs.python.org/3.6/c-api/intro.html#debugging-builds +# A debug build of Python needs Py_DEBUG to select the matching ABI. +# https://docs.python.org/3/c-api/intro.html#debugging-builds # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib if(PYTHON_IS_DEBUG) set_property( diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index a51efb39fa..7b3da9c79d 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -37,7 +37,7 @@ endif() # A user can set versions manually too set(Python_ADDITIONAL_VERSIONS - "3.12;3.11;3.10;3.9;3.8" + "3.12;3.11;3.10;3.9" CACHE INTERNAL "") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") @@ -100,8 +100,8 @@ set(pybind11_INCLUDE_DIRS "${pybind11_INCLUDE_DIR}" "${PYTHON_INCLUDE_DIRS}" CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") -# Python debug libraries expose slightly different objects before 3.8 -# https://docs.python.org/3.6/c-api/intro.html#debugging-builds +# A debug build of Python needs Py_DEBUG to select the matching ABI. +# https://docs.python.org/3/c-api/intro.html#debugging-builds # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib if(PYTHON_IS_DEBUG) set_property(