Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove Python 3.7 support #5191

Merged
merged 18 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,6 @@ jobs:
fail-fast: false
matrix:
clang:
- 3.6
- 3.7
- 3.9
- 7
- 9
- dev
std:
- 11
Expand All @@ -324,8 +319,6 @@ jobs:
include:
- clang: 5
std: 14
- clang: 10
std: 17
- clang: 11
std: 20
- clang: 12
Expand Down Expand Up @@ -503,10 +496,6 @@ jobs:
fail-fast: false
matrix:
include:
- { gcc: 7, std: 11 }
- { gcc: 7, std: 17 }
- { gcc: 8, std: 14 }
- { gcc: 8, std: 17 }
- { gcc: 9, std: 20 }
- { gcc: 10, std: 17 }
- { gcc: 10, std: 20 }
Expand Down Expand Up @@ -727,9 +716,9 @@ jobs:

# This tests an "install" with the CMake tools
install-classic:
name: "🐍 3.7 • Debian • x86 • Install"
name: "🐍 3.9 • Debian • x86 • Install"
runs-on: ubuntu-latest
container: i386/debian:buster
container: i386/debian:bullseye

steps:
- uses: actions/checkout@v1 # v1 is required to run inside docker
Expand Down Expand Up @@ -809,7 +798,6 @@ jobs:
fail-fast: false
matrix:
python:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
Expand All @@ -827,8 +815,6 @@ jobs:
args: -DCMAKE_CXX_STANDARD=20
- python: '3.8'
args: -DCMAKE_CXX_STANDARD=17
- python: '3.7'
args: -DCMAKE_CXX_STANDARD=14


name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}"
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/configure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,22 @@ jobs:

- runs-on: macos-13
arch: x64
cmake: "3.7"
cmake: "3.8"

- runs-on: windows-2019
arch: x64 # x86 compilers seem to be missing on 2019 image
cmake: "3.18"

name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
name: 🐍 3.8 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }}

steps:
- uses: actions/checkout@v4

- name: Setup Python 3.7
- name: Setup Python 3.8
uses: actions/setup-python@v5
with:
python-version: 3.7
python-version: 3.8
architecture: ${{ matrix.arch }}

- name: Prepare env
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,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 (3.7+, or PyPy) and the C++
lines of code and depend on Python (3.8+, or PyPy) 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
Expand Down Expand Up @@ -79,7 +79,7 @@ Goodies
In addition to the core functionality, pybind11 provides some extra
goodies:

- Python 3.7+, and PyPy3 7.3 are supported with an implementation-agnostic
- Python 3.8+, 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
Expand Down
3 changes: 1 addition & 2 deletions docs/advanced/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -826,8 +826,7 @@ An instance can now be pickled as follows:
always use the latest available version. Beware: failure to follow these
instructions will cause important pybind11 memory allocation routines to be
skipped during unpickling, which will likely lead to memory corruption
and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and
version 4 for Python 3.8+.
and/or segmentation faults.

.. seealso::

Expand Down
3 changes: 1 addition & 2 deletions docs/advanced/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,7 @@ Should they throw or fail to catch any exceptions in their call graph,
the C++ runtime calls ``std::terminate()`` to abort immediately.

Similarly, Python exceptions raised in a class's ``__del__`` method do not
propagate, but are logged by Python as an unraisable error. In Python 3.8+, a
`system hook is triggered
propagate, but ``sys.unraisablehook()`` `is triggered
<https://docs.python.org/3/library/sys.html#sys.unraisablehook>`_
and an auditing event is logged.

Expand Down
4 changes: 2 additions & 2 deletions docs/compiling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ with ``PYTHON_EXECUTABLE``. For example:

.. code-block:: bash

cmake -DPYBIND11_PYTHON_VERSION=3.7 ..
cmake -DPYBIND11_PYTHON_VERSION=3.8 ..

# Another method:
cmake -DPYTHON_EXECUTABLE=/path/to/python ..
Expand Down Expand Up @@ -493,7 +493,7 @@ existing targets instead:
cmake_minimum_required(VERSION 3.15...3.22)
project(example LANGUAGES CXX)

find_package(Python 3.7 COMPONENTS Interpreter Development REQUIRED)
find_package(Python 3.8 COMPONENTS Interpreter Development REQUIRED)
find_package(pybind11 CONFIG REQUIRED)
# or add_subdirectory(pybind11)

Expand Down
4 changes: 2 additions & 2 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
} else {
handle src_or_index = src;
// PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls.
#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
#if defined(PYPY_VERSION)
object index;
if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr())
index = reinterpret_steal<object>(PyNumber_Index(src.ptr()));
Expand Down Expand Up @@ -1503,7 +1503,7 @@ struct kw_only {};

/// \ingroup annotations
/// Annotation indicating that all previous arguments are positional-only; the is the equivalent of
/// an unnamed '/' argument (in Python 3.8)
/// an unnamed '/' argument
struct pos_only {};

template <typename T>
Expand Down
10 changes: 0 additions & 10 deletions include/pybind11/detail/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,19 +466,9 @@ extern "C" inline void pybind11_object_dealloc(PyObject *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();
Expand Down
4 changes: 2 additions & 2 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
#endif

#include <Python.h>
#if PY_VERSION_HEX < 0x03070000
# error "PYTHON < 3.7 IS UNSUPPORTED. pybind11 v2.12 was the last to support Python 3.6."
#if PY_VERSION_HEX < 0x03080000
# error "PYTHON < 3.8 IS UNSUPPORTED. pybind11 v2.13 was the last to support Python 3.7."
#endif
#include <frameobject.h>
#include <pythread.h>
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/detail/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ inline void translate_local_exception(std::exception_ptr p) {

inline object get_python_state_dict() {
object state_dict;
#if PYBIND11_INTERNALS_VERSION <= 4 || PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION)
#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION)
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
#else
# if PY_VERSION_HEX < 0x03090000
Expand Down
12 changes: 1 addition & 11 deletions include/pybind11/embed.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,13 @@ inline void initialize_interpreter_pre_pyconfig(bool init_signal_handlers,
detail::precheck_interpreter();
Py_InitializeEx(init_signal_handlers ? 1 : 0);

// 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]));
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.
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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
#if defined(PYPY_VERSION)
// 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
Expand Down
8 changes: 2 additions & 6 deletions include/pybind11/gil.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,7 @@ class gil_scoped_release {
// 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;
auto key = internals.tstate; // NOLINT(readability-qualified-auto)
PYBIND11_TLS_DELETE_VALUE(key);
}
}
Expand All @@ -173,9 +171,7 @@ class gil_scoped_release {
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;
auto key = detail::get_internals().tstate; // NOLINT(readability-qualified-auto)
PYBIND11_TLS_REPLACE_VALUE(key, tstate);
}
}
Expand Down
4 changes: 2 additions & 2 deletions pybind11/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import sys

if sys.version_info < (3, 7): # noqa: UP036
msg = "pybind11 does not support Python < 3.7. v2.12 was the last release supporting Python 3.6."
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."
raise ImportError(msg)


Expand Down
2 changes: 1 addition & 1 deletion pybind11/setup_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def has_flag(compiler: Any, flag: str) -> bool:
cpp_flag_cache = None


@lru_cache()
@lru_cache
def auto_cpp_level(compiler: Any) -> str | int:
"""
Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows.
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ ignore_missing_imports = true


[tool.pylint]
master.py-version = "3.7"
master.py-version = "3.8"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ruff has a Python version too, right? otherwise UP036 would complain above

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it does: target-version = "py37"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will go away if we move to scikit-build-core after 0.10 comes out. ;)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this @Skylion007!

Fixed with commit 49580cf

That made me realize we need to add something like this to the list of useful git grep commands, for the next time when we want to drop a Python version:

git grep -I '[^0-9]37[^0-9]'
git grep -I '[^0-9]38[^0-9]'

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will also go away if define the minimum python version in pyproject.toml standard location instead of setup.cfg :)

reports.output-format = "colorized"
messages_control.disable = [
"design",
Expand All @@ -45,7 +45,7 @@ messages_control.disable = [
]

[tool.ruff]
target-version = "py37"
target-version = "py38"
src = ["src"]

[tool.ruff.lint]
Expand Down
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ classifiers =
Topic :: Utilities
Programming Language :: C++
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Expand All @@ -39,5 +38,5 @@ project_urls =
Chat = https://gitter.im/pybind/Lobby

[options]
python_requires = >=3.7
python_requires = >=3.8
zip_safe = False
5 changes: 2 additions & 3 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
--only-binary=:all:
build~=1.0; python_version>="3.7"
numpy~=1.20.0; python_version=="3.7" and platform_python_implementation=="PyPy"
build~=1.0; python_version>="3.8"
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~=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.8" and python_version<"3.10"
numpy~=1.22.2; platform_python_implementation!="PyPy" and python_version=="3.10"
numpy~=1.26.0; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13"
pytest~=7.0
Expand Down
2 changes: 1 addition & 1 deletion tests/test_builtin_casters.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def cant_convert(v):
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 (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON:
if sys.version_info < (3, 10) and env.CPYTHON:
with env.deprecated_call():
assert convert(Int()) == 42
else:
Expand Down
28 changes: 12 additions & 16 deletions tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,28 +103,24 @@ def ignore_pytest_unraisable_warning(f):
@pytest.mark.xfail(env.PYPY, reason="Failure on PyPy 3.8 (7.3.7)", strict=False)
@ignore_pytest_unraisable_warning
def test_python_alreadyset_in_destructor(monkeypatch, capsys):
hooked = False
triggered = False

if hasattr(sys, "unraisablehook"): # Python 3.8+
hooked = True
# Don't take `sys.unraisablehook`, as that's overwritten by pytest
default_hook = sys.__unraisablehook__
# Don't take `sys.unraisablehook`, as that's overwritten by pytest
default_hook = sys.__unraisablehook__

def hook(unraisable_hook_args):
exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args
if obj == "already_set demo":
nonlocal triggered
triggered = True
default_hook(unraisable_hook_args)
return
def hook(unraisable_hook_args):
exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args
if obj == "already_set demo":
nonlocal triggered
triggered = True
default_hook(unraisable_hook_args)
return

# Use monkeypatch so pytest can apply and remove the patch as appropriate
monkeypatch.setattr(sys, "unraisablehook", hook)
# Use monkeypatch so pytest can apply and remove the patch as appropriate
monkeypatch.setattr(sys, "unraisablehook", hook)

assert m.python_alreadyset_in_destructor("already_set demo") is True
if hooked:
assert triggered is True
assert triggered is True

_, captured_stderr = capsys.readouterr()
assert captured_stderr.startswith("Exception ignored in: 'already_set demo'")
Expand Down
2 changes: 1 addition & 1 deletion tools/FindPythonLibsNew.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ endif()

# Use the Python interpreter to find the libs.
if(NOT PythonLibsNew_FIND_VERSION)
set(PythonLibsNew_FIND_VERSION "3.7")
set(PythonLibsNew_FIND_VERSION "3.8")
endif()

if(NOT CMAKE_VERSION VERSION_LESS "3.27")
Expand Down
2 changes: 1 addition & 1 deletion tools/pybind11NewTools.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ if(NOT Python_FOUND AND NOT Python3_FOUND)
endif()

find_package(
Python 3.7 REQUIRED COMPONENTS ${_pybind11_interp_component} ${_pybind11_dev_component}
Python 3.8 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.
Expand Down
2 changes: 1 addition & 1 deletion tools/pybind11Tools.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ endif()

# A user can set versions manually too
set(Python_ADDITIONAL_VERSIONS
"3.12;3.11;3.10;3.9;3.8;3.7"
"3.12;3.11;3.10;3.9;3.8"
CACHE INTERNAL "")

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
Expand Down