From 46dcd9bc75cd09a24633f2d02eb0adb52e1717e4 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 10 Feb 2022 17:03:33 -0500 Subject: [PATCH 001/122] fix: minor CMake warning fix for unused variable (#3718) --- tools/FindCatch.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/FindCatch.cmake b/tools/FindCatch.cmake index 4d6bffcf6..57bba58b3 100644 --- a/tools/FindCatch.cmake +++ b/tools/FindCatch.cmake @@ -9,6 +9,8 @@ # CATCH_INCLUDE_DIR - path to catch.hpp # CATCH_VERSION - version number +option(DOWNLOAD_CATCH "Download catch2 if not found") + if(NOT Catch_FIND_VERSION) message(FATAL_ERROR "A version number must be specified.") elseif(Catch_FIND_REQUIRED) From 6493f496e30c80f004772c906370c8f4db94b6ec Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 10 Feb 2022 18:28:08 -0800 Subject: [PATCH 002/122] Python 2 removal part 1: tests (C++ code is intentionally ~untouched) (#3688) * `#error BYE_BYE_GOLDEN_SNAKE` * Removing everything related to 2.7 from ci.yml * Commenting-out Centos7 * Removing `PYTHON: 27` from .appveyor.yml * "PY2" removal, mainly from tests. C++ code is not touched. * Systematic removal of `u` prefix from `u"..."` and `u'...'` literals. Collateral cleanup of a couple minor other things. * Cleaning up around case-insensitive hits for `[^a-z]py.*2` in tests/. * Removing obsolete Python 2 mention in compiling.rst * Proper `#error` for Python 2. * Using PY_VERSION_HEX to guard `#error "PYTHON 2 IS NO LONGER SUPPORTED.` * chore: bump pre-commit * style: run pre-commit for pyupgrade 3+ * tests: use sys.version_info, not PY * chore: more Python 2 removal * Uncommenting Centos7 block (PR #3691 showed that it is working again). * Update pre-commit hooks * Fix pre-commit hook * refactor: remove Python 2 from CMake * refactor: remove Python 2 from setup code * refactor: simplify, better static typing * feat: fail with nice messages * refactor: drop Python 2 C++ code * docs: cleanup for Python 3 * revert: intree revert: intree * docs: minor touchup to py2 statement Co-authored-by: Henry Schreiner Co-authored-by: Aaron Gokaslan --- .appveyor.yml | 2 - .github/workflows/ci.yml | 27 --- .github/workflows/pip.yml | 8 +- .pre-commit-config.yaml | 33 ++-- MANIFEST.in | 1 - README.rst | 6 +- docs/Doxyfile | 3 +- docs/advanced/cast/strings.rst | 19 +- docs/advanced/classes.rst | 33 ++-- docs/advanced/exceptions.rst | 6 +- docs/advanced/pycpp/numpy.rst | 8 - docs/basics.rst | 6 +- docs/benchmark.py | 3 +- docs/classes.rst | 14 +- docs/compiling.rst | 14 +- docs/conf.py | 1 - docs/faq.rst | 26 +-- include/pybind11/cast.h | 44 +---- include/pybind11/detail/class.h | 22 +-- include/pybind11/detail/common.h | 121 ++++-------- include/pybind11/detail/internals.h | 13 +- include/pybind11/detail/type_caster_base.h | 13 -- include/pybind11/embed.h | 55 +----- include/pybind11/eval.h | 32 +--- include/pybind11/numpy.h | 4 - include/pybind11/operators.h | 19 -- include/pybind11/pybind11.h | 41 +---- include/pybind11/pytypes.h | 48 +---- noxfile.py | 2 +- pybind11/__init__.py | 7 +- pybind11/__main__.py | 9 +- pybind11/_version.py | 4 +- pybind11/_version.pyi | 6 - pybind11/commands.py | 7 +- pybind11/setup_helpers.py | 203 ++++++++++---------- pybind11/setup_helpers.pyi | 63 ------- pyproject.toml | 21 +-- setup.cfg | 10 +- setup.py | 126 ++++++------- tests/CMakeLists.txt | 16 -- tests/conftest.py | 16 +- tests/cross_module_gil_utils.cpp | 23 +-- tests/env.py | 5 - tests/extra_python_package/test_files.py | 3 - tests/extra_setuptools/test_setuphelper.py | 12 +- tests/test_async.py | 1 - tests/test_buffers.py | 12 +- tests/test_builtin_casters.cpp | 6 +- tests/test_builtin_casters.py | 205 +++++++++------------ tests/test_call_policies.py | 1 - tests/test_callbacks.py | 1 - tests/test_chrono.py | 1 - tests/test_class.py | 10 +- tests/test_cmake_build/test.py | 4 +- tests/test_const_name.py | 4 +- tests/test_constants_and_functions.py | 1 - tests/test_copy_move.py | 1 - tests/test_custom_type_casters.py | 7 +- tests/test_custom_type_setup.py | 2 - tests/test_docstring_options.py | 1 - tests/test_eigen.py | 9 +- tests/test_embed/test_interpreter.cpp | 5 - tests/test_embed/test_interpreter.py | 3 +- tests/test_embed/test_trampoline.py | 2 - tests/test_enum.py | 12 +- tests/test_eval.py | 3 +- tests/test_eval_call.py | 1 - tests/test_exceptions.cpp | 3 - tests/test_exceptions.py | 17 +- tests/test_factory_constructors.py | 10 +- tests/test_gil_scoped.py | 1 - tests/test_iostream.py | 40 +--- tests/test_kwargs_and_defaults.py | 11 +- tests/test_local_bindings.py | 3 +- tests/test_methods_and_attributes.py | 18 +- tests/test_modules.py | 1 - tests/test_multiple_inheritance.py | 11 +- tests/test_numpy_array.py | 1 - tests/test_numpy_dtypes.py | 1 - tests/test_numpy_vectorize.py | 1 - tests/test_opaque_types.py | 3 +- tests/test_operator_overloading.py | 5 +- tests/test_pickling.py | 8 +- tests/test_pytypes.cpp | 4 - tests/test_pytypes.py | 143 +++++--------- tests/test_sequences_and_iterators.py | 1 - tests/test_smart_ptr.py | 1 - tests/test_stl.py | 5 +- tests/test_stl_binders.py | 17 +- tests/test_tagbased_polymorphic.py | 1 - tests/test_thread.py | 6 +- tests/test_union.py | 1 - tests/test_virtual_functions.py | 21 +-- tools/FindPythonLibsNew.cmake | 2 +- tools/libsize.py | 3 - tools/make_changelog.py | 1 - tools/pybind11Common.cmake | 26 --- tools/pybind11Config.cmake.in | 2 - tools/pybind11NewTools.cmake | 32 +--- tools/pybind11Tools.cmake | 9 +- tools/setup_global.py.in | 5 - tools/setup_main.py.in | 2 +- 102 files changed, 529 insertions(+), 1334 deletions(-) delete mode 100644 pybind11/_version.pyi delete mode 100644 pybind11/setup_helpers.pyi diff --git a/.appveyor.yml b/.appveyor.yml index 85445d41a..f3bc9cd49 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -11,8 +11,6 @@ environment: matrix: - PYTHON: 36 CONFIG: Debug - - PYTHON: 27 - CONFIG: Debug install: - ps: | $env:CMAKE_GENERATOR = "Visual Studio 14 2015" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b52c30618..07580decd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,6 @@ jobs: matrix: runs-on: [ubuntu-latest, windows-2022, macos-latest] python: - - '2.7' - '3.5' - '3.6' - '3.9' @@ -49,13 +48,9 @@ jobs: python: '3.6' args: > -DPYBIND11_FINDPYTHON=ON - - runs-on: macos-latest - python: 'pypy-2.7' # Inject a couple Windows 2019 runs - runs-on: windows-2019 python: '3.9' - - runs-on: windows-2019 - python: '2.7' name: "🐍 ${{ matrix.python }} β€’ ${{ matrix.runs-on }} β€’ x64 ${{ matrix.args }}" runs-on: ${{ matrix.runs-on }} @@ -168,22 +163,6 @@ jobs: - name: Interface test run: cmake --build build2 --target test_cmake_build - # Eventually Microsoft might have an action for setting up - # MSVC, but for now, this action works: - - name: Prepare compiler environment for Windows 🐍 2.7 - if: matrix.python == 2.7 && runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1.10.0 - with: - arch: x64 - - # This makes two environment variables available in the following step(s) - - name: Set Windows 🐍 2.7 environment variables - if: matrix.python == 2.7 && runner.os == 'Windows' - shell: bash - run: | - echo "DISTUTILS_USE_SDK=1" >> $GITHUB_ENV - echo "MSSdk=1" >> $GITHUB_ENV - # This makes sure the setup_helpers module can build packages using # setuptools - name: Setuptools helpers test @@ -784,7 +763,6 @@ jobs: fail-fast: false matrix: python: - - 2.7 - 3.6 - 3.7 # todo: check/cpptest does not support 3.8+ yet @@ -832,17 +810,12 @@ jobs: fail-fast: false matrix: python: - - 2.7 - 3.5 - 3.7 std: - 14 include: - - python: 2.7 - std: 17 - args: > - -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" - python: 3.7 std: 17 args: > diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index f74b79f0c..7a5443d14 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -17,19 +17,19 @@ env: jobs: # This builds the sdists and wheels and makes sure the files are exactly as - # expected. Using Windows and Python 2.7, since that is often the most + # expected. Using Windows and Python 3.6, since that is often the most # challenging matrix element. test-packaging: - name: 🐍 2.7 β€’ πŸ“¦ tests β€’ windows-latest + name: 🐍 3.6 β€’ πŸ“¦ tests β€’ windows-latest runs-on: windows-latest steps: - uses: actions/checkout@v2 - - name: Setup 🐍 2.7 + - name: Setup 🐍 3.6 uses: actions/setup-python@v2 with: - python-version: 2.7 + python-version: 3.6 - name: Prepare env run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c31cab71c..6d44d5e76 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,14 +29,15 @@ repos: - id: mixed-line-ending - id: requirements-txt-fixer - id: trailing-whitespace - - id: fix-encoding-pragma - exclude: ^noxfile.py$ +# Upgrade old Python syntax - repo: https://github.com/asottile/pyupgrade rev: v2.31.0 hooks: - id: pyupgrade + args: [--py3-plus] +# Nicely sort includes - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: @@ -44,20 +45,21 @@ repos: # Black, the code formatter, natively supports pre-commit - repo: https://github.com/psf/black - rev: 21.12b0 # 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.0 + rev: v1.12.1 hooks: - id: blacken-docs additional_dependencies: - - black==21.12b0 # keep in sync with black hook + - black==22.1.0 # keep in sync with black hook # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.10 + rev: v1.1.11 hooks: - id: remove-tabs @@ -67,12 +69,15 @@ repos: hooks: - id: pycln +# Checking for common mistakes - repo: https://github.com/pre-commit/pygrep-hooks 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: rst-backticks - id: rst-directive-colons - id: rst-inline-touching-normal @@ -87,6 +92,7 @@ repos: - pep8-naming exclude: ^(docs/.*|tools/.*)$ +# Automatically remove noqa that are not used - repo: https://github.com/asottile/yesqa rev: v1.3.0 hooks: @@ -107,9 +113,9 @@ repos: rev: v0.931 hooks: - id: mypy - # Running per-file misbehaves a bit, so just run on all files, it's fast - pass_filenames: false - additional_dependencies: [typed_ast] + args: [--show-error-codes] + exclude: ^(tests|docs)/ + additional_dependencies: [nox, rich] # Checks the manifest for missing files (native support) - repo: https://github.com/mgedmin/check-manifest @@ -120,6 +126,7 @@ repos: stages: [manual] additional_dependencies: [cmake, ninja] +# Check for spelling - repo: https://github.com/codespell-project/codespell rev: v2.1.0 hooks: @@ -127,20 +134,22 @@ repos: exclude: ".supp$" args: ["-L", "nd,ot,thist"] +# Check for common shell mistakes - repo: https://github.com/shellcheck-py/shellcheck-py - rev: v0.8.0.3 + rev: v0.8.0.4 hooks: - id: shellcheck -# The original pybind11 checks for a few C++ style items +# Disallow some common capitalization mistakes - repo: local hooks: - id: disallow-caps name: Disallow improper capitalization language: pygrep entry: PyBind|Numpy|Cmake|CCache|PyTest - exclude: .pre-commit-config.yaml + exclude: ^\.pre-commit-config.yaml$ +# Clang format the codebase automatically - repo: https://github.com/pre-commit/mirrors-clang-format rev: "v13.0.0" hooks: diff --git a/MANIFEST.in b/MANIFEST.in index aed183e87..033303a74 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,5 @@ recursive-include pybind11/include/pybind11 *.h recursive-include pybind11 *.py recursive-include pybind11 py.typed -recursive-include pybind11 *.pyi include pybind11/share/cmake/pybind11/*.cmake include LICENSE README.rst pyproject.toml setup.py setup.cfg diff --git a/README.rst b/README.rst index 45c4af5a6..6398db4f0 100644 --- a/README.rst +++ b/README.rst @@ -34,7 +34,7 @@ dependency. Think of this library as a tiny self-contained version of Boost.Python with everything stripped away that isn’t relevant for binding generation. Without comments, the core header files only require ~4K -lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++ +lines of code and depend on Python (3.5+, 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 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an - implementation-agnostic interface. +- 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). - It is possible to bind C++11 lambda functions with captured variables. The lambda capture data is stored inside the resulting diff --git a/docs/Doxyfile b/docs/Doxyfile index 62c267556..09138db36 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -18,5 +18,4 @@ ALIASES += "endrst=\endverbatim" QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = NO -PREDEFINED = PY_MAJOR_VERSION=3 \ - PYBIND11_NOINLINE +PREDEFINED = PYBIND11_NOINLINE diff --git a/docs/advanced/cast/strings.rst b/docs/advanced/cast/strings.rst index cfd7e7b7a..e246c5219 100644 --- a/docs/advanced/cast/strings.rst +++ b/docs/advanced/cast/strings.rst @@ -1,14 +1,6 @@ Strings, bytes and Unicode conversions ###################################### -.. note:: - - This section discusses string handling in terms of Python 3 strings. For - Python 2.7, replace all occurrences of ``str`` with ``unicode`` and - ``bytes`` with ``str``. Python 2.7 users may find it best to use ``from - __future__ import unicode_literals`` to avoid unintentionally using ``str`` - instead of ``unicode``. - Passing Python strings to C++ ============================= @@ -58,9 +50,9 @@ Passing bytes to C++ -------------------- A Python ``bytes`` object will be passed to C++ functions that accept -``std::string`` or ``char*`` *without* conversion. On Python 3, in order to -make a function *only* accept ``bytes`` (and not ``str``), declare it as taking -a ``py::bytes`` argument. +``std::string`` or ``char*`` *without* conversion. In order to make a function +*only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes`` +argument. Returning C++ strings to Python @@ -204,11 +196,6 @@ decoded to Python ``str``. } ); -.. warning:: - - Wide character strings may not work as described on Python 2.7 or Python - 3.3 compiled with ``--enable-unicode=ucs2``. - Strings in multibyte encodings such as Shift-JIS must transcoded to a UTF-8/16/32 before being returned to Python. diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index f3339336d..3e8ba7385 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -133,14 +133,14 @@ a virtual method call. >>> from example import * >>> d = Dog() >>> call_go(d) - u'woof! woof! woof! ' + 'woof! woof! woof! ' >>> class Cat(Animal): ... def go(self, n_times): ... return "meow! " * n_times ... >>> c = Cat() >>> call_go(c) - u'meow! meow! meow! ' + 'meow! meow! meow! ' If you are defining a custom constructor in a derived Python class, you *must* ensure that you explicitly call the bound C++ constructor using ``__init__``, @@ -813,26 +813,21 @@ An instance can now be pickled as follows: .. code-block:: python - try: - import cPickle as pickle # Use cPickle on Python 2.7 - except ImportError: - import pickle + import pickle p = Pickleable("test_value") p.setExtra(15) - data = pickle.dumps(p, 2) + data = pickle.dumps(p) .. note:: - Note that only the cPickle module is supported on Python 2.7. - - The second argument to ``dumps`` is also crucial: it selects the pickle - protocol version 2, since the older version 1 is not supported. Newer - versions are also fineβ€”for instance, specify ``-1`` to always use the - latest available version. Beware: failure to follow these instructions - will cause important pybind11 memory allocation routines to be skipped - during unpickling, which will likely lead to memory corruption and/or - segmentation faults. + If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are + not supported. Newer versions are also fine; for instance, specify ``-1`` to + always use the latest available version. Beware: failure to follow these + instructions will cause important pybind11 memory allocation routines to be + skipped during unpickling, which will likely lead to memory corruption + and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and + version 4 for Python 3.8+. .. seealso:: @@ -849,11 +844,9 @@ Python normally uses references in assignments. Sometimes a real copy is needed to prevent changing all copies. The ``copy`` module [#f5]_ provides these capabilities. -On Python 3, a class with pickle support is automatically also (deep)copy +A class with pickle support is automatically also (deep)copy compatible. However, performance can be improved by adding custom -``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods -are mandatory for (deep)copy compatibility, because pybind11 only supports -cPickle. +``__copy__`` and ``__deepcopy__`` methods. For simple classes (deep)copy can be enabled by using the copy constructor, which should look as follows: diff --git a/docs/advanced/exceptions.rst b/docs/advanced/exceptions.rst index 7cd8447b9..2211caf5d 100644 --- a/docs/advanced/exceptions.rst +++ b/docs/advanced/exceptions.rst @@ -328,8 +328,8 @@ an invalid state. Chaining exceptions ('raise from') ================================== -In Python 3.3 a mechanism for indicating that exceptions were caused by other -exceptions was introduced: +Python has a mechanism for indicating that exceptions were caused by other +exceptions: .. code-block:: py @@ -340,7 +340,7 @@ exceptions was introduced: To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It sets the current python error indicator, so to continue propagating the exception -you should ``throw py::error_already_set()`` (Python 3 only). +you should ``throw py::error_already_set()``. .. code-block:: cpp diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index 30daeefff..dbc48440a 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -398,8 +398,6 @@ Ellipsis Python 3 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. -In Python 2, the syntactic sugar ``...`` is not available, but the singleton -``Ellipsis`` (of type ``ellipsis``) can still be used directly. .. code-block:: python @@ -414,8 +412,6 @@ operation on the C++ side: py::array a = /* A NumPy array */; py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; -.. versionchanged:: 2.6 - ``py::ellipsis()`` is now also available in Python 2. Memory view =========== @@ -455,9 +451,5 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer: ); }) -.. note:: - - ``memoryview::from_memory`` is not available in Python 2. - .. versionchanged:: 2.6 ``memoryview::from_memory`` added. diff --git a/docs/basics.rst b/docs/basics.rst index e0479b298..d40992d38 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -166,12 +166,12 @@ load and execute the example: .. code-block:: pycon $ python - Python 2.7.10 (default, Aug 22 2015, 20:33:39) - [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin + Python 3.9.10 (main, Jan 15 2022, 11:48:04) + [Clang 13.0.0 (clang-1300.0.29.3)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import example >>> example.add(1, 2) - 3L + 3 >>> .. _keyword_args: diff --git a/docs/benchmark.py b/docs/benchmark.py index f19079367..a59534292 100644 --- a/docs/benchmark.py +++ b/docs/benchmark.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import datetime as dt import os import random @@ -75,7 +74,7 @@ def generate_dummy_code_boost(nclasses=10): for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: print("{") for i in range(0, 10): - nclasses = 2 ** i + nclasses = 2**i with open("test.cpp", "w") as f: f.write(codegen(nclasses)) n1 = dt.datetime.now() diff --git a/docs/classes.rst b/docs/classes.rst index 13fa8b538..b833bc4ba 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -48,10 +48,10 @@ interactive Python session demonstrating this example is shown below: >>> print(p) >>> p.getName() - u'Molly' + 'Molly' >>> p.setName("Charly") >>> p.getName() - u'Charly' + 'Charly' .. seealso:: @@ -124,10 +124,10 @@ This makes it possible to write >>> p = example.Pet("Molly") >>> p.name - u'Molly' + 'Molly' >>> p.name = "Charly" >>> p.name - u'Charly' + 'Charly' Now suppose that ``Pet::name`` was a private internal variable that can only be accessed via setters and getters. @@ -282,9 +282,9 @@ expose fields and methods of both types: >>> p = example.Dog("Molly") >>> p.name - u'Molly' + 'Molly' >>> p.bark() - u'woof!' + 'woof!' The C++ classes defined above are regular non-polymorphic types with an inheritance relationship. This is reflected in Python: @@ -332,7 +332,7 @@ will automatically recognize this: >>> type(p) PolymorphicDog # automatically downcast >>> p.bark() - u'woof!' + 'woof!' Given a pointer to a polymorphic base, pybind11 performs automatic downcasting to the actual derived type. Note that this goes beyond the usual situation in diff --git a/docs/compiling.rst b/docs/compiling.rst index 25adb9fcf..f7303fd23 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -462,11 +462,8 @@ available in all modes. The targets provided are: ``pybind11::headers`` Just the pybind11 headers and minimum compile requirements - ``pybind11::python2_no_register`` - Quiets the warning/error when mixing C++14 or higher and Python 2 - ``pybind11::pybind11`` - Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only) + Python headers + ``pybind11::headers`` ``pybind11::python_link_helper`` Just the "linking" part of pybind11:module @@ -577,21 +574,12 @@ On Linux, you can compile an example such as the one given in $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) -The flags given here assume that you're using Python 3. For Python 2, just -change the executable appropriately (to ``python`` or ``python2``). - The ``python3 -m pybind11 --includes`` command fetches the include paths for both pybind11 and Python headers. This assumes that pybind11 has been installed using ``pip`` or ``conda``. If it hasn't, you can also manually specify ``-I /include`` together with the Python includes path ``python3-config --includes``. -Note that Python 2.7 modules don't use a special suffix, so you should simply -use ``example.so`` instead of ``example$(python3-config --extension-suffix)``. -Besides, the ``--extension-suffix`` option may or may not be available, depending -on the distribution; in the latter case, the module extension can be manually -set to ``.so``. - On macOS: the build command is almost the same but it also requires passing the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when building the module: diff --git a/docs/conf.py b/docs/conf.py index 092e274e0..99f3be349 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # pybind11 documentation build configuration file, created by # sphinx-quickstart on Sun Oct 11 19:23:48 2015. diff --git a/docs/faq.rst b/docs/faq.rst index e2f477b1f..69c3442e3 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -8,9 +8,7 @@ Frequently asked questions filename of the extension library (without suffixes such as ``.so``). 2. If the above did not fix the issue, you are likely using an incompatible -version of Python (for instance, the extension library was compiled against -Python 2, while the interpreter is running on top of some version of Python -3, or vice versa). +version of Python that does not match what you compiled with. "Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" ======================================================================== @@ -289,27 +287,7 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses the CMake Python detection in a system with several Python versions installed. This difference may cause inconsistencies and errors if *both* mechanisms are -used in the same project. Consider the following CMake code executed in a -system with Python 2.7 and 3.x installed: - -.. code-block:: cmake - - find_package(PythonInterp) - find_package(PythonLibs) - find_package(pybind11) - -It will detect Python 2.7 and pybind11 will pick it as well. - -In contrast this code: - -.. code-block:: cmake - - find_package(pybind11) - find_package(PythonInterp) - find_package(PythonLibs) - -will detect Python 3.x for pybind11 and may crash on -``find_package(PythonLibs)`` afterwards. +used in the same project. There are three possible solutions: diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 2622e5819..b12559ac8 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -325,7 +325,7 @@ public: res = 0; // None is implicitly converted to False } #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + // On PyPy, check that "__bool__" attr exists else if (hasattr(src, PYBIND11_BOOL_ATTR)) { res = PyObject_IsTrue(src.ptr()); } @@ -375,37 +375,16 @@ struct string_caster { static constexpr size_t UTF_N = 8 * sizeof(CharT); bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif handle load_src = src; if (!src) { return false; } if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 return load_bytes(load_src); -#else - if (std::is_same::value) { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { - PyErr_Clear(); - return false; - } - load_src = temp; -#endif } -#if PY_VERSION_HEX >= 0x03030000 - // On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes` - // object by using `PyUnicode_AsUTF8AndSize`. + // For UTF-8 we avoid the need for a temporary `bytes` object by using + // `PyUnicode_AsUTF8AndSize`. if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) { Py_ssize_t size = -1; const auto *buffer @@ -417,7 +396,6 @@ struct string_caster { value = StringType(buffer, static_cast(size)); return true; } -#endif auto utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), @@ -486,7 +464,7 @@ private: template bool load_bytes(enable_if_t::value, handle> src) { if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // We were passed raw bytes; accept it into a std::string or char* // without any encoding attempt. const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); if (bytes) { @@ -922,18 +900,6 @@ struct pyobject_caster { template ::value, int> = 0> bool load(handle src, bool /* convert */) { -#if PY_MAJOR_VERSION < 3 && !defined(PYBIND11_STR_LEGACY_PERMISSIVE) - // For Python 2, without this implicit conversion, Python code would - // need to be cluttered with six.ensure_text() or similar, only to be - // un-cluttered later after Python 2 support is dropped. - if (PYBIND11_SILENCE_MSVC_C4127(std::is_same::value) && isinstance(src)) { - PyObject *str_from_bytes = PyUnicode_FromEncodedObject(src.ptr(), "utf-8", nullptr); - if (!str_from_bytes) - throw error_already_set(); - value = reinterpret_steal(str_from_bytes); - return true; - } -#endif if (!isinstance(src)) { return false; } @@ -1313,7 +1279,7 @@ public: /// \ingroup annotations /// Annotation indicating that all following arguments are keyword-only; the is the equivalent of -/// an unnamed '*' argument (in Python 3) +/// an unnamed '*' argument struct kw_only {}; /// \ingroup annotations diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index ac92ddd63..ebdc07271 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -15,12 +15,12 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) -#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) +#if !defined(PYPY_VERSION) # define PYBIND11_BUILTIN_QUALNAME # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) #else -// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type -// signatures; in 3.3+ this macro expands to nothing: +// In PyPy, we still set __qualname__ so that we can produce reliable function type +// signatures; in CPython this macro expands to nothing: # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \ setattr((PyObject *) obj, "__qualname__", nameobj) #endif @@ -155,7 +155,6 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb } } -#if PY_MAJOR_VERSION >= 3 /** * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, @@ -170,7 +169,6 @@ extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name } return PyType_Type.tp_getattro(obj, name); } -#endif /// metaclass `__call__` function that is used to create all pybind11 objects. extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { @@ -266,9 +264,7 @@ inline PyTypeObject *make_default_metaclass() { type->tp_call = pybind11_meta_call; type->tp_setattro = pybind11_meta_setattro; -#if PY_MAJOR_VERSION >= 3 type->tp_getattro = pybind11_meta_getattro; -#endif type->tp_dealloc = pybind11_meta_dealloc; @@ -613,9 +609,6 @@ extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { /// Give this type a buffer interface. inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; -#if PY_MAJOR_VERSION < 3 - heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; -#endif heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; @@ -628,12 +621,8 @@ inline PyObject *make_new_python_type(const type_record &rec) { auto qualname = name; if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { -#if PY_MAJOR_VERSION >= 3 qualname = reinterpret_steal( PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); -#else - qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); -#endif } object module_; @@ -697,15 +686,10 @@ inline PyObject *make_new_python_type(const type_record &rec) { type->tp_as_number = &heap_type->as_number; type->tp_as_sequence = &heap_type->as_sequence; type->tp_as_mapping = &heap_type->as_mapping; -#if PY_VERSION_HEX >= 0x03050000 type->tp_as_async = &heap_type->as_async; -#endif /* Flags */ type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; -#if PY_MAJOR_VERSION < 3 - type->tp_flags |= Py_TPFLAGS_CHECKTYPES; -#endif if (!rec.is_final) { type->tp_flags |= Py_TPFLAGS_BASETYPE; } diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 8aefaa393..a5d98d71f 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -211,6 +211,9 @@ #endif #include +#if PY_VERSION_HEX < 0x030500f0 +# error "PYTHON 2 IS NO LONGER SUPPORTED. pybind11 v2.9 was the last to support Python 2." +#endif #include #include @@ -266,69 +269,37 @@ // If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and // pybind11::isinstance() is true only for pybind11::str. // However, for Python 2 only (!), the pybind11::str caster -// implicitly decodes bytes to PyUnicodeObject. This is to ease +// implicitly decoded bytes to PyUnicodeObject. This was to ease // the transition from the legacy behavior to the non-permissive // behavior. -#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions -# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) -# define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check -# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION -# define PYBIND11_BYTES_CHECK PyBytes_Check -# define PYBIND11_BYTES_FROM_STRING PyBytes_FromString -# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize -# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize -# define PYBIND11_BYTES_AS_STRING PyBytes_AsString -# define PYBIND11_BYTES_SIZE PyBytes_Size -# define PYBIND11_LONG_CHECK(o) PyLong_Check(o) -# define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) -# define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o)) -# define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o)) -# define PYBIND11_BYTES_NAME "bytes" -# define PYBIND11_STRING_NAME "str" -# define PYBIND11_SLICE_OBJECT PyObject -# define PYBIND11_FROM_STRING PyUnicode_FromString -# define PYBIND11_STR_TYPE ::pybind11::str -# define PYBIND11_BOOL_ATTR "__bool__" -# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) -# define PYBIND11_BUILTINS_MODULE "builtins" +/// Compatibility macros for Python 2 / Python 3 versions TODO: remove +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o)) +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o)) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +#define PYBIND11_BUILTINS_MODULE "builtins" // Providing a separate declaration to make Clang's -Wmissing-prototypes happy. // See comment for PYBIND11_MODULE below for why this is marked "maybe unused". -# define PYBIND11_PLUGIN_IMPL(name) \ - extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \ - extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() - -#else -# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) -# define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check -# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION -# define PYBIND11_BYTES_CHECK PyString_Check -# define PYBIND11_BYTES_FROM_STRING PyString_FromString -# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize -# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize -# define PYBIND11_BYTES_AS_STRING PyString_AsString -# define PYBIND11_BYTES_SIZE PyString_Size -# define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) -# define PYBIND11_LONG_AS_LONGLONG(o) \ - (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) -# define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. -# define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. -# define PYBIND11_BYTES_NAME "str" -# define PYBIND11_STRING_NAME "unicode" -# define PYBIND11_SLICE_OBJECT PySliceObject -# define PYBIND11_FROM_STRING PyString_FromString -# define PYBIND11_STR_TYPE ::pybind11::bytes -# define PYBIND11_BOOL_ATTR "__nonzero__" -# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) -# define PYBIND11_BUILTINS_MODULE "__builtin__" -// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy. -// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". -# define PYBIND11_PLUGIN_IMPL(name) \ - static PyObject *pybind11_init_wrapper(); \ - extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT void init##name(); \ - extern "C" PYBIND11_EXPORT void init##name() { (void) pybind11_init_wrapper(); } \ - PyObject *pybind11_init_wrapper() -#endif +#define PYBIND11_PLUGIN_IMPL(name) \ + 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" { @@ -362,31 +333,15 @@ PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; } \ } -#if PY_VERSION_HEX >= 0x03030000 - -# define PYBIND11_CATCH_INIT_EXCEPTIONS \ - catch (pybind11::error_already_set & e) { \ - pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \ - return nullptr; \ - } \ - catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } - -#else - -# define PYBIND11_CATCH_INIT_EXCEPTIONS \ - catch (pybind11::error_already_set & e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } \ - catch (const std::exception &e) { \ - PyErr_SetString(PyExc_ImportError, e.what()); \ - return nullptr; \ - } - -#endif +#define PYBIND11_CATCH_INIT_EXCEPTIONS \ + catch (pybind11::error_already_set & e) { \ + pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \ + return nullptr; \ + } \ + catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } /** \rst ***Deprecated in favor of PYBIND11_MODULE*** diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 76b4faa8f..3f83113bd 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -82,7 +82,7 @@ inline PyObject *make_object_base_type(PyTypeObject *metaclass); # define PYBIND11_TLS_KEY_INIT(var) PYBIND11_TLS_KEY_REF var = 0; # define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1) # define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) -# if PY_MAJOR_VERSION < 3 || defined(PYPY_VERSION) +# if defined(PYPY_VERSION) // On CPython < 3.4 and on PyPy, `PyThread_set_key_value` strangely does not set // the value if it has already been set. Instead, it must first be deleted and // then set again. @@ -294,7 +294,6 @@ inline internals **&get_internals_pp() { return internals_pp; } -#if PY_VERSION_HEX >= 0x03030000 // forward decl inline void translate_exception(std::exception_ptr); @@ -318,21 +317,11 @@ bool handle_nested_exception(const T &exc, const std::exception_ptr &p) { return false; } -#else - -template -bool handle_nested_exception(const T &, std::exception_ptr &) { - return false; -} -#endif - inline bool raise_err(PyObject *exc_type, const char *msg) { -#if PY_VERSION_HEX >= 0x03030000 if (PyErr_Occurred()) { raise_from(exc_type, msg); return true; } -#endif PyErr_SetString(exc_type, msg); return false; } diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 134e85b96..226f7a994 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -443,17 +443,10 @@ PYBIND11_NOINLINE void instance::allocate_layout() { // 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. -#if PY_VERSION_HEX >= 0x03050000 nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); if (!nonsimple.values_and_holders) { throw std::bad_alloc(); } -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) - throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); } @@ -494,11 +487,9 @@ PYBIND11_NOINLINE std::string error_string() { PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); -#if PY_MAJOR_VERSION >= 3 if (scope.trace != nullptr) { PyException_SetTraceback(scope.value, scope.trace); } -#endif #if !defined(PYPY_VERSION) if (scope.trace) { @@ -547,10 +538,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 < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState *) _Py_atomic_load_relaxed(&_PyThreadState_Current); #elif PY_VERSION_HEX < 0x03050200 return (PyThreadState *) _PyThreadState_Current.value; #else diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h index 84e442221..8e998fc3c 100644 --- a/include/pybind11/embed.h +++ b/include/pybind11/embed.h @@ -19,15 +19,9 @@ # error Embedding the interpreter is not supported with PyPy #endif -#if PY_MAJOR_VERSION >= 3 -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" PyObject *pybind11_init_impl_##name(); \ - extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); } -#else -# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ - extern "C" void pybind11_init_impl_##name(); \ - extern "C" void pybind11_init_impl_##name() { pybind11_init_wrapper_##name(); } -#endif +#define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name(); \ + extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##name(); } /** \rst Add a new module to the table of builtins for the interpreter. Must be @@ -67,11 +61,7 @@ PYBIND11_NAMESPACE_BEGIN(detail) /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. struct embedded_module { -#if PY_MAJOR_VERSION >= 3 using init_t = PyObject *(*) (); -#else - using init_t = void (*)(); -#endif embedded_module(const char *name, init_t init) { if (Py_IsInitialized() != 0) { pybind11_fail("Can't add new modules after the interpreter has been initialized"); @@ -86,42 +76,13 @@ struct embedded_module { struct wide_char_arg_deleter { void operator()(wchar_t *ptr) const { -#if PY_VERSION_HEX >= 0x030500f0 // API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale PyMem_RawFree(ptr); -#else - delete[] ptr; -#endif } }; inline wchar_t *widen_chars(const char *safe_arg) { -#if PY_VERSION_HEX >= 0x030500f0 wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr); -#else - wchar_t *widened_arg = nullptr; - -// warning C4996: 'mbstowcs': This function or variable may be unsafe. -# if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 4996) -# endif - -# if defined(HAVE_BROKEN_MBSTOWCS) && HAVE_BROKEN_MBSTOWCS - size_t count = std::strlen(safe_arg); -# else - size_t count = std::mbstowcs(nullptr, safe_arg, 0); -# endif - if (count != static_cast(-1)) { - widened_arg = new wchar_t[count + 1]; - std::mbstowcs(widened_arg, safe_arg, count + 1); - } - -# if defined(_MSC_VER) -# pragma warning(pop) -# endif - -#endif return widened_arg; } @@ -138,7 +99,6 @@ inline void set_interpreter_argv(int argc, const char *const *argv, bool add_pro } auto argv_size = static_cast(argc); -#if PY_MAJOR_VERSION >= 3 // SetArgv* on python 3 takes wchar_t, so we have to convert. std::unique_ptr widened_argv(new wchar_t *[argv_size]); std::vector> widened_argv_entries; @@ -154,15 +114,6 @@ inline void set_interpreter_argv(int argc, const char *const *argv, bool add_pro } auto *pysys_argv = widened_argv.get(); -#else - // python 2.x - std::vector strings{safe_argv, safe_argv + argv_size}; - std::vector char_strings{argv_size}; - for (std::size_t i = 0; i < argv_size; ++i) - char_strings[i] = &strings[i][0]; - char **pysys_argv = char_strings.data(); -#endif - PySys_SetArgvEx(argc, pysys_argv, static_cast(add_program_dir_to_path)); } diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index 731dedd4b..c157a0095 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -20,10 +20,10 @@ PYBIND11_NAMESPACE_BEGIN(detail) inline void ensure_builtins_in_globals(object &global) { #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000 - // Running exec and eval on Python 2 and 3 adds `builtins` module under - // `__builtins__` key to globals if not yet present. - // Python 3.8 made PyRun_String behave similarly. Let's also do that for - // older versions, for consistency. This was missing from PyPy3.8 7.3.7. + // Running exec and eval adds `builtins` module under `__builtins__` key to + // globals if not yet present. Python 3.8 made PyRun_String behave + // similarly. Let's also do that for older versions, for consistency. This + // was missing from PyPy3.8 7.3.7. if (!global.contains("__builtins__")) global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); #else @@ -94,7 +94,7 @@ void exec(const char (&s)[N], object global = globals(), object local = object() eval(s, global, local); } -#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000 +#if defined(PYPY_VERSION) template object eval_file(str, object, object) { pybind11_fail("eval_file not supported in PyPy3. Use eval"); @@ -133,40 +133,18 @@ object eval_file(str fname, object global = globals(), object local = object()) int closeFile = 1; std::string fname_str = (std::string) fname; -# if PY_VERSION_HEX >= 0x03040000 FILE *f = _Py_fopen_obj(fname.ptr(), "r"); -# elif PY_VERSION_HEX >= 0x03000000 - FILE *f = _Py_fopen(fname.ptr(), "r"); -# else - /* No unicode support in open() :( */ - auto fobj = reinterpret_steal( - PyFile_FromString(const_cast(fname_str.c_str()), const_cast("r"))); - FILE *f = nullptr; - if (fobj) - f = PyFile_AsFile(fobj.ptr()); - closeFile = 0; -# endif if (!f) { PyErr_Clear(); pybind11_fail("File \"" + fname_str + "\" could not be opened!"); } - // In Python2, this should be encoded by getfilesystemencoding. - // We don't boher setting it since Python2 is past EOL anyway. - // See PR#3233 -# if PY_VERSION_HEX >= 0x03000000 if (!global.contains("__file__")) { global["__file__"] = std::move(fname); } -# endif -# if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) - PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), local.ptr()); - (void) closeFile; -# else PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile); -# endif if (!result) { throw error_already_set(); diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 9f4d516f5..5e68f6053 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -263,11 +263,7 @@ private: static npy_api lookup() { module_ m = module_::import("numpy.core.multiarray"); auto c = m.attr("_ARRAY_API"); -#if PY_MAJOR_VERSION >= 3 void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); -#else - void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); -#endif npy_api api; #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); diff --git a/include/pybind11/operators.h b/include/pybind11/operators.h index 7865d35e1..a0c3b78d6 100644 --- a/include/pybind11/operators.h +++ b/include/pybind11/operators.h @@ -91,16 +91,6 @@ struct op_ { using R_type = conditional_t::value, Base, R>; using op = op_impl; cl.def(op::name(), &op::execute, is_operator(), extra...); -#if PY_MAJOR_VERSION < 3 - if (PYBIND11_SILENCE_MSVC_C4127(id == op_truediv) - || PYBIND11_SILENCE_MSVC_C4127(id == op_itruediv)) - cl.def(id == op_itruediv ? "__idiv__" - : ot == op_l ? "__div__" - : "__rdiv__", - &op::execute, - is_operator(), - extra...); -#endif } template void execute_cast(Class &cl, const Extra &...extra) const { @@ -109,15 +99,6 @@ struct op_ { using R_type = conditional_t::value, Base, R>; using op = op_impl; cl.def(op::name(), &op::execute_cast, is_operator(), extra...); -#if PY_MAJOR_VERSION < 3 - if (id == op_truediv || id == op_itruediv) - cl.def(id == op_itruediv ? "__idiv__" - : ot == op_l ? "__div__" - : "__rdiv__", - &op::execute, - is_operator(), - extra...); -#endif } }; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index b95f31a60..7b7a1a05d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -431,9 +431,8 @@ protected: } if (auto *tinfo = detail::get_type_info(*t)) { handle th((PyObject *) tinfo->type); - signature += th.attr("__module__").cast() + "." + - // Python 3.3+, but we backport it to earlier versions - th.attr("__qualname__").cast(); + signature += th.attr("__module__").cast() + "." + + th.attr("__qualname__").cast(); } else if (rec->is_new_style_constructor && arg_index == 0) { // A new-style `__init__` takes `self` as `value_and_holder`. // Rewrite it to the proper class type. @@ -453,15 +452,6 @@ protected: pybind11_fail("Internal error while parsing type signature (2)"); } -#if PY_MAJOR_VERSION < 3 - if (std::strcmp(rec->name, "__next__") == 0) { - std::free(rec->name); - rec->name = guarded_strdup("next"); - } else if (std::strcmp(rec->name, "__bool__") == 0) { - std::free(rec->name); - rec->name = guarded_strdup("__nonzero__"); - } -#endif rec->signature = guarded_strdup(signature.c_str()); rec->args.shrink_to_fit(); rec->nargs = (std::uint16_t) args; @@ -1107,14 +1097,12 @@ protected: } append_note_if_missing_header_is_suspected(msg); -#if PY_VERSION_HEX >= 0x03030000 // Attach additional error info to the exception if supported if (PyErr_Occurred()) { // #HelpAppreciated: unit test coverage for this branch. raise_from(PyExc_TypeError, msg.c_str()); return nullptr; } -#endif PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } @@ -1123,13 +1111,11 @@ protected: "Python type! The signature was\n\t"; msg += it->signature; append_note_if_missing_header_is_suspected(msg); -#if PY_VERSION_HEX >= 0x03030000 // Attach additional error info to the exception if supported if (PyErr_Occurred()) { raise_from(PyExc_TypeError, msg.c_str()); return nullptr; } -#endif PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } @@ -1149,11 +1135,7 @@ public: /// Create a new top-level Python module with the given name and docstring PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead") explicit module_(const char *name, const char *doc = nullptr) { -#if PY_MAJOR_VERSION >= 3 *this = create_extension_module(name, doc, new PyModuleDef()); -#else - *this = create_extension_module(name, doc, nullptr); -#endif } /** \rst @@ -1231,20 +1213,14 @@ public: PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); } -#if PY_MAJOR_VERSION >= 3 using module_def = PyModuleDef; -#else - struct module_def {}; -#endif /** \rst Create a new top-level module that can be used as the main module of a C extension. - For Python 3, ``def`` should point to a statically allocated module_def. - For Python 2, ``def`` can be a nullptr and is completely ignored. + ``def`` should point to a statically allocated module_def. \endrst */ static module_ create_extension_module(const char *name, const char *doc, module_def *def) { -#if PY_MAJOR_VERSION >= 3 // module_def is PyModuleDef // Placement new (not an allocation). def = new (def) @@ -1258,12 +1234,6 @@ public: /* m_clear */ nullptr, /* m_free */ nullptr}; auto *m = PyModule_Create(def); -#else - // Ignore module_def *def; only necessary for Python 3 - (void) def; - auto m = Py_InitModule3( - name, nullptr, options::show_user_defined_docstrings() ? doc : nullptr); -#endif if (m == nullptr) { if (PyErr_Occurred()) { throw error_already_set(); @@ -1290,14 +1260,12 @@ inline dict globals() { return reinterpret_borrow(p ? p : module_::import("__main__").attr("__dict__").ptr()); } -#if PY_VERSION_HEX >= 0x03030000 template ()>> PYBIND11_DEPRECATED("make_simple_namespace should be replaced with " "py::module_::import(\"types\").attr(\"SimpleNamespace\") ") object make_simple_namespace(Args &&...args_) { return module_::import("types").attr("SimpleNamespace")(std::forward(args_)...); } -#endif PYBIND11_NAMESPACE_BEGIN(detail) /// Generic support for creating new Python heap types @@ -2173,9 +2141,6 @@ public: def_property_readonly("value", [](Type value) { return (Scalar) value; }); def("__int__", [](Type value) { return (Scalar) value; }); def("__index__", [](Type value) { return (Scalar) value; }); -#if PY_MAJOR_VERSION < 3 - def("__long__", [](Type value) { return (Scalar) value; }); -#endif attr("__setstate__") = cpp_function( [](detail::value_and_holder &v_h, Scalar arg) { detail::initimpl::setstate( diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 75ea7de88..29f70bd5f 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -434,8 +434,6 @@ private: # pragma warning(pop) #endif -#if PY_VERSION_HEX >= 0x03030000 - /// Replaces the current Python error indicator with the chosen error, performing a /// 'raise from' to indicate that the chosen error was caused by the original error. inline void raise_from(PyObject *type, const char *message) { @@ -473,8 +471,6 @@ inline void raise_from(error_already_set &err, PyObject *type, const char *messa raise_from(type, message); } -#endif - /** \defgroup python_builtins const_name Unless stated otherwise, the following C++ functions behave the same as their Python counterparts. @@ -591,12 +587,9 @@ inline ssize_t hash(handle obj) { PYBIND11_NAMESPACE_BEGIN(detail) inline handle get_function(handle value) { if (value) { -#if PY_MAJOR_VERSION >= 3 if (PyInstanceMethod_Check(value.ptr())) { value = PyInstanceMethod_GET_FUNCTION(value.ptr()); - } else -#endif - if (PyMethod_Check(value.ptr())) { + } else if (PyMethod_Check(value.ptr())) { value = PyMethod_GET_FUNCTION(value.ptr()); } } @@ -608,7 +601,6 @@ inline handle get_function(handle value) { // copied from cpython _PyDict_GetItemStringWithError inline PyObject *dict_getitemstring(PyObject *v, const char *key) { -#if PY_MAJOR_VERSION >= 3 PyObject *kv = nullptr, *rv = nullptr; kv = PyUnicode_FromString(key); if (kv == NULL) { @@ -621,21 +613,14 @@ inline PyObject *dict_getitemstring(PyObject *v, const char *key) { throw error_already_set(); } return rv; -#else - return PyDict_GetItemString(v, key); -#endif } inline PyObject *dict_getitem(PyObject *v, PyObject *key) { -#if PY_MAJOR_VERSION >= 3 PyObject *rv = PyDict_GetItemWithError(v, key); if (rv == NULL && PyErr_Occurred()) { throw error_already_set(); } return rv; -#else - return PyDict_GetItem(v, key); -#endif } // Helper aliases/functions to support implicit casting of values given to python @@ -1273,13 +1258,6 @@ private: /// Return string representation -- always returns a new reference, even if already a str static PyObject *raw_str(PyObject *op) { PyObject *str_value = PyObject_Str(op); -#if PY_MAJOR_VERSION < 3 - if (!str_value) - throw error_already_set(); - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); - str_value = unicode; -#endif return str_value; } }; @@ -1459,11 +1437,7 @@ PYBIND11_NAMESPACE_BEGIN(detail) // unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). template Unsigned as_unsigned(PyObject *o) { - if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long)) -#if PY_VERSION_HEX < 0x03000000 - || PyInt_Check(o) -#endif - ) { + if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))) { unsigned long v = PyLong_AsUnsignedLong(o); return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; } @@ -1922,7 +1896,6 @@ public: return memoryview::from_buffer(const_cast(ptr), shape, strides, true); } -#if PY_MAJOR_VERSION >= 3 /** \rst Creates ``memoryview`` from static memory. @@ -1930,8 +1903,6 @@ public: managed by Python. The caller is responsible for managing the lifetime of ``mem``, which MUST outlive the memoryview constructed here. - This method is not available in Python 2. - See also: Python C API documentation for `PyMemoryView_FromBuffer`_. .. _PyMemoryView_FromMemory: @@ -1950,12 +1921,10 @@ public: return memoryview::from_memory(const_cast(mem), size, true); } -# ifdef PYBIND11_HAS_STRING_VIEW +#ifdef PYBIND11_HAS_STRING_VIEW static memoryview from_memory(std::string_view mem) { return from_memory(const_cast(mem.data()), static_cast(mem.size()), true); } -# endif - #endif }; @@ -2010,11 +1979,7 @@ inline size_t len(handle h) { /// Get the length hint of a Python object. /// Returns 0 when this cannot be determined. inline size_t len_hint(handle h) { -#if PY_VERSION_HEX >= 0x03040000 ssize_t result = PyObject_LengthHint(h.ptr(), 0); -#else - ssize_t result = PyObject_Length(h.ptr()); -#endif if (result < 0) { // Sometimes a length can't be determined at all (eg generators) // In which case simply return 0 @@ -2029,13 +1994,6 @@ inline str repr(handle h) { if (!str_value) { throw error_already_set(); } -#if PY_MAJOR_VERSION < 3 - PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); - Py_XDECREF(str_value); - str_value = unicode; - if (!str_value) - throw error_already_set(); -#endif return reinterpret_steal(str_value); } diff --git a/noxfile.py b/noxfile.py index 4adffac2e..c8ab7441e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,7 +2,7 @@ import nox nox.options.sessions = ["lint", "tests", "tests_packaging"] -PYTHON_VERISONS = ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] +PYTHON_VERISONS = ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] @nox.session(reuse_venv=True) diff --git a/pybind11/__init__.py b/pybind11/__init__.py index 64e999ba0..faf3892cb 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -1,4 +1,9 @@ -# -*- coding: utf-8 -*- +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." + raise ImportError(msg) + from ._version import __version__, version_info from .commands import get_cmake_dir, get_include diff --git a/pybind11/__main__.py b/pybind11/__main__.py index 3235747be..645c76042 100644 --- a/pybind11/__main__.py +++ b/pybind11/__main__.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function - import argparse import sys import sysconfig @@ -8,8 +5,7 @@ import sysconfig from .commands import get_cmake_dir, get_include -def print_includes(): - # type: () -> None +def print_includes() -> None: dirs = [ sysconfig.get_path("include"), sysconfig.get_path("platinclude"), @@ -25,8 +21,7 @@ def print_includes(): print(" ".join("-I" + d for d in unique_dirs)) -def main(): - # type: () -> None +def main() -> None: parser = argparse.ArgumentParser() parser.add_argument( diff --git a/pybind11/_version.py b/pybind11/_version.py index 7519ac903..aee3b1c5b 100644 --- a/pybind11/_version.py +++ b/pybind11/_version.py @@ -1,7 +1,7 @@ -# -*- coding: utf-8 -*- +from typing import Union -def _to_int(s): +def _to_int(s: str) -> Union[int, str]: try: return int(s) except ValueError: diff --git a/pybind11/_version.pyi b/pybind11/_version.pyi deleted file mode 100644 index d45e5dc90..000000000 --- a/pybind11/_version.pyi +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Tuple, Union - -def _to_int(s: str) -> Union[int, str]: ... - -__version__: str -version_info: Tuple[Union[int, str], ...] diff --git a/pybind11/commands.py b/pybind11/commands.py index 11f81d2d6..84d41478a 100644 --- a/pybind11/commands.py +++ b/pybind11/commands.py @@ -1,18 +1,15 @@ -# -*- coding: utf-8 -*- import os DIR = os.path.abspath(os.path.dirname(__file__)) -def get_include(user=False): - # type: (bool) -> str +def get_include(user: bool = False) -> str: installed_path = os.path.join(DIR, "include") source_path = os.path.join(os.path.dirname(DIR), "include") return installed_path if os.path.exists(installed_path) else source_path -def get_cmake_dir(): - # type: () -> str +def get_cmake_dir() -> str: cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11") if os.path.exists(cmake_installed_path): return cmake_installed_path diff --git a/pybind11/setup_helpers.py b/pybind11/setup_helpers.py index 5b7c9aab1..9cbcaa922 100644 --- a/pybind11/setup_helpers.py +++ b/pybind11/setup_helpers.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ This module provides helpers for C++11+ projects using pybind11. @@ -49,6 +47,20 @@ import sysconfig import tempfile import threading import warnings +from functools import lru_cache +from pathlib import Path +from typing import ( + Any, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Tuple, + TypeVar, + Union, +) try: from setuptools import Extension as _Extension @@ -61,7 +73,6 @@ import distutils.ccompiler import distutils.errors WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() -PY2 = sys.version_info[0] < 3 MACOS = sys.platform.startswith("darwin") STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" @@ -73,7 +84,7 @@ STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" # directory into your path if it sits beside your setup.py. -class Pybind11Extension(_Extension): +class Pybind11Extension(_Extension): # type: ignore[misc] """ Build a C++11+ Extension module with pybind11. This automatically adds the recommended flags when you init the extension and assumes C++ sources - you @@ -95,21 +106,18 @@ class Pybind11Extension(_Extension): If you want to add pybind11 headers manually, for example for an exact git checkout, then set ``include_pybind11=False``. - - Warning: do not use property-based access to the instance on Python 2 - - this is an ugly old-style class due to Distutils. """ # flags are prepended, so that they can be further overridden, e.g. by # ``extra_compile_args=["-g"]``. - def _add_cflags(self, flags): + def _add_cflags(self, flags: List[str]) -> None: self.extra_compile_args[:0] = flags - def _add_ldflags(self, flags): + def _add_ldflags(self, flags: List[str]) -> None: self.extra_link_args[:0] = flags - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: self._cxx_level = 0 cxx_std = kwargs.pop("cxx_std", 0) @@ -119,9 +127,7 @@ class Pybind11Extension(_Extension): include_pybind11 = kwargs.pop("include_pybind11", True) - # Can't use super here because distutils has old-style classes in - # Python 2! - _Extension.__init__(self, *args, **kwargs) + super().__init__(*args, **kwargs) # Include the installed package pybind11 headers if include_pybind11: @@ -133,11 +139,10 @@ class Pybind11Extension(_Extension): if pyinc not in self.include_dirs: self.include_dirs.append(pyinc) - except ImportError: + except ModuleNotFoundError: pass - # Have to use the accessor manually to support Python 2 distutils - Pybind11Extension.cxx_std.__set__(self, cxx_std) + self.cxx_std = cxx_std cflags = [] ldflags = [] @@ -157,18 +162,18 @@ class Pybind11Extension(_Extension): self._add_ldflags(ldflags) @property - def cxx_std(self): + def cxx_std(self) -> int: """ - The CXX standard level. If set, will add the required flags. If left - at 0, it will trigger an automatic search when pybind11's build_ext - is used. If None, will have no effect. Besides just the flags, this - may add a register warning/error fix for Python 2 or macos-min 10.9 - or 10.14. + The CXX standard level. If set, will add the required flags. If left at + 0, it will trigger an automatic search when pybind11's build_ext is + used. If None, will have no effect. Besides just the flags, this may + add a macos-min 10.9 or 10.14 flag if MACOSX_DEPLOYMENT_TARGET is + unset. """ return self._cxx_level @cxx_std.setter - def cxx_std(self, level): + def cxx_std(self, level: int) -> None: if self._cxx_level: warnings.warn("You cannot safely change the cxx_level after setting it!") @@ -195,31 +200,20 @@ class Pybind11Extension(_Extension): 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=" + macos_string + macosx_min = "-mmacosx-version-min={}".format(macos_string) cflags += [macosx_min] ldflags += [macosx_min] - if PY2: - if WIN: - # Will be ignored on MSVC 2015, where C++17 is not supported so - # this flag is not valid. - cflags += ["/wd5033"] - elif level >= 17: - cflags += ["-Wno-register"] - elif level >= 14: - cflags += ["-Wno-deprecated-register"] - self._add_cflags(cflags) self._add_ldflags(ldflags) # Just in case someone clever tries to multithread tmp_chdir_lock = threading.Lock() -cpp_cache_lock = threading.Lock() @contextlib.contextmanager -def tmp_chdir(): +def tmp_chdir() -> Iterator[str]: "Prepare and enter a temporary directory, cleanup when done" # Threadsafe @@ -235,7 +229,7 @@ def tmp_chdir(): # cf http://bugs.python.org/issue26689 -def has_flag(compiler, flag): +def has_flag(compiler: Any, flag: str) -> bool: """ Return the flag if a flag name is supported on the specified compiler, otherwise None (can be used as a boolean). @@ -243,13 +237,12 @@ def has_flag(compiler, flag): """ with tmp_chdir(): - fname = "flagcheck.cpp" - with open(fname, "w") as f: - # Don't trigger -Wunused-parameter. - f.write("int main (int, char **) { return 0; }") + fname = Path("flagcheck.cpp") + # Don't trigger -Wunused-parameter. + fname.write_text("int main (int, char **) { return 0; }") try: - compiler.compile([fname], extra_postargs=[flag]) + compiler.compile([str(fname)], extra_postargs=[flag]) except distutils.errors.CompileError: return False return True @@ -259,7 +252,8 @@ def has_flag(compiler, flag): cpp_flag_cache = None -def auto_cpp_level(compiler): +@lru_cache() +def auto_cpp_level(compiler: Any) -> Union[str, int]: """ Return the max supported C++ std level (17, 14, or 11). Returns latest on Windows. """ @@ -267,48 +261,38 @@ def auto_cpp_level(compiler): if WIN: return "latest" - global cpp_flag_cache - - # If this has been previously calculated with the same args, return that - with cpp_cache_lock: - if cpp_flag_cache: - return cpp_flag_cache - levels = [17, 14, 11] for level in levels: if has_flag(compiler, STD_TMPL.format(level)): - with cpp_cache_lock: - cpp_flag_cache = level return level msg = "Unsupported compiler -- at least C++11 support is needed!" raise RuntimeError(msg) -class build_ext(_build_ext): # noqa: N801 +class build_ext(_build_ext): # type: ignore[misc] # noqa: N801 """ Customized build_ext that allows an auto-search for the highest supported C++ level for Pybind11Extension. This is only needed for the auto-search for now, and is completely optional otherwise. """ - def build_extensions(self): + def build_extensions(self) -> None: """ Build extensions, injecting C++ std for Pybind11Extension if needed. """ for ext in self.extensions: if hasattr(ext, "_cxx_level") and ext._cxx_level == 0: - # Python 2 syntax - old-style distutils class - ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler)) + ext.cxx_std = auto_cpp_level(self.compiler) - # Python 2 doesn't allow super here, since distutils uses old-style - # classes! - _build_ext.build_extensions(self) + super().build_extensions() -def intree_extensions(paths, package_dir=None): +def intree_extensions( + paths: Iterable[str], package_dir: Optional[Dict[str, str]] = None +) -> List[Pybind11Extension]: """ Generate Pybind11Extensions from source files directly located in a Python source tree. @@ -318,6 +302,7 @@ def intree_extensions(paths, package_dir=None): not contain an ``__init__.py`` file. """ exts = [] + for path in paths: if package_dir is None: parent, _ = os.path.split(path) @@ -327,24 +312,25 @@ def intree_extensions(paths, package_dir=None): qualified_name = relname.replace(os.path.sep, ".") exts.append(Pybind11Extension(qualified_name, [path])) else: - found = False for prefix, parent in package_dir.items(): if path.startswith(parent): - found = True relname, _ = os.path.splitext(os.path.relpath(path, parent)) qualified_name = relname.replace(os.path.sep, ".") if prefix: qualified_name = prefix + "." + qualified_name exts.append(Pybind11Extension(qualified_name, [path])) - if not found: - raise ValueError( - "path {} is not a child of any of the directories listed " - "in 'package_dir' ({})".format(path, package_dir) - ) + + 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) + raise ValueError(msg) + return exts -def naive_recompile(obj, src): +def naive_recompile(obj: str, src: str) -> bool: """ This will recompile only if the source file changes. It does not check header files, so a more advanced function or Ccache is better if you have @@ -353,7 +339,7 @@ def naive_recompile(obj, src): return os.stat(obj).st_mtime < os.stat(src).st_mtime -def no_recompile(obg, src): +def no_recompile(obg: str, src: str) -> bool: """ This is the safest but slowest choice (and is the default) - will always recompile sources. @@ -361,15 +347,33 @@ def no_recompile(obg, src): return True +S = TypeVar("S", bound="ParallelCompile") + +CCompilerMethod = Callable[ + [ + distutils.ccompiler.CCompiler, + List[str], + Optional[str], + Optional[Union[Tuple[str], Tuple[str, Optional[str]]]], + Optional[List[str]], + bool, + Optional[List[str]], + Optional[List[str]], + Optional[List[str]], + ], + List[str], +] + + # Optional parallel compile utility # inspired by: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils # and: https://github.com/tbenthompson/cppimport/blob/stable/cppimport/build_module.py # and NumPy's parallel distutils module: # https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py -class ParallelCompile(object): +class ParallelCompile: """ Make a parallel compile function. Inspired by - numpy.distutils.ccompiler.CCompiler_compile and cppimport. + numpy.distutils.ccompiler.CCompiler.compile and cppimport. This takes several arguments that allow you to customize the compile function created: @@ -404,35 +408,41 @@ class ParallelCompile(object): __slots__ = ("envvar", "default", "max", "_old", "needs_recompile") - def __init__(self, envvar=None, default=0, max=0, needs_recompile=no_recompile): + def __init__( + self, + envvar: Optional[str] = None, + default: int = 0, + max: int = 0, + needs_recompile: Callable[[str, str], bool] = no_recompile, + ) -> None: self.envvar = envvar self.default = default self.max = max self.needs_recompile = needs_recompile - self._old = [] + self._old = [] # type: List[CCompilerMethod] - def function(self): + def function(self) -> CCompilerMethod: """ Builds a function object usable as distutils.ccompiler.CCompiler.compile. """ def compile_function( - compiler, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - depends=None, - ): + compiler: distutils.ccompiler.CCompiler, + sources: List[str], + output_dir: Optional[str] = None, + macros: Optional[Union[Tuple[str], Tuple[str, Optional[str]]]] = None, + include_dirs: Optional[List[str]] = None, + debug: bool = False, + extra_preargs: Optional[List[str]] = None, + extra_postargs: Optional[List[str]] = None, + depends: Optional[List[str]] = None, + ) -> Any: # These lines are directly from distutils.ccompiler.CCompiler - macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( + macros, objects, extra_postargs, pp_opts, build = compiler._setup_compile( # type: ignore[attr-defined] output_dir, macros, include_dirs, sources, depends, extra_postargs ) - cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs) + cc_args = compiler._get_cc_args(pp_opts, debug, extra_preargs) # type: ignore[attr-defined] # The number of threads; start with default. threads = self.default @@ -441,14 +451,14 @@ class ParallelCompile(object): if self.envvar is not None: threads = int(os.environ.get(self.envvar, self.default)) - def _single_compile(obj): + def _single_compile(obj: Any) -> None: try: src, ext = build[obj] except KeyError: return if not os.path.exists(obj) or self.needs_recompile(obj, src): - compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) + compiler._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) # type: ignore[attr-defined] try: # Importing .synchronize checks for platforms that have some multiprocessing @@ -466,14 +476,9 @@ class ParallelCompile(object): threads = 1 if threads > 1: - pool = ThreadPool(threads) - # In Python 2, ThreadPool can't be used as a context manager. - # Once we are no longer supporting it, this can be 'with pool:' - try: + with ThreadPool(threads) as pool: for _ in pool.imap_unordered(_single_compile, objects): pass - finally: - pool.terminate() else: for ob in objects: _single_compile(ob) @@ -482,13 +487,13 @@ class ParallelCompile(object): return compile_function - def install(self): - distutils.ccompiler.CCompiler.compile = self.function() + def install(self: S) -> S: + distutils.ccompiler.CCompiler.compile = self.function() # type: ignore[assignment] return self - def __enter__(self): + def __enter__(self: S) -> S: self._old.append(distutils.ccompiler.CCompiler.compile) return self.install() - def __exit__(self, *args): - distutils.ccompiler.CCompiler.compile = self._old.pop() + def __exit__(self, *args: Any) -> None: + distutils.ccompiler.CCompiler.compile = self._old.pop() # type: ignore[assignment] diff --git a/pybind11/setup_helpers.pyi b/pybind11/setup_helpers.pyi deleted file mode 100644 index 074744eb8..000000000 --- a/pybind11/setup_helpers.pyi +++ /dev/null @@ -1,63 +0,0 @@ -# IMPORTANT: Should stay in sync with setup_helpers.py (mostly checked by CI / -# pre-commit). - -import contextlib -import distutils.ccompiler -from distutils.command.build_ext import build_ext as _build_ext # type: ignore -from distutils.extension import Extension as _Extension -from types import TracebackType -from typing import Any, Callable, Dict, Iterator, List, Optional, Type, TypeVar, Union - -WIN: bool -PY2: bool -MACOS: bool -STD_TMPL: str - -class Pybind11Extension(_Extension): - def _add_cflags(self, *flags: str) -> None: ... - def _add_lflags(self, *flags: str) -> None: ... - def __init__( - self, *args: Any, cxx_std: int = 0, language: str = "c++", **kwargs: Any - ) -> None: ... - @property - def cxx_std(self) -> int: ... - @cxx_std.setter - def cxx_std(self, level: int) -> None: ... - -@contextlib.contextmanager -def tmp_chdir() -> Iterator[str]: ... -def has_flag(compiler: distutils.ccompiler.CCompiler, flag: str) -> bool: ... -def auto_cpp_level(compiler: distutils.ccompiler.CCompiler) -> Union[int, str]: ... - -class build_ext(_build_ext): # type: ignore - def build_extensions(self) -> None: ... - -def intree_extensions( - paths: Iterator[str], package_dir: Optional[Dict[str, str]] = None -) -> List[Pybind11Extension]: ... -def no_recompile(obj: str, src: str) -> bool: ... -def naive_recompile(obj: str, src: str) -> bool: ... - -T = TypeVar("T", bound="ParallelCompile") - -class ParallelCompile: - envvar: Optional[str] - default: int - max: int - needs_recompile: Callable[[str, str], bool] - def __init__( - self, - envvar: Optional[str] = None, - default: int = 0, - max: int = 0, - needs_recompile: Callable[[str, str], bool] = no_recompile, - ) -> None: ... - def function(self) -> Any: ... - def install(self: T) -> T: ... - def __enter__(self: T) -> T: ... - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ) -> None: ... diff --git a/pyproject.toml b/pyproject.toml index 7d7a1c821..5371c1b33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,20 +22,11 @@ known_first_party = "env,pybind11_cross_module_tests,pybind11_tests," profile = "black" [tool.mypy] -files = "pybind11" -python_version = "2.7" +files = ["pybind11"] +python_version = "3.6" warn_unused_configs = true +strict = true -disallow_any_generics = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_defs = true -disallow_incomplete_defs = true -check_untyped_defs = true -disallow_untyped_decorators = true -no_implicit_optional = true -warn_redundant_casts = true -warn_unused_ignores = true -warn_return_any = true -no_implicit_reexport = true -strict_equality = true +[[tool.mypy.overrides]] +module = ["ghapi.*", "setuptools.*"] +ignore_missing_imports = true diff --git a/setup.cfg b/setup.cfg index 317c44bbf..bd2357e9e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,8 +13,7 @@ classifiers = Topic :: Software Development :: Libraries :: Python Modules Topic :: Utilities Programming Language :: C++ - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 @@ -39,15 +38,12 @@ project_urls = Chat = https://gitter.im/pybind/Lobby [options] -python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +python_requires = >=3.5 zip_safe = False -[bdist_wheel] -universal=1 - [flake8] -max-line-length = 99 +max-line-length = 120 show_source = True exclude = .git, __pycache__, build, dist, docs, tools, venv ignore = diff --git a/setup.py b/setup.py index f83837919..5b46e0c8d 100644 --- a/setup.py +++ b/setup.py @@ -1,56 +1,52 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # Setup script for PyPI; use CMakeFile.txt to build extension modules import contextlib -import io import os import re import shutil import string import subprocess import sys -import tempfile +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Dict, Iterator, List, Union import setuptools.command.sdist -DIR = os.path.abspath(os.path.dirname(__file__)) +DIR = Path(__file__).parent.absolute() VERSION_REGEX = re.compile( r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE ) +VERSION_FILE = Path("pybind11/_version.py") +COMMON_FILE = Path("include/pybind11/detail/common.h") -def build_expected_version_hex(matches): +def build_expected_version_hex(matches: Dict[str, str]) -> str: patch_level_serial = matches["PATCH"] serial = None - try: - major = int(matches["MAJOR"]) - minor = int(matches["MINOR"]) - flds = patch_level_serial.split(".") - if flds: - patch = int(flds[0]) - level = None - if len(flds) == 1: - level = "0" - serial = 0 - elif len(flds) == 2: - level_serial = flds[1] - for level in ("a", "b", "c", "dev"): - if level_serial.startswith(level): - serial = int(level_serial[len(level) :]) - break - except ValueError: - pass + major = int(matches["MAJOR"]) + minor = int(matches["MINOR"]) + flds = patch_level_serial.split(".") + if flds: + patch = int(flds[0]) + if len(flds) == 1: + level = "0" + serial = 0 + elif len(flds) == 2: + level_serial = flds[1] + for level in ("a", "b", "c", "dev"): + if level_serial.startswith(level): + serial = int(level_serial[len(level) :]) + break if serial is None: msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial) raise RuntimeError(msg) - return ( - "0x" - + "{:02x}{:02x}{:02x}{}{:x}".format( - major, minor, patch, level[:1], serial - ).upper() + version_hex_str = "{:02x}{:02x}{:02x}{}{:x}".format( + major, minor, patch, level[:1], serial ) + return "0x{}".format(version_hex_str.upper()) # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers @@ -58,24 +54,25 @@ def build_expected_version_hex(matches): global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False) -setup_py = "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in" +setup_py = Path( + "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in" +) extra_cmd = 'cmdclass["sdist"] = SDist\n' to_src = ( - ("pyproject.toml", "tools/pyproject.toml"), - ("setup.py", setup_py), + (Path("pyproject.toml"), Path("tools/pyproject.toml")), + (Path("setup.py"), setup_py), ) + # Read the listed version -with open("pybind11/_version.py") as f: - code = compile(f.read(), "pybind11/_version.py", "exec") -loc = {} +loc = {} # type: Dict[str, str] +code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec") exec(code, loc) version = loc["__version__"] # Verify that the version matches the one in C++ -with io.open("include/pybind11/detail/common.h", encoding="utf8") as f: - matches = dict(VERSION_REGEX.findall(f.read())) +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( @@ -84,56 +81,44 @@ if version != cpp_version: raise RuntimeError(msg) version_hex = matches.get("HEX", "MISSING") -expected_version_hex = build_expected_version_hex(matches) -if version_hex != expected_version_hex: +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, - expected_version_hex, + version_hex, exp_version_hex ) raise RuntimeError(msg) -def get_and_replace(filename, binary=False, **opts): - with open(filename, "rb" if binary else "r") as f: - contents = f.read() - # Replacement has to be done on text in Python 3 (both work in Python 2) +# TODO: use literals & overload (typing extensions or Python 3.8) +def get_and_replace( + filename: Path, binary: bool = False, **opts: str +) -> Union[bytes, str]: if binary: + contents = filename.read_bytes() return string.Template(contents.decode()).substitute(opts).encode() - else: - return string.Template(contents).substitute(opts) + + return string.Template(filename.read_text()).substitute(opts) # Use our input files instead when making the SDist (and anything that depends # on it, like a wheel) -class SDist(setuptools.command.sdist.sdist): - def make_release_tree(self, base_dir, files): - setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files) +class SDist(setuptools.command.sdist.sdist): # type: ignore[misc] + def make_release_tree(self, base_dir: str, files: List[str]) -> None: + super().make_release_tree(base_dir, files) for to, src in to_src: txt = get_and_replace(src, binary=True, version=version, extra_cmd="") - dest = os.path.join(base_dir, to) + dest = Path(base_dir) / to # This is normally linked, so unlink before writing! - os.unlink(dest) - with open(dest, "wb") as f: - f.write(txt) - - -# Backport from Python 3 -@contextlib.contextmanager -def TemporaryDirectory(): # noqa: N802 - "Prepare a temporary directory, cleanup when done" - try: - tmpdir = tempfile.mkdtemp() - yield tmpdir - finally: - shutil.rmtree(tmpdir) + dest.unlink() + dest.write_bytes(txt) # type: ignore[arg-type] # Remove the CMake install directory when done @contextlib.contextmanager -def remove_output(*sources): +def remove_output(*sources: str) -> Iterator[None]: try: yield finally: @@ -156,9 +141,14 @@ with remove_output("pybind11/include", "pybind11/share"): if "DCMAKE_INSTALL_PREFIX" not in c ] cmd += fcommand - cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) - subprocess.check_call(cmd, **cmake_opts) - subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts) + subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) + subprocess.run( + ["cmake", "--install", tmpdir], + check=True, + cwd=DIR, + stdout=sys.stdout, + stderr=sys.stderr, + ) txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd) code = compile(txt, setup_py, "exec") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9040cf8c0..bce8cceb1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -179,11 +179,6 @@ if(PYBIND11_TEST_FILTER) pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER}) endif() -if(PYTHON_VERSION VERSION_LESS 3.5) - pybind11_filter_tests(PYBIND11_TEST_FILES test_async.cpp MESSAGE - "Skipping test_async on Python 2") -endif() - # Skip tests for CUDA check: # /pybind11/tests/test_constants_and_functions.cpp(125): # error: incompatible exception specifications @@ -388,17 +383,6 @@ function(pybind11_enable_warnings target_name) -diag-disable 11074,11076) endif() endif() - - # Needs to be re-added since the ordering requires these to be after the ones above - if(CMAKE_CXX_STANDARD - AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" - AND PYTHON_VERSION VERSION_LESS 3.0) - if(CMAKE_CXX_STANDARD LESS 17) - target_compile_options(${target_name} PUBLIC -Wno-deprecated-register) - else() - target_compile_options(${target_name} PUBLIC -Wno-register) - endif() - endif() endfunction() set(test_targets pybind11_tests) diff --git a/tests/conftest.py b/tests/conftest.py index 362eb8069..e72ec0ef8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- """pytest configuration Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. -Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences. +Adds docstring and exceptions message sanitizers. """ import contextlib @@ -13,19 +12,14 @@ import textwrap import pytest -import env - # Early diagnostic for failed imports import pybind11_tests # noqa: F401 -_unicode_marker = re.compile(r"u(\'[^\']*\')") _long_marker = re.compile(r"([0-9])L") _hexadecimal = re.compile(r"0x[0-9a-fA-F]+") # Avoid collecting Python3 only files collect_ignore = [] -if env.PY2: - collect_ignore.append("test_async.py") def _strip_and_dedent(s): @@ -45,7 +39,7 @@ def _make_explanation(a, b): ] -class Output(object): +class Output: """Basic output post-processing and comparison""" def __init__(self, string): @@ -83,7 +77,7 @@ class Unordered(Output): return False -class Capture(object): +class Capture: def __init__(self, capfd): self.capfd = capfd self.out = "" @@ -126,7 +120,7 @@ def capture(capsys): return Capture(capsys) -class SanitizedString(object): +class SanitizedString: def __init__(self, sanitizer): self.sanitizer = sanitizer self.string = "" @@ -149,9 +143,7 @@ class SanitizedString(object): def _sanitize_general(s): s = s.strip() s = s.replace("pybind11_tests.", "m.") - s = s.replace("unicode", "str") s = _long_marker.sub(r"\1", s) - s = _unicode_marker.sub(r"\1", s) return s diff --git a/tests/cross_module_gil_utils.cpp b/tests/cross_module_gil_utils.cpp index f3aca1561..2e64da052 100644 --- a/tests/cross_module_gil_utils.cpp +++ b/tests/cross_module_gil_utils.cpp @@ -25,31 +25,14 @@ void gil_acquire() { py::gil_scoped_acquire gil; } constexpr char kModuleName[] = "cross_module_gil_utils"; -#if PY_MAJOR_VERSION >= 3 struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL}; -#else -PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}}; -#endif } // namespace -extern "C" PYBIND11_EXPORT -#if PY_MAJOR_VERSION >= 3 - PyObject * - PyInit_cross_module_gil_utils() -#else - void - initcross_module_gil_utils() -#endif -{ +extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() { - PyObject *m = -#if PY_MAJOR_VERSION >= 3 - PyModule_Create(&moduledef); -#else - Py_InitModule(kModuleName, module_methods); -#endif + PyObject *m = PyModule_Create(&moduledef); if (m != NULL) { static_assert(sizeof(&gil_acquire) == sizeof(void *), @@ -58,7 +41,5 @@ extern "C" PYBIND11_EXPORT m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast(&gil_acquire))); } -#if PY_MAJOR_VERSION >= 3 return m; -#endif } diff --git a/tests/env.py b/tests/env.py index 6172b451b..0345df65d 100644 --- a/tests/env.py +++ b/tests/env.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import platform import sys @@ -11,10 +10,6 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") CPYTHON = platform.python_implementation() == "CPython" PYPY = platform.python_implementation() == "PyPy" -PY2 = sys.version_info.major == 2 - -PY = sys.version_info - def deprecated_call(): """ diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 337a72dfe..70dcfb987 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import contextlib import os import string @@ -64,11 +63,9 @@ py_files = { "__init__.py", "__main__.py", "_version.py", - "_version.pyi", "commands.py", "py.typed", "setup_helpers.py", - "setup_helpers.pyi", } headers = main_headers | detail_headers | stl_headers diff --git a/tests/extra_setuptools/test_setuphelper.py b/tests/extra_setuptools/test_setuphelper.py index 788f368b1..3a771e558 100644 --- a/tests/extra_setuptools/test_setuphelper.py +++ b/tests/extra_setuptools/test_setuphelper.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import os import subprocess import sys @@ -19,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): (tmpdir / "setup.py").write_text( dedent( - u"""\ + """\ import sys sys.path.append({MAIN_DIR!r}) @@ -58,7 +57,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): (tmpdir / "main.cpp").write_text( dedent( - u"""\ + """\ #include int f(int x) { @@ -96,7 +95,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std): (tmpdir / "test.py").write_text( dedent( - u"""\ + """\ import simple_setup assert simple_setup.f(3) == 9 """ @@ -121,10 +120,11 @@ def test_intree_extensions(monkeypatch, tmpdir): subdir.ensure_dir() src = subdir / "ext.cpp" src.ensure() - (ext,) = intree_extensions([src.relto(tmpdir)]) + relpath = src.relto(tmpdir) + (ext,) = intree_extensions([relpath]) assert ext.name == "ext" subdir.ensure("__init__.py") - (ext,) = intree_extensions([src.relto(tmpdir)]) + (ext,) = intree_extensions([relpath]) assert ext.name == "dir.ext" diff --git a/tests/test_async.py b/tests/test_async.py index df4489c49..b9ff9514d 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest asyncio = pytest.importorskip("asyncio") diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 0d5bf16c3..8354b68cd 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import ctypes import io import struct @@ -93,16 +92,16 @@ def test_pointer_to_member_fn(): def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == b"d" if env.PY2 else 0x64 + assert view[0] == 0x64 assert view.readonly with pytest.raises(TypeError): - view[0] = b"\0" if env.PY2 else 0 + view[0] = 0 def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = b"d" if env.PY2 else 0x64 + memoryview(buf)[0] = 0x64 assert buf.value == 0x64 io.BytesIO(b"A").readinto(buf) @@ -110,7 +109,7 @@ def test_selective_readonly_buffer(): buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = b"\0" if env.PY2 else 0 + memoryview(buf)[0] = 0 with pytest.raises(TypeError): io.BytesIO(b"1").readinto(buf) @@ -145,9 +144,6 @@ def test_ctypes_array_2d(): assert not info.readonly -@pytest.mark.skipif( - "env.PYPY and env.PY2", reason="PyPy2 bytes buffer not reported as readonly" -) def test_ctypes_from_buffer(): test_pystr = b"0123456789" for pyarray in (test_pystr, bytearray(test_pystr)): diff --git a/tests/test_builtin_casters.cpp b/tests/test_builtin_casters.cpp index 320ddd8aa..7b67515e5 100644 --- a/tests/test_builtin_casters.cpp +++ b/tests/test_builtin_casters.cpp @@ -110,8 +110,7 @@ TEST_SUBMODULE(builtin_casters, m) { "def"); }); m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); }); -#if PY_MAJOR_VERSION >= 3 - // Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger + // Under Python 2.7, invalid unicode UTF-32 characters didn't appear to trigger // UnicodeDecodeError m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); }); if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { @@ -119,7 +118,6 @@ TEST_SUBMODULE(builtin_casters, m) { return std::wstring({wchar_t(0x61), wchar_t(0xd800)}); }); } -#endif m.def("u8_Z", []() -> char { return 'Z'; }); m.def("u8_eacute", []() -> char { return '\xe9'; }); m.def("u16_ibang", [=]() -> char16_t { return ib16; }); @@ -198,12 +196,10 @@ TEST_SUBMODULE(builtin_casters, m) { []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); }); m.def("string_view_from_bytes", [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); }); -# if PY_MAJOR_VERSION >= 3 m.def("string_view_memoryview", []() { static constexpr auto val = "Have some \360\237\216\202"sv; return py::memoryview::from_memory(val); }); -# endif # ifdef PYBIND11_HAS_U8STRING m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index b1f1e395a..74014c9cf 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -*- +import sys + import pytest import env @@ -12,12 +13,12 @@ def test_simple_string(): def test_unicode_conversion(): """Tests unicode conversion and error reporting.""" - assert m.good_utf8_string() == u"Say utf8β€½ πŸŽ‚ 𝐀" - assert m.good_utf16_string() == u"bβ€½πŸŽ‚π€z" - assert m.good_utf32_string() == u"aπ€πŸŽ‚β€½z" - assert m.good_wchar_string() == u"aβΈ˜π€z" + assert m.good_utf8_string() == "Say utf8β€½ πŸŽ‚ 𝐀" + assert m.good_utf16_string() == "bβ€½πŸŽ‚π€z" + assert m.good_utf32_string() == "aπ€πŸŽ‚β€½z" + assert m.good_wchar_string() == "aβΈ˜π€z" if hasattr(m, "has_u8string"): - assert m.good_utf8_u8string() == u"Say utf8β€½ πŸŽ‚ 𝐀" + assert m.good_utf8_u8string() == "Say utf8β€½ πŸŽ‚ 𝐀" with pytest.raises(UnicodeDecodeError): m.bad_utf8_string() @@ -25,7 +26,7 @@ def test_unicode_conversion(): with pytest.raises(UnicodeDecodeError): m.bad_utf16_string() - # These are provided only if they actually fail (they don't when 32-bit and under Python 2.7) + # These are provided only if they actually fail (they don't when 32-bit) if hasattr(m, "bad_utf32_string"): with pytest.raises(UnicodeDecodeError): m.bad_utf32_string() @@ -37,10 +38,10 @@ def test_unicode_conversion(): m.bad_utf8_u8string() assert m.u8_Z() == "Z" - assert m.u8_eacute() == u"Γ©" - assert m.u16_ibang() == u"β€½" - assert m.u32_mathbfA() == u"𝐀" - assert m.wchar_heart() == u"β™₯" + assert m.u8_eacute() == "Γ©" + assert m.u16_ibang() == "β€½" + assert m.u32_mathbfA() == "𝐀" + assert m.wchar_heart() == "β™₯" if hasattr(m, "has_u8string"): assert m.u8_char8_Z() == "Z" @@ -53,68 +54,68 @@ def test_single_char_arguments(): toolong_message = "Expected a character, but multi-character string found" - assert m.ord_char(u"a") == 0x61 # simple ASCII - assert m.ord_char_lv(u"b") == 0x62 + assert m.ord_char("a") == 0x61 # simple ASCII + assert m.ord_char_lv("b") == 0x62 assert ( - m.ord_char(u"Γ©") == 0xE9 + m.ord_char("Γ©") == 0xE9 ) # requires 2 bytes in utf-8, but can be stuffed in a char with pytest.raises(ValueError) as excinfo: - assert m.ord_char(u"Δ€") == 0x100 # requires 2 bytes, doesn't fit in a char + assert m.ord_char("Δ€") == 0x100 # requires 2 bytes, doesn't fit in a char assert str(excinfo.value) == toobig_message(0x100) with pytest.raises(ValueError) as excinfo: - assert m.ord_char(u"ab") + assert m.ord_char("ab") assert str(excinfo.value) == toolong_message - assert m.ord_char16(u"a") == 0x61 - assert m.ord_char16(u"Γ©") == 0xE9 - assert m.ord_char16_lv(u"Γͺ") == 0xEA - assert m.ord_char16(u"Δ€") == 0x100 - assert m.ord_char16(u"β€½") == 0x203D - assert m.ord_char16(u"β™₯") == 0x2665 - assert m.ord_char16_lv(u"β™‘") == 0x2661 + assert m.ord_char16("a") == 0x61 + assert m.ord_char16("Γ©") == 0xE9 + assert m.ord_char16_lv("Γͺ") == 0xEA + assert m.ord_char16("Δ€") == 0x100 + assert m.ord_char16("β€½") == 0x203D + assert m.ord_char16("β™₯") == 0x2665 + assert m.ord_char16_lv("β™‘") == 0x2661 with pytest.raises(ValueError) as excinfo: - assert m.ord_char16(u"πŸŽ‚") == 0x1F382 # requires surrogate pair + assert m.ord_char16("πŸŽ‚") == 0x1F382 # requires surrogate pair assert str(excinfo.value) == toobig_message(0x10000) with pytest.raises(ValueError) as excinfo: - assert m.ord_char16(u"aa") + assert m.ord_char16("aa") assert str(excinfo.value) == toolong_message - assert m.ord_char32(u"a") == 0x61 - assert m.ord_char32(u"Γ©") == 0xE9 - assert m.ord_char32(u"Δ€") == 0x100 - assert m.ord_char32(u"β€½") == 0x203D - assert m.ord_char32(u"β™₯") == 0x2665 - assert m.ord_char32(u"πŸŽ‚") == 0x1F382 + assert m.ord_char32("a") == 0x61 + assert m.ord_char32("Γ©") == 0xE9 + assert m.ord_char32("Δ€") == 0x100 + assert m.ord_char32("β€½") == 0x203D + assert m.ord_char32("β™₯") == 0x2665 + assert m.ord_char32("πŸŽ‚") == 0x1F382 with pytest.raises(ValueError) as excinfo: - assert m.ord_char32(u"aa") + assert m.ord_char32("aa") assert str(excinfo.value) == toolong_message - assert m.ord_wchar(u"a") == 0x61 - assert m.ord_wchar(u"Γ©") == 0xE9 - assert m.ord_wchar(u"Δ€") == 0x100 - assert m.ord_wchar(u"β€½") == 0x203D - assert m.ord_wchar(u"β™₯") == 0x2665 + assert m.ord_wchar("a") == 0x61 + assert m.ord_wchar("Γ©") == 0xE9 + assert m.ord_wchar("Δ€") == 0x100 + assert m.ord_wchar("β€½") == 0x203D + assert m.ord_wchar("β™₯") == 0x2665 if m.wchar_size == 2: with pytest.raises(ValueError) as excinfo: - assert m.ord_wchar(u"πŸŽ‚") == 0x1F382 # requires surrogate pair + assert m.ord_wchar("πŸŽ‚") == 0x1F382 # requires surrogate pair assert str(excinfo.value) == toobig_message(0x10000) else: - assert m.ord_wchar(u"πŸŽ‚") == 0x1F382 + assert m.ord_wchar("πŸŽ‚") == 0x1F382 with pytest.raises(ValueError) as excinfo: - assert m.ord_wchar(u"aa") + assert m.ord_wchar("aa") assert str(excinfo.value) == toolong_message if hasattr(m, "has_u8string"): - assert m.ord_char8(u"a") == 0x61 # simple ASCII - assert m.ord_char8_lv(u"b") == 0x62 + assert m.ord_char8("a") == 0x61 # simple ASCII + assert m.ord_char8_lv("b") == 0x62 assert ( - m.ord_char8(u"Γ©") == 0xE9 + m.ord_char8("Γ©") == 0xE9 ) # requires 2 bytes in utf-8, but can be stuffed in a char with pytest.raises(ValueError) as excinfo: - assert m.ord_char8(u"Δ€") == 0x100 # requires 2 bytes, doesn't fit in a char + assert m.ord_char8("Δ€") == 0x100 # requires 2 bytes, doesn't fit in a char assert str(excinfo.value) == toobig_message(0x100) with pytest.raises(ValueError) as excinfo: - assert m.ord_char8(u"ab") + assert m.ord_char8("ab") assert str(excinfo.value) == toolong_message @@ -123,18 +124,13 @@ def test_bytes_to_string(): one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" # Issue #816 - def to_bytes(s): - b = s if env.PY2 else s.encode("utf8") - assert isinstance(b, bytes) - return b - - assert m.strlen(to_bytes("hi")) == 2 - assert m.string_length(to_bytes("world")) == 5 - assert m.string_length(to_bytes("a\x00b")) == 3 - assert m.strlen(to_bytes("a\x00b")) == 1 # C-string limitation + assert m.strlen(b"hi") == 2 + assert m.string_length(b"world") == 5 + assert m.string_length("a\x00b".encode()) == 3 + assert m.strlen("a\x00b".encode()) == 1 # C-string limitation # passing in a utf8 encoded string should work - assert m.string_length(u"πŸ’©".encode("utf8")) == 4 + assert m.string_length("πŸ’©".encode()) == 4 @pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no ") @@ -142,26 +138,26 @@ def test_string_view(capture): """Tests support for C++17 string_view arguments and return values""" assert m.string_view_chars("Hi") == [72, 105] assert m.string_view_chars("Hi πŸŽ‚") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] - assert m.string_view16_chars(u"Hi πŸŽ‚") == [72, 105, 32, 0xD83C, 0xDF82] - assert m.string_view32_chars(u"Hi πŸŽ‚") == [72, 105, 32, 127874] + assert m.string_view16_chars("Hi πŸŽ‚") == [72, 105, 32, 0xD83C, 0xDF82] + assert m.string_view32_chars("Hi πŸŽ‚") == [72, 105, 32, 127874] if hasattr(m, "has_u8string"): assert m.string_view8_chars("Hi") == [72, 105] - assert m.string_view8_chars(u"Hi πŸŽ‚") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] + assert m.string_view8_chars("Hi πŸŽ‚") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] - assert m.string_view_return() == u"utf8 secret πŸŽ‚" - assert m.string_view16_return() == u"utf16 secret πŸŽ‚" - assert m.string_view32_return() == u"utf32 secret πŸŽ‚" + assert m.string_view_return() == "utf8 secret πŸŽ‚" + assert m.string_view16_return() == "utf16 secret πŸŽ‚" + assert m.string_view32_return() == "utf32 secret πŸŽ‚" if hasattr(m, "has_u8string"): - assert m.string_view8_return() == u"utf8 secret πŸŽ‚" + assert m.string_view8_return() == "utf8 secret πŸŽ‚" with capture: m.string_view_print("Hi") m.string_view_print("utf8 πŸŽ‚") - m.string_view16_print(u"utf16 πŸŽ‚") - m.string_view32_print(u"utf32 πŸŽ‚") + m.string_view16_print("utf16 πŸŽ‚") + m.string_view32_print("utf32 πŸŽ‚") assert ( capture - == u""" + == """ Hi 2 utf8 πŸŽ‚ 9 utf16 πŸŽ‚ 8 @@ -171,10 +167,10 @@ def test_string_view(capture): if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi") - m.string_view8_print(u"utf8 πŸŽ‚") + m.string_view8_print("utf8 πŸŽ‚") assert ( capture - == u""" + == """ Hi 2 utf8 πŸŽ‚ 9 """ @@ -183,11 +179,11 @@ def test_string_view(capture): with capture: m.string_view_print("Hi, ascii") m.string_view_print("Hi, utf8 πŸŽ‚") - m.string_view16_print(u"Hi, utf16 πŸŽ‚") - m.string_view32_print(u"Hi, utf32 πŸŽ‚") + m.string_view16_print("Hi, utf16 πŸŽ‚") + m.string_view32_print("Hi, utf32 πŸŽ‚") assert ( capture - == u""" + == """ Hi, ascii 9 Hi, utf8 πŸŽ‚ 13 Hi, utf16 πŸŽ‚ 12 @@ -197,22 +193,21 @@ def test_string_view(capture): if hasattr(m, "has_u8string"): with capture: m.string_view8_print("Hi, ascii") - m.string_view8_print(u"Hi, utf8 πŸŽ‚") + m.string_view8_print("Hi, utf8 πŸŽ‚") assert ( capture - == u""" + == """ Hi, ascii 9 Hi, utf8 πŸŽ‚ 13 """ ) assert m.string_view_bytes() == b"abc \x80\x80 def" - assert m.string_view_str() == u"abc β€½ def" - assert m.string_view_from_bytes(u"abc β€½ def".encode("utf-8")) == u"abc β€½ def" + assert m.string_view_str() == "abc β€½ def" + assert m.string_view_from_bytes("abc β€½ def".encode()) == "abc β€½ def" if hasattr(m, "has_u8string"): - assert m.string_view8_str() == u"abc β€½ def" - if not env.PY2: - assert m.string_view_memoryview() == "Have some πŸŽ‚".encode() + assert m.string_view8_str() == "abc β€½ def" + assert m.string_view_memoryview() == "Have some πŸŽ‚".encode() assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success" assert m.str_from_type_with_both_operator_string_and_string_view() == "success" @@ -224,20 +219,8 @@ def test_integer_casting(): assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if env.PY2: - assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' - assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' - assert ( - m.i64_str(long(-999999999999)) # noqa: F821 undefined name 'long' - == "-999999999999" - ) - assert ( - m.u64_str(long(999999999999)) # noqa: F821 undefined name 'long' - == "999999999999" - ) - else: - assert m.i64_str(-999999999999) == "-999999999999" - assert m.u64_str(999999999999) == "999999999999" + assert m.i64_str(-999999999999) == "-999999999999" + assert m.u64_str(999999999999) == "999999999999" with pytest.raises(TypeError) as excinfo: m.u32_str(-1) @@ -252,46 +235,38 @@ def test_integer_casting(): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if env.PY2: - with pytest.raises(TypeError) as excinfo: - m.u32_str(long(-1)) # noqa: F821 undefined name 'long' - assert "incompatible function arguments" in str(excinfo.value) - with pytest.raises(TypeError) as excinfo: - m.u64_str(long(-1)) # noqa: F821 undefined name 'long' - assert "incompatible function arguments" in str(excinfo.value) - def test_int_convert(): - class Int(object): + class Int: def __int__(self): return 42 - class NotInt(object): + class NotInt: pass - class Float(object): + class Float: def __float__(self): return 41.99999 - class Index(object): + class Index: def __index__(self): return 42 - class IntAndIndex(object): + class IntAndIndex: def __int__(self): return 42 def __index__(self): return 0 - class RaisingTypeErrorOnIndex(object): + class RaisingTypeErrorOnIndex: def __index__(self): raise TypeError def __int__(self): return 42 - class RaisingValueErrorOnIndex(object): + class RaisingValueErrorOnIndex: def __index__(self): raise ValueError @@ -311,7 +286,7 @@ def test_int_convert(): cant_convert(3.14159) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) - if (3, 8) <= env.PY < (3, 10) and env.CPYTHON: + if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: with env.deprecated_call(): assert convert(Int()) == 42 else: @@ -348,7 +323,7 @@ def test_numpy_int_convert(): # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) # https://github.com/pybind/pybind11/issues/3408 - if (3, 8) <= env.PY < (3, 10) and env.CPYTHON: + if (3, 8) <= sys.version_info < (3, 10) and env.CPYTHON: with env.deprecated_call(): assert convert(np.float32(3.14159)) == 3 else: @@ -475,7 +450,7 @@ def test_bool_caster(): require_implicit(None) assert convert(None) is False - class A(object): + class A: def __init__(self, x): self.x = x @@ -485,7 +460,7 @@ def test_bool_caster(): def __bool__(self): return self.x - class B(object): + class B: pass # Arbitrary objects are not accepted @@ -515,17 +490,9 @@ def test_numpy_bool(): def test_int_long(): - """In Python 2, a C++ int should return a Python int rather than long - if possible: longs are not always accepted where ints are used (such - as the argument to sys.exit()). A C++ long long is always a Python - long.""" - - import sys - - must_be_long = type(getattr(sys, "maxint", 1) + 1) assert isinstance(m.int_cast(), int) assert isinstance(m.long_cast(), int) - assert isinstance(m.longlong_cast(), must_be_long) + assert isinstance(m.longlong_cast(), int) def test_void_caster_2(): diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 3599cf81a..616056412 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index f41ad86e7..74aec21e5 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import time from threading import Thread diff --git a/tests/test_chrono.py b/tests/test_chrono.py index fdd73d690..f419ef387 100644 --- a/tests/test_chrono.py +++ b/tests/test_chrono.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import datetime import pytest diff --git a/tests/test_class.py b/tests/test_class.py index caafe2068..ff9196f0f 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 @@ -7,7 +6,6 @@ from pybind11_tests import class_ as m def test_repr(): - # In Python 3.3+, repr() accesses __qualname__ assert "pybind11_type" in repr(type(UserType)) assert "UserType" in repr(UserType) @@ -103,8 +101,8 @@ def test_docstrings(doc): def test_qualname(doc): - """Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we - backport the attribute) and that generated docstrings properly use it and the module name""" + """Tests that a properly qualified name is set in __qualname__ and that + generated docstrings properly use it and the module name""" assert m.NestBase.__qualname__ == "NestBase" assert m.NestBase.Nested.__qualname__ == "NestBase.Nested" @@ -130,13 +128,13 @@ def test_qualname(doc): doc(m.NestBase.Nested.fn) == """ fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None - """ # noqa: E501 line too long + """ ) assert ( doc(m.NestBase.Nested.fa) == """ fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None - """ # noqa: E501 line too long + """ ) assert m.NestBase.__module__ == "pybind11_tests.class_" assert m.NestBase.Nested.__module__ == "pybind11_tests.class_" diff --git a/tests/test_cmake_build/test.py b/tests/test_cmake_build/test.py index 972a27bea..5bf129ea3 100644 --- a/tests/test_cmake_build/test.py +++ b/tests/test_cmake_build/test.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- import sys import test_cmake_build -if str is not bytes: # If not Python2 - 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 print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) diff --git a/tests/test_const_name.py b/tests/test_const_name.py index d4e45e5e9..10b0caee2 100644 --- a/tests/test_const_name.py +++ b/tests/test_const_name.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import pytest -import env from pybind11_tests import const_name as m @@ -25,7 +23,7 @@ from pybind11_tests import const_name as m ), ) def test_const_name(func, selector, expected): - if isinstance(func, type(u"") if env.PY2 else str): + if isinstance(func, str): pytest.skip(func) text = func(selector) assert text == expected diff --git a/tests/test_constants_and_functions.py b/tests/test_constants_and_functions.py index ff13bd0f2..5da0b84b8 100644 --- a/tests/test_constants_and_functions.py +++ b/tests/test_constants_and_functions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest m = pytest.importorskip("pybind11_tests.constants_and_functions") diff --git a/tests/test_copy_move.py b/tests/test_copy_move.py index eb1efddd5..f2c0cdceb 100644 --- a/tests/test_copy_move.py +++ b/tests/test_copy_move.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest from pybind11_tests import copy_move_policies as m diff --git a/tests/test_custom_type_casters.py b/tests/test_custom_type_casters.py index a10646ff4..bb47d01bd 100644 --- a/tests/test_custom_type_casters.py +++ b/tests/test_custom_type_casters.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest from pybind11_tests import custom_type_casters as m @@ -19,7 +18,7 @@ def test_noconvert_args(msg): loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b 13 loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) - """ # noqa: E501 line too long + """ ) assert ( msg(a.g("this is a", "this is b", 42)) @@ -28,7 +27,7 @@ def test_noconvert_args(msg): loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b 42 loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) - """ # noqa: E501 line too long + """ ) assert ( msg(a.g("this is a", "this is b", 42, "this is d")) @@ -76,7 +75,7 @@ def test_noconvert_args(msg): 1. (i: int) -> int Invoked with: 4.0 - """ # noqa: E501 line too long + """ ) assert m.ints_only(4) == 2 diff --git a/tests/test_custom_type_setup.py b/tests/test_custom_type_setup.py index ef96f0814..19b44c9de 100644 --- a/tests/test_custom_type_setup.py +++ b/tests/test_custom_type_setup.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import gc import weakref diff --git a/tests/test_docstring_options.py b/tests/test_docstring_options.py index 8ee661388..fcd16b89f 100644 --- a/tests/test_docstring_options.py +++ b/tests/test_docstring_options.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from pybind11_tests import docstring_options as m diff --git a/tests/test_eigen.py b/tests/test_eigen.py index e53826cbb..9a94b35f8 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest from pybind11_tests import ConstructorStats @@ -201,7 +200,7 @@ def test_negative_stride_from_python(msg): double_threer(): incompatible function arguments. The following argument types are supported: 1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None - Invoked with: """ # noqa: E501 line too long + Invoked with: """ + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) ) @@ -213,7 +212,7 @@ def test_negative_stride_from_python(msg): double_threec(): incompatible function arguments. The following argument types are supported: 1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None - Invoked with: """ # noqa: E501 line too long + Invoked with: """ + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) ) @@ -724,13 +723,13 @@ def test_sparse_signature(doc): doc(m.sparse_copy_r) == """ sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32] - """ # noqa: E501 line too long + """ ) assert ( doc(m.sparse_copy_c) == """ sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32] - """ # noqa: E501 line too long + """ ) diff --git a/tests/test_embed/test_interpreter.cpp b/tests/test_embed/test_interpreter.cpp index 5782bda61..f2f2c2960 100644 --- a/tests/test_embed/test_interpreter.cpp +++ b/tests/test_embed/test_interpreter.cpp @@ -126,7 +126,6 @@ TEST_CASE("Override cache") { TEST_CASE("Import error handling") { REQUIRE_NOTHROW(py::module_::import("widget_module")); REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error"); -#if PY_VERSION_HEX >= 0x03030000 REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), Catch::Contains("ImportError: initialization failed")); @@ -142,10 +141,6 @@ TEST_CASE("Import error handling") { locals); REQUIRE(locals["is_keyerror"].cast() == true); REQUIRE(locals["message"].cast() == "'missing'"); -#else - REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"), - Catch::Contains("ImportError: KeyError")); -#endif } TEST_CASE("There can be only one interpreter") { diff --git a/tests/test_embed/test_interpreter.py b/tests/test_embed/test_interpreter.py index 5ab55a4b3..f27944972 100644 --- a/tests/test_embed/test_interpreter.py +++ b/tests/test_embed/test_interpreter.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys from widget_module import Widget @@ -6,7 +5,7 @@ from widget_module import Widget class DerivedWidget(Widget): def __init__(self, message): - super(DerivedWidget, self).__init__(message) + super().__init__(message) def the_answer(self): return 42 diff --git a/tests/test_embed/test_trampoline.py b/tests/test_embed/test_trampoline.py index 87c8fa44c..8e14e8ef0 100644 --- a/tests/test_embed/test_trampoline.py +++ b/tests/test_embed/test_trampoline.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import trampoline_module diff --git a/tests/test_enum.py b/tests/test_enum.py index 14c754e72..f14a72398 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import pytest -import env from pybind11_tests import enums as m @@ -241,10 +239,7 @@ def test_char_underlying_enum(): # Issue #1331/PR #1334: assert type(m.ScopedCharEnum.Positive.__int__()) is int assert int(m.ScopedChar16Enum.Zero) == 0 assert hash(m.ScopedChar32Enum.Positive) == 1 - if env.PY2: - assert m.ScopedCharEnum.Positive.__getstate__() == 1 # long - else: - assert type(m.ScopedCharEnum.Positive.__getstate__()) is int + assert type(m.ScopedCharEnum.Positive.__getstate__()) is int assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive with pytest.raises(TypeError): # Even if the underlying type is char, only an int can be used to construct the enum: @@ -255,10 +250,7 @@ def test_bool_underlying_enum(): assert type(m.ScopedBoolEnum.TRUE.__int__()) is int assert int(m.ScopedBoolEnum.FALSE) == 0 assert hash(m.ScopedBoolEnum.TRUE) == 1 - if env.PY2: - assert m.ScopedBoolEnum.TRUE.__getstate__() == 1 # long - else: - assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int + assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE # Enum could construct with a bool # (bool is a strict subclass of int, and False will be converted to 0) diff --git a/tests/test_eval.py b/tests/test_eval.py index 1bbd991bc..51b6b796b 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import os import pytest @@ -18,7 +17,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError) +@pytest.mark.xfail("env.PYPY", raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_eval_call.py b/tests/test_eval_call.py index 373b67bac..fd1da2a5c 100644 --- a/tests/test_eval_call.py +++ b/tests/test_eval_call.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is called from 'test_eval.py' if "call_test2" in locals(): diff --git a/tests/test_exceptions.cpp b/tests/test_exceptions.cpp index f6f93611a..3e9a3d771 100644 --- a/tests/test_exceptions.cpp +++ b/tests/test_exceptions.cpp @@ -276,8 +276,6 @@ TEST_SUBMODULE(exceptions, m) { m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); }); -#if PY_VERSION_HEX >= 0x03030000 - m.def("raise_from", []() { PyErr_SetString(PyExc_ValueError, "inner"); py::raise_from(PyExc_ValueError, "outer"); @@ -301,5 +299,4 @@ TEST_SUBMODULE(exceptions, m) { std::throw_with_nested(std::runtime_error("Outer Exception")); } }); -#endif } diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index d698b1312..e0da55dfd 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import sys import pytest @@ -24,7 +23,6 @@ def test_error_already_set(msg): assert msg(excinfo.value) == "foo" -@pytest.mark.skipif("env.PY2") def test_raise_from(msg): with pytest.raises(ValueError) as excinfo: m.raise_from() @@ -32,7 +30,6 @@ def test_raise_from(msg): assert msg(excinfo.value.__cause__) == "inner" -@pytest.mark.skipif("env.PY2") def test_raise_from_already_set(msg): with pytest.raises(ValueError) as excinfo: m.raise_from_already_set() @@ -102,7 +99,7 @@ def ignore_pytest_unraisable_warning(f): @ignore_pytest_unraisable_warning def test_python_alreadyset_in_destructor(monkeypatch, capsys): hooked = False - triggered = [False] # mutable, so Python 2.7 closure can modify it + triggered = False if hasattr(sys, "unraisablehook"): # Python 3.8+ hooked = True @@ -112,7 +109,8 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys): def hook(unraisable_hook_args): exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args if obj == "already_set demo": - triggered[0] = True + nonlocal triggered + triggered = True default_hook(unraisable_hook_args) return @@ -121,11 +119,11 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys): assert m.python_alreadyset_in_destructor("already_set demo") is True if hooked: - assert triggered[0] is True + assert triggered is True _, captured_stderr = capsys.readouterr() - # Error message is different in Python 2 and 3, check for words that appear in both - assert "ignored" in captured_stderr and "already_set demo" in captured_stderr + assert captured_stderr.startswith("Exception ignored in: 'already_set demo'") + assert captured_stderr.rstrip().endswith("KeyError: 'bar'") def test_exception_matches(): @@ -239,7 +237,6 @@ def test_nested_throws(capture): assert str(excinfo.value) == "this is a helper-defined translated exception" -@pytest.mark.skipif("env.PY2") def test_throw_nested_exception(): with pytest.raises(RuntimeError) as excinfo: m.throw_nested_exception() @@ -249,7 +246,7 @@ def test_throw_nested_exception(): # This can often happen if you wrap a pybind11 class in a Python wrapper def test_invalid_repr(): - class MyRepr(object): + class MyRepr: def __repr__(self): raise AttributeError("Example error") diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 8bc026985..120a587c4 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- import re import pytest -import env # noqa: F401 from pybind11_tests import ConstructorStats from pybind11_tests import factory_constructors as m from pybind11_tests.factory_constructors import tag @@ -82,7 +80,7 @@ def test_init_factory_signature(msg): 4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle) Invoked with: 'invalid', 'constructor', 'arguments' - """ # noqa: E501 line too long + """ ) assert ( @@ -465,12 +463,10 @@ def test_reallocation_g(capture, msg): ) -@pytest.mark.skipif("env.PY2") def test_invalid_self(): - """Tests invocation of the pybind-registered base class with an invalid `self` argument. You - can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" + """Tests invocation of the pybind-registered base class with an invalid `self` argument.""" - class NotPybindDerived(object): + class NotPybindDerived: pass # Attempts to initialize with an invalid type passed as `self`: diff --git a/tests/test_gil_scoped.py b/tests/test_gil_scoped.py index 0a1d62747..52374b0cc 100644 --- a/tests/test_gil_scoped.py +++ b/tests/test_gil_scoped.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import multiprocessing import threading diff --git a/tests/test_iostream.py b/tests/test_iostream.py index 7f18ca65c..5bbdf6955 100644 --- a/tests/test_iostream.py +++ b/tests/test_iostream.py @@ -1,44 +1,8 @@ -# -*- coding: utf-8 -*- -import sys -from contextlib import contextmanager +from contextlib import redirect_stderr, redirect_stdout +from io import StringIO from pybind11_tests import iostream as m -try: - # Python 3 - from io import StringIO -except ImportError: - # Python 2 - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO - -try: - # Python 3.4 - from contextlib import redirect_stdout -except ImportError: - - @contextmanager - def redirect_stdout(target): - original = sys.stdout - sys.stdout = target - yield - sys.stdout = original - - -try: - # Python 3.5 - from contextlib import redirect_stderr -except ImportError: - - @contextmanager - def redirect_stderr(target): - original = sys.stderr - sys.stderr = target - yield - sys.stderr = original - def test_captured(capsys): msg = "I've been redirected to Python, I hope!" diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index d61cf2aa5..ab7017886 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import pytest -import env # noqa: F401 from pybind11_tests import kwargs_and_defaults as m @@ -82,7 +80,7 @@ def test_mixed_args_and_kwargs(msg): 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: 1 - """ # noqa: E501 line too long + """ ) with pytest.raises(TypeError) as excinfo: assert mpa() @@ -93,7 +91,7 @@ def test_mixed_args_and_kwargs(msg): 1. (arg0: int, arg1: float, *args) -> tuple Invoked with: - """ # noqa: E501 line too long + """ ) assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == ( @@ -127,7 +125,7 @@ def test_mixed_args_and_kwargs(msg): 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple Invoked with: 1; kwargs: i=1 - """ # noqa: E501 line too long + """ ) with pytest.raises(TypeError) as excinfo: assert mpakd(1, 2, j=1) @@ -138,7 +136,7 @@ def test_mixed_args_and_kwargs(msg): 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple Invoked with: 1, 2; kwargs: j=1 - """ # noqa: E501 line too long + """ ) # Arguments after a py::args are automatically keyword-only (pybind 2.9+) @@ -343,7 +341,6 @@ def test_signatures(): ) -@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index 52b1b6335..654d96d49 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 @@ -200,7 +199,7 @@ def test_stl_caster_vs_stl_bind(msg): 1. (arg0: pybind11_cross_module_tests.VectorInt) -> int Invoked with: [1, 2, 3] - """ # noqa: E501 line too long + """ ) diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index fa026f9ed..5a95303e1 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 @@ -494,26 +493,17 @@ def test_overload_ordering(): assert m.overload_order("string") == 1 assert m.overload_order(0) == 4 - # Different for Python 2 vs. 3 - uni_name = type(u"").__name__ - assert "1. overload_order(arg0: int) -> int" in m.overload_order.__doc__ - assert ( - "2. overload_order(arg0: {}) -> int".format(uni_name) - in m.overload_order.__doc__ - ) - assert ( - "3. overload_order(arg0: {}) -> int".format(uni_name) - in m.overload_order.__doc__ - ) + assert "2. overload_order(arg0: str) -> int" in m.overload_order.__doc__ + assert "3. overload_order(arg0: str) -> int" in m.overload_order.__doc__ assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__ with pytest.raises(TypeError) as err: m.overload_order(1.1) assert "1. (arg0: int) -> int" in str(err.value) - assert "2. (arg0: {}) -> int".format(uni_name) in str(err.value) - assert "3. (arg0: {}) -> int".format(uni_name) in str(err.value) + assert "2. (arg0: str) -> int" in str(err.value) + assert "3. (arg0: str) -> int" in str(err.value) assert "4. (arg0: int) -> int" in str(err.value) diff --git a/tests/test_modules.py b/tests/test_modules.py index 49e1ea5e3..15db8355e 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from pybind11_tests import ConstructorStats from pybind11_tests import modules as m from pybind11_tests.modules import subsubmodule as ms diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index abdf25d60..3a1d88d71 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 @@ -13,8 +12,7 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2") +@pytest.mark.xfail("env.PYPY") def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -53,15 +51,14 @@ def test_multiple_inheritance_mix2(): assert mt.bar() == 4 -@pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2") +@pytest.mark.xfail("env.PYPY") def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): def __init__(self, i, j): m.Base1.__init__(self, i) m.Base2.__init__(self, j) - class B1(object): + class B1: def v(self): return 1 @@ -96,7 +93,7 @@ def test_multiple_inheritance_python(): def v(self): return 2 - class B3(object): + class B3: def v(self): return 3 diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index e4138f023..45be592a8 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 0098eccb8..65fd90d7b 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import re import pytest diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index de5c9a607..7e8c015c4 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest from pybind11_tests import numpy_vectorize as m diff --git a/tests/test_opaque_types.py b/tests/test_opaque_types.py index 5495cb6b4..8b9d8f59c 100644 --- a/tests/test_opaque_types.py +++ b/tests/test_opaque_types.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest from pybind11_tests import ConstructorStats, UserType @@ -40,7 +39,7 @@ def test_pointers(msg): 1. (arg0: capsule) -> int Invoked with: [1, 2, 3] - """ # noqa: E501 line too long + """ ) assert m.return_null_str() is None diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index 8cf375b6d..b228da3cc 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import pytest -import env from pybind11_tests import ConstructorStats from pybind11_tests import operators as m @@ -151,5 +149,4 @@ def test_overriding_eq_reset_hash(): def test_return_set_of_unhashable(): with pytest.raises(TypeError) as excinfo: m.get_unhashable_HashMe_set() - if not env.PY2: - assert str(excinfo.value.__cause__).startswith("unhashable type:") + assert str(excinfo.value.__cause__).startswith("unhashable type:") diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 9f68f37dc..52802ace8 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -1,14 +1,10 @@ -# -*- coding: utf-8 -*- +import pickle + import pytest import env from pybind11_tests import pickling as m -try: - import cPickle as pickle # Use cPickle on Python 2.7 -except ImportError: - import pickle - @pytest.mark.parametrize("cls_name", ["Pickleable", "PickleableNew"]) def test_roundtrip(cls_name): diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 9364cccf6..1ed237ea2 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -87,7 +87,6 @@ TEST_SUBMODULE(pytypes, m) { m.def("tuple_size_t", []() { return py::tuple{(py::size_t) 0}; }); m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); }); -#if PY_VERSION_HEX >= 0x03030000 // test_simple_namespace m.def("get_simple_namespace", []() { auto ns = py::module_::import("types").attr("SimpleNamespace")( @@ -96,7 +95,6 @@ TEST_SUBMODULE(pytypes, m) { py::setattr(ns, "right", py::int_(2)); return ns; }); -#endif // test_str m.def("str_from_char_ssize_t", []() { return py::str{"red", (py::ssize_t) 3}; }); @@ -423,12 +421,10 @@ TEST_SUBMODULE(pytypes, m) { return py::memoryview::from_buffer(static_cast(nullptr), 1, "B", {}, {}); }); -#if PY_MAJOR_VERSION >= 3 m.def("test_memoryview_from_memory", []() { const char *buf = "\xff\xe1\xab\x37"; return py::memoryview::from_memory(buf, static_cast(strlen(buf))); }); -#endif // test_builtin_functions m.def("get_len", [](py::handle h) { return py::len(h); }); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 2cd6c3f03..f2b1f6b6e 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -1,11 +1,8 @@ -# -*- coding: utf-8 -*- -from __future__ import division - import sys import pytest -import env +import env # noqa: F401 from pybind11_tests import debug_enabled from pybind11_tests import pytypes as m @@ -123,7 +120,6 @@ def test_tuple(): assert m.get_tuple() == (42, None, "spam") -@pytest.mark.skipif("env.PY2") def test_simple_namespace(): ns = m.get_simple_namespace() assert ns.attr == 42 @@ -140,7 +136,7 @@ def test_str(doc): assert doc(m.str_from_bytes) == "str_from_bytes() -> str" - class A(object): + class A: def __str__(self): return "this is a str" @@ -158,24 +154,14 @@ def test_str(doc): malformed_utf8 = b"\x80" if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): assert m.str_from_object(malformed_utf8) is malformed_utf8 - elif env.PY2: - with pytest.raises(UnicodeDecodeError): - m.str_from_object(malformed_utf8) else: assert m.str_from_object(malformed_utf8) == "b'\\x80'" - if env.PY2: - with pytest.raises(UnicodeDecodeError): - m.str_from_handle(malformed_utf8) - else: - assert m.str_from_handle(malformed_utf8) == "b'\\x80'" + assert m.str_from_handle(malformed_utf8) == "b'\\x80'" assert m.str_from_string_from_str("this is a str") == "this is a str" - ucs_surrogates_str = u"\udcc3" - if env.PY2: - assert u"\udcc3" == m.str_from_string_from_str(ucs_surrogates_str) - else: - with pytest.raises(UnicodeEncodeError): - m.str_from_string_from_str(ucs_surrogates_str) + ucs_surrogates_str = "\udcc3" + with pytest.raises(UnicodeEncodeError): + m.str_from_string_from_str(ucs_surrogates_str) def test_bytes(doc): @@ -184,9 +170,7 @@ def test_bytes(doc): assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_str().decode() == "bar" - assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "str" if env.PY2 else "bytes" - ) + assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes" def test_bytearray(doc): @@ -278,11 +262,6 @@ def test_constructors(): """C++ default and converting constructors are equivalent to type calls in Python""" types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set] expected = {t.__name__: t() for t in types} - if env.PY2: - # Note that bytes.__name__ == 'str' in Python 2. - # pybind11::str is unicode even under Python 2. - expected["bytes"] = bytes() - expected["str"] = unicode() # noqa: F821 assert m.default_constructors() == expected data = { @@ -300,11 +279,6 @@ def test_constructors(): } inputs = {k.__name__: v for k, v in data.items()} expected = {k.__name__: k(v) for k, v in data.items()} - if env.PY2: # Similar to the above. See comments above. - inputs["bytes"] = b"41" - inputs["str"] = 42 - expected["bytes"] = b"41" - expected["str"] = u"42" assert m.converting_constructors(inputs) == expected assert m.cast_functions(inputs) == expected @@ -340,46 +314,39 @@ def test_non_converting_constructors(): def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str - assert cvt(u"Str") == u"Str" - assert cvt(b"Bytes") == u"Bytes" if env.PY2 else "b'Bytes'" - assert cvt(None) == u"None" - assert cvt(False) == u"False" - assert cvt(True) == u"True" - assert cvt(42) == u"42" - assert cvt(2 ** 65) == u"36893488147419103232" - assert cvt(-1.50) == u"-1.5" - assert cvt(()) == u"()" - assert cvt((18,)) == u"(18,)" - assert cvt([]) == u"[]" - assert cvt([28]) == u"[28]" - assert cvt({}) == u"{}" - assert cvt({3: 4}) == u"{3: 4}" - assert cvt(set()) == u"set([])" if env.PY2 else "set()" - assert cvt({3, 3}) == u"set([3])" if env.PY2 else "{3}" + assert cvt("Str") == "Str" + assert cvt(b"Bytes") == "b'Bytes'" + assert cvt(None) == "None" + assert cvt(False) == "False" + assert cvt(True) == "True" + assert cvt(42) == "42" + assert cvt(2**65) == "36893488147419103232" + assert cvt(-1.50) == "-1.5" + assert cvt(()) == "()" + assert cvt((18,)) == "(18,)" + assert cvt([]) == "[]" + assert cvt([28]) == "[28]" + assert cvt({}) == "{}" + assert cvt({3: 4}) == "{3: 4}" + assert cvt(set()) == "set()" + assert cvt({3, 3}) == "{3}" - valid_orig = u"Η±" + valid_orig = "Η±" valid_utf8 = valid_orig.encode("utf-8") valid_cvt = cvt(valid_utf8) if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): assert valid_cvt is valid_utf8 else: - assert type(valid_cvt) is unicode if env.PY2 else str # noqa: F821 - if env.PY2: - assert valid_cvt == valid_orig - else: - assert valid_cvt == "b'\\xc7\\xb1'" + assert type(valid_cvt) is str + assert valid_cvt == "b'\\xc7\\xb1'" malformed_utf8 = b"\x80" if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): assert cvt(malformed_utf8) is malformed_utf8 else: - if env.PY2: - with pytest.raises(UnicodeDecodeError): - cvt(malformed_utf8) - else: - malformed_cvt = cvt(malformed_utf8) - assert type(malformed_cvt) is str - assert malformed_cvt == "b'\\x80'" + malformed_cvt = cvt(malformed_utf8) + assert type(malformed_cvt) is str + assert malformed_cvt == "b'\\x80'" def test_implicit_casting(): @@ -428,14 +395,14 @@ def test_print(capture): def test_hash(): - class Hashable(object): + class Hashable: def __init__(self, value): self.value = value def __hash__(self): return self.value - class Unhashable(object): + class Unhashable: __hash__ = None assert m.hash_function(Hashable(42)) == 42 @@ -493,12 +460,7 @@ def test_memoryview(method, args, fmt, expected_view): view = method(*args) assert isinstance(view, memoryview) assert view.format == fmt - if isinstance(expected_view, bytes) or not env.PY2: - view_as_list = list(view) - else: - # Using max to pick non-zero byte (big-endian vs little-endian). - view_as_list = [max(ord(c) for c in s) for s in view] - assert view_as_list == list(expected_view) + assert list(view) == list(expected_view) @pytest.mark.xfail("env.PYPY", reason="getrefcount is not available") @@ -522,12 +484,7 @@ def test_memoryview_from_buffer_empty_shape(): view = m.test_memoryview_from_buffer_empty_shape() assert isinstance(view, memoryview) assert view.format == "B" - if env.PY2: - # Python 2 behavior is weird, but Python 3 (the future) is fine. - # PyPy3 has List[int] Invoked with: - """ # noqa: E501 line too long + """ ) with pytest.raises(TypeError) as excinfo: @@ -295,7 +294,7 @@ def test_stl_pass_by_pointer(msg): 1. (v: List[int] = None) -> List[int] Invoked with: None - """ # noqa: E501 line too long + """ ) assert m.stl_pass_by_pointer([1, 2, 3]) == [1, 2, 3] diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 59c5ab6b5..d5e9ccced 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- import pytest -import env from pybind11_tests import stl_binders as m @@ -74,18 +72,13 @@ def test_vector_buffer(): assert v[1] == 2 v[2] = 5 mv = memoryview(v) # We expose the buffer interface - if not env.PY2: - assert mv[2] == 5 - mv[2] = 6 - else: - assert mv[2] == "\x05" - mv[2] = "\x06" + assert mv[2] == 5 + mv[2] = 6 assert v[2] == 6 - if not env.PY2: - mv = memoryview(b) - v = m.VectorUChar(mv[::2]) - assert v[1] == 3 + mv = memoryview(b) + v = m.VectorUChar(mv[::2]) + assert v[1] == 3 with pytest.raises(RuntimeError) as excinfo: m.create_undeclstruct() # Undeclared struct contents, no buffer interface diff --git a/tests/test_tagbased_polymorphic.py b/tests/test_tagbased_polymorphic.py index 64eb8a3c1..84f0ea717 100644 --- a/tests/test_tagbased_polymorphic.py +++ b/tests/test_tagbased_polymorphic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from pybind11_tests import tagbased_polymorphic as m diff --git a/tests/test_thread.py b/tests/test_thread.py index f9db1baba..e89991f9d 100644 --- a/tests/test_thread.py +++ b/tests/test_thread.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import threading from pybind11_tests import thread as m @@ -7,7 +5,7 @@ from pybind11_tests import thread as m class Thread(threading.Thread): def __init__(self, fn): - super(Thread, self).__init__() + super().__init__() self.fn = fn self.e = None @@ -19,7 +17,7 @@ class Thread(threading.Thread): self.e = e def join(self): - super(Thread, self).join() + super().join() if self.e: raise self.e diff --git a/tests/test_union.py b/tests/test_union.py index 2a2c12fb4..e1866e701 100644 --- a/tests/test_union.py +++ b/tests/test_union.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from pybind11_tests import union_ as m diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index 4f25cac4a..14a8384a8 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest import env # noqa: F401 @@ -10,12 +9,12 @@ from pybind11_tests import ConstructorStats # noqa: E402 def test_override(capture, msg): class ExtendedExampleVirt(m.ExampleVirt): def __init__(self, state): - super(ExtendedExampleVirt, self).__init__(state + 1) + super().__init__(state + 1) self.data = "Hello world" def run(self, value): print("ExtendedExampleVirt::run(%i), calling parent.." % value) - return super(ExtendedExampleVirt, self).run(value + 1) + return super().run(value + 1) def run_bool(self): print("ExtendedExampleVirt::run_bool()") @@ -29,7 +28,7 @@ def test_override(capture, msg): class ExtendedExampleVirt2(ExtendedExampleVirt): def __init__(self, state): - super(ExtendedExampleVirt2, self).__init__(state + 1) + super().__init__(state + 1) def get_string2(self): return "override2" @@ -41,7 +40,7 @@ def test_override(capture, msg): capture == """ Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2) - """ # noqa: E501 line too long + """ ) with pytest.raises(RuntimeError) as excinfo: @@ -59,7 +58,7 @@ def test_override(capture, msg): == """ ExtendedExampleVirt::run(20), calling parent.. Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2) - """ # noqa: E501 line too long + """ ) with capture: assert m.runExampleVirtBool(ex12p) is False @@ -76,7 +75,7 @@ def test_override(capture, msg): == """ ExtendedExampleVirt::run(50), calling parent.. Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2) - """ # noqa: E501 line too long + """ ) cstats = ConstructorStats.get(m.ExampleVirt) @@ -97,7 +96,7 @@ def test_alias_delay_initialization1(capture): class B(m.A): def __init__(self): - super(B, self).__init__() + super().__init__() def f(self): print("In python f()") @@ -137,7 +136,7 @@ def test_alias_delay_initialization2(capture): class B2(m.A2): def __init__(self): - super(B2, self).__init__() + super().__init__() def f(self): print("In python B2.f()") @@ -245,7 +244,7 @@ def test_dispatch_issue(msg): class PyClass2(m.DispatchIssue): def dispatch(self): with pytest.raises(RuntimeError) as excinfo: - super(PyClass2, self).dispatch() + super().dispatch() assert ( msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"' @@ -262,7 +261,7 @@ def test_recursive_dispatch_issue(msg): class Data(m.Data): def __init__(self, value): - super(Data, self).__init__() + super().__init__() self.value = value class Adder(m.Adder): diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index 3605aebcf..b3acaaae9 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -92,7 +92,7 @@ endif() # Use the Python interpreter to find the libs. if(NOT PythonLibsNew_FIND_VERSION) - set(PythonLibsNew_FIND_VERSION "") + set(PythonLibsNew_FIND_VERSION "3.5") endif() find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required} diff --git a/tools/libsize.py b/tools/libsize.py index 1551477e6..5fb20334d 100644 --- a/tools/libsize.py +++ b/tools/libsize.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import division, print_function - import os import sys diff --git a/tools/make_changelog.py b/tools/make_changelog.py index 629c284d3..839040a93 100755 --- a/tools/make_changelog.py +++ b/tools/make_changelog.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import re diff --git a/tools/pybind11Common.cmake b/tools/pybind11Common.cmake index df2478121..bbb722ac7 100644 --- a/tools/pybind11Common.cmake +++ b/tools/pybind11Common.cmake @@ -8,7 +8,6 @@ Adds the following targets:: pybind11::lto - Link time optimizations (manual selection) pybind11::thin_lto - Link time optimizations (manual selection) pybind11::python_link_helper - Adds link to Python libraries - pybind11::python2_no_register - Avoid warning/error with Python 2 + C++14/7 pybind11::windows_extras - MSVC bigobj and mp for building multithreaded pybind11::opt_size - avoid optimizations that increase code size @@ -66,31 +65,6 @@ set_property( APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) -# ----------------------- no register ---------------------- - -# Workaround for Python 2.7 and C++17 (C++14 as a warning) incompatibility -# This adds the flags -Wno-register and -Wno-deprecated-register if the compiler -# is Clang 3.9+ or AppleClang and the compile language is CXX, or /wd5033 for MSVC (all languages, -# since MSVC didn't recognize COMPILE_LANGUAGE until CMake 3.11+). - -add_library(pybind11::python2_no_register INTERFACE IMPORTED ${optional_global}) -set(clang_4plus - "$,$,3.9>>>") -set(no_register "$>") - -if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11) - set(cxx_no_register "${no_register}") -else() - set(cxx_no_register "$,${no_register}>") -endif() - -set(msvc "$") - -set_property( - TARGET pybind11::python2_no_register - PROPERTY INTERFACE_COMPILE_OPTIONS - "$<${cxx_no_register}:-Wno-register;-Wno-deprecated-register>" "$<${msvc}:/wd5033>") - # --------------------------- link helper --------------------------- add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 262020d14..6c084d1db 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -51,8 +51,6 @@ complex applications, and they are available in all modes: Python headers too. ``pybind11::python_link_helper`` Just the "linking" part of ``pybind11:module``, for CMake < 3.15. -``pybind11::python2_no_register`` - Quiets the warning/error when mixing C++14+ and Python 2, also included in ``pybind11::module``. ``pybind11::thin_lto`` An alternative to ``INTERPROCEDURAL_OPTIMIZATION``. ``pybind11::lto`` diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index 0b4e21cce..dbf41e071 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -22,9 +22,7 @@ else() set(_pybind11_quiet "") endif() -if(NOT Python_FOUND - AND NOT Python3_FOUND - AND NOT Python2_FOUND) +if(NOT Python_FOUND AND NOT Python3_FOUND) if(NOT DEFINED Python_FIND_IMPLEMENTATIONS) set(Python_FIND_IMPLEMENTATIONS CPython PyPy) endif() @@ -34,7 +32,7 @@ if(NOT Python_FOUND set(Python_ROOT_DIR "$ENV{pythonLocation}") endif() - find_package(Python REQUIRED COMPONENTS Interpreter Development ${_pybind11_quiet}) + find_package(Python 3.5 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. @@ -51,19 +49,10 @@ if(Python_FOUND) set(_Python Python CACHE INTERNAL "" FORCE) -elseif(Python3_FOUND AND NOT Python2_FOUND) +elseif(Python3_FOUND) set(_Python Python3 CACHE INTERNAL "" FORCE) -elseif(Python2_FOUND AND NOT Python3_FOUND) - set(_Python - Python2 - CACHE INTERNAL "" FORCE) -else() - message(AUTHOR_WARNING "Python2 and Python3 both present, pybind11 in " - "PYBIND11_NOPYTHON mode (manually activate to silence warning)") - set(_pybind11_nopython ON) - return() endif() if(PYBIND11_MASTER_PROJECT) @@ -137,7 +126,7 @@ if(PYTHON_IS_DEBUG) PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) endif() -# Check on every access - since Python2 and Python3 could have been used - do nothing in that case. +# Check on every access - since Python can change - do nothing in that case. if(DEFINED ${_Python}_INCLUDE_DIRS) # Only add Python for build - must be added during the import for config @@ -159,13 +148,6 @@ if(DEFINED ${_Python}_INCLUDE_DIRS) CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located") endif() -if(DEFINED ${_Python}_VERSION AND ${_Python}_VERSION VERSION_LESS 3) - set_property( - TARGET pybind11::pybind11 - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python2_no_register) -endif() - # In CMake 3.18+, you can find these separately, so include an if if(TARGET ${_Python}::Python) set_property( @@ -205,8 +187,6 @@ function(pybind11_add_module target_name) python_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) elseif("${_Python}" STREQUAL "Python3") python3_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) - elseif("${_Python}" STREQUAL "Python2") - python2_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) else() message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}") endif() @@ -223,10 +203,6 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) endif() - if(DEFINED ${_Python}_VERSION AND ${_Python}_VERSION VERSION_LESS 3) - target_link_libraries(${target_name} PRIVATE pybind11::python2_no_register) - endif() - # -fvisibility=hidden is required to allow multiple modules compiled against # different pybind versions to work properly, and for some features (e.g. # py::module_local). We force it on everything inside the `pybind11` diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index c255e5cfd..4ee8e52ce 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -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.4" + "3.11;3.10;3.9;3.8;3.7;3.6;3.5" CACHE INTERNAL "") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") @@ -122,13 +122,6 @@ set_property( INTERFACE_LINK_LIBRARIES pybind11::python_link_helper "$<$,$>:$>") -if(PYTHON_VERSION VERSION_LESS 3) - set_property( - TARGET pybind11::pybind11 - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python2_no_register) -endif() - set_property( TARGET pybind11::embed APPEND diff --git a/tools/setup_global.py.in b/tools/setup_global.py.in index 8b7e53871..63257447e 100644 --- a/tools/setup_global.py.in +++ b/tools/setup_global.py.in @@ -4,14 +4,9 @@ # 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. -import contextlib import glob import os import re -import shutil -import subprocess -import sys -import tempfile # Setuptools has to be before distutils from setuptools import setup diff --git a/tools/setup_main.py.in b/tools/setup_main.py.in index 533a75ae7..00d0fd57b 100644 --- a/tools/setup_main.py.in +++ b/tools/setup_main.py.in @@ -20,7 +20,7 @@ setup( "pybind11.share.cmake.pybind11", ], package_data={ - "pybind11": ["py.typed", "*.pyi"], + "pybind11": ["py.typed"], "pybind11.include.pybind11": ["*.h"], "pybind11.include.pybind11.detail": ["*.h"], "pybind11.include.pybind11.stl": ["*.h"], From 1a432b426fb9588789450fc4e7a3bf16f9c5d7f1 Mon Sep 17 00:00:00 2001 From: Chris Ohk Date: Sat, 12 Feb 2022 00:42:48 +0900 Subject: [PATCH 003/122] docs: Correct minor typos (#3721) --- noxfile.py | 4 ++-- tests/test_sequences_and_iterators.cpp | 2 +- tools/pybind11Config.cmake.in | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/noxfile.py b/noxfile.py index c8ab7441e..cfa84826d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,7 +2,7 @@ import nox nox.options.sessions = ["lint", "tests", "tests_packaging"] -PYTHON_VERISONS = ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] +PYTHON_VERSIONS = ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] @nox.session(reuse_venv=True) @@ -14,7 +14,7 @@ def lint(session: nox.Session) -> None: session.run("pre-commit", "run", "-a") -@nox.session(python=PYTHON_VERISONS) +@nox.session(python=PYTHON_VERSIONS) def tests(session: nox.Session) -> None: """ Run the tests (requires a compiler). diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index addbfe9bb..b867f49a2 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -452,7 +452,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) { [](IntPairs &self) { return py::make_value_iterator(self, py::call_guard()); }, py::keep_alive<0, 1>()); - // test_iterater_referencing + // test_iterator_referencing py::class_(m, "NonCopyableInt") .def(py::init()) .def("set", &NonCopyableInt::set) diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 6c084d1db..56e28cf29 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -86,7 +86,7 @@ you can either use the basic targets, or use the FindPython tools: set_target_properties(MyModule2 PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON CXX_VISIBILITY_PRESET ON - VISIBLITY_INLINES_HIDDEN ON) + VISIBILITY_INLINES_HIDDEN ON) If you build targets yourself, you may be interested in stripping the output for reduced size; this is the one other feature that the helper function gives you. From 522c59ceb27e83750c121d5da3d8b67ac446b754 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 11 Feb 2022 19:06:16 -0500 Subject: [PATCH 004/122] 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 Co-authored-by: Ralf W. Grosse-Kunstleve --- .github/CONTRIBUTING.md | 11 +++--- .github/workflows/ci.yml | 15 ++++----- .pre-commit-config.yaml | 39 ++++++++++++---------- README.rst | 14 ++++---- docs/advanced/cast/overview.rst | 3 +- docs/advanced/functions.rst | 21 ++++++------ docs/advanced/pycpp/numpy.rst | 2 +- docs/benchmark.py | 25 ++++++-------- docs/changelog.rst | 2 +- docs/compiling.rst | 9 +++-- docs/conf.py | 4 +-- docs/faq.rst | 2 +- docs/requirements.txt | 8 ++--- docs/upgrade.rst | 2 +- include/pybind11/cast.h | 2 +- include/pybind11/chrono.h | 11 ------ include/pybind11/detail/common.h | 19 +++-------- include/pybind11/detail/type_caster_base.h | 11 +++--- include/pybind11/pybind11.h | 6 ++-- include/pybind11/stl/filesystem.h | 3 +- noxfile.py | 2 +- pybind11/__init__.py | 4 +-- pybind11/setup_helpers.py | 10 +++--- setup.cfg | 16 ++------- setup.py | 20 ++++------- tests/extra_python_package/test_files.py | 18 +++++----- tests/extra_setuptools/test_setuphelper.py | 4 +-- tests/pytest.ini | 9 +++-- tests/requirements.txt | 16 ++++----- tests/test_builtin_casters.py | 2 +- tests/test_callbacks.py | 12 ++----- tests/test_chrono.py | 2 +- tests/test_cmake_build/test.py | 2 +- tests/test_eigen.py | 6 ++-- tests/test_exceptions.py | 2 +- tests/test_numpy_array.py | 13 +++----- tests/test_numpy_dtypes.py | 4 +-- tests/test_opaque_types.py | 2 +- tests/test_pytypes.py | 4 +-- tests/test_sequences_and_iterators.py | 21 ++++-------- tests/test_smart_ptr.py | 12 +++---- tests/test_virtual_functions.py | 4 +-- tools/FindPythonLibsNew.cmake | 2 +- tools/libsize.py | 4 +-- tools/pybind11Config.cmake.in | 2 +- tools/pybind11NewTools.cmake | 2 +- tools/pybind11Tools.cmake | 2 +- tools/setup_global.py.in | 3 +- tools/setup_main.py.in | 3 +- 49 files changed, 171 insertions(+), 241 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e8294c83c..c8ec91ff7 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -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 don’t 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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07580decd..b38d22835 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d44d5e76..564d50042 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/README.rst b/README.rst index 6398db4f0..0edcd9179 100644 --- a/README.rst +++ b/README.rst @@ -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 isn’t 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. -- It’s 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 Xcode’s 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 diff --git a/docs/advanced/cast/overview.rst b/docs/advanced/cast/overview.rst index 6a834a3e5..011bd4c7a 100644 --- a/docs/advanced/cast/overview.rst +++ b/docs/advanced/cast/overview.rst @@ -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``. diff --git a/docs/advanced/functions.rst b/docs/advanced/functions.rst index bf5b5fa00..69e3d8a1d 100644 --- a/docs/advanced/functions.rst +++ b/docs/advanced/functions.rst @@ -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 ========================= diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst index dbc48440a..8ad341004 100644 --- a/docs/advanced/pycpp/numpy.rst +++ b/docs/advanced/pycpp/numpy.rst @@ -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. diff --git a/docs/benchmark.py b/docs/benchmark.py index a59534292..2150b6ca7 100644 --- a/docs/benchmark.py +++ b/docs/benchmark.py @@ -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_(m, "cl%03i")\n' % (cl, cl) + bindings += f' py::class_(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")\n' % (cl, cl) + bindings += f' py::class_("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())\n' - % (fn, cl, fn) - ) + bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy())\n' decl += "};\n\n" bindings += " ;\n" diff --git a/docs/changelog.rst b/docs/changelog.rst index 63c7f41d8..9d7693cdd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -800,7 +800,7 @@ Packaging / building improvements: `#2338 `_ and `#2370 `_ - * Full integration with CMake’s 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 diff --git a/docs/compiling.rst b/docs/compiling.rst index f7303fd23..4ae9234e1 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -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 `_), diff --git a/docs/conf.py b/docs/conf.py index 99f3be349..00796fbdb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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): diff --git a/docs/faq.rst b/docs/faq.rst index 69c3442e3..a6b991312 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -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 diff --git a/docs/requirements.txt b/docs/requirements.txt index b2801b1f0..e452ed261 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -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 diff --git a/docs/upgrade.rst b/docs/upgrade.rst index d91d51e6f..6a9db2d08 100644 --- a/docs/upgrade.rst +++ b/docs/upgrade.rst @@ -524,7 +524,7 @@ include a declaration of the form: PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) -Continuing to do so won’t 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. diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index b12559ac8..553d7616c 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1585,7 +1585,7 @@ unpacking_collector collect_arguments(Args &&...args) { template template object object_api::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."); } diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h index ad5e97b02..167ea0e3d 100644 --- a/include/pybind11/chrono.h +++ b/include/pybind11/chrono.h @@ -18,17 +18,6 @@ #include #include -// 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) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index a5d98d71f..c1d2de13a 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -211,8 +211,8 @@ #endif #include -#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 #include @@ -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 - object’s 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 object’s 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 property’s 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 diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 226f7a994..ff7d7cf8c 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -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 diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 7b7a1a05d..22b629f40 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -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(m); } }; diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index 747992f51..93727424c 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -13,8 +13,7 @@ #include #ifdef __has_include -# if defined(PYBIND11_CPP17) && __has_include() && \ - PY_VERSION_HEX >= 0x03060000 +# if defined(PYBIND11_CPP17) && __has_include() # include # define PYBIND11_HAS_FILESYSTEM 1 # endif diff --git a/noxfile.py b/noxfile.py index cfa84826d..4b6adcfbd 100644 --- a/noxfile.py +++ b/noxfile.py @@ -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) diff --git a/pybind11/__init__.py b/pybind11/__init__.py index faf3892cb..a1be96981 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -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) diff --git a/pybind11/setup_helpers.py b/pybind11/setup_helpers.py index 9cbcaa922..e3271ca60 100644 --- a/pybind11/setup_helpers.py +++ b/pybind11/setup_helpers.py @@ -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: """ diff --git a/setup.cfg b/setup.cfg index bd2357e9e..11b4fde8f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 diff --git a/setup.py b/setup.py index 5b46e0c8d..a149755bf 100644 --- a/setup.py +++ b/setup.py @@ -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) diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index 70dcfb987..a6b13bc5f 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -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", diff --git a/tests/extra_setuptools/test_setuphelper.py b/tests/extra_setuptools/test_setuphelper.py index 3a771e558..d5d3093bf 100644 --- a/tests/extra_setuptools/test_setuphelper.py +++ b/tests/extra_setuptools/test_setuphelper.py @@ -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", ) diff --git a/tests/pytest.ini b/tests/pytest.ini index a3871d6c3..792ba361f 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -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 diff --git a/tests/requirements.txt b/tests/requirements.txt index 98ca46d28..1130c3fd5 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -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" diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 74014c9cf..02207f24f 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -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" diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 74aec21e5..0b1047bbf 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -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}") diff --git a/tests/test_chrono.py b/tests/test_chrono.py index f419ef387..7f47b37a2 100644 --- a/tests/test_chrono.py +++ b/tests/test_chrono.py @@ -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) diff --git a/tests/test_cmake_build/test.py b/tests/test_cmake_build/test.py index 5bf129ea3..807fd43b4 100644 --- a/tests/test_cmake_build/test.py +++ b/tests/test_cmake_build/test.py @@ -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") diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 9a94b35f8..093773a51 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -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): diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index e0da55dfd..0be61804a 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -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 diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 45be592a8..504963b16 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -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 diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 65fd90d7b..7df60583f 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -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')]", diff --git a/tests/test_opaque_types.py b/tests/test_opaque_types.py index 8b9d8f59c..5d4f2a1bf 100644 --- a/tests/test_opaque_types.py +++ b/tests/test_opaque_types.py @@ -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]" diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index f2b1f6b6e..07ace0049 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -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 diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index e1441c07c..062e3b3d3 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -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 diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 4097df28b..2f204e01b 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -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 diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index 14a8384a8..4d00d3690 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -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): diff --git a/tools/FindPythonLibsNew.cmake b/tools/FindPythonLibsNew.cmake index b3acaaae9..7aeffa441 100644 --- a/tools/FindPythonLibsNew.cmake +++ b/tools/FindPythonLibsNew.cmake @@ -92,7 +92,7 @@ endif() # Use the Python interpreter to find the libs. if(NOT PythonLibsNew_FIND_VERSION) - set(PythonLibsNew_FIND_VERSION "3.5") + set(PythonLibsNew_FIND_VERSION "3.6") endif() find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} ${_pythonlibs_required} diff --git a/tools/libsize.py b/tools/libsize.py index 5fb20334d..1ac9afbe8 100644 --- a/tools/libsize.py +++ b/tools/libsize.py @@ -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() diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 56e28cf29..9383e8c67 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -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@ diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index dbf41e071..abba0fe0e 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -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. diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 4ee8e52ce..3d1503df8 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -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}") diff --git a/tools/setup_global.py.in b/tools/setup_global.py.in index 63257447e..8aa387178 100644 --- a/tools/setup_global.py.in +++ b/tools/setup_global.py.in @@ -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. diff --git a/tools/setup_main.py.in b/tools/setup_main.py.in index 00d0fd57b..738d73faa 100644 --- a/tools/setup_main.py.in +++ b/tools/setup_main.py.in @@ -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) From 44156477a39368c02dcbe19b771f0ab6fe8ae9bf Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 14 Feb 2022 09:29:06 -0800 Subject: [PATCH 005/122] Adding MSVC 2022 C++20 GitHub Action (#3732) * Trying another way of adding windows-2022 C++20 * ci: try merging 2019 & 2022 * Revert "ci: try merging 2019 & 2022" This reverts commit b158a1b79f69a43478dce5b89702858ad5952386. Co-authored-by: Henry Schreiner --- .github/workflows/ci.yml | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b38d22835..629879a5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -852,6 +852,51 @@ jobs: - name: Run all checks run: cmake --build build -t check + windows-2022: + strategy: + fail-fast: false + matrix: + python: + - 3.9 + + name: "🐍 ${{ matrix.python }} β€’ MSVC 2022 C++20 β€’ x64" + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Prepare env + run: | + python3 -m pip install -r tests/requirements.txt + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.12 + + - name: Configure C++20 + run: > + cmake -S . -B build + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=OFF + -DCMAKE_CXX_STANDARD=20 + + - name: Build C++20 + run: cmake --build build -j 2 + + - name: Python tests + run: cmake --build build --target pytest + + - name: C++20 tests + run: cmake --build build --target cpptest -j 2 + + - name: Interface test C++20 + run: cmake --build build --target test_cmake_build + mingw: name: "🐍 3 β€’ windows-latest β€’ ${{ matrix.sys }}" runs-on: windows-latest From a97e9d8cacadfddc3ff19790242e335499639168 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 14 Feb 2022 11:36:22 -0800 Subject: [PATCH 006/122] Dropping MSVC 2015 (#3722) * Changing `_MSC_VER` guard to `< 1910` (dropping MSVC 2015). * Removing MSVC 2015 from ci.yml, and .appveyor.yml entirely. * Bringing back .appveyor.yml from master. * appveyor Visual Studio 2017 * 1st manual pass, builds & tests with unix_clang, before pre-commit. * After clang-format (via pre-commit). * Manual pass looking for "2015", builds & tests with unix_clang, before pre-commit. * Backtracking for include/pybind11 changes in previous commit. git checkout d07865846c7d31dd61111e6df801864327b65070 include/pybind11/attr.h include/pybind11/detail/common.h include/pybind11/functional.h -------------------- CI #4160 errors observed: https://github.com/pybind/pybind11/pull/3722/commits/2a26873727214c5f1e159cba98f5c625b908381a https://github.com/pybind/pybind11/runs/5168332130?check_suite_focus=true $ grep ' error C' *.txt | sed 's/2022-02-12[^ ]*//' | sed 's/^[0-9][0-9]*//' | sed 's/^.*\.txt: //' | sort | uniqD:\a\pybind11\pybind11\include\pybind11\cast.h(1364,1): error C2752: 'pybind11::detail::type_caster,void>': more than one partial specialization matches the template argument list [D:\a\pybind11\pybind11\build\tests\pybind11_tests.vcxproj] d:\a\pybind11\pybind11\include\pybind11\detail/common.h(1023): error C2737: 'pybind11::overload_cast': 'constexpr' object must be initialized [D:\a\pybind11\pybind11\build\tests\cross_module_gil_utils.vcxproj] d:\a\pybind11\pybind11\include\pybind11\detail/common.h(1023): error C2737: 'pybind11::overload_cast': 'constexpr' object must be initialized [D:\a\pybind11\pybind11\build\tests\pybind11_cross_module_tests.vcxproj] d:\a\pybind11\pybind11\include\pybind11\detail/common.h(1023): error C2737: 'pybind11::overload_cast': 'constexpr' object must be initialized [D:\a\pybind11\pybind11\build\tests\pybind11_tests.vcxproj] d:\a\pybind11\pybind11\include\pybind11\detail/common.h(1023): error C2737: 'pybind11::overload_cast': 'constexpr' object must be initialized [D:\a\pybind11\pybind11\build\tests\test_embed\external_module.vcxproj] D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\build\tests\pybind11_tests.vcxproj] D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] $ grep ': error C2737' *.txt | sed 's/^.*MSVC//' | sed 's/___.*//' | sort | uniq _2017 $ grep ': error C2752' *.txt 3______3.8_____MSVC_2019_____x86_-DCMAKE_CXX_STANDARD=17.txt:2022-02-12T16:12:45.9921122Z D:\a\pybind11\pybind11\include\pybind11\cast.h(1364,1): error C2752: 'pybind11::detail::type_caster,void>': more than one partial specialization matches the template argument list [D:\a\pybind11\pybind11\build\tests\pybind11_tests.vcxproj] $ grep ': fatal error C1001:' *.txt 10______pypy-3.8-v7.3.7_____windows-2022_____x64.txt:2022-02-12T16:12:56.3163683Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] 1______3.6_____MSVC_2019_____x86.txt:2022-02-12T16:12:47.6774625Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\build\tests\pybind11_tests.vcxproj] 16______3.6_____windows-latest_____x64_-DPYBIND11_FINDPYTHON=ON.txt:2022-02-12T16:12:27.0556151Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] 17______3.9_____windows-2019_____x64.txt:2022-02-12T16:12:30.3822566Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] 2______3.7_____MSVC_2019_____x86.txt:2022-02-12T16:12:38.7018911Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\build\tests\pybind11_tests.vcxproj] 6______3.6_____windows-2022_____x64.txt:2022-02-12T16:12:00.4513642Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] 7______3.9_____windows-2022_____x64.txt:2022-02-12T16:11:43.6306160Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] 8______3.10_____windows-2022_____x64.txt:2022-02-12T16:11:49.9589644Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] 9______pypy-3.7-v7.3.7_____windows-2022_____x64.txt:2022-02-12T16:11:53.7912112Z D:\a\pybind11\pybind11\include\pybind11\detail/common.h(624): fatal error C1001: Internal compiler error. [D:\a\pybind11\pybind11\tests\pybind11_tests.vcxproj] * common.h: is_template_base_of * Re-applying 4 changes from 2a26873727214c5f1e159cba98f5c625b908381a that work universally. * `overload_cast = {};` only for MSVC 2017 and Clang 5 * Refining condition for using is_template_base_of workaround. * Undoing MSVC 2015 workaround in test_constants_and_functions.cpp * CentOS7: silence_unused_warnings * Tweaks in response to reviews. * Adding windows-2022 C++20 * Trying another way of adding windows-2022 C++20 --- .appveyor.yml | 4 +- .github/workflows/ci.yml | 47 -------------- README.rst | 2 +- docs/advanced/cast/stl.rst | 2 - docs/advanced/classes.rst | 7 -- docs/basics.rst | 3 +- docs/classes.rst | 3 +- docs/faq.rst | 14 ---- include/pybind11/attr.h | 5 +- include/pybind11/detail/common.h | 27 ++++---- include/pybind11/functional.h | 3 +- tests/pybind11_tests.h | 6 -- tests/test_class.cpp | 12 +--- tests/test_const_name.cpp | 89 +++++++++++--------------- tests/test_constants_and_functions.cpp | 15 ++--- tests/test_eigen.cpp | 3 - tests/test_factory_constructors.cpp | 7 -- tests/test_iostream.cpp | 4 -- tests/test_smart_ptr.cpp | 4 -- tests/test_stl.cpp | 2 +- tests/test_virtual_functions.cpp | 3 +- 21 files changed, 67 insertions(+), 195 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f3bc9cd49..360760ac8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,6 +1,6 @@ version: 1.0.{build} image: -- Visual Studio 2015 +- Visual Studio 2017 test: off skip_branch_with_pr: true build: @@ -13,7 +13,7 @@ environment: CONFIG: Debug install: - ps: | - $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + $env:CMAKE_GENERATOR = "Visual Studio 15 2017" if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" python -W ignore -m pip install --upgrade pip wheel diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 629879a5b..6bf56c4e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -755,53 +755,6 @@ jobs: - name: Python tests run: cmake --build build -t pytest - win32-msvc2015: - name: "🐍 ${{ matrix.python }} β€’ MSVC 2015 β€’ x64" - runs-on: windows-latest - strategy: - fail-fast: false - matrix: - python: - - 3.6 - - 3.7 - # todo: check/cpptest does not support 3.8+ yet - - steps: - - uses: actions/checkout@v2 - - - name: Setup 🐍 ${{ matrix.python }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python }} - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.12 - - - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.10.0 - with: - toolset: 14.0 - - - name: Prepare env - run: | - python -m pip install -r tests/requirements.txt - - # First build - C++11 mode and inplace - - name: Configure - run: > - cmake -S . -B build - -G "Visual Studio 14 2015" -A x64 - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - - - name: Build C++14 - run: cmake --build build -j 2 - - - name: Run all checks - run: cmake --build build -t check - - win32-msvc2017: name: "🐍 ${{ matrix.python }} β€’ MSVC 2017 β€’ x64" runs-on: windows-2016 diff --git a/README.rst b/README.rst index 0edcd9179..3c75edb57 100644 --- a/README.rst +++ b/README.rst @@ -122,7 +122,7 @@ Supported compilers 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 +3. Microsoft Visual Studio 2017 or newer 4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) 5. Cygwin/GCC (previously tested on 2.5.1) 6. NVCC (CUDA 11.0 tested in CI) diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst index b8622ee09..109763f7a 100644 --- a/docs/advanced/cast/stl.rst +++ b/docs/advanced/cast/stl.rst @@ -87,8 +87,6 @@ included to tell pybind11 how to visit the variant. pybind11 only supports the modern implementation of ``boost::variant`` which makes use of variadic templates. This requires Boost 1.56 or newer. - Additionally, on Windows, MSVC 2017 is required because ``boost::variant`` - falls back to the old non-variadic implementation on MSVC 2015. .. _opaque: diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 3e8ba7385..49ddf5c0b 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -1118,13 +1118,6 @@ described trampoline: py::class_(m, "A") // <-- `Trampoline` here .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! -.. note:: - - MSVC 2015 has a compiler bug (fixed in version 2017) which - requires a more explicit function binding in the form of - ``.def("foo", static_cast(&Publicist::foo));`` - where ``int (A::*)() const`` is the type of ``A::foo``. - Binding final classes ===================== diff --git a/docs/basics.rst b/docs/basics.rst index d40992d38..e9b24c7fa 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -32,8 +32,7 @@ The last line will both compile and run the tests. Windows ------- -On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies -on various C++11 language features that break older versions of Visual Studio. +On Windows, only **Visual Studio 2017** and newer are supported. .. Note:: diff --git a/docs/classes.rst b/docs/classes.rst index b833bc4ba..0c687b7c5 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -434,8 +434,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth .def("set", overload_cast_()(&Pet::set), "Set the pet's age") .def("set", overload_cast_()(&Pet::set), "Set the pet's name"); -.. [#cpp14] A compiler which supports the ``-std=c++14`` flag - or Visual Studio 2015 Update 2 and newer. +.. [#cpp14] A compiler which supports the ``-std=c++14`` flag. .. note:: diff --git a/docs/faq.rst b/docs/faq.rst index a6b991312..28498e7df 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -220,20 +220,6 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids potential serious issues when loading multiple modules and is required for proper pybind operation. See the previous FAQ entry for more details. -Working with ancient Visual Studio 2008 builds on Windows -========================================================= - -The official Windows distributions of Python are compiled using truly -ancient versions of Visual Studio that lack good C++11 support. Some users -implicitly assume that it would be impossible to load a plugin built with -Visual Studio 2015 into a Python distribution that was compiled using Visual -Studio 2008. However, no such issue exists: it's perfectly legitimate to -interface DLLs that are built with different compilers and/or C libraries. -Common gotchas to watch out for involve not ``free()``-ing memory region -that that were ``malloc()``-ed in another shared library, using data -structures with incompatible ABIs, and so on. pybind11 is very careful not -to make these types of mistakes. - How can I properly handle Ctrl-C in long-running functions? =========================================================== diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index de95f89d0..38139afa3 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -61,7 +61,7 @@ struct base { PYBIND11_DEPRECATED( "base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() {} // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + base() = default; }; /// Keep patient alive while nurse lives @@ -82,8 +82,7 @@ struct metaclass { handle value; PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - // NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute - metaclass() {} + metaclass() = default; /// Override pybind11's default metaclass explicit metaclass(handle value) : value(value) {} diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index c1d2de13a..1070d686e 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -47,7 +47,7 @@ // or newer. # if _MSVC_LANG >= 201402L # define PYBIND11_CPP14 -# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# if _MSVC_LANG > 201402L # define PYBIND11_CPP17 # if _MSVC_LANG >= 202002L # define PYBIND11_CPP20 @@ -81,10 +81,8 @@ # error pybind11 requires gcc 4.8 or newer # endif #elif defined(_MSC_VER) -// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features -// (e.g. std::negation) added in 2015u3: -# if _MSC_FULL_VER < 190024210 -# error pybind11 requires MSVC 2015 update 3 or newer +# if _MSC_VER < 1910 +# error pybind11 2.10+ requires MSVC 2017 or newer # endif #endif @@ -149,7 +147,7 @@ /* Don't let Python.h #define (v)snprintf as macro because they are implemented properly in Visual Studio since 2015. */ -#if defined(_MSC_VER) && _MSC_VER >= 1900 +#if defined(_MSC_VER) # define HAVE_SNPRINTF 1 #endif @@ -561,7 +559,7 @@ static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); /// from __cpp_future__ import (convenient aliases from C++14/17) -#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +#if defined(PYBIND11_CPP14) using std::conditional_t; using std::enable_if_t; using std::remove_cv_t; @@ -824,10 +822,10 @@ struct is_template_base_of_impl { /// Check if a template is the base of a type. For example: /// `is_template_base_of` is true if `struct T : Base {}` where U can be anything template