chore: drop Python 3.5 (#3719)

* chore: drop Python 3.5 support

* chore: more fstrings with flynt's help

* ci: drop Python 3.5

* chore: bump dependency versions

* docs: touch up py::args

* tests: remove deprecation warning

* Ban smartquotes

* Very minor tweaks (by-product of reviewing PR #3719).

Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
This commit is contained in:
Henry Schreiner 2022-02-11 19:06:16 -05:00 committed by GitHub
parent 1a432b426f
commit 522c59ceb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 171 additions and 241 deletions

View File

@ -93,11 +93,10 @@ cmake --build build -j4
Tips: Tips:
* You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3 * You can use `virtualenv` (faster, from PyPI) instead of `venv`.
only).
* You can select any name for your environment folder; if it contains "env" it * You can select any name for your environment folder; if it contains "env" it
will be ignored by git. will be ignored by git.
* If you dont have CMake 3.14+, just add “cmake” to the pip install command. * If you don't have CMake 3.14+, just add "cmake" to the pip install command.
* You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
FindPython uses `-DPython_ROOT_DIR=/path/to` or FindPython uses `-DPython_ROOT_DIR=/path/to` or
@ -105,7 +104,7 @@ Tips:
### Configuration options ### Configuration options
In CMake, configuration options are given with “-D”. Options are stored in the In CMake, configuration options are given with "-D". Options are stored in the
build directory, in the `CMakeCache.txt` file, so they are remembered for each build directory, in the `CMakeCache.txt` file, so they are remembered for each
build directory. Two selections are special - the generator, given with `-G`, build directory. Two selections are special - the generator, given with `-G`,
and the compiler, which is selected based on environment variables `CXX` and and the compiler, which is selected based on environment variables `CXX` and
@ -115,7 +114,7 @@ after the initial run.
The valid options are: The valid options are:
* `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo * `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo
* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+s FindPython instead of the * `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+'s FindPython instead of the
classic, deprecated, custom FindPythonLibs classic, deprecated, custom FindPythonLibs
* `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests) * `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests)
* `-DBUILD_TESTING=ON`: Enable the tests * `-DBUILD_TESTING=ON`: Enable the tests
@ -257,7 +256,7 @@ The report is sent to stderr; you can pipe it into a file if you wish.
### Build recipes ### Build recipes
This builds with the Intel compiler (assuming it is in your path, along with a This builds with the Intel compiler (assuming it is in your path, along with a
recent CMake and Python 3): recent CMake and Python):
```bash ```bash
python3 -m venv venv python3 -m venv venv

View File

@ -15,6 +15,8 @@ concurrency:
env: env:
PIP_ONLY_BINARY: numpy PIP_ONLY_BINARY: numpy
FORCE_COLOR: 3
PYTEST_TIMEOUT: 300
jobs: jobs:
# This is the "main" test suite, which tests a large number of different # This is the "main" test suite, which tests a large number of different
@ -25,7 +27,6 @@ jobs:
matrix: matrix:
runs-on: [ubuntu-latest, windows-2022, macos-latest] runs-on: [ubuntu-latest, windows-2022, macos-latest]
python: python:
- '3.5'
- '3.6' - '3.6'
- '3.9' - '3.9'
- '3.10' - '3.10'
@ -167,7 +168,7 @@ jobs:
# setuptools # setuptools
- name: Setuptools helpers test - name: Setuptools helpers test
run: pytest tests/extra_setuptools run: pytest tests/extra_setuptools
if: "!(matrix.python == '3.5' && matrix.runs-on == 'windows-2022')" if: "!(matrix.runs-on == 'windows-2022')"
deadsnakes: deadsnakes:
@ -624,9 +625,9 @@ jobs:
# This tests an "install" with the CMake tools # This tests an "install" with the CMake tools
install-classic: install-classic:
name: "🐍 3.5 • Debian • x86 • Install" name: "🐍 3.7 • Debian • x86 • Install"
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: i386/debian:stretch container: i386/debian:buster
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
@ -635,7 +636,7 @@ jobs:
run: | run: |
apt-get update apt-get update
apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip
pip3 install "pytest==3.1.*" pip3 install "pytest==6.*"
- name: Configure for install - name: Configure for install
run: > run: >
@ -704,12 +705,10 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python: python:
- 3.5
- 3.6 - 3.6
- 3.7 - 3.7
- 3.8 - 3.8
- 3.9 - 3.9
- pypy-3.6
include: include:
- python: 3.9 - python: 3.9
@ -810,7 +809,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python: python:
- 3.5 - 3.6
- 3.7 - 3.7
std: std:
- 14 - 14

View File

@ -15,7 +15,7 @@
repos: repos:
# Standard hooks # Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0 rev: "v4.1.0"
hooks: hooks:
- id: check-added-large-files - id: check-added-large-files
- id: check-case-conflict - id: check-case-conflict
@ -32,26 +32,26 @@ repos:
# Upgrade old Python syntax # Upgrade old Python syntax
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.31.0 rev: "v2.31.0"
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py3-plus] args: [--py36-plus]
# Nicely sort includes # Nicely sort includes
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.10.1 rev: "5.10.1"
hooks: hooks:
- id: isort - id: isort
# Black, the code formatter, natively supports pre-commit # Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.1.0 # Keep in sync with blacken-docs rev: "22.1.0" # Keep in sync with blacken-docs
hooks: hooks:
- id: black - id: black
# Also code format the docs # Also code format the docs
- repo: https://github.com/asottile/blacken-docs - repo: https://github.com/asottile/blacken-docs
rev: v1.12.1 rev: "v1.12.1"
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: additional_dependencies:
@ -59,32 +59,37 @@ repos:
# Changes tabs to spaces # Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.1.11 rev: "v1.1.11"
hooks: hooks:
- id: remove-tabs - id: remove-tabs
- repo: https://github.com/sirosen/texthooks
rev: "0.2.2"
hooks:
- id: fix-ligatures
- id: fix-smartquotes
# Autoremoves unused imports # Autoremoves unused imports
- repo: https://github.com/hadialqattan/pycln - repo: https://github.com/hadialqattan/pycln
rev: v1.1.0 rev: "v1.1.0"
hooks: hooks:
- id: pycln - id: pycln
# Checking for common mistakes # Checking for common mistakes
- repo: https://github.com/pre-commit/pygrep-hooks - repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0 rev: "v1.9.0"
hooks: hooks:
- id: python-check-blanket-noqa - id: python-check-blanket-noqa
- id: python-check-blanket-type-ignore - id: python-check-blanket-type-ignore
- id: python-no-log-warn - id: python-no-log-warn
# Python 3.6 - id: python-use-type-annotations
# - id: python-use-type-annotations
- id: rst-backticks - id: rst-backticks
- id: rst-directive-colons - id: rst-directive-colons
- id: rst-inline-touching-normal - id: rst-inline-touching-normal
# Flake8 also supports pre-commit natively (same author) # Flake8 also supports pre-commit natively (same author)
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 4.0.1 rev: "4.0.1"
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: &flake8_dependencies additional_dependencies: &flake8_dependencies
@ -94,14 +99,14 @@ repos:
# Automatically remove noqa that are not used # Automatically remove noqa that are not used
- repo: https://github.com/asottile/yesqa - repo: https://github.com/asottile/yesqa
rev: v1.3.0 rev: "v1.3.0"
hooks: hooks:
- id: yesqa - id: yesqa
additional_dependencies: *flake8_dependencies additional_dependencies: *flake8_dependencies
# CMake formatting # CMake formatting
- repo: https://github.com/cheshirekow/cmake-format-precommit - repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13 rev: "v0.6.13"
hooks: hooks:
- id: cmake-format - id: cmake-format
additional_dependencies: [pyyaml] additional_dependencies: [pyyaml]
@ -110,7 +115,7 @@ repos:
# Check static types with mypy # Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.931 rev: "v0.931"
hooks: hooks:
- id: mypy - id: mypy
args: [--show-error-codes] args: [--show-error-codes]
@ -128,7 +133,7 @@ repos:
# Check for spelling # Check for spelling
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.1.0 rev: "v2.1.0"
hooks: hooks:
- id: codespell - id: codespell
exclude: ".supp$" exclude: ".supp$"
@ -136,7 +141,7 @@ repos:
# Check for common shell mistakes # Check for common shell mistakes
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.8.0.4 rev: "v0.8.0.4"
hooks: hooks:
- id: shellcheck - id: shellcheck

View File

@ -32,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary
dependency. dependency.
Think of this library as a tiny self-contained version of Boost.Python Think of this library as a tiny self-contained version of Boost.Python
with everything stripped away that isnt relevant for binding with everything stripped away that isn't relevant for binding
generation. Without comments, the core header files only require ~4K generation. Without comments, the core header files only require ~4K
lines of code and depend on Python (3.5+, or PyPy) and the C++ lines of code and depend on Python (3.6+, or PyPy) and the C++
standard library. This compact implementation was possible thanks to standard library. This compact implementation was possible thanks to
some of the new C++11 language features (specifically: tuples, lambda some of the new C++11 language features (specifically: tuples, lambda
functions and variadic templates). Since its creation, this library has functions and variadic templates). Since its creation, this library has
@ -78,8 +78,8 @@ Goodies
In addition to the core functionality, pybind11 provides some extra In addition to the core functionality, pybind11 provides some extra
goodies: goodies:
- Python 3.5+, and PyPy3 7.3 are supported with an implementation-agnostic - Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic
interface (pybind11 2.9 was the last version to support Python 2). 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 - It is possible to bind C++11 lambda functions with captured
variables. The lambda capture data is stored inside the resulting variables. The lambda capture data is stored inside the resulting
@ -88,8 +88,8 @@ goodies:
- pybind11 uses C++11 move constructors and move assignment operators - pybind11 uses C++11 move constructors and move assignment operators
whenever possible to efficiently transfer custom data types. whenever possible to efficiently transfer custom data types.
- Its easy to expose the internal storage of custom data types through - It's easy to expose the internal storage of custom data types through
Pythons buffer protocols. This is handy e.g. for fast conversion Pythons' buffer protocols. This is handy e.g. for fast conversion
between C++ matrix classes like Eigen and NumPy without expensive between C++ matrix classes like Eigen and NumPy without expensive
copy operations. copy operations.
@ -119,7 +119,7 @@ goodies:
Supported compilers Supported compilers
------------------- -------------------
1. Clang/LLVM 3.3 or newer (for Apple Xcodes clang, this is 5.0.0 or 1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or
newer) newer)
2. GCC 4.8 or newer 2. GCC 4.8 or newer
3. Microsoft Visual Studio 2015 Update 3 or newer 3. Microsoft Visual Studio 2015 Update 3 or newer

View File

@ -167,5 +167,4 @@ as arguments and return values, refer to the section on binding :ref:`classes`.
+------------------------------------+---------------------------+-----------------------------------+ +------------------------------------+---------------------------+-----------------------------------+
.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and .. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires ``os.PathLike`` is converted to ``std::filesystem::path``.
Python 3.6 (for ``__fspath__`` support).

View File

@ -372,7 +372,7 @@ like so:
Keyword-only arguments Keyword-only arguments
====================== ======================
Python 3 introduced keyword-only arguments by specifying an unnamed ``*`` Python implements keyword-only arguments by specifying an unnamed ``*``
argument in a function definition: argument in a function definition:
.. code-block:: python .. code-block:: python
@ -395,19 +395,18 @@ argument annotations when registering the function:
m.def("f", [](int a, int b) { /* ... */ }, m.def("f", [](int a, int b) { /* ... */ },
py::arg("a"), py::kw_only(), py::arg("b")); py::arg("a"), py::kw_only(), py::arg("b"));
Note that you currently cannot combine this with a ``py::args`` argument. This
feature does *not* require Python 3 to work.
.. versionadded:: 2.6 .. versionadded:: 2.6
As of pybind11 2.9, a ``py::args`` argument implies that any following arguments A ``py::args`` argument implies that any following arguments are keyword-only,
are keyword-only, as if ``py::kw_only()`` had been specified in the same as if ``py::kw_only()`` had been specified in the same relative location of the
relative location of the argument list as the ``py::args`` argument. The argument list as the ``py::args`` argument. The ``py::kw_only()`` may be
``py::kw_only()`` may be included to be explicit about this, but is not included to be explicit about this, but is not required.
required. (Prior to 2.9 ``py::args`` may only occur at the end of the argument
list, or immediately before a ``py::kwargs`` argument at the end). .. versionchanged:: 2.9
This can now be combined with ``py::args``. Before, ``py::args`` could only
occur at the end of the argument list, or immediately before a ``py::kwargs``
argument at the end.
.. versionadded:: 2.9
Positional-only arguments Positional-only arguments
========================= =========================

View File

@ -395,7 +395,7 @@ uses of ``py::array``:
Ellipsis Ellipsis
======== ========
Python 3 provides a convenient ``...`` ellipsis notation that is often used to Python provides a convenient ``...`` ellipsis notation that is often used to
slice multidimensional arrays. For instance, the following snippet extracts the slice multidimensional arrays. For instance, the following snippet extracts the
middle dimensions of a tensor with the first and last index set to zero. middle dimensions of a tensor with the first and last index set to zero.

View File

@ -11,20 +11,20 @@ def generate_dummy_code_pybind11(nclasses=10):
bindings = "" bindings = ""
for cl in range(nclasses): for cl in range(nclasses):
decl += "class cl%03i;\n" % cl decl += f"class cl{cl:03};\n"
decl += "\n" decl += "\n"
for cl in range(nclasses): for cl in range(nclasses):
decl += "class cl%03i {\n" % cl decl += f"class {cl:03} {{\n"
decl += "public:\n" decl += "public:\n"
bindings += ' py::class_<cl%03i>(m, "cl%03i")\n' % (cl, cl) bindings += f' py::class_<cl{cl:03}>(m, "cl{cl:03}")\n'
for fn in range(nfns): for fn in range(nfns):
ret = random.randint(0, nclasses - 1) ret = random.randint(0, nclasses - 1)
params = [random.randint(0, nclasses - 1) for i in range(nargs)] params = [random.randint(0, nclasses - 1) for i in range(nargs)]
decl += " cl%03i *fn_%03i(" % (ret, fn) decl += f" cl{ret:03} *fn_{fn:03}("
decl += ", ".join("cl%03i *" % p for p in params) decl += ", ".join(f"cl{p:03} *" for p in params)
decl += ");\n" decl += ");\n"
bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn) bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n'
decl += "};\n\n" decl += "};\n\n"
bindings += " ;\n" bindings += " ;\n"
@ -42,23 +42,20 @@ def generate_dummy_code_boost(nclasses=10):
bindings = "" bindings = ""
for cl in range(nclasses): for cl in range(nclasses):
decl += "class cl%03i;\n" % cl decl += f"class cl{cl:03};\n"
decl += "\n" decl += "\n"
for cl in range(nclasses): for cl in range(nclasses):
decl += "class cl%03i {\n" % cl decl += "class cl%03i {\n" % cl
decl += "public:\n" decl += "public:\n"
bindings += ' py::class_<cl%03i>("cl%03i")\n' % (cl, cl) bindings += f' py::class_<cl{cl:03}>("cl{cl:03}")\n'
for fn in range(nfns): for fn in range(nfns):
ret = random.randint(0, nclasses - 1) ret = random.randint(0, nclasses - 1)
params = [random.randint(0, nclasses - 1) for i in range(nargs)] params = [random.randint(0, nclasses - 1) for i in range(nargs)]
decl += " cl%03i *fn_%03i(" % (ret, fn) decl += f" cl{ret:03} *fn_{fn:03}("
decl += ", ".join("cl%03i *" % p for p in params) decl += ", ".join(f"cl{p:03} *" for p in params)
decl += ");\n" decl += ");\n"
bindings += ( bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy<py::manage_new_object>())\n'
' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n'
% (fn, cl, fn)
)
decl += "};\n\n" decl += "};\n\n"
bindings += " ;\n" bindings += " ;\n"

View File

@ -800,7 +800,7 @@ Packaging / building improvements:
`#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and `#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and
`#2370 <https://github.com/pybind/pybind11/pull/2370>`_ `#2370 <https://github.com/pybind/pybind11/pull/2370>`_
* Full integration with CMakes C++ standard system and compile features * Full integration with CMake's C++ standard system and compile features
replaces ``PYBIND11_CPP_STANDARD``. replaces ``PYBIND11_CPP_STANDARD``.
* Generated config file is now portable to different Python/compiler/CMake * Generated config file is now portable to different Python/compiler/CMake

View File

@ -417,10 +417,10 @@ existing targets instead:
.. code-block:: cmake .. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.19) cmake_minimum_required(VERSION 3.15...3.22)
project(example LANGUAGES CXX) project(example LANGUAGES CXX)
find_package(Python COMPONENTS Interpreter Development REQUIRED) find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED)
find_package(pybind11 CONFIG REQUIRED) find_package(pybind11 CONFIG REQUIRED)
# or add_subdirectory(pybind11) # or add_subdirectory(pybind11)
@ -433,9 +433,8 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``.
.. warning:: .. warning::
If you use FindPython2 and FindPython3 to dual-target Python, use the If you use FindPython to multi-target Python versions, use the individual
individual targets listed below, and avoid targets that directly include targets listed below, and avoid targets that directly include Python parts.
Python parts.
There are `many ways to hint or force a discovery of a specific Python There are `many ways to hint or force a discovery of a specific Python
installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_), installation <https://cmake.org/cmake/help/latest/module/FindPython.html>`_),

View File

@ -344,9 +344,9 @@ def generate_doxygen_xml(app):
subprocess.call(["doxygen", "--version"]) subprocess.call(["doxygen", "--version"])
retcode = subprocess.call(["doxygen"], cwd=app.confdir) retcode = subprocess.call(["doxygen"], cwd=app.confdir)
if retcode < 0: if retcode < 0:
sys.stderr.write("doxygen error code: {}\n".format(-retcode)) sys.stderr.write(f"doxygen error code: {-retcode}\n")
except OSError as e: except OSError as e:
sys.stderr.write("doxygen execution failed: {}\n".format(e)) sys.stderr.write(f"doxygen execution failed: {e}\n")
def prepare(app): def prepare(app):

View File

@ -145,7 +145,7 @@ using C++14 template metaprogramming.
.. _`faq:hidden_visibility`: .. _`faq:hidden_visibility`:
"SomeClass declared with greater visibility than the type of its field SomeClass::member [-Wattributes]" "'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]"
============================================================================================================ ============================================================================================================
This error typically indicates that you are compiling without the required This error typically indicates that you are compiling without the required

View File

@ -1,5 +1,5 @@
breathe==4.31.0 breathe==4.32.0
sphinx==3.5.4 sphinx==4.4.0
sphinx_rtd_theme==1.0.0 sphinx_rtd_theme==1.0.0
sphinxcontrib-moderncmakedomain==3.19 sphinxcontrib-moderncmakedomain==3.21.4
sphinxcontrib-svg2pdfconverter==1.1.1 sphinxcontrib-svg2pdfconverter==1.2.0

View File

@ -524,7 +524,7 @@ include a declaration of the form:
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>) PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
Continuing to do so wont cause an error or even a deprecation warning, Continuing to do so won't cause an error or even a deprecation warning,
but it's completely redundant. but it's completely redundant.

View File

@ -1585,7 +1585,7 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
template <typename Derived> template <typename Derived>
template <return_value_policy policy, typename... Args> template <return_value_policy policy, typename... Args>
object object_api<Derived>::operator()(Args &&...args) const { object object_api<Derived>::operator()(Args &&...args) const {
#if !defined(NDEBUG) && PY_VERSION_HEX >= 0x03060000 #ifndef NDEBUG
if (!PyGILState_Check()) { if (!PyGILState_Check()) {
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure."); pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
} }

View File

@ -18,17 +18,6 @@
#include <datetime.h> #include <datetime.h>
#include <mutex> #include <mutex>
// Backport the PyDateTime_DELTA functions from Python3.3 if required
#ifndef PyDateTime_DELTA_GET_DAYS
# define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta *) o)->days)
#endif
#ifndef PyDateTime_DELTA_GET_SECONDS
# define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta *) o)->seconds)
#endif
#ifndef PyDateTime_DELTA_GET_MICROSECONDS
# define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta *) o)->microseconds)
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)

View File

@ -211,8 +211,8 @@
#endif #endif
#include <Python.h> #include <Python.h>
#if PY_VERSION_HEX < 0x030500f0 #if PY_VERSION_HEX < 0x03060000
# error "PYTHON 2 IS NO LONGER SUPPORTED. pybind11 v2.9 was the last to support Python 2." # error "PYTHON < 3.6 IS UNSUPPORTED. pybind11 v2.9 was the last to support Python 2 and 3.5."
#endif #endif
#include <frameobject.h> #include <frameobject.h>
#include <pythread.h> #include <pythread.h>
@ -301,15 +301,6 @@
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \ extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200
extern "C" {
struct _Py_atomic_address {
void *value;
};
PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current;
}
#endif
#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code #define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
#define PYBIND11_STRINGIFY(x) #x #define PYBIND11_STRINGIFY(x) #x
#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) #define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
@ -438,7 +429,7 @@ enum class return_value_policy : uint8_t {
/** Reference an existing object (i.e. do not create a new copy) and take /** Reference an existing object (i.e. do not create a new copy) and take
ownership. Python will call the destructor and delete operator when the ownership. Python will call the destructor and delete operator when the
objects reference count reaches zero. Undefined behavior ensues when object's reference count reaches zero. Undefined behavior ensues when
the C++ side does the same.. */ the C++ side does the same.. */
take_ownership, take_ownership,
@ -454,7 +445,7 @@ enum class return_value_policy : uint8_t {
move, move,
/** Reference an existing object, but do not take ownership. The C++ side /** Reference an existing object, but do not take ownership. The C++ side
is responsible for managing the objects lifetime and deallocating it is responsible for managing the object's lifetime and deallocating it
when it is no longer used. Warning: undefined behavior will ensue when when it is no longer used. Warning: undefined behavior will ensue when
the C++ side deletes an object that is still referenced and used by the C++ side deletes an object that is still referenced and used by
Python. */ Python. */
@ -463,7 +454,7 @@ enum class return_value_policy : uint8_t {
/** This policy only applies to methods and properties. It references the /** This policy only applies to methods and properties. It references the
object without taking ownership similar to the above object without taking ownership similar to the above
return_value_policy::reference policy. In contrast to that policy, the return_value_policy::reference policy. In contrast to that policy, the
function or propertys implicit this argument (called the parent) is function or property's implicit this argument (called the parent) is
considered to be the the owner of the return value (the child). considered to be the the owner of the return value (the child).
pybind11 then couples the lifetime of the parent to the child via a pybind11 then couples the lifetime of the parent to the child via a
reference relationship that ensures that the parent cannot be garbage reference relationship that ensures that the parent cannot be garbage

View File

@ -439,10 +439,11 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
// instance_registered) // instance_registered)
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 // in particular, need to be 0). Use Python's memory allocation
// they default to using pymalloc, which is designed to be efficient for small allocations // functions: Python is using pymalloc, which is designed to be
// like the one we're doing here; in earlier versions (and for larger allocations) they are // efficient for small allocations like the one we're doing here;
// just wrappers around malloc. // for larger allocations they are just wrappers around malloc.
// TODO: is this still true for pure Python 3.6?
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
if (!nonsimple.values_and_holders) { if (!nonsimple.values_and_holders) {
throw std::bad_alloc(); throw std::bad_alloc();
@ -538,8 +539,6 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
inline PyThreadState *get_thread_state_unchecked() { inline PyThreadState *get_thread_state_unchecked() {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
return PyThreadState_GET(); return PyThreadState_GET();
#elif PY_VERSION_HEX < 0x03050200
return (PyThreadState *) _PyThreadState_Current.value;
#else #else
return _PyThreadState_UncheckedGet(); return _PyThreadState_UncheckedGet();
#endif #endif

View File

@ -1213,7 +1213,7 @@ public:
PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */);
} }
using module_def = PyModuleDef; using module_def = PyModuleDef; // TODO: Can this be removed (it was needed only for Python 2)?
/** \rst /** \rst
Create a new top-level module that can be used as the main module of a C extension. Create a new top-level module that can be used as the main module of a C extension.
@ -1242,7 +1242,7 @@ public:
} }
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when // TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
// returned from PyInit_... // returned from PyInit_...
// For Python 2, reinterpret_borrow is correct. // For Python 2, reinterpret_borrow was correct.
return reinterpret_borrow<module_>(m); return reinterpret_borrow<module_>(m);
} }
}; };

View File

@ -13,8 +13,7 @@
#include <string> #include <string>
#ifdef __has_include #ifdef __has_include
# if defined(PYBIND11_CPP17) && __has_include(<filesystem>) && \ # if defined(PYBIND11_CPP17) && __has_include(<filesystem>)
PY_VERSION_HEX >= 0x03060000
# include <filesystem> # include <filesystem>
# define PYBIND11_HAS_FILESYSTEM 1 # define PYBIND11_HAS_FILESYSTEM 1
# endif # endif

View File

@ -2,7 +2,7 @@ import nox
nox.options.sessions = ["lint", "tests", "tests_packaging"] nox.options.sessions = ["lint", "tests", "tests_packaging"]
PYTHON_VERSIONS = ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
@nox.session(reuse_venv=True) @nox.session(reuse_venv=True)

View File

@ -1,7 +1,7 @@
import sys import sys
if sys.version_info < (3, 5): if sys.version_info < (3, 6):
msg = "pybind11 does not support Python < 3.5. 2.9 was the last release supporting older Pythons." msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
raise ImportError(msg) raise ImportError(msg)

View File

@ -200,7 +200,7 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2]) current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2])
desired_macos = (10, 9) if level < 17 else (10, 14) desired_macos = (10, 9) if level < 17 else (10, 14)
macos_string = ".".join(str(x) for x in min(current_macos, desired_macos)) macos_string = ".".join(str(x) for x in min(current_macos, desired_macos))
macosx_min = "-mmacosx-version-min={}".format(macos_string) macosx_min = f"-mmacosx-version-min={macos_string}"
cflags += [macosx_min] cflags += [macosx_min]
ldflags += [macosx_min] ldflags += [macosx_min]
@ -322,9 +322,9 @@ def intree_extensions(
if not exts: if not exts:
msg = ( msg = (
"path {path} is not a child of any of the directories listed " f"path {path} is not a child of any of the directories listed "
"in 'package_dir' ({package_dir})" f"in 'package_dir' ({package_dir})"
).format(path=path, package_dir=package_dir) )
raise ValueError(msg) raise ValueError(msg)
return exts return exts
@ -419,7 +419,7 @@ class ParallelCompile:
self.default = default self.default = default
self.max = max self.max = max
self.needs_recompile = needs_recompile self.needs_recompile = needs_recompile
self._old = [] # type: List[CCompilerMethod] self._old: List[CCompilerMethod] = []
def function(self) -> CCompilerMethod: def function(self) -> CCompilerMethod:
""" """

View File

@ -14,7 +14,6 @@ classifiers =
Topic :: Utilities Topic :: Utilities
Programming Language :: C++ Programming Language :: C++
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
@ -38,7 +37,7 @@ project_urls =
Chat = https://gitter.im/pybind/Lobby Chat = https://gitter.im/pybind/Lobby
[options] [options]
python_requires = >=3.5 python_requires = >=3.6
zip_safe = False zip_safe = False
@ -46,14 +45,5 @@ zip_safe = False
max-line-length = 120 max-line-length = 120
show_source = True show_source = True
exclude = .git, __pycache__, build, dist, docs, tools, venv exclude = .git, __pycache__, build, dist, docs, tools, venv
ignore = extend-ignore = E203, E722, B950
# required for pretty matrix formatting: multiple spaces after `,` and `[` select = C,E,F,N,W,B,B9
E201, E241, W504,
# camelcase 'cPickle' imported as lowercase 'pickle'
N813
# Black conflict
W503, E203
[tool:pytest]
timeout = 300

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# Setup script for PyPI; use CMakeFile.txt to build extension modules # Setup script for PyPI; use CMakeFile.txt to build extension modules
@ -41,12 +41,10 @@ def build_expected_version_hex(matches: Dict[str, str]) -> str:
serial = int(level_serial[len(level) :]) serial = int(level_serial[len(level) :])
break break
if serial is None: if serial is None:
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial) msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
raise RuntimeError(msg) raise RuntimeError(msg)
version_hex_str = "{:02x}{:02x}{:02x}{}{:x}".format( version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
major, minor, patch, level[:1], serial return f"0x{version_hex_str.upper()}"
)
return "0x{}".format(version_hex_str.upper())
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
@ -66,7 +64,7 @@ to_src = (
# Read the listed version # Read the listed version
loc = {} # type: Dict[str, str] loc: Dict[str, str] = {}
code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec") code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
exec(code, loc) exec(code, loc)
version = loc["__version__"] version = loc["__version__"]
@ -75,17 +73,13 @@ version = loc["__version__"]
matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8"))) matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
if version != cpp_version: if version != cpp_version:
msg = "Python version {} does not match C++ version {}!".format( msg = f"Python version {version} does not match C++ version {cpp_version}!"
version, cpp_version
)
raise RuntimeError(msg) raise RuntimeError(msg)
version_hex = matches.get("HEX", "MISSING") version_hex = matches.get("HEX", "MISSING")
exp_version_hex = build_expected_version_hex(matches) exp_version_hex = build_expected_version_hex(matches)
if version_hex != exp_version_hex: if version_hex != exp_version_hex:
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format( msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
version_hex, exp_version_hex
)
raise RuntimeError(msg) raise RuntimeError(msg)

View File

@ -145,9 +145,9 @@ def test_build_sdist(monkeypatch, tmpdir):
contents = f.read().decode("utf8") contents = f.read().decode("utf8")
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
files = {"pybind11/{}".format(n) for n in all_files} files = {f"pybind11/{n}" for n in all_files}
files |= sdist_files files |= sdist_files
files |= {"pybind11{}".format(n) for n in local_sdist_files} files |= {f"pybind11{n}" for n in local_sdist_files}
files.add("pybind11.egg-info/entry_points.txt") files.add("pybind11.egg-info/entry_points.txt")
files.add("pybind11.egg-info/requires.txt") files.add("pybind11.egg-info/requires.txt")
assert simpler == files assert simpler == files
@ -200,9 +200,9 @@ def test_build_global_dist(monkeypatch, tmpdir):
) as f: ) as f:
pyproject_toml = f.read() pyproject_toml = f.read()
files = {"pybind11/{}".format(n) for n in all_files} files = {f"pybind11/{n}" for n in all_files}
files |= sdist_files files |= sdist_files
files |= {"pybind11_global{}".format(n) for n in local_sdist_files} files |= {f"pybind11_global{n}" for n in local_sdist_files}
assert simpler == files assert simpler == files
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f: with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
@ -227,7 +227,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl") (wheel,) = tmpdir.visit("*.whl")
files = {"pybind11/{}".format(n) for n in all_files} files = {f"pybind11/{n}" for n in all_files}
files |= { files |= {
"dist-info/LICENSE", "dist-info/LICENSE",
"dist-info/METADATA", "dist-info/METADATA",
@ -241,9 +241,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
names = z.namelist() names = z.namelist()
trimmed = {n for n in names if "dist-info" not in n} trimmed = {n for n in names if "dist-info" not in n}
trimmed |= { trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n}
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
}
assert files == trimmed assert files == trimmed
@ -257,8 +255,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl") (wheel,) = tmpdir.visit("*.whl")
files = {"data/data/{}".format(n) for n in src_files} files = {f"data/data/{n}" for n in src_files}
files |= {"data/headers/{}".format(n[8:]) for n in headers} files |= {f"data/headers/{n[8:]}" for n in headers}
files |= { files |= {
"dist-info/LICENSE", "dist-info/LICENSE",
"dist-info/METADATA", "dist-info/METADATA",

View File

@ -18,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
(tmpdir / "setup.py").write_text( (tmpdir / "setup.py").write_text(
dedent( dedent(
"""\ f"""\
import sys import sys
sys.path.append({MAIN_DIR!r}) sys.path.append({MAIN_DIR!r})
@ -51,7 +51,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
ext_modules=ext_modules, ext_modules=ext_modules,
) )
""" """
).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel), ),
encoding="ascii", encoding="ascii",
) )

View File

@ -1,12 +1,15 @@
[pytest] [pytest]
minversion = 3.1 minversion = 3.10
norecursedirs = test_* extra_* norecursedirs = test_* extra_*
xfail_strict = True xfail_strict = True
addopts = addopts =
# show summary of skipped tests # show summary of tests
-rs -ra
# capture only Python print and C++ py::print, but not C output (low-level Python errors) # capture only Python print and C++ py::print, but not C output (low-level Python errors)
--capture=sys --capture=sys
# Show local info when a failure occurs
--showlocals
log_cli_level = info
filterwarnings = filterwarnings =
# make warnings into errors but ignore certain third-party extension issues # make warnings into errors but ignore certain third-party extension issues
error error

View File

@ -1,12 +1,8 @@
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy" numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
numpy==1.20.0; 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.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11" numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
py @ git+https://github.com/pytest-dev/py; python_version>="3.11" numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
pytest==4.6.9; python_version<"3.5" pytest==7.0.0
pytest==6.1.2; python_version=="3.5"
pytest==6.2.4; python_version>="3.6"
pytest-timeout pytest-timeout
scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6" scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10" scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10"

View File

@ -50,7 +50,7 @@ def test_single_char_arguments():
"""Tests failures for passing invalid inputs to char-accepting functions""" """Tests failures for passing invalid inputs to char-accepting functions"""
def toobig_message(r): def toobig_message(r):
return "Character code point not in range({:#x})".format(r) return f"Character code point not in range({r:#x})"
toolong_message = "Expected a character, but multi-character string found" toolong_message = "Expected a character, but multi-character string found"

View File

@ -17,7 +17,7 @@ def test_callbacks():
return "func2", a, b, c, d return "func2", a, b, c, d
def func3(a): def func3(a):
return "func3({})".format(a) return f"func3({a})"
assert m.test_callback1(func1) == "func1" assert m.test_callback1(func1) == "func1"
assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5) assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
@ -188,14 +188,8 @@ def test_callback_num_times():
if not rep: if not rep:
print() print()
print( print(
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format( f"callback_num_times: {num_millions:d} million / {td:.3f} seconds = {rate:.3f} million / second"
num_millions, td, rate
)
) )
if len(rates) > 1: if len(rates) > 1:
print("Min Mean Max") print("Min Mean Max")
print( print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}")
"{:6.3f} {:6.3f} {:6.3f}".format(
min(rates), sum(rates) / len(rates), max(rates)
)
)

View File

@ -100,7 +100,7 @@ SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif(
) )
def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch): def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
if tz is not None: if tz is not None:
monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz)) monkeypatch.setenv("TZ", f"/usr/share/zoneinfo/{tz}")
# Roundtrip the time # Roundtrip the time
datetime2 = m.test_chrono2(time1) datetime2 = m.test_chrono2(time1)

View File

@ -5,4 +5,4 @@ import test_cmake_build
assert isinstance(__file__, str) # Test this is properly set assert isinstance(__file__, str) # Test this is properly set
assert test_cmake_build.add(1, 2) == 3 assert test_cmake_build.add(1, 2) == 3
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3")

View File

@ -221,9 +221,7 @@ def test_nonunit_stride_to_python():
assert np.all(m.diagonal(ref) == ref.diagonal()) assert np.all(m.diagonal(ref) == ref.diagonal())
assert np.all(m.diagonal_1(ref) == ref.diagonal(1)) assert np.all(m.diagonal_1(ref) == ref.diagonal(1))
for i in range(-5, 7): for i in range(-5, 7):
assert np.all( assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), f"m.diagonal_n({i})"
m.diagonal_n(ref, i) == ref.diagonal(i)
), "m.diagonal_n({})".format(i)
assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4]) assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4])
assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:]) assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:])
@ -236,7 +234,7 @@ def test_eigen_ref_to_python():
mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]])) mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]]))
assert np.all( assert np.all(
mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]]) mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]])
), "cholesky{}".format(i) ), f"cholesky{i}"
def assign_both(a1, a2, r, c, v): def assign_both(a1, a2, r, c, v):

View File

@ -88,7 +88,7 @@ def test_python_call_in_catch():
def ignore_pytest_unraisable_warning(f): def ignore_pytest_unraisable_warning(f):
unraisable = "PytestUnraisableExceptionWarning" unraisable = "PytestUnraisableExceptionWarning"
if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6 if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6
dec = pytest.mark.filterwarnings("ignore::pytest.{}".format(unraisable)) dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
return dec(f) return dec(f)
else: else:
return f return f

View File

@ -18,9 +18,7 @@ def test_dtypes():
assert check.numpy == check.pybind11, check assert check.numpy == check.pybind11, check
if check.numpy.num != check.pybind11.num: if check.numpy.num != check.pybind11.num:
print( print(
"NOTE: typenum mismatch for {}: {} != {}".format( f"NOTE: typenum mismatch for {check}: {check.numpy.num} != {check.pybind11.num}"
check, check.numpy.num, check.pybind11.num
)
) )
@ -116,9 +114,7 @@ def test_at_fail(arr, dim):
for func in m.at_t, m.mutate_at_t: for func in m.at_t, m.mutate_at_t:
with pytest.raises(IndexError) as excinfo: with pytest.raises(IndexError) as excinfo:
func(arr, *([0] * dim)) func(arr, *([0] * dim))
assert str(excinfo.value) == "index dimension mismatch: {} (ndim = 2)".format( assert str(excinfo.value) == f"index dimension mismatch: {dim} (ndim = 2)"
dim
)
def test_at(arr): def test_at(arr):
@ -192,8 +188,6 @@ def test_make_empty_shaped_array():
def test_wrap(): def test_wrap():
def assert_references(a, b, base=None): def assert_references(a, b, base=None):
from distutils.version import LooseVersion
if base is None: if base is None:
base = a base = a
assert a is not b assert a is not b
@ -204,7 +198,8 @@ def test_wrap():
assert a.flags.f_contiguous == b.flags.f_contiguous assert a.flags.f_contiguous == b.flags.f_contiguous
assert a.flags.writeable == b.flags.writeable assert a.flags.writeable == b.flags.writeable
assert a.flags.aligned == b.flags.aligned assert a.flags.aligned == b.flags.aligned
if LooseVersion(np.__version__) >= LooseVersion("1.14.0"): # 1.13 supported Python 3.6
if tuple(int(x) for x in np.__version__.split(".")[:2]) >= (1, 14):
assert a.flags.writebackifcopy == b.flags.writebackifcopy assert a.flags.writebackifcopy == b.flags.writebackifcopy
else: else:
assert a.flags.updateifcopy == b.flags.updateifcopy assert a.flags.updateifcopy == b.flags.updateifcopy

View File

@ -14,7 +14,7 @@ def simple_dtype():
return np.dtype( return np.dtype(
{ {
"names": ["bool_", "uint_", "float_", "ldbl_"], "names": ["bool_", "uint_", "float_", "ldbl_"],
"formats": ["?", "u4", "f4", "f{}".format(ld.itemsize)], "formats": ["?", "u4", "f4", f"f{ld.itemsize}"],
"offsets": [0, 4, 8, (16 if ld.alignment > 4 else 12)], "offsets": [0, 4, 8, (16 if ld.alignment > 4 else 12)],
} }
) )
@ -125,7 +125,7 @@ def test_dtype(simple_dtype):
assert [x.replace(" ", "") for x in m.print_dtypes()] == [ assert [x.replace(" ", "") for x in m.print_dtypes()] == [
simple_dtype_fmt(), simple_dtype_fmt(),
packed_dtype_fmt(), packed_dtype_fmt(),
"[('a',{}),('b',{})]".format(simple_dtype_fmt(), packed_dtype_fmt()), f"[('a',{simple_dtype_fmt()}),('b',{packed_dtype_fmt()})]",
partial_dtype_fmt(), partial_dtype_fmt(),
partial_nested_fmt(), partial_nested_fmt(),
"[('a','S3'),('b','S3')]", "[('a','S3'),('b','S3')]",

View File

@ -12,7 +12,7 @@ def test_string_list():
assert lst.back() == "Element 2" assert lst.back() == "Element 2"
for i, k in enumerate(lst, start=1): for i, k in enumerate(lst, start=1):
assert k == "Element {}".format(i) assert k == f"Element {i}"
lst.pop_back() lst.pop_back()
assert m.print_opaque_list(lst) == "Opaque list: [Element 1]" assert m.print_opaque_list(lst) == "Opaque list: [Element 1]"

View File

@ -305,8 +305,8 @@ def test_non_converting_constructors():
for move in [True, False]: for move in [True, False]:
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.nonconverting_constructor(t, v, move) m.nonconverting_constructor(t, v, move)
expected_error = "Object of type '{}' is not an instance of '{}'".format( expected_error = (
type(v).__name__, t f"Object of type '{type(v).__name__}' is not an instance of '{t}'"
) )
assert str(excinfo.value) == expected_error assert str(excinfo.value) == expected_error

View File

@ -1,20 +1,10 @@
import pytest import pytest
from pytest import approx
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import sequences_and_iterators as m from pybind11_tests import sequences_and_iterators as m
def isclose(a, b, rel_tol=1e-05, abs_tol=0.0):
"""Like math.isclose() from Python 3.5"""
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0):
return all(
isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list)
)
def test_slice_constructors(): def test_slice_constructors():
assert m.make_forward_slice_size_t() == slice(0, -1, 1) assert m.make_forward_slice_size_t() == slice(0, -1, 1)
assert m.make_reversed_slice_object() == slice(None, None, -1) assert m.make_reversed_slice_object() == slice(None, None, -1)
@ -117,7 +107,8 @@ def test_sequence():
assert 12.34 not in s assert 12.34 not in s
s[0], s[3] = 12.34, 56.78 s[0], s[3] = 12.34, 56.78
assert 12.34 in s assert 12.34 in s
assert isclose(s[0], 12.34) and isclose(s[3], 56.78) assert s[0] == approx(12.34, rel=1e-05)
assert s[3] == approx(56.78, rel=1e-05)
rev = reversed(s) rev = reversed(s)
assert cstats.values() == ["of size", "5"] assert cstats.values() == ["of size", "5"]
@ -132,14 +123,14 @@ def test_sequence():
assert cstats.values() == ["of size", "0"] assert cstats.values() == ["of size", "0"]
expected = [0, 56.78, 0, 0, 12.34] expected = [0, 56.78, 0, 0, 12.34]
assert allclose(rev, expected) assert rev == approx(expected, rel=1e-05)
assert allclose(rev2, expected) assert rev2 == approx(expected, rel=1e-05)
assert rev == rev2 assert rev == rev2
rev[0::2] = m.Sequence([2.0, 2.0, 2.0]) rev[0::2] = m.Sequence([2.0, 2.0, 2.0])
assert cstats.values() == ["of size", "3", "from std::vector"] assert cstats.values() == ["of size", "3", "from std::vector"]
assert allclose(rev, [2, 56.78, 2, 0, 2]) assert rev == approx([2, 56.78, 2, 0, 2], rel=1e-05)
assert cstats.alive() == 4 assert cstats.alive() == 4
del it del it

View File

@ -15,7 +15,7 @@ def test_smart_ptr(capture):
m.print_object_2(o) m.print_object_2(o)
m.print_object_3(o) m.print_object_3(o)
m.print_object_4(o) m.print_object_4(o)
assert capture == "MyObject1[{i}]\n".format(i=i) * 4 assert capture == f"MyObject1[{i}]\n" * 4
for i, o in enumerate( for i, o in enumerate(
[m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], start=4 [m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], start=4
@ -33,13 +33,11 @@ def test_smart_ptr(capture):
m.print_myobject1_4(o) m.print_myobject1_4(o)
times = 4 if isinstance(o, int) else 8 times = 4 if isinstance(o, int) else 8
assert capture == "MyObject1[{i}]\n".format(i=i) * times assert capture == f"MyObject1[{i}]\n" * times
cstats = ConstructorStats.get(m.MyObject1) cstats = ConstructorStats.get(m.MyObject1)
assert cstats.alive() == 0 assert cstats.alive() == 0
expected_values = ["MyObject1[{}]".format(i) for i in range(1, 7)] + [ expected_values = [f"MyObject1[{i}]" for i in range(1, 7)] + ["MyObject1[7]"] * 4
"MyObject1[7]"
] * 4
assert cstats.values() == expected_values assert cstats.values() == expected_values
assert cstats.default_constructions == 0 assert cstats.default_constructions == 0
assert cstats.copy_constructions == 0 assert cstats.copy_constructions == 0
@ -57,7 +55,7 @@ def test_smart_ptr(capture):
m.print_myobject2_2(o) m.print_myobject2_2(o)
m.print_myobject2_3(o) m.print_myobject2_3(o)
m.print_myobject2_4(o) m.print_myobject2_4(o)
assert capture == "MyObject2[{i}]\n".format(i=i) * 4 assert capture == f"MyObject2[{i}]\n" * 4
cstats = ConstructorStats.get(m.MyObject2) cstats = ConstructorStats.get(m.MyObject2)
assert cstats.alive() == 1 assert cstats.alive() == 1
@ -80,7 +78,7 @@ def test_smart_ptr(capture):
m.print_myobject3_2(o) m.print_myobject3_2(o)
m.print_myobject3_3(o) m.print_myobject3_3(o)
m.print_myobject3_4(o) m.print_myobject3_4(o)
assert capture == "MyObject3[{i}]\n".format(i=i) * 4 assert capture == f"MyObject3[{i}]\n" * 4
cstats = ConstructorStats.get(m.MyObject3) cstats = ConstructorStats.get(m.MyObject3)
assert cstats.alive() == 1 assert cstats.alive() == 1

View File

@ -13,7 +13,7 @@ def test_override(capture, msg):
self.data = "Hello world" self.data = "Hello world"
def run(self, value): def run(self, value):
print("ExtendedExampleVirt::run(%i), calling parent.." % value) print(f"ExtendedExampleVirt::run({value}), calling parent..")
return super().run(value + 1) return super().run(value + 1)
def run_bool(self): def run_bool(self):
@ -24,7 +24,7 @@ def test_override(capture, msg):
return "override1" return "override1"
def pure_virtual(self): def pure_virtual(self):
print("ExtendedExampleVirt::pure_virtual(): %s" % self.data) print(f"ExtendedExampleVirt::pure_virtual(): {self.data}")
class ExtendedExampleVirt2(ExtendedExampleVirt): class ExtendedExampleVirt2(ExtendedExampleVirt):
def __init__(self, state): def __init__(self, state):

View File

@ -92,7 +92,7 @@ endif()
# Use the Python interpreter to find the libs. # Use the Python interpreter to find the libs.
if(NOT PythonLibsNew_FIND_VERSION) if(NOT PythonLibsNew_FIND_VERSION)
set(PythonLibsNew_FIND_VERSION "3.5") set(PythonLibsNew_FIND_VERSION "3.6")
endif() endif()
find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required} find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required}

View File

@ -13,7 +13,7 @@ lib = sys.argv[1]
save = sys.argv[2] save = sys.argv[2]
if not os.path.exists(lib): if not os.path.exists(lib):
sys.exit("Error: requested file ({}) does not exist".format(lib)) sys.exit(f"Error: requested file ({lib}) does not exist")
libsize = os.path.getsize(lib) libsize = os.path.getsize(lib)
@ -28,7 +28,7 @@ if os.path.exists(save):
if change == 0: if change == 0:
print(" (no change)") print(" (no change)")
else: else:
print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize)) print(f" (change of {change:+} bytes = {change / oldsize:+.2%})")
else: else:
print() print()

View File

@ -193,7 +193,7 @@ Using ``find_package`` with version info is not recommended except for release v
.. code-block:: cmake .. code-block:: cmake
find_package(pybind11 CONFIG) find_package(pybind11 CONFIG)
find_package(pybind11 2.0 EXACT CONFIG REQUIRED) find_package(pybind11 2.9 EXACT CONFIG REQUIRED)
#]=============================================================================] #]=============================================================================]
@PACKAGE_INIT@ @PACKAGE_INIT@

View File

@ -32,7 +32,7 @@ if(NOT Python_FOUND AND NOT Python3_FOUND)
set(Python_ROOT_DIR "$ENV{pythonLocation}") set(Python_ROOT_DIR "$ENV{pythonLocation}")
endif() endif()
find_package(Python 3.5 REQUIRED COMPONENTS Interpreter Development ${_pybind11_quiet}) find_package(Python 3.6 REQUIRED COMPONENTS Interpreter Development ${_pybind11_quiet})
# If we are in submodule mode, export the Python targets to global targets. # If we are in submodule mode, export the Python targets to global targets.
# If this behavior is not desired, FindPython _before_ pybind11. # If this behavior is not desired, FindPython _before_ pybind11.

View File

@ -43,7 +43,7 @@ endif()
# A user can set versions manually too # A user can set versions manually too
set(Python_ADDITIONAL_VERSIONS set(Python_ADDITIONAL_VERSIONS
"3.11;3.10;3.9;3.8;3.7;3.6;3.5" "3.11;3.10;3.9;3.8;3.7;3.6"
CACHE INTERNAL "") CACHE INTERNAL "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Setup script for pybind11-global (in the sdist or in tools/setup_global.py in the repository) # Setup script for pybind11-global (in the sdist or in tools/setup_global.py in the repository)
# This package is targeted for easy use from CMake. # This package is targeted for easy use from CMake.

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Setup script (in the sdist or in tools/setup_main.py in the repository) # Setup script (in the sdist or in tools/setup_main.py in the repository)