Python 2 removal part 1: tests (C++ code is intentionally ~untouched) (#3688)

* `#error BYE_BYE_GOLDEN_SNAKE`

* Removing everything related to 2.7 from ci.yml

* Commenting-out Centos7

* Removing `PYTHON: 27` from .appveyor.yml

* "PY2" removal, mainly from tests. C++ code is not touched.

* Systematic removal of `u` prefix from `u"..."` and `u'...'` literals. Collateral cleanup of a couple minor other things.

* Cleaning up around case-insensitive hits for `[^a-z]py.*2` in tests/.

* Removing obsolete Python 2 mention in compiling.rst

* Proper `#error` for Python 2.

* Using PY_VERSION_HEX to guard `#error "PYTHON 2 IS NO LONGER SUPPORTED.`

* chore: bump pre-commit

* style: run pre-commit for pyupgrade 3+

* tests: use sys.version_info, not PY

* chore: more Python 2 removal

* Uncommenting Centos7 block (PR #3691 showed that it is working again).

* Update pre-commit hooks

* Fix pre-commit hook

* refactor: remove Python 2 from CMake

* refactor: remove Python 2 from setup code

* refactor: simplify, better static typing

* feat: fail with nice messages

* refactor: drop Python 2 C++ code

* docs: cleanup for Python 3

* revert: intree

revert: intree

* docs: minor touchup to py2 statement

Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com>
This commit is contained in:
Ralf W. Grosse-Kunstleve 2022-02-10 18:28:08 -08:00 committed by GitHub
parent 46dcd9bc75
commit 6493f496e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
102 changed files with 529 additions and 1334 deletions

View File

@ -11,8 +11,6 @@ environment:
matrix: matrix:
- PYTHON: 36 - PYTHON: 36
CONFIG: Debug CONFIG: Debug
- PYTHON: 27
CONFIG: Debug
install: install:
- ps: | - ps: |
$env:CMAKE_GENERATOR = "Visual Studio 14 2015" $env:CMAKE_GENERATOR = "Visual Studio 14 2015"

View File

@ -25,7 +25,6 @@ jobs:
matrix: matrix:
runs-on: [ubuntu-latest, windows-2022, macos-latest] runs-on: [ubuntu-latest, windows-2022, macos-latest]
python: python:
- '2.7'
- '3.5' - '3.5'
- '3.6' - '3.6'
- '3.9' - '3.9'
@ -49,13 +48,9 @@ jobs:
python: '3.6' python: '3.6'
args: > args: >
-DPYBIND11_FINDPYTHON=ON -DPYBIND11_FINDPYTHON=ON
- runs-on: macos-latest
python: 'pypy-2.7'
# Inject a couple Windows 2019 runs # Inject a couple Windows 2019 runs
- runs-on: windows-2019 - runs-on: windows-2019
python: '3.9' python: '3.9'
- runs-on: windows-2019
python: '2.7'
name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}"
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
@ -168,22 +163,6 @@ jobs:
- name: Interface test - name: Interface test
run: cmake --build build2 --target test_cmake_build 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 # This makes sure the setup_helpers module can build packages using
# setuptools # setuptools
- name: Setuptools helpers test - name: Setuptools helpers test
@ -784,7 +763,6 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python: python:
- 2.7
- 3.6 - 3.6
- 3.7 - 3.7
# todo: check/cpptest does not support 3.8+ yet # todo: check/cpptest does not support 3.8+ yet
@ -832,17 +810,12 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
python: python:
- 2.7
- 3.5 - 3.5
- 3.7 - 3.7
std: std:
- 14 - 14
include: include:
- python: 2.7
std: 17
args: >
-DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR"
- python: 3.7 - python: 3.7
std: 17 std: 17
args: > args: >

View File

@ -17,19 +17,19 @@ env:
jobs: jobs:
# This builds the sdists and wheels and makes sure the files are exactly as # 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. # challenging matrix element.
test-packaging: test-packaging:
name: 🐍 2.7 • 📦 tests • windows-latest name: 🐍 3.6 • 📦 tests • windows-latest
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Setup 🐍 2.7 - name: Setup 🐍 3.6
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 2.7 python-version: 3.6
- name: Prepare env - name: Prepare env
run: | run: |

View File

@ -29,14 +29,15 @@ repos:
- id: mixed-line-ending - id: mixed-line-ending
- id: requirements-txt-fixer - id: requirements-txt-fixer
- id: trailing-whitespace - id: trailing-whitespace
- id: fix-encoding-pragma
exclude: ^noxfile.py$
# Upgrade old Python syntax
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.31.0 rev: v2.31.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py3-plus]
# Nicely sort includes
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.10.1 rev: 5.10.1
hooks: hooks:
@ -44,20 +45,21 @@ repos:
# Black, the code formatter, natively supports pre-commit # Black, the code formatter, natively supports pre-commit
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 21.12b0 # Keep in sync with blacken-docs rev: 22.1.0 # Keep in sync with blacken-docs
hooks: hooks:
- id: black - id: black
# Also code format the docs
- repo: https://github.com/asottile/blacken-docs - repo: https://github.com/asottile/blacken-docs
rev: v1.12.0 rev: v1.12.1
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: 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 # Changes tabs to spaces
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.1.10 rev: v1.1.11
hooks: hooks:
- id: remove-tabs - id: remove-tabs
@ -67,12 +69,15 @@ repos:
hooks: hooks:
- id: pycln - id: pycln
# Checking for common mistakes
- repo: https://github.com/pre-commit/pygrep-hooks - repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0 rev: v1.9.0
hooks: hooks:
- id: python-check-blanket-noqa - id: python-check-blanket-noqa
- id: python-check-blanket-type-ignore - id: python-check-blanket-type-ignore
- id: python-no-log-warn - id: python-no-log-warn
# Python 3.6
# - id: python-use-type-annotations
- id: rst-backticks - id: rst-backticks
- id: rst-directive-colons - id: rst-directive-colons
- id: rst-inline-touching-normal - id: rst-inline-touching-normal
@ -87,6 +92,7 @@ repos:
- pep8-naming - pep8-naming
exclude: ^(docs/.*|tools/.*)$ exclude: ^(docs/.*|tools/.*)$
# Automatically remove noqa that are not used
- repo: https://github.com/asottile/yesqa - repo: https://github.com/asottile/yesqa
rev: v1.3.0 rev: v1.3.0
hooks: hooks:
@ -107,9 +113,9 @@ repos:
rev: v0.931 rev: v0.931
hooks: hooks:
- id: mypy - id: mypy
# Running per-file misbehaves a bit, so just run on all files, it's fast args: [--show-error-codes]
pass_filenames: false exclude: ^(tests|docs)/
additional_dependencies: [typed_ast] additional_dependencies: [nox, rich]
# Checks the manifest for missing files (native support) # Checks the manifest for missing files (native support)
- repo: https://github.com/mgedmin/check-manifest - repo: https://github.com/mgedmin/check-manifest
@ -120,6 +126,7 @@ repos:
stages: [manual] stages: [manual]
additional_dependencies: [cmake, ninja] additional_dependencies: [cmake, ninja]
# Check for spelling
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.1.0 rev: v2.1.0
hooks: hooks:
@ -127,20 +134,22 @@ repos:
exclude: ".supp$" exclude: ".supp$"
args: ["-L", "nd,ot,thist"] args: ["-L", "nd,ot,thist"]
# Check for common shell mistakes
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.8.0.3 rev: v0.8.0.4
hooks: hooks:
- id: shellcheck - id: shellcheck
# The original pybind11 checks for a few C++ style items # Disallow some common capitalization mistakes
- repo: local - repo: local
hooks: hooks:
- id: disallow-caps - id: disallow-caps
name: Disallow improper capitalization name: Disallow improper capitalization
language: pygrep language: pygrep
entry: PyBind|Numpy|Cmake|CCache|PyTest 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 - repo: https://github.com/pre-commit/mirrors-clang-format
rev: "v13.0.0" rev: "v13.0.0"
hooks: hooks:

View File

@ -1,6 +1,5 @@
recursive-include pybind11/include/pybind11 *.h recursive-include pybind11/include/pybind11 *.h
recursive-include pybind11 *.py recursive-include pybind11 *.py
recursive-include pybind11 py.typed recursive-include pybind11 py.typed
recursive-include pybind11 *.pyi
include pybind11/share/cmake/pybind11/*.cmake include pybind11/share/cmake/pybind11/*.cmake
include LICENSE README.rst pyproject.toml setup.py setup.cfg include LICENSE README.rst pyproject.toml setup.py setup.cfg

View File

@ -34,7 +34,7 @@ dependency.
Think of this library as a tiny self-contained version of Boost.Python Think of this library as a tiny self-contained version of Boost.Python
with everything stripped away that isnt relevant for binding with everything stripped away that isnt relevant for binding
generation. Without comments, the core header files only require ~4K generation. Without comments, the core header files only require ~4K
lines of code and depend on Python (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 standard library. This compact implementation was possible thanks to
some of the new C++11 language features (specifically: tuples, lambda some of the new C++11 language features (specifically: tuples, lambda
functions and variadic templates). Since its creation, this library has functions and variadic templates). Since its creation, this library has
@ -78,8 +78,8 @@ Goodies
In addition to the core functionality, pybind11 provides some extra In addition to the core functionality, pybind11 provides some extra
goodies: goodies:
- Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an - Python 3.5+, and PyPy3 7.3 are supported with an implementation-agnostic
implementation-agnostic interface. interface (pybind11 2.9 was the last version to support Python 2).
- It is possible to bind C++11 lambda functions with captured - It is possible to bind C++11 lambda functions with captured
variables. The lambda capture data is stored inside the resulting variables. The lambda capture data is stored inside the resulting

View File

@ -18,5 +18,4 @@ ALIASES += "endrst=\endverbatim"
QUIET = YES QUIET = YES
WARNINGS = YES WARNINGS = YES
WARN_IF_UNDOCUMENTED = NO WARN_IF_UNDOCUMENTED = NO
PREDEFINED = PY_MAJOR_VERSION=3 \ PREDEFINED = PYBIND11_NOINLINE
PYBIND11_NOINLINE

View File

@ -1,14 +1,6 @@
Strings, bytes and Unicode conversions 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++ 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 A Python ``bytes`` object will be passed to C++ functions that accept
``std::string`` or ``char*`` *without* conversion. On Python 3, in order to ``std::string`` or ``char*`` *without* conversion. In order to make a function
make a function *only* accept ``bytes`` (and not ``str``), declare it as taking *only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes``
a ``py::bytes`` argument. argument.
Returning C++ strings to Python 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 Strings in multibyte encodings such as Shift-JIS must transcoded to a
UTF-8/16/32 before being returned to Python. UTF-8/16/32 before being returned to Python.

View File

@ -133,14 +133,14 @@ a virtual method call.
>>> from example import * >>> from example import *
>>> d = Dog() >>> d = Dog()
>>> call_go(d) >>> call_go(d)
u'woof! woof! woof! ' 'woof! woof! woof! '
>>> class Cat(Animal): >>> class Cat(Animal):
... def go(self, n_times): ... def go(self, n_times):
... return "meow! " * n_times ... return "meow! " * n_times
... ...
>>> c = Cat() >>> c = Cat()
>>> call_go(c) >>> call_go(c)
u'meow! meow! meow! ' 'meow! meow! meow! '
If you are defining a custom constructor in a derived Python class, you *must* 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__``, 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 .. code-block:: python
try: import pickle
import cPickle as pickle # Use cPickle on Python 2.7
except ImportError:
import pickle
p = Pickleable("test_value") p = Pickleable("test_value")
p.setExtra(15) p.setExtra(15)
data = pickle.dumps(p, 2) data = pickle.dumps(p)
.. note:: .. note::
Note that only the cPickle module is supported on Python 2.7. 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
The second argument to ``dumps`` is also crucial: it selects the pickle always use the latest available version. Beware: failure to follow these
protocol version 2, since the older version 1 is not supported. Newer instructions will cause important pybind11 memory allocation routines to be
versions are also fine—for instance, specify ``-1`` to always use the skipped during unpickling, which will likely lead to memory corruption
latest available version. Beware: failure to follow these instructions and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and
will cause important pybind11 memory allocation routines to be skipped version 4 for Python 3.8+.
during unpickling, which will likely lead to memory corruption and/or
segmentation faults.
.. seealso:: .. 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 to prevent changing all copies. The ``copy`` module [#f5]_ provides these
capabilities. 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 compatible. However, performance can be improved by adding custom
``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods ``__copy__`` and ``__deepcopy__`` methods.
are mandatory for (deep)copy compatibility, because pybind11 only supports
cPickle.
For simple classes (deep)copy can be enabled by using the copy constructor, For simple classes (deep)copy can be enabled by using the copy constructor,
which should look as follows: which should look as follows:

View File

@ -328,8 +328,8 @@ an invalid state.
Chaining exceptions ('raise from') Chaining exceptions ('raise from')
================================== ==================================
In Python 3.3 a mechanism for indicating that exceptions were caused by other Python has a mechanism for indicating that exceptions were caused by other
exceptions was introduced: exceptions:
.. code-block:: py .. 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 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 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 .. code-block:: cpp

View File

@ -398,8 +398,6 @@ Ellipsis
Python 3 provides a convenient ``...`` ellipsis notation that is often used to Python 3 provides a convenient ``...`` ellipsis notation that is often used to
slice multidimensional arrays. For instance, the following snippet extracts the slice multidimensional arrays. For instance, the following snippet extracts the
middle dimensions of a tensor with the first and last index set to zero. middle dimensions of a tensor with the first and last index set to zero.
In Python 2, the syntactic sugar ``...`` is not available, but the singleton
``Ellipsis`` (of type ``ellipsis``) can still be used directly.
.. code-block:: python .. code-block:: python
@ -414,8 +412,6 @@ operation on the C++ side:
py::array a = /* A NumPy array */; py::array a = /* A NumPy array */;
py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; 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 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 .. versionchanged:: 2.6
``memoryview::from_memory`` added. ``memoryview::from_memory`` added.

View File

@ -166,12 +166,12 @@ load and execute the example:
.. code-block:: pycon .. code-block:: pycon
$ python $ python
Python 2.7.10 (default, Aug 22 2015, 20:33:39) Python 3.9.10 (main, Jan 15 2022, 11:48:04)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin [Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information. Type "help", "copyright", "credits" or "license" for more information.
>>> import example >>> import example
>>> example.add(1, 2) >>> example.add(1, 2)
3L 3
>>> >>>
.. _keyword_args: .. _keyword_args:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import datetime as dt import datetime as dt
import os import os
import random import random
@ -75,7 +74,7 @@ def generate_dummy_code_boost(nclasses=10):
for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]:
print("{") print("{")
for i in range(0, 10): for i in range(0, 10):
nclasses = 2 ** i nclasses = 2**i
with open("test.cpp", "w") as f: with open("test.cpp", "w") as f:
f.write(codegen(nclasses)) f.write(codegen(nclasses))
n1 = dt.datetime.now() n1 = dt.datetime.now()

View File

@ -48,10 +48,10 @@ interactive Python session demonstrating this example is shown below:
>>> print(p) >>> print(p)
<example.Pet object at 0x10cd98060> <example.Pet object at 0x10cd98060>
>>> p.getName() >>> p.getName()
u'Molly' 'Molly'
>>> p.setName("Charly") >>> p.setName("Charly")
>>> p.getName() >>> p.getName()
u'Charly' 'Charly'
.. seealso:: .. seealso::
@ -124,10 +124,10 @@ This makes it possible to write
>>> p = example.Pet("Molly") >>> p = example.Pet("Molly")
>>> p.name >>> p.name
u'Molly' 'Molly'
>>> p.name = "Charly" >>> p.name = "Charly"
>>> p.name >>> p.name
u'Charly' 'Charly'
Now suppose that ``Pet::name`` was a private internal variable Now suppose that ``Pet::name`` was a private internal variable
that can only be accessed via setters and getters. 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 = example.Dog("Molly")
>>> p.name >>> p.name
u'Molly' 'Molly'
>>> p.bark() >>> p.bark()
u'woof!' 'woof!'
The C++ classes defined above are regular non-polymorphic types with an The C++ classes defined above are regular non-polymorphic types with an
inheritance relationship. This is reflected in Python: inheritance relationship. This is reflected in Python:
@ -332,7 +332,7 @@ will automatically recognize this:
>>> type(p) >>> type(p)
PolymorphicDog # automatically downcast PolymorphicDog # automatically downcast
>>> p.bark() >>> p.bark()
u'woof!' 'woof!'
Given a pointer to a polymorphic base, pybind11 performs automatic downcasting 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 to the actual derived type. Note that this goes beyond the usual situation in

View File

@ -462,11 +462,8 @@ available in all modes. The targets provided are:
``pybind11::headers`` ``pybind11::headers``
Just the pybind11 headers and minimum compile requirements 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`` ``pybind11::pybind11``
Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only) Python headers + ``pybind11::headers``
``pybind11::python_link_helper`` ``pybind11::python_link_helper``
Just the "linking" part of pybind11:module 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) $ 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 The ``python3 -m pybind11 --includes`` command fetches the include paths for
both pybind11 and Python headers. This assumes that pybind11 has been installed 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 using ``pip`` or ``conda``. If it hasn't, you can also manually specify
``-I <path-to-pybind11>/include`` together with the Python includes path ``-I <path-to-pybind11>/include`` together with the Python includes path
``python3-config --includes``. ``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 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 the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when
building the module: building the module:

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
# #
# pybind11 documentation build configuration file, created by # pybind11 documentation build configuration file, created by
# sphinx-quickstart on Sun Oct 11 19:23:48 2015. # sphinx-quickstart on Sun Oct 11 19:23:48 2015.

View File

@ -8,9 +8,7 @@ Frequently asked questions
filename of the extension library (without suffixes such as ``.so``). 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 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 version of Python that does not match what you compiled with.
Python 2, while the interpreter is running on top of some version of Python
3, or vice versa).
"Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" "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. the CMake Python detection in a system with several Python versions installed.
This difference may cause inconsistencies and errors if *both* mechanisms are This difference may cause inconsistencies and errors if *both* mechanisms are
used in the same project. Consider the following CMake code executed in a used in the same project.
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.
There are three possible solutions: There are three possible solutions:

View File

@ -325,7 +325,7 @@ public:
res = 0; // None is implicitly converted to False res = 0; // None is implicitly converted to False
} }
#if defined(PYPY_VERSION) #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)) { else if (hasattr(src, PYBIND11_BOOL_ATTR)) {
res = PyObject_IsTrue(src.ptr()); res = PyObject_IsTrue(src.ptr());
} }
@ -375,37 +375,16 @@ struct string_caster {
static constexpr size_t UTF_N = 8 * sizeof(CharT); static constexpr size_t UTF_N = 8 * sizeof(CharT);
bool load(handle src, bool) { bool load(handle src, bool) {
#if PY_MAJOR_VERSION < 3
object temp;
#endif
handle load_src = src; handle load_src = src;
if (!src) { if (!src) {
return false; return false;
} }
if (!PyUnicode_Check(load_src.ptr())) { if (!PyUnicode_Check(load_src.ptr())) {
#if PY_MAJOR_VERSION >= 3
return load_bytes(load_src); return load_bytes(load_src);
#else
if (std::is_same<CharT, char>::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<object>(PyUnicode_FromObject(load_src.ptr()));
if (!temp) {
PyErr_Clear();
return false;
}
load_src = temp;
#endif
} }
#if PY_VERSION_HEX >= 0x03030000 // For UTF-8 we avoid the need for a temporary `bytes` object by using
// On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes` // `PyUnicode_AsUTF8AndSize`.
// object by using `PyUnicode_AsUTF8AndSize`.
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) { if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) {
Py_ssize_t size = -1; Py_ssize_t size = -1;
const auto *buffer const auto *buffer
@ -417,7 +396,6 @@ struct string_caster {
value = StringType(buffer, static_cast<size_t>(size)); value = StringType(buffer, static_cast<size_t>(size));
return true; return true;
} }
#endif
auto utfNbytes auto utfNbytes
= reinterpret_steal<object>(PyUnicode_AsEncodedString(load_src.ptr(), = reinterpret_steal<object>(PyUnicode_AsEncodedString(load_src.ptr(),
@ -486,7 +464,7 @@ private:
template <typename C = CharT> template <typename C = CharT>
bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src) { bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src) {
if (PYBIND11_BYTES_CHECK(src.ptr())) { 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. // without any encoding attempt.
const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr());
if (bytes) { if (bytes) {
@ -922,18 +900,6 @@ struct pyobject_caster {
template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0> template <typename T = type, enable_if_t<std::is_base_of<object, T>::value, int> = 0>
bool load(handle src, bool /* convert */) { 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<T, str>::value) && isinstance<bytes>(src)) {
PyObject *str_from_bytes = PyUnicode_FromEncodedObject(src.ptr(), "utf-8", nullptr);
if (!str_from_bytes)
throw error_already_set();
value = reinterpret_steal<type>(str_from_bytes);
return true;
}
#endif
if (!isinstance<type>(src)) { if (!isinstance<type>(src)) {
return false; return false;
} }
@ -1313,7 +1279,7 @@ public:
/// \ingroup annotations /// \ingroup annotations
/// Annotation indicating that all following arguments are keyword-only; the is the equivalent of /// 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 {}; struct kw_only {};
/// \ingroup annotations /// \ingroup annotations

View File

@ -15,12 +15,12 @@
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
#if PY_VERSION_HEX >= 0x03030000 && !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
# define PYBIND11_BUILTIN_QUALNAME # define PYBIND11_BUILTIN_QUALNAME
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj)
#else #else
// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type // In PyPy, we still set __qualname__ so that we can produce reliable function type
// signatures; in 3.3+ this macro expands to nothing: // signatures; in CPython this macro expands to nothing:
# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \ # define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) \
setattr((PyObject *) obj, "__qualname__", nameobj) setattr((PyObject *) obj, "__qualname__", nameobj)
#endif #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 * 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, * 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); return PyType_Type.tp_getattro(obj, name);
} }
#endif
/// metaclass `__call__` function that is used to create all pybind11 objects. /// metaclass `__call__` function that is used to create all pybind11 objects.
extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { 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_call = pybind11_meta_call;
type->tp_setattro = pybind11_meta_setattro; type->tp_setattro = pybind11_meta_setattro;
#if PY_MAJOR_VERSION >= 3
type->tp_getattro = pybind11_meta_getattro; type->tp_getattro = pybind11_meta_getattro;
#endif
type->tp_dealloc = pybind11_meta_dealloc; 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. /// Give this type a buffer interface.
inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; 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_getbuffer = pybind11_getbuffer;
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; 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; auto qualname = name;
if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) {
#if PY_MAJOR_VERSION >= 3
qualname = reinterpret_steal<object>( qualname = reinterpret_steal<object>(
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
#else
qualname = str(rec.scope.attr("__qualname__").cast<std::string>() + "." + rec.name);
#endif
} }
object module_; 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_number = &heap_type->as_number;
type->tp_as_sequence = &heap_type->as_sequence; type->tp_as_sequence = &heap_type->as_sequence;
type->tp_as_mapping = &heap_type->as_mapping; type->tp_as_mapping = &heap_type->as_mapping;
#if PY_VERSION_HEX >= 0x03050000
type->tp_as_async = &heap_type->as_async; type->tp_as_async = &heap_type->as_async;
#endif
/* Flags */ /* Flags */
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; 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) { if (!rec.is_final) {
type->tp_flags |= Py_TPFLAGS_BASETYPE; type->tp_flags |= Py_TPFLAGS_BASETYPE;
} }

View File

@ -211,6 +211,9 @@
#endif #endif
#include <Python.h> #include <Python.h>
#if PY_VERSION_HEX < 0x030500f0
# error "PYTHON 2 IS NO LONGER SUPPORTED. pybind11 v2.9 was the last to support Python 2."
#endif
#include <frameobject.h> #include <frameobject.h>
#include <pythread.h> #include <pythread.h>
@ -266,69 +269,37 @@
// If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and // If UNDEFINED, pybind11::str can only hold PyUnicodeObject, and
// pybind11::isinstance<str>() is true only for pybind11::str. // pybind11::isinstance<str>() is true only for pybind11::str.
// However, for Python 2 only (!), the pybind11::str caster // 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 // the transition from the legacy behavior to the non-permissive
// behavior. // behavior.
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions /// Compatibility macros for Python 2 / Python 3 versions TODO: remove
# define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) #define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
# define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check #define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
# define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION #define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
# define PYBIND11_BYTES_CHECK PyBytes_Check #define PYBIND11_BYTES_CHECK PyBytes_Check
# define PYBIND11_BYTES_FROM_STRING PyBytes_FromString #define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
# define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
# define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize #define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
# define PYBIND11_BYTES_AS_STRING PyBytes_AsString #define PYBIND11_BYTES_AS_STRING PyBytes_AsString
# define PYBIND11_BYTES_SIZE PyBytes_Size #define PYBIND11_BYTES_SIZE PyBytes_Size
# define PYBIND11_LONG_CHECK(o) PyLong_Check(o) #define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
# define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(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_SIGNED(o) PyLong_FromSsize_t((ssize_t) (o))
# define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o)) #define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) (o))
# define PYBIND11_BYTES_NAME "bytes" #define PYBIND11_BYTES_NAME "bytes"
# define PYBIND11_STRING_NAME "str" #define PYBIND11_STRING_NAME "str"
# define PYBIND11_SLICE_OBJECT PyObject #define PYBIND11_SLICE_OBJECT PyObject
# define PYBIND11_FROM_STRING PyUnicode_FromString #define PYBIND11_FROM_STRING PyUnicode_FromString
# define PYBIND11_STR_TYPE ::pybind11::str #define PYBIND11_STR_TYPE ::pybind11::str
# define PYBIND11_BOOL_ATTR "__bool__" #define PYBIND11_BOOL_ATTR "__bool__"
# define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) #define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
# define PYBIND11_BUILTINS_MODULE "builtins" #define PYBIND11_BUILTINS_MODULE "builtins"
// Providing a separate declaration to make Clang's -Wmissing-prototypes happy. // Providing a separate declaration to make Clang's -Wmissing-prototypes happy.
// See comment for PYBIND11_MODULE below for why this is marked "maybe unused". // See comment for PYBIND11_MODULE below for why this is marked "maybe unused".
# define PYBIND11_PLUGIN_IMPL(name) \ #define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \ extern "C" PYBIND11_MAYBE_UNUSED PYBIND11_EXPORT PyObject *PyInit_##name(); \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#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
#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 #if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200
extern "C" { 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) { \
# define PYBIND11_CATCH_INIT_EXCEPTIONS \ pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
catch (pybind11::error_already_set & e) { \ return nullptr; \
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \ } \
return nullptr; \ catch (const std::exception &e) { \
} \ PyErr_SetString(PyExc_ImportError, e.what()); \
catch (const std::exception &e) { \ return nullptr; \
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
/** \rst /** \rst
***Deprecated in favor of PYBIND11_MODULE*** ***Deprecated in favor of PYBIND11_MODULE***

View File

@ -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_INIT(var) PYBIND11_TLS_KEY_REF var = 0;
# define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1) # define PYBIND11_TLS_KEY_CREATE(var) (((var) = PyThread_create_key()) != -1)
# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) # 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 // 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 // the value if it has already been set. Instead, it must first be deleted and
// then set again. // then set again.
@ -294,7 +294,6 @@ inline internals **&get_internals_pp() {
return internals_pp; return internals_pp;
} }
#if PY_VERSION_HEX >= 0x03030000
// forward decl // forward decl
inline void translate_exception(std::exception_ptr); 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; return false;
} }
#else
template <class T>
bool handle_nested_exception(const T &, std::exception_ptr &) {
return false;
}
#endif
inline bool raise_err(PyObject *exc_type, const char *msg) { inline bool raise_err(PyObject *exc_type, const char *msg) {
#if PY_VERSION_HEX >= 0x03030000
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
raise_from(exc_type, msg); raise_from(exc_type, msg);
return true; return true;
} }
#endif
PyErr_SetString(exc_type, msg); PyErr_SetString(exc_type, msg);
return false; return false;
} }

View File

@ -443,17 +443,10 @@ PYBIND11_NOINLINE void instance::allocate_layout() {
// they default to using pymalloc, which is designed to be efficient for small allocations // 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 // like the one we're doing here; in earlier versions (and for larger allocations) they are
// just wrappers around malloc. // just wrappers around malloc.
#if PY_VERSION_HEX >= 0x03050000
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
if (!nonsimple.values_and_holders) { if (!nonsimple.values_and_holders) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
#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 nonsimple.status
= reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]); = reinterpret_cast<std::uint8_t *>(&nonsimple.values_and_holders[flags_at]);
} }
@ -494,11 +487,9 @@ PYBIND11_NOINLINE std::string error_string() {
PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace);
#if PY_MAJOR_VERSION >= 3
if (scope.trace != nullptr) { if (scope.trace != nullptr) {
PyException_SetTraceback(scope.value, scope.trace); PyException_SetTraceback(scope.value, scope.trace);
} }
#endif
#if !defined(PYPY_VERSION) #if !defined(PYPY_VERSION)
if (scope.trace) { 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() { inline PyThreadState *get_thread_state_unchecked() {
#if defined(PYPY_VERSION) #if defined(PYPY_VERSION)
return PyThreadState_GET(); 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 #elif PY_VERSION_HEX < 0x03050200
return (PyThreadState *) _PyThreadState_Current.value; return (PyThreadState *) _PyThreadState_Current.value;
#else #else

View File

@ -19,15 +19,9 @@
# error Embedding the interpreter is not supported with PyPy # error Embedding the interpreter is not supported with PyPy
#endif #endif
#if PY_MAJOR_VERSION >= 3 #define PYBIND11_EMBEDDED_MODULE_IMPL(name) \
# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ extern "C" PyObject *pybind11_init_impl_##name(); \
extern "C" PyObject *pybind11_init_impl_##name(); \ extern "C" PyObject *pybind11_init_impl_##name() { return pybind11_init_wrapper_##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
/** \rst /** \rst
Add a new module to the table of builtins for the interpreter. Must be 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. /// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks.
struct embedded_module { struct embedded_module {
#if PY_MAJOR_VERSION >= 3
using init_t = PyObject *(*) (); using init_t = PyObject *(*) ();
#else
using init_t = void (*)();
#endif
embedded_module(const char *name, init_t init) { embedded_module(const char *name, init_t init) {
if (Py_IsInitialized() != 0) { if (Py_IsInitialized() != 0) {
pybind11_fail("Can't add new modules after the interpreter has been initialized"); 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 { struct wide_char_arg_deleter {
void operator()(wchar_t *ptr) const { 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 // API docs: https://docs.python.org/3/c-api/sys.html#c.Py_DecodeLocale
PyMem_RawFree(ptr); PyMem_RawFree(ptr);
#else
delete[] ptr;
#endif
} }
}; };
inline wchar_t *widen_chars(const char *safe_arg) { inline wchar_t *widen_chars(const char *safe_arg) {
#if PY_VERSION_HEX >= 0x030500f0
wchar_t *widened_arg = Py_DecodeLocale(safe_arg, nullptr); 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<size_t>(-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; 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<size_t>(argc); auto argv_size = static_cast<size_t>(argc);
#if PY_MAJOR_VERSION >= 3
// SetArgv* on python 3 takes wchar_t, so we have to convert. // SetArgv* on python 3 takes wchar_t, so we have to convert.
std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]); std::unique_ptr<wchar_t *[]> widened_argv(new wchar_t *[argv_size]);
std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> widened_argv_entries; std::vector<std::unique_ptr<wchar_t[], wide_char_arg_deleter>> 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(); auto *pysys_argv = widened_argv.get();
#else
// python 2.x
std::vector<std::string> strings{safe_argv, safe_argv + argv_size};
std::vector<char *> 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<int>(add_program_dir_to_path)); PySys_SetArgvEx(argc, pysys_argv, static_cast<int>(add_program_dir_to_path));
} }

View File

@ -20,10 +20,10 @@ PYBIND11_NAMESPACE_BEGIN(detail)
inline void ensure_builtins_in_globals(object &global) { inline void ensure_builtins_in_globals(object &global) {
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000 #if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x03080000
// Running exec and eval on Python 2 and 3 adds `builtins` module under // Running exec and eval adds `builtins` module under `__builtins__` key to
// `__builtins__` key to globals if not yet present. // globals if not yet present. Python 3.8 made PyRun_String behave
// Python 3.8 made PyRun_String behave similarly. Let's also do that for // similarly. Let's also do that for older versions, for consistency. This
// older versions, for consistency. This was missing from PyPy3.8 7.3.7. // was missing from PyPy3.8 7.3.7.
if (!global.contains("__builtins__")) if (!global.contains("__builtins__"))
global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE); global["__builtins__"] = module_::import(PYBIND11_BUILTINS_MODULE);
#else #else
@ -94,7 +94,7 @@ void exec(const char (&s)[N], object global = globals(), object local = object()
eval<eval_statements>(s, global, local); eval<eval_statements>(s, global, local);
} }
#if defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03000000 #if defined(PYPY_VERSION)
template <eval_mode mode = eval_statements> template <eval_mode mode = eval_statements>
object eval_file(str, object, object) { object eval_file(str, object, object) {
pybind11_fail("eval_file not supported in PyPy3. Use eval"); 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; int closeFile = 1;
std::string fname_str = (std::string) fname; std::string fname_str = (std::string) fname;
# if PY_VERSION_HEX >= 0x03040000
FILE *f = _Py_fopen_obj(fname.ptr(), "r"); 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<object>(
PyFile_FromString(const_cast<char *>(fname_str.c_str()), const_cast<char *>("r")));
FILE *f = nullptr;
if (fobj)
f = PyFile_AsFile(fobj.ptr());
closeFile = 0;
# endif
if (!f) { if (!f) {
PyErr_Clear(); PyErr_Clear();
pybind11_fail("File \"" + fname_str + "\" could not be opened!"); 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__")) { if (!global.contains("__file__")) {
global["__file__"] = std::move(fname); 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 PyObject *result
= PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile); = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), local.ptr(), closeFile);
# endif
if (!result) { if (!result) {
throw error_already_set(); throw error_already_set();

View File

@ -263,11 +263,7 @@ private:
static npy_api lookup() { static npy_api lookup() {
module_ m = module_::import("numpy.core.multiarray"); module_ m = module_::import("numpy.core.multiarray");
auto c = m.attr("_ARRAY_API"); auto c = m.attr("_ARRAY_API");
#if PY_MAJOR_VERSION >= 3
void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL);
#else
void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr());
#endif
npy_api api; npy_api api;
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);

View File

@ -91,16 +91,6 @@ struct op_ {
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>; using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute, is_operator(), extra...); 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 <typename Class, typename... Extra> template <typename Class, typename... Extra>
void execute_cast(Class &cl, const Extra &...extra) const { void execute_cast(Class &cl, const Extra &...extra) const {
@ -109,15 +99,6 @@ struct op_ {
using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>; using R_type = conditional_t<std::is_same<R, self_t>::value, Base, R>;
using op = op_impl<id, ot, Base, L_type, R_type>; using op = op_impl<id, ot, Base, L_type, R_type>;
cl.def(op::name(), &op::execute_cast, is_operator(), extra...); 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
} }
}; };

View File

@ -431,9 +431,8 @@ protected:
} }
if (auto *tinfo = detail::get_type_info(*t)) { if (auto *tinfo = detail::get_type_info(*t)) {
handle th((PyObject *) tinfo->type); handle th((PyObject *) tinfo->type);
signature += th.attr("__module__").cast<std::string>() + "." + signature += th.attr("__module__").cast<std::string>() + "."
// Python 3.3+, but we backport it to earlier versions + th.attr("__qualname__").cast<std::string>();
th.attr("__qualname__").cast<std::string>();
} else if (rec->is_new_style_constructor && arg_index == 0) { } else if (rec->is_new_style_constructor && arg_index == 0) {
// A new-style `__init__` takes `self` as `value_and_holder`. // A new-style `__init__` takes `self` as `value_and_holder`.
// Rewrite it to the proper class type. // Rewrite it to the proper class type.
@ -453,15 +452,6 @@ protected:
pybind11_fail("Internal error while parsing type signature (2)"); 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->signature = guarded_strdup(signature.c_str());
rec->args.shrink_to_fit(); rec->args.shrink_to_fit();
rec->nargs = (std::uint16_t) args; rec->nargs = (std::uint16_t) args;
@ -1107,14 +1097,12 @@ protected:
} }
append_note_if_missing_header_is_suspected(msg); append_note_if_missing_header_is_suspected(msg);
#if PY_VERSION_HEX >= 0x03030000
// Attach additional error info to the exception if supported // Attach additional error info to the exception if supported
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
// #HelpAppreciated: unit test coverage for this branch. // #HelpAppreciated: unit test coverage for this branch.
raise_from(PyExc_TypeError, msg.c_str()); raise_from(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} }
#endif
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} }
@ -1123,13 +1111,11 @@ protected:
"Python type! The signature was\n\t"; "Python type! The signature was\n\t";
msg += it->signature; msg += it->signature;
append_note_if_missing_header_is_suspected(msg); append_note_if_missing_header_is_suspected(msg);
#if PY_VERSION_HEX >= 0x03030000
// Attach additional error info to the exception if supported // Attach additional error info to the exception if supported
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
raise_from(PyExc_TypeError, msg.c_str()); raise_from(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} }
#endif
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} }
@ -1149,11 +1135,7 @@ public:
/// Create a new top-level Python module with the given name and docstring /// Create a new top-level Python module with the given name and docstring
PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead") PYBIND11_DEPRECATED("Use PYBIND11_MODULE or module_::create_extension_module instead")
explicit module_(const char *name, const char *doc = nullptr) { explicit module_(const char *name, const char *doc = nullptr) {
#if PY_MAJOR_VERSION >= 3
*this = create_extension_module(name, doc, new PyModuleDef()); *this = create_extension_module(name, doc, new PyModuleDef());
#else
*this = create_extension_module(name, doc, nullptr);
#endif
} }
/** \rst /** \rst
@ -1231,20 +1213,14 @@ public:
PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */);
} }
#if PY_MAJOR_VERSION >= 3
using module_def = PyModuleDef; using module_def = PyModuleDef;
#else
struct module_def {};
#endif
/** \rst /** \rst
Create a new top-level module that can be used as the main module of a C extension. Create a new top-level module that can be used as the main module of a C extension.
For Python 3, ``def`` should point to a statically allocated module_def. ``def`` should point to a statically allocated module_def.
For Python 2, ``def`` can be a nullptr and is completely ignored.
\endrst */ \endrst */
static module_ create_extension_module(const char *name, const char *doc, module_def *def) { static module_ create_extension_module(const char *name, const char *doc, module_def *def) {
#if PY_MAJOR_VERSION >= 3
// module_def is PyModuleDef // module_def is PyModuleDef
// Placement new (not an allocation). // Placement new (not an allocation).
def = new (def) def = new (def)
@ -1258,12 +1234,6 @@ public:
/* m_clear */ nullptr, /* m_clear */ nullptr,
/* m_free */ nullptr}; /* m_free */ nullptr};
auto *m = PyModule_Create(def); 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 (m == nullptr) {
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
throw error_already_set(); throw error_already_set();
@ -1290,14 +1260,12 @@ inline dict globals() {
return reinterpret_borrow<dict>(p ? p : module_::import("__main__").attr("__dict__").ptr()); return reinterpret_borrow<dict>(p ? p : module_::import("__main__").attr("__dict__").ptr());
} }
#if PY_VERSION_HEX >= 0x03030000
template <typename... Args, typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>> template <typename... Args, typename = detail::enable_if_t<args_are_all_keyword_or_ds<Args...>()>>
PYBIND11_DEPRECATED("make_simple_namespace should be replaced with " PYBIND11_DEPRECATED("make_simple_namespace should be replaced with "
"py::module_::import(\"types\").attr(\"SimpleNamespace\") ") "py::module_::import(\"types\").attr(\"SimpleNamespace\") ")
object make_simple_namespace(Args &&...args_) { object make_simple_namespace(Args &&...args_) {
return module_::import("types").attr("SimpleNamespace")(std::forward<Args>(args_)...); return module_::import("types").attr("SimpleNamespace")(std::forward<Args>(args_)...);
} }
#endif
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
/// Generic support for creating new Python heap types /// Generic support for creating new Python heap types
@ -2173,9 +2141,6 @@ public:
def_property_readonly("value", [](Type value) { return (Scalar) value; }); def_property_readonly("value", [](Type value) { return (Scalar) value; });
def("__int__", [](Type value) { return (Scalar) value; }); def("__int__", [](Type value) { return (Scalar) value; });
def("__index__", [](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( attr("__setstate__") = cpp_function(
[](detail::value_and_holder &v_h, Scalar arg) { [](detail::value_and_holder &v_h, Scalar arg) {
detail::initimpl::setstate<Base>( detail::initimpl::setstate<Base>(

View File

@ -434,8 +434,6 @@ private:
# pragma warning(pop) # pragma warning(pop)
#endif #endif
#if PY_VERSION_HEX >= 0x03030000
/// Replaces the current Python error indicator with the chosen error, performing a /// 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. /// 'raise from' to indicate that the chosen error was caused by the original error.
inline void raise_from(PyObject *type, const char *message) { 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); raise_from(type, message);
} }
#endif
/** \defgroup python_builtins const_name /** \defgroup python_builtins const_name
Unless stated otherwise, the following C++ functions behave the same Unless stated otherwise, the following C++ functions behave the same
as their Python counterparts. as their Python counterparts.
@ -591,12 +587,9 @@ inline ssize_t hash(handle obj) {
PYBIND11_NAMESPACE_BEGIN(detail) PYBIND11_NAMESPACE_BEGIN(detail)
inline handle get_function(handle value) { inline handle get_function(handle value) {
if (value) { if (value) {
#if PY_MAJOR_VERSION >= 3
if (PyInstanceMethod_Check(value.ptr())) { if (PyInstanceMethod_Check(value.ptr())) {
value = PyInstanceMethod_GET_FUNCTION(value.ptr()); value = PyInstanceMethod_GET_FUNCTION(value.ptr());
} else } else if (PyMethod_Check(value.ptr())) {
#endif
if (PyMethod_Check(value.ptr())) {
value = PyMethod_GET_FUNCTION(value.ptr()); value = PyMethod_GET_FUNCTION(value.ptr());
} }
} }
@ -608,7 +601,6 @@ inline handle get_function(handle value) {
// copied from cpython _PyDict_GetItemStringWithError // copied from cpython _PyDict_GetItemStringWithError
inline PyObject *dict_getitemstring(PyObject *v, const char *key) { inline PyObject *dict_getitemstring(PyObject *v, const char *key) {
#if PY_MAJOR_VERSION >= 3
PyObject *kv = nullptr, *rv = nullptr; PyObject *kv = nullptr, *rv = nullptr;
kv = PyUnicode_FromString(key); kv = PyUnicode_FromString(key);
if (kv == NULL) { if (kv == NULL) {
@ -621,21 +613,14 @@ inline PyObject *dict_getitemstring(PyObject *v, const char *key) {
throw error_already_set(); throw error_already_set();
} }
return rv; return rv;
#else
return PyDict_GetItemString(v, key);
#endif
} }
inline PyObject *dict_getitem(PyObject *v, PyObject *key) { inline PyObject *dict_getitem(PyObject *v, PyObject *key) {
#if PY_MAJOR_VERSION >= 3
PyObject *rv = PyDict_GetItemWithError(v, key); PyObject *rv = PyDict_GetItemWithError(v, key);
if (rv == NULL && PyErr_Occurred()) { if (rv == NULL && PyErr_Occurred()) {
throw error_already_set(); throw error_already_set();
} }
return rv; return rv;
#else
return PyDict_GetItem(v, key);
#endif
} }
// Helper aliases/functions to support implicit casting of values given to python // 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 /// Return string representation -- always returns a new reference, even if already a str
static PyObject *raw_str(PyObject *op) { static PyObject *raw_str(PyObject *op) {
PyObject *str_value = PyObject_Str(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; 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). // unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes).
template <typename Unsigned> template <typename Unsigned>
Unsigned as_unsigned(PyObject *o) { Unsigned as_unsigned(PyObject *o) {
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long)) if (PYBIND11_SILENCE_MSVC_C4127(sizeof(Unsigned) <= sizeof(unsigned long))) {
#if PY_VERSION_HEX < 0x03000000
|| PyInt_Check(o)
#endif
) {
unsigned long v = PyLong_AsUnsignedLong(o); unsigned long v = PyLong_AsUnsignedLong(o);
return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v;
} }
@ -1922,7 +1896,6 @@ public:
return memoryview::from_buffer(const_cast<T *>(ptr), shape, strides, true); return memoryview::from_buffer(const_cast<T *>(ptr), shape, strides, true);
} }
#if PY_MAJOR_VERSION >= 3
/** \rst /** \rst
Creates ``memoryview`` from static memory. Creates ``memoryview`` from static memory.
@ -1930,8 +1903,6 @@ public:
managed by Python. The caller is responsible for managing the lifetime managed by Python. The caller is responsible for managing the lifetime
of ``mem``, which MUST outlive the memoryview constructed here. 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`_. See also: Python C API documentation for `PyMemoryView_FromBuffer`_.
.. _PyMemoryView_FromMemory: .. _PyMemoryView_FromMemory:
@ -1950,12 +1921,10 @@ public:
return memoryview::from_memory(const_cast<void *>(mem), size, true); return memoryview::from_memory(const_cast<void *>(mem), size, true);
} }
# ifdef PYBIND11_HAS_STRING_VIEW #ifdef PYBIND11_HAS_STRING_VIEW
static memoryview from_memory(std::string_view mem) { static memoryview from_memory(std::string_view mem) {
return from_memory(const_cast<char *>(mem.data()), static_cast<ssize_t>(mem.size()), true); return from_memory(const_cast<char *>(mem.data()), static_cast<ssize_t>(mem.size()), true);
} }
# endif
#endif #endif
}; };
@ -2010,11 +1979,7 @@ inline size_t len(handle h) {
/// Get the length hint of a Python object. /// Get the length hint of a Python object.
/// Returns 0 when this cannot be determined. /// Returns 0 when this cannot be determined.
inline size_t len_hint(handle h) { inline size_t len_hint(handle h) {
#if PY_VERSION_HEX >= 0x03040000
ssize_t result = PyObject_LengthHint(h.ptr(), 0); ssize_t result = PyObject_LengthHint(h.ptr(), 0);
#else
ssize_t result = PyObject_Length(h.ptr());
#endif
if (result < 0) { if (result < 0) {
// Sometimes a length can't be determined at all (eg generators) // Sometimes a length can't be determined at all (eg generators)
// In which case simply return 0 // In which case simply return 0
@ -2029,13 +1994,6 @@ inline str repr(handle h) {
if (!str_value) { if (!str_value) {
throw error_already_set(); 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>(str_value); return reinterpret_steal<str>(str_value);
} }

View File

@ -2,7 +2,7 @@ import nox
nox.options.sessions = ["lint", "tests", "tests_packaging"] 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) @nox.session(reuse_venv=True)

View File

@ -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 ._version import __version__, version_info
from .commands import get_cmake_dir, get_include from .commands import get_cmake_dir, get_include

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import argparse import argparse
import sys import sys
import sysconfig import sysconfig
@ -8,8 +5,7 @@ import sysconfig
from .commands import get_cmake_dir, get_include from .commands import get_cmake_dir, get_include
def print_includes(): def print_includes() -> None:
# type: () -> None
dirs = [ dirs = [
sysconfig.get_path("include"), sysconfig.get_path("include"),
sysconfig.get_path("platinclude"), sysconfig.get_path("platinclude"),
@ -25,8 +21,7 @@ def print_includes():
print(" ".join("-I" + d for d in unique_dirs)) print(" ".join("-I" + d for d in unique_dirs))
def main(): def main() -> None:
# type: () -> None
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- from typing import Union
def _to_int(s): def _to_int(s: str) -> Union[int, str]:
try: try:
return int(s) return int(s)
except ValueError: except ValueError:

View File

@ -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], ...]

View File

@ -1,18 +1,15 @@
# -*- coding: utf-8 -*-
import os import os
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = os.path.abspath(os.path.dirname(__file__))
def get_include(user=False): def get_include(user: bool = False) -> str:
# type: (bool) -> str
installed_path = os.path.join(DIR, "include") installed_path = os.path.join(DIR, "include")
source_path = os.path.join(os.path.dirname(DIR), "include") source_path = os.path.join(os.path.dirname(DIR), "include")
return installed_path if os.path.exists(installed_path) else source_path return installed_path if os.path.exists(installed_path) else source_path
def get_cmake_dir(): def get_cmake_dir() -> str:
# type: () -> str
cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11") cmake_installed_path = os.path.join(DIR, "share", "cmake", "pybind11")
if os.path.exists(cmake_installed_path): if os.path.exists(cmake_installed_path):
return cmake_installed_path return cmake_installed_path

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
""" """
This module provides helpers for C++11+ projects using pybind11. This module provides helpers for C++11+ projects using pybind11.
@ -49,6 +47,20 @@ import sysconfig
import tempfile import tempfile
import threading import threading
import warnings 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: try:
from setuptools import Extension as _Extension from setuptools import Extension as _Extension
@ -61,7 +73,6 @@ import distutils.ccompiler
import distutils.errors import distutils.errors
WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform()
PY2 = sys.version_info[0] < 3
MACOS = sys.platform.startswith("darwin") MACOS = sys.platform.startswith("darwin")
STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" 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. # 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 Build a C++11+ Extension module with pybind11. This automatically adds the
recommended flags when you init the extension and assumes C++ sources - you 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 If you want to add pybind11 headers manually, for example for an exact
git checkout, then set ``include_pybind11=False``. 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 # flags are prepended, so that they can be further overridden, e.g. by
# ``extra_compile_args=["-g"]``. # ``extra_compile_args=["-g"]``.
def _add_cflags(self, flags): def _add_cflags(self, flags: List[str]) -> None:
self.extra_compile_args[:0] = flags 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 self.extra_link_args[:0] = flags
def __init__(self, *args, **kwargs): def __init__(self, *args: Any, **kwargs: Any) -> None:
self._cxx_level = 0 self._cxx_level = 0
cxx_std = kwargs.pop("cxx_std", 0) cxx_std = kwargs.pop("cxx_std", 0)
@ -119,9 +127,7 @@ class Pybind11Extension(_Extension):
include_pybind11 = kwargs.pop("include_pybind11", True) include_pybind11 = kwargs.pop("include_pybind11", True)
# Can't use super here because distutils has old-style classes in super().__init__(*args, **kwargs)
# Python 2!
_Extension.__init__(self, *args, **kwargs)
# Include the installed package pybind11 headers # Include the installed package pybind11 headers
if include_pybind11: if include_pybind11:
@ -133,11 +139,10 @@ class Pybind11Extension(_Extension):
if pyinc not in self.include_dirs: if pyinc not in self.include_dirs:
self.include_dirs.append(pyinc) self.include_dirs.append(pyinc)
except ImportError: except ModuleNotFoundError:
pass pass
# Have to use the accessor manually to support Python 2 distutils self.cxx_std = cxx_std
Pybind11Extension.cxx_std.__set__(self, cxx_std)
cflags = [] cflags = []
ldflags = [] ldflags = []
@ -157,18 +162,18 @@ class Pybind11Extension(_Extension):
self._add_ldflags(ldflags) self._add_ldflags(ldflags)
@property @property
def cxx_std(self): def cxx_std(self) -> int:
""" """
The CXX standard level. If set, will add the required flags. If left The CXX standard level. If set, will add the required flags. If left at
at 0, it will trigger an automatic search when pybind11's build_ext 0, it will trigger an automatic search when pybind11's build_ext is
is used. If None, will have no effect. Besides just the flags, this used. If None, will have no effect. Besides just the flags, this may
may add a register warning/error fix for Python 2 or macos-min 10.9 add a macos-min 10.9 or 10.14 flag if MACOSX_DEPLOYMENT_TARGET is
or 10.14. unset.
""" """
return self._cxx_level return self._cxx_level
@cxx_std.setter @cxx_std.setter
def cxx_std(self, level): def cxx_std(self, level: int) -> None:
if self._cxx_level: if self._cxx_level:
warnings.warn("You cannot safely change the cxx_level after setting it!") 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]) current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2])
desired_macos = (10, 9) if level < 17 else (10, 14) desired_macos = (10, 9) if level < 17 else (10, 14)
macos_string = ".".join(str(x) for x in min(current_macos, desired_macos)) macos_string = ".".join(str(x) for x in min(current_macos, desired_macos))
macosx_min = "-mmacosx-version-min=" + macos_string macosx_min = "-mmacosx-version-min={}".format(macos_string)
cflags += [macosx_min] cflags += [macosx_min]
ldflags += [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_cflags(cflags)
self._add_ldflags(ldflags) self._add_ldflags(ldflags)
# Just in case someone clever tries to multithread # Just in case someone clever tries to multithread
tmp_chdir_lock = threading.Lock() tmp_chdir_lock = threading.Lock()
cpp_cache_lock = threading.Lock()
@contextlib.contextmanager @contextlib.contextmanager
def tmp_chdir(): def tmp_chdir() -> Iterator[str]:
"Prepare and enter a temporary directory, cleanup when done" "Prepare and enter a temporary directory, cleanup when done"
# Threadsafe # Threadsafe
@ -235,7 +229,7 @@ def tmp_chdir():
# cf http://bugs.python.org/issue26689 # 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 Return the flag if a flag name is supported on the
specified compiler, otherwise None (can be used as a boolean). specified compiler, otherwise None (can be used as a boolean).
@ -243,13 +237,12 @@ def has_flag(compiler, flag):
""" """
with tmp_chdir(): with tmp_chdir():
fname = "flagcheck.cpp" fname = Path("flagcheck.cpp")
with open(fname, "w") as f: # Don't trigger -Wunused-parameter.
# Don't trigger -Wunused-parameter. fname.write_text("int main (int, char **) { return 0; }")
f.write("int main (int, char **) { return 0; }")
try: try:
compiler.compile([fname], extra_postargs=[flag]) compiler.compile([str(fname)], extra_postargs=[flag])
except distutils.errors.CompileError: except distutils.errors.CompileError:
return False return False
return True return True
@ -259,7 +252,8 @@ def has_flag(compiler, flag):
cpp_flag_cache = None 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. 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: if WIN:
return "latest" 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] levels = [17, 14, 11]
for level in levels: for level in levels:
if has_flag(compiler, STD_TMPL.format(level)): if has_flag(compiler, STD_TMPL.format(level)):
with cpp_cache_lock:
cpp_flag_cache = level
return level return level
msg = "Unsupported compiler -- at least C++11 support is needed!" msg = "Unsupported compiler -- at least C++11 support is needed!"
raise RuntimeError(msg) 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 Customized build_ext that allows an auto-search for the highest supported
C++ level for Pybind11Extension. This is only needed for the auto-search C++ level for Pybind11Extension. This is only needed for the auto-search
for now, and is completely optional otherwise. 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. Build extensions, injecting C++ std for Pybind11Extension if needed.
""" """
for ext in self.extensions: for ext in self.extensions:
if hasattr(ext, "_cxx_level") and ext._cxx_level == 0: if hasattr(ext, "_cxx_level") and ext._cxx_level == 0:
# Python 2 syntax - old-style distutils class ext.cxx_std = auto_cpp_level(self.compiler)
ext.__class__.cxx_std.__set__(ext, auto_cpp_level(self.compiler))
# Python 2 doesn't allow super here, since distutils uses old-style super().build_extensions()
# classes!
_build_ext.build_extensions(self)
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 Generate Pybind11Extensions from source files directly located in a Python
source tree. source tree.
@ -318,6 +302,7 @@ def intree_extensions(paths, package_dir=None):
not contain an ``__init__.py`` file. not contain an ``__init__.py`` file.
""" """
exts = [] exts = []
for path in paths: for path in paths:
if package_dir is None: if package_dir is None:
parent, _ = os.path.split(path) parent, _ = os.path.split(path)
@ -327,24 +312,25 @@ def intree_extensions(paths, package_dir=None):
qualified_name = relname.replace(os.path.sep, ".") qualified_name = relname.replace(os.path.sep, ".")
exts.append(Pybind11Extension(qualified_name, [path])) exts.append(Pybind11Extension(qualified_name, [path]))
else: else:
found = False
for prefix, parent in package_dir.items(): for prefix, parent in package_dir.items():
if path.startswith(parent): if path.startswith(parent):
found = True
relname, _ = os.path.splitext(os.path.relpath(path, parent)) relname, _ = os.path.splitext(os.path.relpath(path, parent))
qualified_name = relname.replace(os.path.sep, ".") qualified_name = relname.replace(os.path.sep, ".")
if prefix: if prefix:
qualified_name = prefix + "." + qualified_name qualified_name = prefix + "." + qualified_name
exts.append(Pybind11Extension(qualified_name, [path])) exts.append(Pybind11Extension(qualified_name, [path]))
if not found:
raise ValueError( if not exts:
"path {} is not a child of any of the directories listed " msg = (
"in 'package_dir' ({})".format(path, package_dir) "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 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 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 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 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 This is the safest but slowest choice (and is the default) - will always
recompile sources. recompile sources.
@ -361,15 +347,33 @@ def no_recompile(obg, src):
return True 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 # Optional parallel compile utility
# inspired by: http://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils # 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: https://github.com/tbenthompson/cppimport/blob/stable/cppimport/build_module.py
# and NumPy's parallel distutils module: # and NumPy's parallel distutils module:
# https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py # https://github.com/numpy/numpy/blob/master/numpy/distutils/ccompiler.py
class ParallelCompile(object): class ParallelCompile:
""" """
Make a parallel compile function. Inspired by 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 This takes several arguments that allow you to customize the compile
function created: function created:
@ -404,35 +408,41 @@ class ParallelCompile(object):
__slots__ = ("envvar", "default", "max", "_old", "needs_recompile") __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.envvar = envvar
self.default = default self.default = default
self.max = max self.max = max
self.needs_recompile = needs_recompile 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. Builds a function object usable as distutils.ccompiler.CCompiler.compile.
""" """
def compile_function( def compile_function(
compiler, compiler: distutils.ccompiler.CCompiler,
sources, sources: List[str],
output_dir=None, output_dir: Optional[str] = None,
macros=None, macros: Optional[Union[Tuple[str], Tuple[str, Optional[str]]]] = None,
include_dirs=None, include_dirs: Optional[List[str]] = None,
debug=0, debug: bool = False,
extra_preargs=None, extra_preargs: Optional[List[str]] = None,
extra_postargs=None, extra_postargs: Optional[List[str]] = None,
depends=None, depends: Optional[List[str]] = None,
): ) -> Any:
# These lines are directly from distutils.ccompiler.CCompiler # 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 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. # The number of threads; start with default.
threads = self.default threads = self.default
@ -441,14 +451,14 @@ class ParallelCompile(object):
if self.envvar is not None: if self.envvar is not None:
threads = int(os.environ.get(self.envvar, self.default)) threads = int(os.environ.get(self.envvar, self.default))
def _single_compile(obj): def _single_compile(obj: Any) -> None:
try: try:
src, ext = build[obj] src, ext = build[obj]
except KeyError: except KeyError:
return return
if not os.path.exists(obj) or self.needs_recompile(obj, src): 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: try:
# Importing .synchronize checks for platforms that have some multiprocessing # Importing .synchronize checks for platforms that have some multiprocessing
@ -466,14 +476,9 @@ class ParallelCompile(object):
threads = 1 threads = 1
if threads > 1: if threads > 1:
pool = ThreadPool(threads) with ThreadPool(threads) as pool:
# 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:
for _ in pool.imap_unordered(_single_compile, objects): for _ in pool.imap_unordered(_single_compile, objects):
pass pass
finally:
pool.terminate()
else: else:
for ob in objects: for ob in objects:
_single_compile(ob) _single_compile(ob)
@ -482,13 +487,13 @@ class ParallelCompile(object):
return compile_function return compile_function
def install(self): def install(self: S) -> S:
distutils.ccompiler.CCompiler.compile = self.function() distutils.ccompiler.CCompiler.compile = self.function() # type: ignore[assignment]
return self return self
def __enter__(self): def __enter__(self: S) -> S:
self._old.append(distutils.ccompiler.CCompiler.compile) self._old.append(distutils.ccompiler.CCompiler.compile)
return self.install() return self.install()
def __exit__(self, *args): def __exit__(self, *args: Any) -> None:
distutils.ccompiler.CCompiler.compile = self._old.pop() distutils.ccompiler.CCompiler.compile = self._old.pop() # type: ignore[assignment]

View File

@ -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: ...

View File

@ -22,20 +22,11 @@ known_first_party = "env,pybind11_cross_module_tests,pybind11_tests,"
profile = "black" profile = "black"
[tool.mypy] [tool.mypy]
files = "pybind11" files = ["pybind11"]
python_version = "2.7" python_version = "3.6"
warn_unused_configs = true warn_unused_configs = true
strict = true
disallow_any_generics = true [[tool.mypy.overrides]]
disallow_subclassing_any = true module = ["ghapi.*", "setuptools.*"]
disallow_untyped_calls = true ignore_missing_imports = 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

View File

@ -13,8 +13,7 @@ classifiers =
Topic :: Software Development :: Libraries :: Python Modules Topic :: Software Development :: Libraries :: Python Modules
Topic :: Utilities Topic :: Utilities
Programming Language :: C++ Programming Language :: C++
Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
@ -39,15 +38,12 @@ project_urls =
Chat = https://gitter.im/pybind/Lobby Chat = https://gitter.im/pybind/Lobby
[options] [options]
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* python_requires = >=3.5
zip_safe = False zip_safe = False
[bdist_wheel]
universal=1
[flake8] [flake8]
max-line-length = 99 max-line-length = 120
show_source = True show_source = True
exclude = .git, __pycache__, build, dist, docs, tools, venv exclude = .git, __pycache__, build, dist, docs, tools, venv
ignore = ignore =

126
setup.py
View File

@ -1,56 +1,52 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*-
# Setup script for PyPI; use CMakeFile.txt to build extension modules # Setup script for PyPI; use CMakeFile.txt to build extension modules
import contextlib import contextlib
import io
import os import os
import re import re
import shutil import shutil
import string import string
import subprocess import subprocess
import sys import sys
import tempfile from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Dict, Iterator, List, Union
import setuptools.command.sdist import setuptools.command.sdist
DIR = os.path.abspath(os.path.dirname(__file__)) DIR = Path(__file__).parent.absolute()
VERSION_REGEX = re.compile( VERSION_REGEX = re.compile(
r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE 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"] patch_level_serial = matches["PATCH"]
serial = None serial = None
try: major = int(matches["MAJOR"])
major = int(matches["MAJOR"]) minor = int(matches["MINOR"])
minor = int(matches["MINOR"]) flds = patch_level_serial.split(".")
flds = patch_level_serial.split(".") if flds:
if flds: patch = int(flds[0])
patch = int(flds[0]) if len(flds) == 1:
level = None level = "0"
if len(flds) == 1: serial = 0
level = "0" elif len(flds) == 2:
serial = 0 level_serial = flds[1]
elif len(flds) == 2: for level in ("a", "b", "c", "dev"):
level_serial = flds[1] if level_serial.startswith(level):
for level in ("a", "b", "c", "dev"): serial = int(level_serial[len(level) :])
if level_serial.startswith(level): break
serial = int(level_serial[len(level) :])
break
except ValueError:
pass
if serial is None: if serial is None:
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial) msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
raise RuntimeError(msg) raise RuntimeError(msg)
return ( version_hex_str = "{:02x}{:02x}{:02x}{}{:x}".format(
"0x" major, minor, patch, level[:1], serial
+ "{:02x}{:02x}{:02x}{}{:x}".format(
major, minor, patch, level[:1], serial
).upper()
) )
return "0x{}".format(version_hex_str.upper())
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
@ -58,24 +54,25 @@ def build_expected_version_hex(matches):
global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False) 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' extra_cmd = 'cmdclass["sdist"] = SDist\n'
to_src = ( to_src = (
("pyproject.toml", "tools/pyproject.toml"), (Path("pyproject.toml"), Path("tools/pyproject.toml")),
("setup.py", setup_py), (Path("setup.py"), setup_py),
) )
# Read the listed version # Read the listed version
with open("pybind11/_version.py") as f: loc = {} # type: Dict[str, str]
code = compile(f.read(), "pybind11/_version.py", "exec") code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
loc = {}
exec(code, loc) exec(code, loc)
version = loc["__version__"] version = loc["__version__"]
# Verify that the version matches the one in C++ # 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(COMMON_FILE.read_text(encoding="utf8")))
matches = dict(VERSION_REGEX.findall(f.read()))
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches) cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
if version != cpp_version: if version != cpp_version:
msg = "Python version {} does not match C++ version {}!".format( msg = "Python version {} does not match C++ version {}!".format(
@ -84,56 +81,44 @@ if version != cpp_version:
raise RuntimeError(msg) raise RuntimeError(msg)
version_hex = matches.get("HEX", "MISSING") version_hex = matches.get("HEX", "MISSING")
expected_version_hex = build_expected_version_hex(matches) exp_version_hex = build_expected_version_hex(matches)
if version_hex != expected_version_hex: if version_hex != exp_version_hex:
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format( msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
version_hex, version_hex, exp_version_hex
expected_version_hex,
) )
raise RuntimeError(msg) raise RuntimeError(msg)
def get_and_replace(filename, binary=False, **opts): # TODO: use literals & overload (typing extensions or Python 3.8)
with open(filename, "rb" if binary else "r") as f: def get_and_replace(
contents = f.read() filename: Path, binary: bool = False, **opts: str
# Replacement has to be done on text in Python 3 (both work in Python 2) ) -> Union[bytes, str]:
if binary: if binary:
contents = filename.read_bytes()
return string.Template(contents.decode()).substitute(opts).encode() 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 # Use our input files instead when making the SDist (and anything that depends
# on it, like a wheel) # on it, like a wheel)
class SDist(setuptools.command.sdist.sdist): class SDist(setuptools.command.sdist.sdist): # type: ignore[misc]
def make_release_tree(self, base_dir, files): def make_release_tree(self, base_dir: str, files: List[str]) -> None:
setuptools.command.sdist.sdist.make_release_tree(self, base_dir, files) super().make_release_tree(base_dir, files)
for to, src in to_src: for to, src in to_src:
txt = get_and_replace(src, binary=True, version=version, extra_cmd="") 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! # This is normally linked, so unlink before writing!
os.unlink(dest) dest.unlink()
with open(dest, "wb") as f: dest.write_bytes(txt) # type: ignore[arg-type]
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)
# Remove the CMake install directory when done # Remove the CMake install directory when done
@contextlib.contextmanager @contextlib.contextmanager
def remove_output(*sources): def remove_output(*sources: str) -> Iterator[None]:
try: try:
yield yield
finally: finally:
@ -156,9 +141,14 @@ with remove_output("pybind11/include", "pybind11/share"):
if "DCMAKE_INSTALL_PREFIX" not in c if "DCMAKE_INSTALL_PREFIX" not in c
] ]
cmd += fcommand cmd += fcommand
cmake_opts = dict(cwd=DIR, stdout=sys.stdout, stderr=sys.stderr) subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
subprocess.check_call(cmd, **cmake_opts) subprocess.run(
subprocess.check_call(["cmake", "--install", tmpdir], **cmake_opts) ["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) txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd)
code = compile(txt, setup_py, "exec") code = compile(txt, setup_py, "exec")

View File

@ -179,11 +179,6 @@ if(PYBIND11_TEST_FILTER)
pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER}) pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER})
endif() 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: # Skip tests for CUDA check:
# /pybind11/tests/test_constants_and_functions.cpp(125): # /pybind11/tests/test_constants_and_functions.cpp(125):
# error: incompatible exception specifications # error: incompatible exception specifications
@ -388,17 +383,6 @@ function(pybind11_enable_warnings target_name)
-diag-disable 11074,11076) -diag-disable 11074,11076)
endif() endif()
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() endfunction()
set(test_targets pybind11_tests) set(test_targets pybind11_tests)

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""pytest configuration """pytest configuration
Extends output capture as needed by pybind11: ignore constructors, optional unordered lines. 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 import contextlib
@ -13,19 +12,14 @@ import textwrap
import pytest import pytest
import env
# Early diagnostic for failed imports # Early diagnostic for failed imports
import pybind11_tests # noqa: F401 import pybind11_tests # noqa: F401
_unicode_marker = re.compile(r"u(\'[^\']*\')")
_long_marker = re.compile(r"([0-9])L") _long_marker = re.compile(r"([0-9])L")
_hexadecimal = re.compile(r"0x[0-9a-fA-F]+") _hexadecimal = re.compile(r"0x[0-9a-fA-F]+")
# Avoid collecting Python3 only files # Avoid collecting Python3 only files
collect_ignore = [] collect_ignore = []
if env.PY2:
collect_ignore.append("test_async.py")
def _strip_and_dedent(s): 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""" """Basic output post-processing and comparison"""
def __init__(self, string): def __init__(self, string):
@ -83,7 +77,7 @@ class Unordered(Output):
return False return False
class Capture(object): class Capture:
def __init__(self, capfd): def __init__(self, capfd):
self.capfd = capfd self.capfd = capfd
self.out = "" self.out = ""
@ -126,7 +120,7 @@ def capture(capsys):
return Capture(capsys) return Capture(capsys)
class SanitizedString(object): class SanitizedString:
def __init__(self, sanitizer): def __init__(self, sanitizer):
self.sanitizer = sanitizer self.sanitizer = sanitizer
self.string = "" self.string = ""
@ -149,9 +143,7 @@ class SanitizedString(object):
def _sanitize_general(s): def _sanitize_general(s):
s = s.strip() s = s.strip()
s = s.replace("pybind11_tests.", "m.") s = s.replace("pybind11_tests.", "m.")
s = s.replace("unicode", "str")
s = _long_marker.sub(r"\1", s) s = _long_marker.sub(r"\1", s)
s = _unicode_marker.sub(r"\1", s)
return s return s

View File

@ -25,31 +25,14 @@ void gil_acquire() { py::gil_scoped_acquire gil; }
constexpr char kModuleName[] = "cross_module_gil_utils"; constexpr char kModuleName[] = "cross_module_gil_utils";
#if PY_MAJOR_VERSION >= 3
struct PyModuleDef moduledef struct PyModuleDef moduledef
= {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL}; = {PyModuleDef_HEAD_INIT, kModuleName, NULL, 0, NULL, NULL, NULL, NULL, NULL};
#else
PyMethodDef module_methods[] = {{NULL, NULL, 0, NULL}};
#endif
} // namespace } // namespace
extern "C" PYBIND11_EXPORT extern "C" PYBIND11_EXPORT PyObject *PyInit_cross_module_gil_utils() {
#if PY_MAJOR_VERSION >= 3
PyObject *
PyInit_cross_module_gil_utils()
#else
void
initcross_module_gil_utils()
#endif
{
PyObject *m = PyObject *m = PyModule_Create(&moduledef);
#if PY_MAJOR_VERSION >= 3
PyModule_Create(&moduledef);
#else
Py_InitModule(kModuleName, module_methods);
#endif
if (m != NULL) { if (m != NULL) {
static_assert(sizeof(&gil_acquire) == sizeof(void *), static_assert(sizeof(&gil_acquire) == sizeof(void *),
@ -58,7 +41,5 @@ extern "C" PYBIND11_EXPORT
m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire))); m, "gil_acquire_funcaddr", PyLong_FromVoidPtr(reinterpret_cast<void *>(&gil_acquire)));
} }
#if PY_MAJOR_VERSION >= 3
return m; return m;
#endif
} }

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import platform import platform
import sys import sys
@ -11,10 +10,6 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin")
CPYTHON = platform.python_implementation() == "CPython" CPYTHON = platform.python_implementation() == "CPython"
PYPY = platform.python_implementation() == "PyPy" PYPY = platform.python_implementation() == "PyPy"
PY2 = sys.version_info.major == 2
PY = sys.version_info
def deprecated_call(): def deprecated_call():
""" """

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import contextlib import contextlib
import os import os
import string import string
@ -64,11 +63,9 @@ py_files = {
"__init__.py", "__init__.py",
"__main__.py", "__main__.py",
"_version.py", "_version.py",
"_version.pyi",
"commands.py", "commands.py",
"py.typed", "py.typed",
"setup_helpers.py", "setup_helpers.py",
"setup_helpers.pyi",
} }
headers = main_headers | detail_headers | stl_headers headers = main_headers | detail_headers | stl_headers

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import os import os
import subprocess import subprocess
import sys import sys
@ -19,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
(tmpdir / "setup.py").write_text( (tmpdir / "setup.py").write_text(
dedent( dedent(
u"""\ """\
import sys import sys
sys.path.append({MAIN_DIR!r}) sys.path.append({MAIN_DIR!r})
@ -58,7 +57,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
(tmpdir / "main.cpp").write_text( (tmpdir / "main.cpp").write_text(
dedent( dedent(
u"""\ """\
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
int f(int x) { int f(int x) {
@ -96,7 +95,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
(tmpdir / "test.py").write_text( (tmpdir / "test.py").write_text(
dedent( dedent(
u"""\ """\
import simple_setup import simple_setup
assert simple_setup.f(3) == 9 assert simple_setup.f(3) == 9
""" """
@ -121,10 +120,11 @@ def test_intree_extensions(monkeypatch, tmpdir):
subdir.ensure_dir() subdir.ensure_dir()
src = subdir / "ext.cpp" src = subdir / "ext.cpp"
src.ensure() src.ensure()
(ext,) = intree_extensions([src.relto(tmpdir)]) relpath = src.relto(tmpdir)
(ext,) = intree_extensions([relpath])
assert ext.name == "ext" assert ext.name == "ext"
subdir.ensure("__init__.py") subdir.ensure("__init__.py")
(ext,) = intree_extensions([src.relto(tmpdir)]) (ext,) = intree_extensions([relpath])
assert ext.name == "dir.ext" assert ext.name == "dir.ext"

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
asyncio = pytest.importorskip("asyncio") asyncio = pytest.importorskip("asyncio")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import ctypes import ctypes
import io import io
import struct import struct
@ -93,16 +92,16 @@ def test_pointer_to_member_fn():
def test_readonly_buffer(): def test_readonly_buffer():
buf = m.BufferReadOnly(0x64) buf = m.BufferReadOnly(0x64)
view = memoryview(buf) view = memoryview(buf)
assert view[0] == b"d" if env.PY2 else 0x64 assert view[0] == 0x64
assert view.readonly assert view.readonly
with pytest.raises(TypeError): with pytest.raises(TypeError):
view[0] = b"\0" if env.PY2 else 0 view[0] = 0
def test_selective_readonly_buffer(): def test_selective_readonly_buffer():
buf = m.BufferReadOnlySelect() buf = m.BufferReadOnlySelect()
memoryview(buf)[0] = b"d" if env.PY2 else 0x64 memoryview(buf)[0] = 0x64
assert buf.value == 0x64 assert buf.value == 0x64
io.BytesIO(b"A").readinto(buf) io.BytesIO(b"A").readinto(buf)
@ -110,7 +109,7 @@ def test_selective_readonly_buffer():
buf.readonly = True buf.readonly = True
with pytest.raises(TypeError): with pytest.raises(TypeError):
memoryview(buf)[0] = b"\0" if env.PY2 else 0 memoryview(buf)[0] = 0
with pytest.raises(TypeError): with pytest.raises(TypeError):
io.BytesIO(b"1").readinto(buf) io.BytesIO(b"1").readinto(buf)
@ -145,9 +144,6 @@ def test_ctypes_array_2d():
assert not info.readonly 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(): def test_ctypes_from_buffer():
test_pystr = b"0123456789" test_pystr = b"0123456789"
for pyarray in (test_pystr, bytearray(test_pystr)): for pyarray in (test_pystr, bytearray(test_pystr)):

View File

@ -110,8 +110,7 @@ TEST_SUBMODULE(builtin_casters, m) {
"def"); "def");
}); });
m.def("bad_utf16_string", [=]() { return std::u16string({b16, char16_t(0xd800), z16}); }); 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 didn't appear to trigger
// Under Python 2.7, invalid unicode UTF-32 characters don't appear to trigger
// UnicodeDecodeError // UnicodeDecodeError
m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); }); m.def("bad_utf32_string", [=]() { return std::u32string({a32, char32_t(0xd800), z32}); });
if (PYBIND11_SILENCE_MSVC_C4127(sizeof(wchar_t) == 2)) { 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)}); return std::wstring({wchar_t(0x61), wchar_t(0xd800)});
}); });
} }
#endif
m.def("u8_Z", []() -> char { return 'Z'; }); m.def("u8_Z", []() -> char { return 'Z'; });
m.def("u8_eacute", []() -> char { return '\xe9'; }); m.def("u8_eacute", []() -> char { return '\xe9'; });
m.def("u16_ibang", [=]() -> char16_t { return ib16; }); 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); }); []() { return [](py::str s) { return s; }("abc \342\200\275 def"sv); });
m.def("string_view_from_bytes", m.def("string_view_from_bytes",
[](const py::bytes &b) { return [](std::string_view s) { return s; }(b); }); [](const py::bytes &b) { return [](std::string_view s) { return s; }(b); });
# if PY_MAJOR_VERSION >= 3
m.def("string_view_memoryview", []() { m.def("string_view_memoryview", []() {
static constexpr auto val = "Have some \360\237\216\202"sv; static constexpr auto val = "Have some \360\237\216\202"sv;
return py::memoryview::from_memory(val); return py::memoryview::from_memory(val);
}); });
# endif
# ifdef PYBIND11_HAS_U8STRING # ifdef PYBIND11_HAS_U8STRING
m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); }); m.def("string_view8_print", [](std::u8string_view s) { py::print(s, s.size()); });

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- import sys
import pytest import pytest
import env import env
@ -12,12 +13,12 @@ def test_simple_string():
def test_unicode_conversion(): def test_unicode_conversion():
"""Tests unicode conversion and error reporting.""" """Tests unicode conversion and error reporting."""
assert m.good_utf8_string() == u"Say utf8‽ 🎂 𝐀" assert m.good_utf8_string() == "Say utf8‽ 🎂 𝐀"
assert m.good_utf16_string() == u"b‽🎂𝐀z" assert m.good_utf16_string() == "b‽🎂𝐀z"
assert m.good_utf32_string() == u"a𝐀🎂‽z" assert m.good_utf32_string() == "a𝐀🎂‽z"
assert m.good_wchar_string() == u"a⸘𝐀z" assert m.good_wchar_string() == "a⸘𝐀z"
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
assert m.good_utf8_u8string() == u"Say utf8‽ 🎂 𝐀" assert m.good_utf8_u8string() == "Say utf8‽ 🎂 𝐀"
with pytest.raises(UnicodeDecodeError): with pytest.raises(UnicodeDecodeError):
m.bad_utf8_string() m.bad_utf8_string()
@ -25,7 +26,7 @@ def test_unicode_conversion():
with pytest.raises(UnicodeDecodeError): with pytest.raises(UnicodeDecodeError):
m.bad_utf16_string() 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"): if hasattr(m, "bad_utf32_string"):
with pytest.raises(UnicodeDecodeError): with pytest.raises(UnicodeDecodeError):
m.bad_utf32_string() m.bad_utf32_string()
@ -37,10 +38,10 @@ def test_unicode_conversion():
m.bad_utf8_u8string() m.bad_utf8_u8string()
assert m.u8_Z() == "Z" assert m.u8_Z() == "Z"
assert m.u8_eacute() == u"é" assert m.u8_eacute() == "é"
assert m.u16_ibang() == u"" assert m.u16_ibang() == ""
assert m.u32_mathbfA() == u"𝐀" assert m.u32_mathbfA() == "𝐀"
assert m.wchar_heart() == u"" assert m.wchar_heart() == ""
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
assert m.u8_char8_Z() == "Z" 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" toolong_message = "Expected a character, but multi-character string found"
assert m.ord_char(u"a") == 0x61 # simple ASCII assert m.ord_char("a") == 0x61 # simple ASCII
assert m.ord_char_lv(u"b") == 0x62 assert m.ord_char_lv("b") == 0x62
assert ( assert (
m.ord_char(u"é") == 0xE9 m.ord_char("é") == 0xE9
) # requires 2 bytes in utf-8, but can be stuffed in a char ) # requires 2 bytes in utf-8, but can be stuffed in a char
with pytest.raises(ValueError) as excinfo: 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) assert str(excinfo.value) == toobig_message(0x100)
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
assert m.ord_char(u"ab") assert m.ord_char("ab")
assert str(excinfo.value) == toolong_message assert str(excinfo.value) == toolong_message
assert m.ord_char16(u"a") == 0x61 assert m.ord_char16("a") == 0x61
assert m.ord_char16(u"é") == 0xE9 assert m.ord_char16("é") == 0xE9
assert m.ord_char16_lv(u"ê") == 0xEA assert m.ord_char16_lv("ê") == 0xEA
assert m.ord_char16(u"Ā") == 0x100 assert m.ord_char16("Ā") == 0x100
assert m.ord_char16(u"") == 0x203D assert m.ord_char16("") == 0x203D
assert m.ord_char16(u"") == 0x2665 assert m.ord_char16("") == 0x2665
assert m.ord_char16_lv(u"") == 0x2661 assert m.ord_char16_lv("") == 0x2661
with pytest.raises(ValueError) as excinfo: 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) assert str(excinfo.value) == toobig_message(0x10000)
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
assert m.ord_char16(u"aa") assert m.ord_char16("aa")
assert str(excinfo.value) == toolong_message assert str(excinfo.value) == toolong_message
assert m.ord_char32(u"a") == 0x61 assert m.ord_char32("a") == 0x61
assert m.ord_char32(u"é") == 0xE9 assert m.ord_char32("é") == 0xE9
assert m.ord_char32(u"Ā") == 0x100 assert m.ord_char32("Ā") == 0x100
assert m.ord_char32(u"") == 0x203D assert m.ord_char32("") == 0x203D
assert m.ord_char32(u"") == 0x2665 assert m.ord_char32("") == 0x2665
assert m.ord_char32(u"🎂") == 0x1F382 assert m.ord_char32("🎂") == 0x1F382
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
assert m.ord_char32(u"aa") assert m.ord_char32("aa")
assert str(excinfo.value) == toolong_message assert str(excinfo.value) == toolong_message
assert m.ord_wchar(u"a") == 0x61 assert m.ord_wchar("a") == 0x61
assert m.ord_wchar(u"é") == 0xE9 assert m.ord_wchar("é") == 0xE9
assert m.ord_wchar(u"Ā") == 0x100 assert m.ord_wchar("Ā") == 0x100
assert m.ord_wchar(u"") == 0x203D assert m.ord_wchar("") == 0x203D
assert m.ord_wchar(u"") == 0x2665 assert m.ord_wchar("") == 0x2665
if m.wchar_size == 2: if m.wchar_size == 2:
with pytest.raises(ValueError) as excinfo: 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) assert str(excinfo.value) == toobig_message(0x10000)
else: else:
assert m.ord_wchar(u"🎂") == 0x1F382 assert m.ord_wchar("🎂") == 0x1F382
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
assert m.ord_wchar(u"aa") assert m.ord_wchar("aa")
assert str(excinfo.value) == toolong_message assert str(excinfo.value) == toolong_message
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
assert m.ord_char8(u"a") == 0x61 # simple ASCII assert m.ord_char8("a") == 0x61 # simple ASCII
assert m.ord_char8_lv(u"b") == 0x62 assert m.ord_char8_lv("b") == 0x62
assert ( assert (
m.ord_char8(u"é") == 0xE9 m.ord_char8("é") == 0xE9
) # requires 2 bytes in utf-8, but can be stuffed in a char ) # requires 2 bytes in utf-8, but can be stuffed in a char
with pytest.raises(ValueError) as excinfo: 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) assert str(excinfo.value) == toobig_message(0x100)
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
assert m.ord_char8(u"ab") assert m.ord_char8("ab")
assert str(excinfo.value) == toolong_message 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.""" one-way: the only way to return bytes to Python is via the pybind11::bytes class."""
# Issue #816 # Issue #816
def to_bytes(s): assert m.strlen(b"hi") == 2
b = s if env.PY2 else s.encode("utf8") assert m.string_length(b"world") == 5
assert isinstance(b, bytes) assert m.string_length("a\x00b".encode()) == 3
return b assert m.strlen("a\x00b".encode()) == 1 # C-string limitation
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
# passing in a utf8 encoded string should work # 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 <string_view>") @pytest.mark.skipif(not hasattr(m, "has_string_view"), reason="no <string_view>")
@ -142,26 +138,26 @@ def test_string_view(capture):
"""Tests support for C++17 string_view arguments and return values""" """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]
assert m.string_view_chars("Hi 🎂") == [72, 105, 32, 0xF0, 0x9F, 0x8E, 0x82] 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_view16_chars("Hi 🎂") == [72, 105, 32, 0xD83C, 0xDF82]
assert m.string_view32_chars(u"Hi 🎂") == [72, 105, 32, 127874] assert m.string_view32_chars("Hi 🎂") == [72, 105, 32, 127874]
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
assert m.string_view8_chars("Hi") == [72, 105] 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_view_return() == "utf8 secret 🎂"
assert m.string_view16_return() == u"utf16 secret 🎂" assert m.string_view16_return() == "utf16 secret 🎂"
assert m.string_view32_return() == u"utf32 secret 🎂" assert m.string_view32_return() == "utf32 secret 🎂"
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
assert m.string_view8_return() == u"utf8 secret 🎂" assert m.string_view8_return() == "utf8 secret 🎂"
with capture: with capture:
m.string_view_print("Hi") m.string_view_print("Hi")
m.string_view_print("utf8 🎂") m.string_view_print("utf8 🎂")
m.string_view16_print(u"utf16 🎂") m.string_view16_print("utf16 🎂")
m.string_view32_print(u"utf32 🎂") m.string_view32_print("utf32 🎂")
assert ( assert (
capture capture
== u""" == """
Hi 2 Hi 2
utf8 🎂 9 utf8 🎂 9
utf16 🎂 8 utf16 🎂 8
@ -171,10 +167,10 @@ def test_string_view(capture):
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
with capture: with capture:
m.string_view8_print("Hi") m.string_view8_print("Hi")
m.string_view8_print(u"utf8 🎂") m.string_view8_print("utf8 🎂")
assert ( assert (
capture capture
== u""" == """
Hi 2 Hi 2
utf8 🎂 9 utf8 🎂 9
""" """
@ -183,11 +179,11 @@ def test_string_view(capture):
with capture: with capture:
m.string_view_print("Hi, ascii") m.string_view_print("Hi, ascii")
m.string_view_print("Hi, utf8 🎂") m.string_view_print("Hi, utf8 🎂")
m.string_view16_print(u"Hi, utf16 🎂") m.string_view16_print("Hi, utf16 🎂")
m.string_view32_print(u"Hi, utf32 🎂") m.string_view32_print("Hi, utf32 🎂")
assert ( assert (
capture capture
== u""" == """
Hi, ascii 9 Hi, ascii 9
Hi, utf8 🎂 13 Hi, utf8 🎂 13
Hi, utf16 🎂 12 Hi, utf16 🎂 12
@ -197,22 +193,21 @@ def test_string_view(capture):
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
with capture: with capture:
m.string_view8_print("Hi, ascii") m.string_view8_print("Hi, ascii")
m.string_view8_print(u"Hi, utf8 🎂") m.string_view8_print("Hi, utf8 🎂")
assert ( assert (
capture capture
== u""" == """
Hi, ascii 9 Hi, ascii 9
Hi, utf8 🎂 13 Hi, utf8 🎂 13
""" """
) )
assert m.string_view_bytes() == b"abc \x80\x80 def" assert m.string_view_bytes() == b"abc \x80\x80 def"
assert m.string_view_str() == u"abc ‽ def" assert m.string_view_str() == "abc ‽ def"
assert m.string_view_from_bytes(u"abc ‽ def".encode("utf-8")) == u"abc ‽ def" assert m.string_view_from_bytes("abc ‽ def".encode()) == "abc ‽ def"
if hasattr(m, "has_u8string"): if hasattr(m, "has_u8string"):
assert m.string_view8_str() == u"abc ‽ def" assert m.string_view8_str() == "abc ‽ def"
if not env.PY2: assert m.string_view_memoryview() == "Have some 🎂".encode()
assert m.string_view_memoryview() == "Have some 🎂".encode()
assert m.bytes_from_type_with_both_operator_string_and_string_view() == b"success" 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" 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.i64_str(-1) == "-1"
assert m.i32_str(2000000000) == "2000000000" assert m.i32_str(2000000000) == "2000000000"
assert m.u32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000"
if env.PY2: assert m.i64_str(-999999999999) == "-999999999999"
assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.u64_str(999999999999) == "999999999999"
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"
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.u32_str(-1) m.u32_str(-1)
@ -252,46 +235,38 @@ def test_integer_casting():
m.i32_str(3000000000) m.i32_str(3000000000)
assert "incompatible function arguments" in str(excinfo.value) 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(): def test_int_convert():
class Int(object): class Int:
def __int__(self): def __int__(self):
return 42 return 42
class NotInt(object): class NotInt:
pass pass
class Float(object): class Float:
def __float__(self): def __float__(self):
return 41.99999 return 41.99999
class Index(object): class Index:
def __index__(self): def __index__(self):
return 42 return 42
class IntAndIndex(object): class IntAndIndex:
def __int__(self): def __int__(self):
return 42 return 42
def __index__(self): def __index__(self):
return 0 return 0
class RaisingTypeErrorOnIndex(object): class RaisingTypeErrorOnIndex:
def __index__(self): def __index__(self):
raise TypeError raise TypeError
def __int__(self): def __int__(self):
return 42 return 42
class RaisingValueErrorOnIndex(object): class RaisingValueErrorOnIndex:
def __index__(self): def __index__(self):
raise ValueError raise ValueError
@ -311,7 +286,7 @@ def test_int_convert():
cant_convert(3.14159) cant_convert(3.14159)
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) # 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(): with env.deprecated_call():
assert convert(Int()) == 42 assert convert(Int()) == 42
else: else:
@ -348,7 +323,7 @@ def test_numpy_int_convert():
# TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar)
# TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7) # TODO: PyPy 3.8 does not behave like CPython 3.8 here yet (7.3.7)
# https://github.com/pybind/pybind11/issues/3408 # 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(): with env.deprecated_call():
assert convert(np.float32(3.14159)) == 3 assert convert(np.float32(3.14159)) == 3
else: else:
@ -475,7 +450,7 @@ def test_bool_caster():
require_implicit(None) require_implicit(None)
assert convert(None) is False assert convert(None) is False
class A(object): class A:
def __init__(self, x): def __init__(self, x):
self.x = x self.x = x
@ -485,7 +460,7 @@ def test_bool_caster():
def __bool__(self): def __bool__(self):
return self.x return self.x
class B(object): class B:
pass pass
# Arbitrary objects are not accepted # Arbitrary objects are not accepted
@ -515,17 +490,9 @@ def test_numpy_bool():
def test_int_long(): 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.int_cast(), int)
assert isinstance(m.long_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(): def test_void_caster_2():

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import time import time
from threading import Thread from threading import Thread

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import datetime import datetime
import pytest import pytest

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
@ -7,7 +6,6 @@ from pybind11_tests import class_ as m
def test_repr(): def test_repr():
# In Python 3.3+, repr() accesses __qualname__
assert "pybind11_type" in repr(type(UserType)) assert "pybind11_type" in repr(type(UserType))
assert "UserType" in repr(UserType) assert "UserType" in repr(UserType)
@ -103,8 +101,8 @@ def test_docstrings(doc):
def test_qualname(doc): def test_qualname(doc):
"""Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we """Tests that a properly qualified name is set in __qualname__ and that
backport the attribute) and that generated docstrings properly use it and the module name""" generated docstrings properly use it and the module name"""
assert m.NestBase.__qualname__ == "NestBase" assert m.NestBase.__qualname__ == "NestBase"
assert m.NestBase.Nested.__qualname__ == "NestBase.Nested" assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
@ -130,13 +128,13 @@ def test_qualname(doc):
doc(m.NestBase.Nested.fn) doc(m.NestBase.Nested.fn)
== """ == """
fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
""" # noqa: E501 line too long """
) )
assert ( assert (
doc(m.NestBase.Nested.fa) doc(m.NestBase.Nested.fa)
== """ == """
fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None 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.__module__ == "pybind11_tests.class_"
assert m.NestBase.Nested.__module__ == "pybind11_tests.class_" assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"

View File

@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
import sys import sys
import test_cmake_build 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 assert test_cmake_build.add(1, 2) == 3
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env
from pybind11_tests import const_name as m 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): def test_const_name(func, selector, expected):
if isinstance(func, type(u"") if env.PY2 else str): if isinstance(func, str):
pytest.skip(func) pytest.skip(func)
text = func(selector) text = func(selector)
assert text == expected assert text == expected

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
m = pytest.importorskip("pybind11_tests.constants_and_functions") m = pytest.importorskip("pybind11_tests.constants_and_functions")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import copy_move_policies as m from pybind11_tests import copy_move_policies as m

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import custom_type_casters as m 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 loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
13 13
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
""" # noqa: E501 line too long """
) )
assert ( assert (
msg(a.g("this is a", "this is b", 42)) 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 loading ArgInspector1 argument WITH conversion allowed. Argument value = this is b
42 42
loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2) loading ArgInspector2 argument WITH conversion allowed. Argument value = (default arg inspector 2)
""" # noqa: E501 line too long """
) )
assert ( assert (
msg(a.g("this is a", "this is b", 42, "this is d")) 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 1. (i: int) -> int
Invoked with: 4.0 Invoked with: 4.0
""" # noqa: E501 line too long """
) )
assert m.ints_only(4) == 2 assert m.ints_only(4) == 2

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
import gc import gc
import weakref import weakref

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from pybind11_tests import docstring_options as m from pybind11_tests import docstring_options as m

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import ConstructorStats 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: double_threer(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None 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")) + 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: double_threec(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None 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")) + 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) doc(m.sparse_copy_r)
== """ == """
sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32] sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32]
""" # noqa: E501 line too long """
) )
assert ( assert (
doc(m.sparse_copy_c) doc(m.sparse_copy_c)
== """ == """
sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32] sparse_copy_c(arg0: scipy.sparse.csc_matrix[numpy.float32]) -> scipy.sparse.csc_matrix[numpy.float32]
""" # noqa: E501 line too long """
) )

View File

@ -126,7 +126,6 @@ TEST_CASE("Override cache") {
TEST_CASE("Import error handling") { TEST_CASE("Import error handling") {
REQUIRE_NOTHROW(py::module_::import("widget_module")); REQUIRE_NOTHROW(py::module_::import("widget_module"));
REQUIRE_THROWS_WITH(py::module_::import("throw_exception"), "ImportError: C++ Error"); 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"), REQUIRE_THROWS_WITH(py::module_::import("throw_error_already_set"),
Catch::Contains("ImportError: initialization failed")); Catch::Contains("ImportError: initialization failed"));
@ -142,10 +141,6 @@ TEST_CASE("Import error handling") {
locals); locals);
REQUIRE(locals["is_keyerror"].cast<bool>() == true); REQUIRE(locals["is_keyerror"].cast<bool>() == true);
REQUIRE(locals["message"].cast<std::string>() == "'missing'"); REQUIRE(locals["message"].cast<std::string>() == "'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") { TEST_CASE("There can be only one interpreter") {

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import sys import sys
from widget_module import Widget from widget_module import Widget
@ -6,7 +5,7 @@ from widget_module import Widget
class DerivedWidget(Widget): class DerivedWidget(Widget):
def __init__(self, message): def __init__(self, message):
super(DerivedWidget, self).__init__(message) super().__init__(message)
def the_answer(self): def the_answer(self):
return 42 return 42

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
import trampoline_module import trampoline_module

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env
from pybind11_tests import enums as m 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 type(m.ScopedCharEnum.Positive.__int__()) is int
assert int(m.ScopedChar16Enum.Zero) == 0 assert int(m.ScopedChar16Enum.Zero) == 0
assert hash(m.ScopedChar32Enum.Positive) == 1 assert hash(m.ScopedChar32Enum.Positive) == 1
if env.PY2: assert type(m.ScopedCharEnum.Positive.__getstate__()) is int
assert m.ScopedCharEnum.Positive.__getstate__() == 1 # long
else:
assert type(m.ScopedCharEnum.Positive.__getstate__()) is int
assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive assert m.ScopedWCharEnum(1) == m.ScopedWCharEnum.Positive
with pytest.raises(TypeError): with pytest.raises(TypeError):
# Even if the underlying type is char, only an int can be used to construct the enum: # 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 type(m.ScopedBoolEnum.TRUE.__int__()) is int
assert int(m.ScopedBoolEnum.FALSE) == 0 assert int(m.ScopedBoolEnum.FALSE) == 0
assert hash(m.ScopedBoolEnum.TRUE) == 1 assert hash(m.ScopedBoolEnum.TRUE) == 1
if env.PY2: assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int
assert m.ScopedBoolEnum.TRUE.__getstate__() == 1 # long
else:
assert type(m.ScopedBoolEnum.TRUE.__getstate__()) is int
assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE assert m.ScopedBoolEnum(1) == m.ScopedBoolEnum.TRUE
# Enum could construct with a bool # Enum could construct with a bool
# (bool is a strict subclass of int, and False will be converted to 0) # (bool is a strict subclass of int, and False will be converted to 0)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import os import os
import pytest import pytest
@ -18,7 +17,7 @@ def test_evals(capture):
assert m.test_eval_failure() 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(): def test_eval_file():
filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py")
assert m.test_eval_file(filename) assert m.test_eval_file(filename)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# This file is called from 'test_eval.py' # This file is called from 'test_eval.py'
if "call_test2" in locals(): if "call_test2" in locals():

View File

@ -276,8 +276,6 @@ TEST_SUBMODULE(exceptions, m) {
m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); }); m.def("throw_should_be_translated_to_key_error", []() { throw shared_exception(); });
#if PY_VERSION_HEX >= 0x03030000
m.def("raise_from", []() { m.def("raise_from", []() {
PyErr_SetString(PyExc_ValueError, "inner"); PyErr_SetString(PyExc_ValueError, "inner");
py::raise_from(PyExc_ValueError, "outer"); py::raise_from(PyExc_ValueError, "outer");
@ -301,5 +299,4 @@ TEST_SUBMODULE(exceptions, m) {
std::throw_with_nested(std::runtime_error("Outer Exception")); std::throw_with_nested(std::runtime_error("Outer Exception"));
} }
}); });
#endif
} }

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import sys import sys
import pytest import pytest
@ -24,7 +23,6 @@ def test_error_already_set(msg):
assert msg(excinfo.value) == "foo" assert msg(excinfo.value) == "foo"
@pytest.mark.skipif("env.PY2")
def test_raise_from(msg): def test_raise_from(msg):
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
m.raise_from() m.raise_from()
@ -32,7 +30,6 @@ def test_raise_from(msg):
assert msg(excinfo.value.__cause__) == "inner" assert msg(excinfo.value.__cause__) == "inner"
@pytest.mark.skipif("env.PY2")
def test_raise_from_already_set(msg): def test_raise_from_already_set(msg):
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
m.raise_from_already_set() m.raise_from_already_set()
@ -102,7 +99,7 @@ def ignore_pytest_unraisable_warning(f):
@ignore_pytest_unraisable_warning @ignore_pytest_unraisable_warning
def test_python_alreadyset_in_destructor(monkeypatch, capsys): def test_python_alreadyset_in_destructor(monkeypatch, capsys):
hooked = False hooked = False
triggered = [False] # mutable, so Python 2.7 closure can modify it triggered = False
if hasattr(sys, "unraisablehook"): # Python 3.8+ if hasattr(sys, "unraisablehook"): # Python 3.8+
hooked = True hooked = True
@ -112,7 +109,8 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys):
def hook(unraisable_hook_args): def hook(unraisable_hook_args):
exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args
if obj == "already_set demo": if obj == "already_set demo":
triggered[0] = True nonlocal triggered
triggered = True
default_hook(unraisable_hook_args) default_hook(unraisable_hook_args)
return return
@ -121,11 +119,11 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys):
assert m.python_alreadyset_in_destructor("already_set demo") is True assert m.python_alreadyset_in_destructor("already_set demo") is True
if hooked: if hooked:
assert triggered[0] is True assert triggered is True
_, captured_stderr = capsys.readouterr() _, captured_stderr = capsys.readouterr()
# Error message is different in Python 2 and 3, check for words that appear in both assert captured_stderr.startswith("Exception ignored in: 'already_set demo'")
assert "ignored" in captured_stderr and "already_set demo" in captured_stderr assert captured_stderr.rstrip().endswith("KeyError: 'bar'")
def test_exception_matches(): def test_exception_matches():
@ -239,7 +237,6 @@ def test_nested_throws(capture):
assert str(excinfo.value) == "this is a helper-defined translated exception" assert str(excinfo.value) == "this is a helper-defined translated exception"
@pytest.mark.skipif("env.PY2")
def test_throw_nested_exception(): def test_throw_nested_exception():
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
m.throw_nested_exception() 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 # This can often happen if you wrap a pybind11 class in a Python wrapper
def test_invalid_repr(): def test_invalid_repr():
class MyRepr(object): class MyRepr:
def __repr__(self): def __repr__(self):
raise AttributeError("Example error") raise AttributeError("Example error")

View File

@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
import re import re
import pytest import pytest
import env # noqa: F401
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import factory_constructors as m from pybind11_tests import factory_constructors as m
from pybind11_tests.factory_constructors import tag 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) 4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle)
Invoked with: 'invalid', 'constructor', 'arguments' Invoked with: 'invalid', 'constructor', 'arguments'
""" # noqa: E501 line too long """
) )
assert ( assert (
@ -465,12 +463,10 @@ def test_reallocation_g(capture, msg):
) )
@pytest.mark.skipif("env.PY2")
def test_invalid_self(): def test_invalid_self():
"""Tests invocation of the pybind-registered base class with an invalid `self` argument. You """Tests invocation of the pybind-registered base class with an invalid `self` argument."""
can only actually do this on Python 3: Python 2 raises an exception itself if you try."""
class NotPybindDerived(object): class NotPybindDerived:
pass pass
# Attempts to initialize with an invalid type passed as `self`: # Attempts to initialize with an invalid type passed as `self`:

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import multiprocessing import multiprocessing
import threading import threading

View File

@ -1,44 +1,8 @@
# -*- coding: utf-8 -*- from contextlib import redirect_stderr, redirect_stdout
import sys from io import StringIO
from contextlib import contextmanager
from pybind11_tests import iostream as m 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): def test_captured(capsys):
msg = "I've been redirected to Python, I hope!" msg = "I've been redirected to Python, I hope!"

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401
from pybind11_tests import kwargs_and_defaults as m 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 1. (arg0: int, arg1: float, *args) -> tuple
Invoked with: 1 Invoked with: 1
""" # noqa: E501 line too long """
) )
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
assert mpa() assert mpa()
@ -93,7 +91,7 @@ def test_mixed_args_and_kwargs(msg):
1. (arg0: int, arg1: float, *args) -> tuple 1. (arg0: int, arg1: float, *args) -> tuple
Invoked with: Invoked with:
""" # noqa: E501 line too long """
) )
assert mpk(-2, 3.5, pi=3.14159, e=2.71828) == ( 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 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple
Invoked with: 1; kwargs: i=1 Invoked with: 1; kwargs: i=1
""" # noqa: E501 line too long """
) )
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
assert mpakd(1, 2, j=1) 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 1. (i: int = 1, j: float = 3.14159, *args, **kwargs) -> tuple
Invoked with: 1, 2; kwargs: j=1 Invoked with: 1, 2; kwargs: j=1
""" # noqa: E501 line too long """
) )
# Arguments after a py::args are automatically keyword-only (pybind 2.9+) # 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(): def test_args_refcount():
"""Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
arguments""" arguments"""

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
@ -200,7 +199,7 @@ def test_stl_caster_vs_stl_bind(msg):
1. (arg0: pybind11_cross_module_tests.VectorInt) -> int 1. (arg0: pybind11_cross_module_tests.VectorInt) -> int
Invoked with: [1, 2, 3] Invoked with: [1, 2, 3]
""" # noqa: E501 line too long """
) )

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
@ -494,26 +493,17 @@ def test_overload_ordering():
assert m.overload_order("string") == 1 assert m.overload_order("string") == 1
assert m.overload_order(0) == 4 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 "1. overload_order(arg0: int) -> int" in m.overload_order.__doc__
assert ( assert "2. overload_order(arg0: str) -> int" in m.overload_order.__doc__
"2. overload_order(arg0: {}) -> int".format(uni_name) assert "3. overload_order(arg0: str) -> int" in m.overload_order.__doc__
in m.overload_order.__doc__
)
assert (
"3. overload_order(arg0: {}) -> int".format(uni_name)
in m.overload_order.__doc__
)
assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__ assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__
with pytest.raises(TypeError) as err: with pytest.raises(TypeError) as err:
m.overload_order(1.1) m.overload_order(1.1)
assert "1. (arg0: int) -> int" in str(err.value) assert "1. (arg0: int) -> int" in str(err.value)
assert "2. (arg0: {}) -> int".format(uni_name) in str(err.value) assert "2. (arg0: str) -> int" in str(err.value)
assert "3. (arg0: {}) -> int".format(uni_name) in str(err.value) assert "3. (arg0: str) -> int" in str(err.value)
assert "4. (arg0: int) -> int" in str(err.value) assert "4. (arg0: int) -> int" in str(err.value)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import modules as m from pybind11_tests import modules as m
from pybind11_tests.modules import subsubmodule as ms from pybind11_tests.modules import subsubmodule as ms

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
@ -13,8 +12,7 @@ def test_multiple_inheritance_cpp():
assert mt.bar() == 4 assert mt.bar() == 4
@pytest.mark.skipif("env.PYPY and env.PY2") @pytest.mark.xfail("env.PYPY")
@pytest.mark.xfail("env.PYPY and not env.PY2")
def test_multiple_inheritance_mix1(): def test_multiple_inheritance_mix1():
class Base1: class Base1:
def __init__(self, i): def __init__(self, i):
@ -53,15 +51,14 @@ def test_multiple_inheritance_mix2():
assert mt.bar() == 4 assert mt.bar() == 4
@pytest.mark.skipif("env.PYPY and env.PY2") @pytest.mark.xfail("env.PYPY")
@pytest.mark.xfail("env.PYPY and not env.PY2")
def test_multiple_inheritance_python(): def test_multiple_inheritance_python():
class MI1(m.Base1, m.Base2): class MI1(m.Base1, m.Base2):
def __init__(self, i, j): def __init__(self, i, j):
m.Base1.__init__(self, i) m.Base1.__init__(self, i)
m.Base2.__init__(self, j) m.Base2.__init__(self, j)
class B1(object): class B1:
def v(self): def v(self):
return 1 return 1
@ -96,7 +93,7 @@ def test_multiple_inheritance_python():
def v(self): def v(self):
return 2 return 2
class B3(object): class B3:
def v(self): def v(self):
return 3 return 3

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import re import re
import pytest import pytest

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import numpy_vectorize as m from pybind11_tests import numpy_vectorize as m

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import ConstructorStats, UserType from pybind11_tests import ConstructorStats, UserType
@ -40,7 +39,7 @@ def test_pointers(msg):
1. (arg0: capsule) -> int 1. (arg0: capsule) -> int
Invoked with: [1, 2, 3] Invoked with: [1, 2, 3]
""" # noqa: E501 line too long """
) )
assert m.return_null_str() is None assert m.return_null_str() is None

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats
from pybind11_tests import operators as m from pybind11_tests import operators as m
@ -151,5 +149,4 @@ def test_overriding_eq_reset_hash():
def test_return_set_of_unhashable(): def test_return_set_of_unhashable():
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.get_unhashable_HashMe_set() 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:")

View File

@ -1,14 +1,10 @@
# -*- coding: utf-8 -*- import pickle
import pytest import pytest
import env import env
from pybind11_tests import pickling as m 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"]) @pytest.mark.parametrize("cls_name", ["Pickleable", "PickleableNew"])
def test_roundtrip(cls_name): def test_roundtrip(cls_name):

View File

@ -87,7 +87,6 @@ TEST_SUBMODULE(pytypes, m) {
m.def("tuple_size_t", []() { return py::tuple{(py::size_t) 0}; }); m.def("tuple_size_t", []() { return py::tuple{(py::size_t) 0}; });
m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); }); m.def("get_tuple", []() { return py::make_tuple(42, py::none(), "spam"); });
#if PY_VERSION_HEX >= 0x03030000
// test_simple_namespace // test_simple_namespace
m.def("get_simple_namespace", []() { m.def("get_simple_namespace", []() {
auto ns = py::module_::import("types").attr("SimpleNamespace")( auto ns = py::module_::import("types").attr("SimpleNamespace")(
@ -96,7 +95,6 @@ TEST_SUBMODULE(pytypes, m) {
py::setattr(ns, "right", py::int_(2)); py::setattr(ns, "right", py::int_(2));
return ns; return ns;
}); });
#endif
// test_str // test_str
m.def("str_from_char_ssize_t", []() { return py::str{"red", (py::ssize_t) 3}; }); 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<void *>(nullptr), 1, "B", {}, {}); return py::memoryview::from_buffer(static_cast<void *>(nullptr), 1, "B", {}, {});
}); });
#if PY_MAJOR_VERSION >= 3
m.def("test_memoryview_from_memory", []() { m.def("test_memoryview_from_memory", []() {
const char *buf = "\xff\xe1\xab\x37"; const char *buf = "\xff\xe1\xab\x37";
return py::memoryview::from_memory(buf, static_cast<py::ssize_t>(strlen(buf))); return py::memoryview::from_memory(buf, static_cast<py::ssize_t>(strlen(buf)));
}); });
#endif
// test_builtin_functions // test_builtin_functions
m.def("get_len", [](py::handle h) { return py::len(h); }); m.def("get_len", [](py::handle h) { return py::len(h); });

View File

@ -1,11 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import division
import sys import sys
import pytest import pytest
import env import env # noqa: F401
from pybind11_tests import debug_enabled from pybind11_tests import debug_enabled
from pybind11_tests import pytypes as m from pybind11_tests import pytypes as m
@ -123,7 +120,6 @@ def test_tuple():
assert m.get_tuple() == (42, None, "spam") assert m.get_tuple() == (42, None, "spam")
@pytest.mark.skipif("env.PY2")
def test_simple_namespace(): def test_simple_namespace():
ns = m.get_simple_namespace() ns = m.get_simple_namespace()
assert ns.attr == 42 assert ns.attr == 42
@ -140,7 +136,7 @@ def test_str(doc):
assert doc(m.str_from_bytes) == "str_from_bytes() -> str" assert doc(m.str_from_bytes) == "str_from_bytes() -> str"
class A(object): class A:
def __str__(self): def __str__(self):
return "this is a str" return "this is a str"
@ -158,24 +154,14 @@ def test_str(doc):
malformed_utf8 = b"\x80" malformed_utf8 = b"\x80"
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
assert m.str_from_object(malformed_utf8) is malformed_utf8 assert m.str_from_object(malformed_utf8) is malformed_utf8
elif env.PY2:
with pytest.raises(UnicodeDecodeError):
m.str_from_object(malformed_utf8)
else: else:
assert m.str_from_object(malformed_utf8) == "b'\\x80'" assert m.str_from_object(malformed_utf8) == "b'\\x80'"
if env.PY2: assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
with pytest.raises(UnicodeDecodeError):
m.str_from_handle(malformed_utf8)
else:
assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
assert m.str_from_string_from_str("this is a str") == "this is a str" assert m.str_from_string_from_str("this is a str") == "this is a str"
ucs_surrogates_str = u"\udcc3" ucs_surrogates_str = "\udcc3"
if env.PY2: with pytest.raises(UnicodeEncodeError):
assert u"\udcc3" == m.str_from_string_from_str(ucs_surrogates_str) m.str_from_string_from_str(ucs_surrogates_str)
else:
with pytest.raises(UnicodeEncodeError):
m.str_from_string_from_str(ucs_surrogates_str)
def test_bytes(doc): def test_bytes(doc):
@ -184,9 +170,7 @@ def test_bytes(doc):
assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_string().decode() == "foo"
assert m.bytes_from_str().decode() == "bar" assert m.bytes_from_str().decode() == "bar"
assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes"
"str" if env.PY2 else "bytes"
)
def test_bytearray(doc): def test_bytearray(doc):
@ -278,11 +262,6 @@ def test_constructors():
"""C++ default and converting constructors are equivalent to type calls in Python""" """C++ default and converting constructors are equivalent to type calls in Python"""
types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set] types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set]
expected = {t.__name__: t() for t in types} 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 assert m.default_constructors() == expected
data = { data = {
@ -300,11 +279,6 @@ def test_constructors():
} }
inputs = {k.__name__: v for k, v in data.items()} inputs = {k.__name__: v for k, v in data.items()}
expected = {k.__name__: k(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.converting_constructors(inputs) == expected
assert m.cast_functions(inputs) == expected assert m.cast_functions(inputs) == expected
@ -340,46 +314,39 @@ def test_non_converting_constructors():
def test_pybind11_str_raw_str(): def test_pybind11_str_raw_str():
# specifically to exercise pybind11::str::raw_str # specifically to exercise pybind11::str::raw_str
cvt = m.convert_to_pybind11_str cvt = m.convert_to_pybind11_str
assert cvt(u"Str") == u"Str" assert cvt("Str") == "Str"
assert cvt(b"Bytes") == u"Bytes" if env.PY2 else "b'Bytes'" assert cvt(b"Bytes") == "b'Bytes'"
assert cvt(None) == u"None" assert cvt(None) == "None"
assert cvt(False) == u"False" assert cvt(False) == "False"
assert cvt(True) == u"True" assert cvt(True) == "True"
assert cvt(42) == u"42" assert cvt(42) == "42"
assert cvt(2 ** 65) == u"36893488147419103232" assert cvt(2**65) == "36893488147419103232"
assert cvt(-1.50) == u"-1.5" assert cvt(-1.50) == "-1.5"
assert cvt(()) == u"()" assert cvt(()) == "()"
assert cvt((18,)) == u"(18,)" assert cvt((18,)) == "(18,)"
assert cvt([]) == u"[]" assert cvt([]) == "[]"
assert cvt([28]) == u"[28]" assert cvt([28]) == "[28]"
assert cvt({}) == u"{}" assert cvt({}) == "{}"
assert cvt({3: 4}) == u"{3: 4}" assert cvt({3: 4}) == "{3: 4}"
assert cvt(set()) == u"set([])" if env.PY2 else "set()" assert cvt(set()) == "set()"
assert cvt({3, 3}) == u"set([3])" if env.PY2 else "{3}" assert cvt({3, 3}) == "{3}"
valid_orig = u"DZ" valid_orig = "DZ"
valid_utf8 = valid_orig.encode("utf-8") valid_utf8 = valid_orig.encode("utf-8")
valid_cvt = cvt(valid_utf8) valid_cvt = cvt(valid_utf8)
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
assert valid_cvt is valid_utf8 assert valid_cvt is valid_utf8
else: else:
assert type(valid_cvt) is unicode if env.PY2 else str # noqa: F821 assert type(valid_cvt) is str
if env.PY2: assert valid_cvt == "b'\\xc7\\xb1'"
assert valid_cvt == valid_orig
else:
assert valid_cvt == "b'\\xc7\\xb1'"
malformed_utf8 = b"\x80" malformed_utf8 = b"\x80"
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
assert cvt(malformed_utf8) is malformed_utf8 assert cvt(malformed_utf8) is malformed_utf8
else: else:
if env.PY2: malformed_cvt = cvt(malformed_utf8)
with pytest.raises(UnicodeDecodeError): assert type(malformed_cvt) is str
cvt(malformed_utf8) assert malformed_cvt == "b'\\x80'"
else:
malformed_cvt = cvt(malformed_utf8)
assert type(malformed_cvt) is str
assert malformed_cvt == "b'\\x80'"
def test_implicit_casting(): def test_implicit_casting():
@ -428,14 +395,14 @@ def test_print(capture):
def test_hash(): def test_hash():
class Hashable(object): class Hashable:
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
def __hash__(self): def __hash__(self):
return self.value return self.value
class Unhashable(object): class Unhashable:
__hash__ = None __hash__ = None
assert m.hash_function(Hashable(42)) == 42 assert m.hash_function(Hashable(42)) == 42
@ -493,12 +460,7 @@ def test_memoryview(method, args, fmt, expected_view):
view = method(*args) view = method(*args)
assert isinstance(view, memoryview) assert isinstance(view, memoryview)
assert view.format == fmt assert view.format == fmt
if isinstance(expected_view, bytes) or not env.PY2: assert list(view) == list(expected_view)
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)
@pytest.mark.xfail("env.PYPY", reason="getrefcount is not available") @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() view = m.test_memoryview_from_buffer_empty_shape()
assert isinstance(view, memoryview) assert isinstance(view, memoryview)
assert view.format == "B" assert view.format == "B"
if env.PY2: assert bytes(view) == b""
# Python 2 behavior is weird, but Python 3 (the future) is fine.
# PyPy3 has <memoryview, while CPython 2 has <memory
assert bytes(view).startswith(b"<memory")
else:
assert bytes(view) == b""
def test_test_memoryview_from_buffer_invalid_strides(): def test_test_memoryview_from_buffer_invalid_strides():
@ -536,14 +493,10 @@ def test_test_memoryview_from_buffer_invalid_strides():
def test_test_memoryview_from_buffer_nullptr(): def test_test_memoryview_from_buffer_nullptr():
if env.PY2: with pytest.raises(ValueError):
m.test_memoryview_from_buffer_nullptr() m.test_memoryview_from_buffer_nullptr()
else:
with pytest.raises(ValueError):
m.test_memoryview_from_buffer_nullptr()
@pytest.mark.skipif("env.PY2")
def test_memoryview_from_memory(): def test_memoryview_from_memory():
view = m.test_memoryview_from_memory() view = m.test_memoryview_from_memory()
assert isinstance(view, memoryview) assert isinstance(view, memoryview)
@ -563,9 +516,9 @@ def test_builtin_functions():
def test_isinstance_string_types(): def test_isinstance_string_types():
assert m.isinstance_pybind11_bytes(b"") assert m.isinstance_pybind11_bytes(b"")
assert not m.isinstance_pybind11_bytes(u"") assert not m.isinstance_pybind11_bytes("")
assert m.isinstance_pybind11_str(u"") assert m.isinstance_pybind11_str("")
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
assert m.isinstance_pybind11_str(b"") assert m.isinstance_pybind11_str(b"")
else: else:
@ -575,24 +528,21 @@ def test_isinstance_string_types():
def test_pass_bytes_or_unicode_to_string_types(): def test_pass_bytes_or_unicode_to_string_types():
assert m.pass_to_pybind11_bytes(b"Bytes") == 5 assert m.pass_to_pybind11_bytes(b"Bytes") == 5
with pytest.raises(TypeError): with pytest.raises(TypeError):
m.pass_to_pybind11_bytes(u"Str") m.pass_to_pybind11_bytes("Str")
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE") or env.PY2: if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
assert m.pass_to_pybind11_str(b"Bytes") == 5 assert m.pass_to_pybind11_str(b"Bytes") == 5
else: else:
with pytest.raises(TypeError): with pytest.raises(TypeError):
m.pass_to_pybind11_str(b"Bytes") m.pass_to_pybind11_str(b"Bytes")
assert m.pass_to_pybind11_str(u"Str") == 3 assert m.pass_to_pybind11_str("Str") == 3
assert m.pass_to_std_string(b"Bytes") == 5 assert m.pass_to_std_string(b"Bytes") == 5
assert m.pass_to_std_string(u"Str") == 3 assert m.pass_to_std_string("Str") == 3
malformed_utf8 = b"\x80" malformed_utf8 = b"\x80"
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"): if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
assert m.pass_to_pybind11_str(malformed_utf8) == 1 assert m.pass_to_pybind11_str(malformed_utf8) == 1
elif env.PY2:
with pytest.raises(UnicodeDecodeError):
m.pass_to_pybind11_str(malformed_utf8)
else: else:
with pytest.raises(TypeError): with pytest.raises(TypeError):
m.pass_to_pybind11_str(malformed_utf8) m.pass_to_pybind11_str(malformed_utf8)
@ -609,12 +559,14 @@ def test_weakref(create_weakref, create_weakref_with_callback):
from weakref import getweakrefcount from weakref import getweakrefcount
# Apparently, you cannot weakly reference an object() # Apparently, you cannot weakly reference an object()
class WeaklyReferenced(object): class WeaklyReferenced:
pass pass
callback_called = False
def callback(wr): def callback(wr):
# No `nonlocal` in Python 2 nonlocal callback_called
callback.called = True callback_called = True
obj = WeaklyReferenced() obj = WeaklyReferenced()
assert getweakrefcount(obj) == 0 assert getweakrefcount(obj) == 0
@ -623,13 +575,12 @@ def test_weakref(create_weakref, create_weakref_with_callback):
obj = WeaklyReferenced() obj = WeaklyReferenced()
assert getweakrefcount(obj) == 0 assert getweakrefcount(obj) == 0
callback.called = False
wr = create_weakref_with_callback(obj, callback) # noqa: F841 wr = create_weakref_with_callback(obj, callback) # noqa: F841
assert getweakrefcount(obj) == 1 assert getweakrefcount(obj) == 1
assert not callback.called assert not callback_called
del obj del obj
pytest.gc_collect() pytest.gc_collect()
assert callback.called assert callback_called
def test_cpp_iterators(): def test_cpp_iterators():

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import ConstructorStats from pybind11_tests import ConstructorStats

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
m = pytest.importorskip("pybind11_tests.smart_ptr") m = pytest.importorskip("pybind11_tests.smart_ptr")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
from pybind11_tests import ConstructorStats, UserType from pybind11_tests import ConstructorStats, UserType
@ -283,7 +282,7 @@ def test_stl_pass_by_pointer(msg):
1. (v: List[int] = None) -> List[int] 1. (v: List[int] = None) -> List[int]
Invoked with: Invoked with:
""" # noqa: E501 line too long """
) )
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
@ -295,7 +294,7 @@ def test_stl_pass_by_pointer(msg):
1. (v: List[int] = None) -> List[int] 1. (v: List[int] = None) -> List[int]
Invoked with: None Invoked with: None
""" # noqa: E501 line too long """
) )
assert m.stl_pass_by_pointer([1, 2, 3]) == [1, 2, 3] assert m.stl_pass_by_pointer([1, 2, 3]) == [1, 2, 3]

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env
from pybind11_tests import stl_binders as m from pybind11_tests import stl_binders as m
@ -74,18 +72,13 @@ def test_vector_buffer():
assert v[1] == 2 assert v[1] == 2
v[2] = 5 v[2] = 5
mv = memoryview(v) # We expose the buffer interface mv = memoryview(v) # We expose the buffer interface
if not env.PY2: assert mv[2] == 5
assert mv[2] == 5 mv[2] = 6
mv[2] = 6
else:
assert mv[2] == "\x05"
mv[2] = "\x06"
assert v[2] == 6 assert v[2] == 6
if not env.PY2: mv = memoryview(b)
mv = memoryview(b) v = m.VectorUChar(mv[::2])
v = m.VectorUChar(mv[::2]) assert v[1] == 3
assert v[1] == 3
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
m.create_undeclstruct() # Undeclared struct contents, no buffer interface m.create_undeclstruct() # Undeclared struct contents, no buffer interface

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from pybind11_tests import tagbased_polymorphic as m from pybind11_tests import tagbased_polymorphic as m

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
import threading import threading
from pybind11_tests import thread as m from pybind11_tests import thread as m
@ -7,7 +5,7 @@ from pybind11_tests import thread as m
class Thread(threading.Thread): class Thread(threading.Thread):
def __init__(self, fn): def __init__(self, fn):
super(Thread, self).__init__() super().__init__()
self.fn = fn self.fn = fn
self.e = None self.e = None
@ -19,7 +17,7 @@ class Thread(threading.Thread):
self.e = e self.e = e
def join(self): def join(self):
super(Thread, self).join() super().join()
if self.e: if self.e:
raise self.e raise self.e

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from pybind11_tests import union_ as m from pybind11_tests import union_ as m

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import pytest import pytest
import env # noqa: F401 import env # noqa: F401
@ -10,12 +9,12 @@ from pybind11_tests import ConstructorStats # noqa: E402
def test_override(capture, msg): def test_override(capture, msg):
class ExtendedExampleVirt(m.ExampleVirt): class ExtendedExampleVirt(m.ExampleVirt):
def __init__(self, state): def __init__(self, state):
super(ExtendedExampleVirt, self).__init__(state + 1) super().__init__(state + 1)
self.data = "Hello world" self.data = "Hello world"
def run(self, value): def run(self, value):
print("ExtendedExampleVirt::run(%i), calling parent.." % value) print("ExtendedExampleVirt::run(%i), calling parent.." % value)
return super(ExtendedExampleVirt, self).run(value + 1) return super().run(value + 1)
def run_bool(self): def run_bool(self):
print("ExtendedExampleVirt::run_bool()") print("ExtendedExampleVirt::run_bool()")
@ -29,7 +28,7 @@ def test_override(capture, msg):
class ExtendedExampleVirt2(ExtendedExampleVirt): class ExtendedExampleVirt2(ExtendedExampleVirt):
def __init__(self, state): def __init__(self, state):
super(ExtendedExampleVirt2, self).__init__(state + 1) super().__init__(state + 1)
def get_string2(self): def get_string2(self):
return "override2" return "override2"
@ -41,7 +40,7 @@ def test_override(capture, msg):
capture capture
== """ == """
Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2) Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
""" # noqa: E501 line too long """
) )
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
@ -59,7 +58,7 @@ def test_override(capture, msg):
== """ == """
ExtendedExampleVirt::run(20), calling parent.. ExtendedExampleVirt::run(20), calling parent..
Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2) Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
""" # noqa: E501 line too long """
) )
with capture: with capture:
assert m.runExampleVirtBool(ex12p) is False assert m.runExampleVirtBool(ex12p) is False
@ -76,7 +75,7 @@ def test_override(capture, msg):
== """ == """
ExtendedExampleVirt::run(50), calling parent.. ExtendedExampleVirt::run(50), calling parent..
Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2) Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
""" # noqa: E501 line too long """
) )
cstats = ConstructorStats.get(m.ExampleVirt) cstats = ConstructorStats.get(m.ExampleVirt)
@ -97,7 +96,7 @@ def test_alias_delay_initialization1(capture):
class B(m.A): class B(m.A):
def __init__(self): def __init__(self):
super(B, self).__init__() super().__init__()
def f(self): def f(self):
print("In python f()") print("In python f()")
@ -137,7 +136,7 @@ def test_alias_delay_initialization2(capture):
class B2(m.A2): class B2(m.A2):
def __init__(self): def __init__(self):
super(B2, self).__init__() super().__init__()
def f(self): def f(self):
print("In python B2.f()") print("In python B2.f()")
@ -245,7 +244,7 @@ def test_dispatch_issue(msg):
class PyClass2(m.DispatchIssue): class PyClass2(m.DispatchIssue):
def dispatch(self): def dispatch(self):
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
super(PyClass2, self).dispatch() super().dispatch()
assert ( assert (
msg(excinfo.value) msg(excinfo.value)
== 'Tried to call pure virtual function "Base::dispatch"' == 'Tried to call pure virtual function "Base::dispatch"'
@ -262,7 +261,7 @@ def test_recursive_dispatch_issue(msg):
class Data(m.Data): class Data(m.Data):
def __init__(self, value): def __init__(self, value):
super(Data, self).__init__() super().__init__()
self.value = value self.value = value
class Adder(m.Adder): class Adder(m.Adder):

View File

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

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import division, print_function
import os import os
import sys import sys

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re import re

View File

@ -8,7 +8,6 @@ Adds the following targets::
pybind11::lto - Link time optimizations (manual selection) pybind11::lto - Link time optimizations (manual selection)
pybind11::thin_lto - Link time optimizations (manual selection) pybind11::thin_lto - Link time optimizations (manual selection)
pybind11::python_link_helper - Adds link to Python libraries 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::windows_extras - MSVC bigobj and mp for building multithreaded
pybind11::opt_size - avoid optimizations that increase code size pybind11::opt_size - avoid optimizations that increase code size
@ -66,31 +65,6 @@ set_property(
APPEND APPEND
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) 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
"$<AND:$<CXX_COMPILER_ID:Clang>,$<NOT:$<VERSION_LESS:$<CXX_COMPILER_VERSION>,3.9>>>")
set(no_register "$<OR:${clang_4plus},$<CXX_COMPILER_ID:AppleClang>>")
if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11)
set(cxx_no_register "${no_register}")
else()
set(cxx_no_register "$<AND:$<COMPILE_LANGUAGE:CXX>,${no_register}>")
endif()
set(msvc "$<CXX_COMPILER_ID:MSVC>")
set_property(
TARGET pybind11::python2_no_register
PROPERTY INTERFACE_COMPILE_OPTIONS
"$<${cxx_no_register}:-Wno-register;-Wno-deprecated-register>" "$<${msvc}:/wd5033>")
# --------------------------- link helper --------------------------- # --------------------------- link helper ---------------------------
add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global})

View File

@ -51,8 +51,6 @@ complex applications, and they are available in all modes:
Python headers too. Python headers too.
``pybind11::python_link_helper`` ``pybind11::python_link_helper``
Just the "linking" part of ``pybind11:module``, for CMake < 3.15. 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`` ``pybind11::thin_lto``
An alternative to ``INTERPROCEDURAL_OPTIMIZATION``. An alternative to ``INTERPROCEDURAL_OPTIMIZATION``.
``pybind11::lto`` ``pybind11::lto``

View File

@ -22,9 +22,7 @@ else()
set(_pybind11_quiet "") set(_pybind11_quiet "")
endif() endif()
if(NOT Python_FOUND if(NOT Python_FOUND AND NOT Python3_FOUND)
AND NOT Python3_FOUND
AND NOT Python2_FOUND)
if(NOT DEFINED Python_FIND_IMPLEMENTATIONS) if(NOT DEFINED Python_FIND_IMPLEMENTATIONS)
set(Python_FIND_IMPLEMENTATIONS CPython PyPy) set(Python_FIND_IMPLEMENTATIONS CPython PyPy)
endif() endif()
@ -34,7 +32,7 @@ if(NOT Python_FOUND
set(Python_ROOT_DIR "$ENV{pythonLocation}") set(Python_ROOT_DIR "$ENV{pythonLocation}")
endif() 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 we are in submodule mode, export the Python targets to global targets.
# If this behavior is not desired, FindPython _before_ pybind11. # If this behavior is not desired, FindPython _before_ pybind11.
@ -51,19 +49,10 @@ if(Python_FOUND)
set(_Python set(_Python
Python Python
CACHE INTERNAL "" FORCE) CACHE INTERNAL "" FORCE)
elseif(Python3_FOUND AND NOT Python2_FOUND) elseif(Python3_FOUND)
set(_Python set(_Python
Python3 Python3
CACHE INTERNAL "" FORCE) 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() endif()
if(PYBIND11_MASTER_PROJECT) if(PYBIND11_MASTER_PROJECT)
@ -137,7 +126,7 @@ if(PYTHON_IS_DEBUG)
PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG) PROPERTY INTERFACE_COMPILE_DEFINITIONS Py_DEBUG)
endif() 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) if(DEFINED ${_Python}_INCLUDE_DIRS)
# Only add Python for build - must be added during the import for config # 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") CACHE INTERNAL "Directories where pybind11 and possibly Python headers are located")
endif() 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 # In CMake 3.18+, you can find these separately, so include an if
if(TARGET ${_Python}::Python) if(TARGET ${_Python}::Python)
set_property( set_property(
@ -205,8 +187,6 @@ function(pybind11_add_module target_name)
python_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) python_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS})
elseif("${_Python}" STREQUAL "Python3") elseif("${_Python}" STREQUAL "Python3")
python3_add_library(${target_name} ${lib_type} ${ARG_UNPARSED_ARGUMENTS}) 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() else()
message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}") message(FATAL_ERROR "Cannot detect FindPython version: ${_Python}")
endif() endif()
@ -223,10 +203,6 @@ function(pybind11_add_module target_name)
target_link_libraries(${target_name} PRIVATE pybind11::windows_extras) target_link_libraries(${target_name} PRIVATE pybind11::windows_extras)
endif() 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 # -fvisibility=hidden is required to allow multiple modules compiled against
# different pybind versions to work properly, and for some features (e.g. # different pybind versions to work properly, and for some features (e.g.
# py::module_local). We force it on everything inside the `pybind11` # py::module_local). We force it on everything inside the `pybind11`

View File

@ -43,7 +43,7 @@ endif()
# A user can set versions manually too # A user can set versions manually too
set(Python_ADDITIONAL_VERSIONS set(Python_ADDITIONAL_VERSIONS
"3.11;3.10;3.9;3.8;3.7;3.6;3.5;3.4" "3.11;3.10;3.9;3.8;3.7;3.6;3.5"
CACHE INTERNAL "") CACHE INTERNAL "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
@ -122,13 +122,6 @@ set_property(
INTERFACE_LINK_LIBRARIES pybind11::python_link_helper INTERFACE_LINK_LIBRARIES pybind11::python_link_helper
"$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>") "$<$<OR:$<PLATFORM_ID:Windows>,$<PLATFORM_ID:Cygwin>>:$<BUILD_INTERFACE:${PYTHON_LIBRARIES}>>")
if(PYTHON_VERSION VERSION_LESS 3)
set_property(
TARGET pybind11::pybind11
APPEND
PROPERTY INTERFACE_LINK_LIBRARIES pybind11::python2_no_register)
endif()
set_property( set_property(
TARGET pybind11::embed TARGET pybind11::embed
APPEND APPEND

Some files were not shown because too many files have changed in this diff Show More