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:
* You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3
only).
* You can use `virtualenv` (faster, from PyPI) instead of `venv`.
* You can select any name for your environment folder; if it contains "env" it
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+
* In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`.
FindPython uses `-DPython_ROOT_DIR=/path/to` or
@ -105,7 +104,7 @@ Tips:
### 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. Two selections are special - the generator, given with `-G`,
and the compiler, which is selected based on environment variables `CXX` and
@ -115,7 +114,7 @@ after the initial run.
The valid options are:
* `-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
* `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables 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
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
python3 -m venv venv

View File

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

View File

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

View File

@ -32,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary
dependency.
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
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
some of the new C++11 language features (specifically: tuples, lambda
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
goodies:
- Python 3.5+, and PyPy3 7.3 are supported with an implementation-agnostic
interface (pybind11 2.9 was the last version to support Python 2).
- 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 and 3.5).
- It is possible to bind C++11 lambda functions with captured
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
whenever possible to efficiently transfer custom data types.
- Its easy to expose the internal storage of custom data types through
Pythons buffer protocols. This is handy e.g. for fast conversion
- It's easy to expose the internal storage of custom data types through
Pythons' buffer protocols. This is handy e.g. for fast conversion
between C++ matrix classes like Eigen and NumPy without expensive
copy operations.
@ -119,7 +119,7 @@ goodies:
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)
2. GCC 4.8 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
``os.PathLike`` is converted to ``std::filesystem::path``, but this requires
Python 3.6 (for ``__fspath__`` support).
``os.PathLike`` is converted to ``std::filesystem::path``.

View File

@ -372,7 +372,7 @@ like so:
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:
.. code-block:: python
@ -395,19 +395,18 @@ argument annotations when registering the function:
m.def("f", [](int a, int 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
As of pybind11 2.9, a ``py::args`` argument implies that any following arguments
are keyword-only, as if ``py::kw_only()`` had been specified in the same
relative location of the argument list as the ``py::args`` argument. The
``py::kw_only()`` may be included to be explicit about this, but is not
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).
A ``py::args`` argument implies that any following arguments are keyword-only,
as if ``py::kw_only()`` had been specified in the same relative location of the
argument list as the ``py::args`` argument. The ``py::kw_only()`` may be
included to be explicit about this, but is not required.
.. 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
=========================

View File

@ -395,7 +395,7 @@ uses of ``py::array``:
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
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 = ""
for cl in range(nclasses):
decl += "class cl%03i;\n" % cl
decl += f"class cl{cl:03};\n"
decl += "\n"
for cl in range(nclasses):
decl += "class cl%03i {\n" % cl
decl += f"class {cl:03} {{\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):
ret = random.randint(0, nclasses - 1)
params = [random.randint(0, nclasses - 1) for i in range(nargs)]
decl += " cl%03i *fn_%03i(" % (ret, fn)
decl += ", ".join("cl%03i *" % p for p in params)
decl += f" cl{ret:03} *fn_{fn:03}("
decl += ", ".join(f"cl{p:03} *" for p in params)
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"
bindings += " ;\n"
@ -42,23 +42,20 @@ def generate_dummy_code_boost(nclasses=10):
bindings = ""
for cl in range(nclasses):
decl += "class cl%03i;\n" % cl
decl += f"class cl{cl:03};\n"
decl += "\n"
for cl in range(nclasses):
decl += "class cl%03i {\n" % cl
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):
ret = random.randint(0, nclasses - 1)
params = [random.randint(0, nclasses - 1) for i in range(nargs)]
decl += " cl%03i *fn_%03i(" % (ret, fn)
decl += ", ".join("cl%03i *" % p for p in params)
decl += f" cl{ret:03} *fn_{fn:03}("
decl += ", ".join(f"cl{p:03} *" for p in params)
decl += ");\n"
bindings += (
' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy<py::manage_new_object>())\n'
% (fn, cl, fn)
)
bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy<py::manage_new_object>())\n'
decl += "};\n\n"
bindings += " ;\n"

View File

@ -800,7 +800,7 @@ Packaging / building improvements:
`#2338 <https://github.com/pybind/pybind11/pull/2338>`_ and
`#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``.
* Generated config file is now portable to different Python/compiler/CMake

View File

@ -417,10 +417,10 @@ existing targets instead:
.. code-block:: cmake
cmake_minimum_required(VERSION 3.15...3.19)
cmake_minimum_required(VERSION 3.15...3.22)
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)
# or add_subdirectory(pybind11)
@ -433,9 +433,8 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``.
.. warning::
If you use FindPython2 and FindPython3 to dual-target Python, use the
individual targets listed below, and avoid targets that directly include
Python parts.
If you use FindPython to multi-target Python versions, use the individual
targets listed below, and avoid targets that directly include Python parts.
There are `many ways to hint or force a discovery of a specific Python
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"])
retcode = subprocess.call(["doxygen"], cwd=app.confdir)
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:
sys.stderr.write("doxygen execution failed: {}\n".format(e))
sys.stderr.write(f"doxygen execution failed: {e}\n")
def prepare(app):

View File

@ -145,7 +145,7 @@ using C++14 template metaprogramming.
.. _`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

View File

@ -1,5 +1,5 @@
breathe==4.31.0
sphinx==3.5.4
breathe==4.32.0
sphinx==4.4.0
sphinx_rtd_theme==1.0.0
sphinxcontrib-moderncmakedomain==3.19
sphinxcontrib-svg2pdfconverter==1.1.1
sphinxcontrib-moderncmakedomain==3.21.4
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>)
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.

View File

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

View File

@ -18,17 +18,6 @@
#include <datetime.h>
#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(detail)

View File

@ -211,8 +211,8 @@
#endif
#include <Python.h>
#if PY_VERSION_HEX < 0x030500f0
# error "PYTHON 2 IS NO LONGER SUPPORTED. pybind11 v2.9 was the last to support Python 2."
#if PY_VERSION_HEX < 0x03060000
# error "PYTHON < 3.6 IS UNSUPPORTED. pybind11 v2.9 was the last to support Python 2 and 3.5."
#endif
#include <frameobject.h>
#include <pythread.h>
@ -301,15 +301,6 @@
extern "C" PYBIND11_MAYBE_UNUSED 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_STRINGIFY(x) #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
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.. */
take_ownership,
@ -454,7 +445,7 @@ enum class return_value_policy : uint8_t {
move,
/** 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
the C++ side deletes an object that is still referenced and used by
Python. */
@ -463,7 +454,7 @@ enum class return_value_policy : uint8_t {
/** This policy only applies to methods and properties. It references the
object without taking ownership similar to the above
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).
pybind11 then couples the lifetime of the parent to the child via a
reference relationship that ensures that the parent cannot be garbage

View File

@ -439,10 +439,11 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
// instance_registered)
// 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
// they default to using pymalloc, which is designed to be efficient for small allocations
// like the one we're doing here; in earlier versions (and for larger allocations) they are
// just wrappers around malloc.
// in particular, need to be 0). Use Python's memory allocation
// functions: Python is using pymalloc, which is designed to be
// efficient for small allocations like the one we're doing here;
// 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 *));
if (!nonsimple.values_and_holders) {
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() {
#if defined(PYPY_VERSION)
return PyThreadState_GET();
#elif PY_VERSION_HEX < 0x03050200
return (PyThreadState *) _PyThreadState_Current.value;
#else
return _PyThreadState_UncheckedGet();
#endif

View File

@ -1213,7 +1213,7 @@ public:
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
Create a new top-level module that can be used as the main module of a C extension.
@ -1241,8 +1241,8 @@ public:
pybind11_fail("Internal error in module_::create_extension_module()");
}
// TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when
// returned from PyInit_...
// For Python 2, reinterpret_borrow is correct.
// returned from PyInit_...
// For Python 2, reinterpret_borrow was correct.
return reinterpret_borrow<module_>(m);
}
};

View File

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

View File

@ -2,7 +2,7 @@ import nox
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)

View File

@ -1,7 +1,7 @@
import sys
if sys.version_info < (3, 5):
msg = "pybind11 does not support Python < 3.5. 2.9 was the last release supporting older Pythons."
if sys.version_info < (3, 6):
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
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])
desired_macos = (10, 9) if level < 17 else (10, 14)
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]
ldflags += [macosx_min]
@ -322,9 +322,9 @@ def intree_extensions(
if not exts:
msg = (
"path {path} is not a child of any of the directories listed "
"in 'package_dir' ({package_dir})"
).format(path=path, package_dir=package_dir)
f"path {path} is not a child of any of the directories listed "
f"in 'package_dir' ({package_dir})"
)
raise ValueError(msg)
return exts
@ -419,7 +419,7 @@ class ParallelCompile:
self.default = default
self.max = max
self.needs_recompile = needs_recompile
self._old = [] # type: List[CCompilerMethod]
self._old: List[CCompilerMethod] = []
def function(self) -> CCompilerMethod:
"""

View File

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

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
@ -41,12 +41,10 @@ def build_expected_version_hex(matches: Dict[str, str]) -> str:
serial = int(level_serial[len(level) :])
break
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)
version_hex_str = "{:02x}{:02x}{:02x}{}{:x}".format(
major, minor, patch, level[:1], serial
)
return "0x{}".format(version_hex_str.upper())
version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
return f"0x{version_hex_str.upper()}"
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
@ -66,7 +64,7 @@ to_src = (
# 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")
exec(code, loc)
version = loc["__version__"]
@ -75,17 +73,13 @@ version = loc["__version__"]
matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
if version != cpp_version:
msg = "Python version {} does not match C++ version {}!".format(
version, cpp_version
)
msg = f"Python version {version} does not match C++ version {cpp_version}!"
raise RuntimeError(msg)
version_hex = matches.get("HEX", "MISSING")
exp_version_hex = build_expected_version_hex(matches)
if version_hex != exp_version_hex:
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
version_hex, exp_version_hex
)
msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
raise RuntimeError(msg)

View File

@ -145,9 +145,9 @@ def test_build_sdist(monkeypatch, tmpdir):
contents = f.read().decode("utf8")
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 |= {"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/requires.txt")
assert simpler == files
@ -200,9 +200,9 @@ def test_build_global_dist(monkeypatch, tmpdir):
) as f:
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 |= {"pybind11_global{}".format(n) for n in local_sdist_files}
files |= {f"pybind11_global{n}" for n in local_sdist_files}
assert simpler == files
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")
files = {"pybind11/{}".format(n) for n in all_files}
files = {f"pybind11/{n}" for n in all_files}
files |= {
"dist-info/LICENSE",
"dist-info/METADATA",
@ -241,9 +241,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
names = z.namelist()
trimmed = {n for n in names if "dist-info" not in n}
trimmed |= {
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
}
trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n}
assert files == trimmed
@ -257,8 +255,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl")
files = {"data/data/{}".format(n) for n in src_files}
files |= {"data/headers/{}".format(n[8:]) for n in headers}
files = {f"data/data/{n}" for n in src_files}
files |= {f"data/headers/{n[8:]}" for n in headers}
files |= {
"dist-info/LICENSE",
"dist-info/METADATA",

View File

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

View File

@ -1,12 +1,15 @@
[pytest]
minversion = 3.1
minversion = 3.10
norecursedirs = test_* extra_*
xfail_strict = True
addopts =
# show summary of skipped tests
-rs
# show summary of tests
-ra
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
--capture=sys
# Show local info when a failure occurs
--showlocals
log_cli_level = info
filterwarnings =
# make warnings into errors but ignore certain third-party extension issues
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.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.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11"
py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
pytest==4.6.9; python_version<"3.5"
pytest==6.1.2; python_version=="3.5"
pytest==6.2.4; python_version>="3.6"
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
pytest==7.0.0
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.6" and python_version<"3.10"
scipy==1.5.4; platform_python_implementation!="PyPy" 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"""
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"

View File

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

View File

@ -100,7 +100,7 @@ SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif(
)
def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
if tz is not None:
monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz))
monkeypatch.setenv("TZ", f"/usr/share/zoneinfo/{tz}")
# Roundtrip the time
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 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_1(ref) == ref.diagonal(1))
for i in range(-5, 7):
assert np.all(
m.diagonal_n(ref, i) == ref.diagonal(i)
), "m.diagonal_n({})".format(i)
assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), f"m.diagonal_n({i})"
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:])
@ -236,7 +234,7 @@ def test_eigen_ref_to_python():
mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]]))
assert np.all(
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):

View File

@ -88,7 +88,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
dec = pytest.mark.filterwarnings("ignore::pytest.{}".format(unraisable))
dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
return dec(f)
else:
return f

View File

@ -18,9 +18,7 @@ def test_dtypes():
assert check.numpy == check.pybind11, check
if check.numpy.num != check.pybind11.num:
print(
"NOTE: typenum mismatch for {}: {} != {}".format(
check, check.numpy.num, check.pybind11.num
)
f"NOTE: typenum mismatch for {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:
with pytest.raises(IndexError) as excinfo:
func(arr, *([0] * dim))
assert str(excinfo.value) == "index dimension mismatch: {} (ndim = 2)".format(
dim
)
assert str(excinfo.value) == f"index dimension mismatch: {dim} (ndim = 2)"
def test_at(arr):
@ -192,8 +188,6 @@ def test_make_empty_shaped_array():
def test_wrap():
def assert_references(a, b, base=None):
from distutils.version import LooseVersion
if base is None:
base = a
assert a is not b
@ -204,7 +198,8 @@ def test_wrap():
assert a.flags.f_contiguous == b.flags.f_contiguous
assert a.flags.writeable == b.flags.writeable
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
else:
assert a.flags.updateifcopy == b.flags.updateifcopy

View File

@ -14,7 +14,7 @@ def simple_dtype():
return np.dtype(
{
"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)],
}
)
@ -125,7 +125,7 @@ def test_dtype(simple_dtype):
assert [x.replace(" ", "") for x in m.print_dtypes()] == [
simple_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_nested_fmt(),
"[('a','S3'),('b','S3')]",

View File

@ -12,7 +12,7 @@ def test_string_list():
assert lst.back() == "Element 2"
for i, k in enumerate(lst, start=1):
assert k == "Element {}".format(i)
assert k == f"Element {i}"
lst.pop_back()
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]:
with pytest.raises(TypeError) as excinfo:
m.nonconverting_constructor(t, v, move)
expected_error = "Object of type '{}' is not an instance of '{}'".format(
type(v).__name__, t
expected_error = (
f"Object of type '{type(v).__name__}' is not an instance of '{t}'"
)
assert str(excinfo.value) == expected_error

View File

@ -1,20 +1,10 @@
import pytest
from pytest import approx
from pybind11_tests import ConstructorStats
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():
assert m.make_forward_slice_size_t() == slice(0, -1, 1)
assert m.make_reversed_slice_object() == slice(None, None, -1)
@ -117,7 +107,8 @@ def test_sequence():
assert 12.34 not in s
s[0], s[3] = 12.34, 56.78
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)
assert cstats.values() == ["of size", "5"]
@ -132,14 +123,14 @@ def test_sequence():
assert cstats.values() == ["of size", "0"]
expected = [0, 56.78, 0, 0, 12.34]
assert allclose(rev, expected)
assert allclose(rev2, expected)
assert rev == approx(expected, rel=1e-05)
assert rev2 == approx(expected, rel=1e-05)
assert rev == rev2
rev[0::2] = m.Sequence([2.0, 2.0, 2.0])
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
del it

View File

@ -15,7 +15,7 @@ def test_smart_ptr(capture):
m.print_object_2(o)
m.print_object_3(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(
[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)
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)
assert cstats.alive() == 0
expected_values = ["MyObject1[{}]".format(i) for i in range(1, 7)] + [
"MyObject1[7]"
] * 4
expected_values = [f"MyObject1[{i}]" for i in range(1, 7)] + ["MyObject1[7]"] * 4
assert cstats.values() == expected_values
assert cstats.default_constructions == 0
assert cstats.copy_constructions == 0
@ -57,7 +55,7 @@ def test_smart_ptr(capture):
m.print_myobject2_2(o)
m.print_myobject2_3(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)
assert cstats.alive() == 1
@ -80,7 +78,7 @@ def test_smart_ptr(capture):
m.print_myobject3_2(o)
m.print_myobject3_3(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)
assert cstats.alive() == 1

View File

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

View File

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

View File

@ -13,7 +13,7 @@ lib = sys.argv[1]
save = sys.argv[2]
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)
@ -28,7 +28,7 @@ if os.path.exists(save):
if change == 0:
print(" (no change)")
else:
print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize))
print(f" (change of {change:+} bytes = {change / oldsize:+.2%})")
else:
print()

View File

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

View File

@ -32,7 +32,7 @@ if(NOT Python_FOUND AND NOT Python3_FOUND)
set(Python_ROOT_DIR "$ENV{pythonLocation}")
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 this behavior is not desired, FindPython _before_ pybind11.

View File

@ -43,7 +43,7 @@ endif()
# A user can set versions manually too
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 "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
# 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.

View File

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