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"],