From 81e9faa67e0f399d5f522984bd65992e3b26942f Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 14 Aug 2020 20:04:07 -0400 Subject: [PATCH 01/11] tests: refactor and cleanup --- .github/workflows/ci.yml | 24 ++++- setup.cfg | 3 + tests/CMakeLists.txt | 4 +- tests/conftest.py | 136 +++++++++++++++------------ tests/pybind11_tests.cpp | 2 - tests/pytest.ini | 27 ++++++ tests/test_async.py | 5 +- tests/test_buffers.py | 25 +++-- tests/test_builtin_casters.py | 17 ++-- tests/test_call_policies.py | 2 +- tests/test_class.py | 6 +- tests/test_eigen.cpp | 2 - tests/test_eigen.py | 20 ++-- tests/test_eval.py | 2 +- tests/test_factory_constructors.py | 2 +- tests/test_kwargs_and_defaults.py | 4 +- tests/test_local_bindings.py | 2 +- tests/test_methods_and_attributes.py | 4 +- tests/test_multiple_inheritance.py | 7 +- tests/test_numpy_array.py | 11 +-- tests/test_numpy_dtypes.py | 7 +- tests/test_numpy_vectorize.py | 5 +- tests/test_pickling.py | 2 +- tests/test_pytypes.py | 26 ++--- tests/test_stl_binders.py | 15 ++- tests/test_virtual_functions.py | 2 +- 26 files changed, 201 insertions(+), 161 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3acbe9d3c..92eb11daef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,11 +17,11 @@ jobs: runs-on: [ubuntu-latest, windows-latest, macos-latest] arch: [x64] max-cxx-std: [17] + dev: [false] python: - 2.7 - 3.5 - 3.8 - - 3.9-dev - pypy2 - pypy3 @@ -30,22 +30,38 @@ jobs: python: 3.6 arch: x64 max-cxx-std: 17 + dev: false - runs-on: macos-latest python: 3.7 arch: x64 max-cxx-std: 17 + dev: false - runs-on: windows-2016 python: 3.7 arch: x86 max-cxx-std: 14 + dev: false - runs-on: windows-latest python: 3.6 arch: x64 max-cxx-std: 17 + dev: false - runs-on: windows-latest python: 3.7 arch: x64 max-cxx-std: 17 + dev: false + + - runs-on: ubuntu-latest + python: 3.9-dev + arch: x64 + max-cxx-std: 17 + dev: true + - runs-on: macos-latest + python: 3.9-dev + arch: x64 + max-cxx-std: 17 + dev: true exclude: # Currently 32bit only, and we build 64bit @@ -53,23 +69,29 @@ jobs: python: pypy2 arch: x64 max-cxx-std: 17 + dev: false - runs-on: windows-latest python: pypy3 arch: x64 max-cxx-std: 17 + dev: false # Currently broken on embed_test - runs-on: windows-latest python: 3.8 arch: x64 max-cxx-std: 17 + dev: false - runs-on: windows-latest python: 3.9-dev arch: x64 max-cxx-std: 17 + dev: false + name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • ${{ matrix.arch }}" runs-on: ${{ matrix.runs-on }} + continue-on-error: ${{ matrix.dev }} steps: - uses: actions/checkout@v2 diff --git a/setup.cfg b/setup.cfg index 002f38d10e..fd6df03601 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,3 +10,6 @@ ignore = E201, E241, W504, # camelcase 'cPickle' imported as lowercase 'pickle' N813 +per-file-ignores = + tests/*: N803 + tests/conftest.py: N802 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 17f2a5e9bd..6ea3fd6193 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -266,8 +266,8 @@ if(NOT PYBIND11_PYTEST_FOUND) if(pytest_not_found) message(FATAL_ERROR "Running the tests requires pytest. Please install it manually" " (try: ${PYTHON_EXECUTABLE} -m pip install pytest)") - elseif(pytest_version VERSION_LESS 3.0) - message(FATAL_ERROR "Running the tests requires pytest >= 3.0. Found: ${pytest_version}" + elseif(pytest_version VERSION_LESS 3.6) + message(FATAL_ERROR "Running the tests requires pytest >= 3.6. Found: ${pytest_version}" "Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)") endif() set(PYBIND11_PYTEST_FOUND diff --git a/tests/conftest.py b/tests/conftest.py index 45a264a3ad..99783ff099 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,24 +5,23 @@ Adds docstring and exceptions message sanitizers: ignore Python 2 vs 3 differences. """ -import pytest -import textwrap +import contextlib import difflib +import gc +import platform import re import sys -import contextlib -import platform -import gc +import textwrap + +import pytest + +# Early diagnostic for failed imports +import pybind11_tests # noqa: F401 _unicode_marker = re.compile(r'u(\'[^\']*\')') _long_marker = re.compile(r'([0-9])L') _hexadecimal = re.compile(r'0x[0-9a-fA-F]+') -# test_async.py requires support for async and await -collect_ignore = [] -if sys.version_info[:2] < (3, 5): - collect_ignore.append("test_async.py") - def _strip_and_dedent(s): """For triple-quote strings""" @@ -192,63 +191,76 @@ def gc_collect(): def pytest_configure(): - """Add import suppression and test requirements to `pytest` namespace""" - try: - import numpy as np - except ImportError: - np = None - try: - import scipy - except ImportError: - scipy = None - try: - from pybind11_tests.eigen import have_eigen - except ImportError: - have_eigen = False - - # Provide simple `six`-like aliases. - pytest.PY2 = (sys.version_info.major == 2) - pytest.CPYTHON = (platform.python_implementation() == "CPython") - pytest.PYPY = (platform.python_implementation() == "PyPy") - - skipif = pytest.mark.skipif pytest.suppress = suppress - pytest.requires_numpy = skipif(not np, reason="numpy is not installed") - pytest.requires_scipy = skipif(not np, reason="scipy is not installed") - pytest.requires_eigen_and_numpy = skipif(not have_eigen or not np, - reason="eigen and/or numpy are not installed") - pytest.requires_eigen_and_scipy = skipif( - not have_eigen or not scipy, reason="eigen and/or scipy are not installed") - pytest.unsupported_on_pypy = skipif(pytest.PYPY, reason="unsupported on PyPy") - pytest.bug_in_pypy = pytest.mark.xfail(pytest.PYPY, reason="bug in PyPy") - pytest.unsupported_on_pypy3 = skipif(pytest.PYPY and not pytest.PY2, - reason="unsupported on PyPy3") - pytest.unsupported_on_pypy_lt_6 = skipif(pytest.PYPY and sys.pypy_version_info[0] < 6, - reason="unsupported on PyPy<6") - pytest.unsupported_on_py2 = skipif(pytest.PY2, - reason="unsupported on Python 2.x") pytest.gc_collect = gc_collect -def _test_import_pybind11(): - """Early diagnostic for test module initialization errors +# Platform tools - When there is an error during initialization, the first import will report the - real error while all subsequent imports will report nonsense. This import test - is done early (in the pytest configuration file, before any tests) in order to - avoid the noise of having all tests fail with identical error messages. - Any possible exception is caught here and reported manually *without* the stack - trace. This further reduces noise since the trace would only show pytest internals - which are not useful for debugging pybind11 module issues. - """ - # noinspection PyBroadException - try: - import pybind11_tests # noqa: F401 imported but unused - except Exception as e: - print("Failed to import pybind11_tests from pytest:") - print(" {}: {}".format(type(e).__name__, e)) - sys.exit(1) +PLAT = sys.platform +IMPL = platform.python_implementation() +PYVM = sys.version_info.major +PYN = "py{}".format(PYVM) -_test_import_pybind11() +@pytest.fixture +def PY2(): + return PYVM == 2 + + +# Markers + + +def pytest_collection_modifyitems(items): + for item in items: + names = set(mark.name for mark in item.iter_markers()) + if "unix" in names: + if "linux" not in names: + item.add_marker(pytest.mark.linux) + if "darwin" not in names: + item.add_marker(pytest.mark.darwin) + if "pypy" in names: + if "pypy2" not in names: + item.add_marker(pytest.mark.pypy2) + if "pypy3" not in names: + item.add_marker(pytest.mark.pypy3) + + if "xfail_{}".format(IMPL.lower()) in names: + item.add_marker( + pytest.mark.xfail(reason="expected to fail on {}".format(IMPL)) + ) + if "xfail_{}".format(PLAT.lower()) in names: + item.add_marker( + pytest.mark.xfail(reason="expected to fail on {}".format(PLAT)) + ) + if "xfail_{}".format(PYN.lower()) in names: + item.add_marker( + pytest.mark.xfail(reason="expected to fail on Python {}".format(PYVM)) + ) + if "xfail_pypy{}".format(PYVM) in names and PLAT == "PyPy": + item.add_marker( + pytest.mark.xfail(reason="expected to fail on PyPy {}".format(PYVM)) + ) + + +ALL_PLAT = {"darwin", "linux", "win32"} +ALL_IMPL = {"cpython", "pypy2", "pypy3"} +ALL_PY = {"py2", "py3"} + + +def pytest_runtest_setup(item): + names = {mark.name for mark in item.iter_markers()} + + supported_platforms = ALL_PLAT.intersection(names) + if supported_platforms and PLAT not in supported_platforms: + pytest.skip("cannot run on platform {}".format(PLAT)) + + supported_impls = ALL_IMPL.intersection(names) + impl = IMPL.lower() + (str(PYVM) if IMPL == "PyPy" else "") + if supported_impls and impl not in supported_impls: + pytest.skip("cannot run on implementation {}".format(impl)) + + supported_pythons = ALL_PY.intersection(names) + if supported_pythons and PYN.lower() not in supported_pythons: + pytest.skip("cannot run on Python {}".format(PYVM)) diff --git a/tests/pybind11_tests.cpp b/tests/pybind11_tests.cpp index bc7d2c3e7a..76e0298e83 100644 --- a/tests/pybind11_tests.cpp +++ b/tests/pybind11_tests.cpp @@ -88,6 +88,4 @@ PYBIND11_MODULE(pybind11_tests, m) { for (const auto &initializer : initializers()) initializer(m); - - if (!py::hasattr(m, "have_eigen")) m.attr("have_eigen") = false; } diff --git a/tests/pytest.ini b/tests/pytest.ini index f209964a47..10931afd3b 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -4,8 +4,14 @@ norecursedirs = test_cmake_build test_embed addopts = # show summary of skipped tests -rs + # Report xfails + -rx # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys + # Error if markers are not listed below as well + # Requires pytest 4.5, so not added here + # --strict-markers +xfail_strict=true filterwarnings = # make warnings into errors but ignore certain third-party extension issues error @@ -14,3 +20,24 @@ filterwarnings = # bogus numpy ABI warning (see numpy/#432) ignore:.*numpy.dtype size changed.*:RuntimeWarning ignore:.*numpy.ufunc size changed.*:RuntimeWarning +markers = + linux: runs on Linux + darwin: runs on MacOS + win32: runs on Windows + unix: shortcut for linux + darwin + cpython: runs on CPython + pypy: runs on PyPy (2 or 3) + pypy2: runs on PyPy2 + pypy3: runs on PyPy3 + py2: runs on Python 2 + py3: runs on Python 3 + xfail_linux: expected failure on Linux + xfail_darwin: expected failure on MacOS + xfail_win32: expected failure on Windows + xfail_unix: shortcut for xfail_linux + xfail_darwin + xfail_cpython: expected failure on CPython + xfail_pypy: expected failure on PyPy + xfail_py2: expected failure on Python 2 + xfail_py3: expected failure on Python 3 + xfail_pypy2: expected failure on PyPy 2 + xfail_pypy3: expected failure on PyPy 3 diff --git a/tests/test_async.py b/tests/test_async.py index e9292c9d9c..df4489c499 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -import asyncio import pytest -from pybind11_tests import async_module as m + +asyncio = pytest.importorskip("asyncio") +m = pytest.importorskip("pybind11_tests.async_module") @pytest.fixture diff --git a/tests/test_buffers.py b/tests/test_buffers.py index db1871e6ae..0b858f9ffd 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -7,10 +7,7 @@ from pybind11_tests import buffers as m from pybind11_tests import ConstructorStats -pytestmark = pytest.requires_numpy - -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") def test_from_python(): @@ -38,7 +35,7 @@ def test_from_python(): # PyPy: Memory leak in the "np.array(m, copy=False)" call # https://bitbucket.org/pypy/pypy/issues/2444 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -73,7 +70,7 @@ def test_to_python(): assert cstats.move_assignments == 0 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_inherited_protocol(): """SquareMatrix is derived from Matrix and inherits the buffer protocol""" @@ -82,7 +79,7 @@ def test_inherited_protocol(): assert np.asarray(matrix).shape == (5, 5) -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_pointer_to_member_fn(): for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: buf = cls() @@ -91,19 +88,19 @@ def test_pointer_to_member_fn(): assert value == 0x12345678 -@pytest.unsupported_on_pypy -def test_readonly_buffer(): +@pytest.mark.cpython +def test_readonly_buffer(PY2): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == b'd' if pytest.PY2 else 0x64 + assert view[0] == b'd' if PY2 else 0x64 assert view.readonly -@pytest.unsupported_on_pypy -def test_selective_readonly_buffer(): +@pytest.mark.cpython +def test_selective_readonly_buffer(PY2): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = b'd' if pytest.PY2 else 0x64 + memoryview(buf)[0] = b'd' if PY2 else 0x64 assert buf.value == 0x64 io.BytesIO(b'A').readinto(buf) @@ -111,6 +108,6 @@ def test_selective_readonly_buffer(): buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = b'\0' if pytest.PY2 else 0 + memoryview(buf)[0] = b'\0' if PY2 else 0 with pytest.raises(TypeError): io.BytesIO(b'1').readinto(buf) diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index c905766f83..09555de312 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -111,16 +111,13 @@ def toobig_message(r): assert str(excinfo.value) == toolong_message -def test_bytes_to_string(): +def test_bytes_to_string(PY2): """Tests the ability to pass bytes to C++ string-accepting functions. Note that this is one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" # Issue #816 def to_bytes(s): - if pytest.PY2: - b = s - else: - b = s.encode("utf8") + b = s if PY2 else s.encode("utf8") assert isinstance(b, bytes) return b @@ -191,13 +188,13 @@ def test_string_view(capture): """ -def test_integer_casting(): +def test_integer_casting(PY2): """Issue #929 - out-of-range integer values shouldn't be accepted""" assert m.i32_str(-1) == "-1" assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if pytest.PY2: + if PY2: assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-999999999999)) == "-999999999999" # noqa: F821 undefined name @@ -219,7 +216,7 @@ def test_integer_casting(): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if pytest.PY2: + if 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) @@ -360,9 +357,9 @@ class B(object): assert convert(A(False)) is False -@pytest.requires_numpy def test_numpy_bool(): - import numpy as np + np = pytest.importorskip("numpy") + convert, noconvert = m.bool_passthrough, m.bool_passthrough_noconvert def cant_convert(v): diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 0e3230c573..cc960d105e 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -71,7 +71,7 @@ def test_keep_alive_return_value(capture): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index bbf8481a4d..bce7c867c8 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -261,7 +261,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -308,7 +308,7 @@ def test_aligned(): # https://bitbucket.org/pypy/pypy/issues/2742 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -317,7 +317,7 @@ class PyFinalChild(m.IsFinal): # https://bitbucket.org/pypy/pypy/issues/2742 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp index aba088d72b..56aa1a4a6f 100644 --- a/tests/test_eigen.cpp +++ b/tests/test_eigen.cpp @@ -87,8 +87,6 @@ TEST_SUBMODULE(eigen, m) { using SparseMatrixR = Eigen::SparseMatrix; using SparseMatrixC = Eigen::SparseMatrix; - m.attr("have_eigen") = true; - // various tests m.def("double_col", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return 2.0f * x; }); m.def("double_row", [](const Eigen::RowVectorXf &x) -> Eigen::RowVectorXf { return 2.0f * x; }); diff --git a/tests/test_eigen.py b/tests/test_eigen.py index ae868da513..ac68471474 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -2,17 +2,15 @@ import pytest from pybind11_tests import ConstructorStats -pytestmark = pytest.requires_eigen_and_numpy +np = pytest.importorskip("numpy") +m = pytest.importorskip("pybind11_tests.eigen") -with pytest.suppress(ImportError): - from pybind11_tests import eigen as m - import numpy as np - ref = np.array([[ 0., 3, 0, 0, 0, 11], - [22, 0, 0, 0, 17, 11], - [ 7, 5, 0, 1, 0, 11], - [ 0, 0, 0, 0, 0, 11], - [ 0, 0, 14, 0, 8, 11]]) +ref = np.array([[ 0., 3, 0, 0, 0, 11], + [22, 0, 0, 0, 17, 11], + [ 7, 5, 0, 1, 0, 11], + [ 0, 0, 0, 0, 0, 11], + [ 0, 0, 14, 0, 8, 11]]) def assert_equal_ref(mat): @@ -646,8 +644,8 @@ def test_named_arguments(): assert str(excinfo.value) == 'Nonconformable matrices!' -@pytest.requires_eigen_and_scipy def test_sparse(): + pytest.importorskip("scipy") assert_sparse_equal_ref(m.sparse_r()) assert_sparse_equal_ref(m.sparse_c()) assert_sparse_equal_ref(m.sparse_copy_r(m.sparse_r())) @@ -656,8 +654,8 @@ def test_sparse(): assert_sparse_equal_ref(m.sparse_copy_c(m.sparse_r())) -@pytest.requires_eigen_and_scipy def test_sparse_signature(doc): + pytest.importorskip("scipy") assert doc(m.sparse_copy_r) == """ sparse_copy_r(arg0: scipy.sparse.csr_matrix[numpy.float32]) -> scipy.sparse.csr_matrix[numpy.float32] """ # noqa: E501 line too long diff --git a/tests/test_eval.py b/tests/test_eval.py index 66bec55f8b..273634ebfa 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -15,7 +15,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.unsupported_on_pypy3 +@pytest.mark.xfail_pypy3 def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 49e6f4f331..a3d15313f0 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -418,7 +418,7 @@ def test_reallocations(capture, msg): """) -@pytest.unsupported_on_py2 +@pytest.mark.py3 def test_invalid_self(): """Tests invocation of the pybind-registered base class with an invalid `self` argument. You can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index df354ad3fa..aa9b453850 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -146,8 +146,8 @@ def test_keyword_only_args(msg): """ -@pytest.mark.xfail(pytest.PYPY and pytest.PY2, - reason="PyPy2 doesn't seem to double count") +# PyPy2 doesn't seem to double count +@pytest.mark.xfail_pypy2 def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index 913cf0ee5b..56fe9acc91 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -153,7 +153,7 @@ def test_internal_locals_differ(): assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() -@pytest.bug_in_pypy +@pytest.mark.xfail_pypy def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `` while the other exports `std::vector` via `py:bind_vector` and `py::module_local`""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 25a01c7186..df0341bc73 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -258,7 +258,7 @@ def test_property_rvalue_policy(): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -300,7 +300,7 @@ class PythonDerivedDynamicClass(m.DynamicClass): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index bb602f84bb..16cece6aff 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -11,7 +11,7 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.bug_in_pypy +@pytest.mark.xfail_pypy def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -32,7 +32,6 @@ def __init__(self, i, j): def test_multiple_inheritance_mix2(): - class Base2: def __init__(self, i): self.i = i @@ -51,7 +50,7 @@ def __init__(self, i, j): assert mt.bar() == 4 -@pytest.bug_in_pypy +@pytest.mark.xfail_pypy def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): @@ -256,7 +255,7 @@ def test_mi_static_properties(): assert d.static_value == 0 -@pytest.unsupported_on_pypy_lt_6 +# Requires PyPy 6+ def test_mi_dynamic_attributes(): """Mixing bases with and without dynamic attribute support""" diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 1b6599dfe4..7c99139d16 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -2,10 +2,7 @@ import pytest from pybind11_tests import numpy_array as m -pytestmark = pytest.requires_numpy - -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") def test_dtypes(): @@ -243,7 +240,7 @@ def test_numpy_view(capture): """ -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_cast_numpy_int64_to_uint64(): m.function_taking_uint64(123) m.function_taking_uint64(np.uint64(123)) @@ -424,7 +421,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -436,7 +433,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index d173435fe6..d9b36cdf08 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -3,10 +3,7 @@ import pytest from pybind11_tests import numpy_dtypes as m -pytestmark = pytest.requires_numpy - -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") @pytest.fixture(scope='module') @@ -294,7 +291,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.unsupported_on_pypy +@pytest.mark.cpython def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index bd3c01347c..54e44cd8d3 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -2,10 +2,7 @@ import pytest from pybind11_tests import numpy_vectorize as m -pytestmark = pytest.requires_numpy - -with pytest.suppress(ImportError): - import numpy as np +np = pytest.importorskip("numpy") def test_vectorize(capture): diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 58d67a6339..0087756ce6 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -22,7 +22,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.unsupported_on_pypy +@pytest.mark.cpython @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 289b4aab49..bbdad8f1ea 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -108,12 +108,12 @@ def __repr__(self): assert s1 == s2 -def test_bytes(doc): +def test_bytes(doc, PY2): assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_str().decode() == "bar" assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "str" if pytest.PY2 else "bytes" + "str" if PY2 else "bytes" ) @@ -220,11 +220,11 @@ def test_constructors(): assert noconv2[k] is expected[k] -def test_pybind11_str_raw_str(): +def test_pybind11_str_raw_str(PY2): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str assert cvt(u"Str") == u"Str" - assert cvt(b'Bytes') == u"Bytes" if pytest.PY2 else "b'Bytes'" + assert cvt(b'Bytes') == u"Bytes" if PY2 else "b'Bytes'" assert cvt(None) == u"None" assert cvt(False) == u"False" assert cvt(True) == u"True" @@ -237,8 +237,8 @@ def test_pybind11_str_raw_str(): assert cvt([28]) == u"[28]" assert cvt({}) == u"{}" assert cvt({3: 4}) == u"{3: 4}" - assert cvt(set()) == u"set([])" if pytest.PY2 else "set()" - assert cvt({3, 3}) == u"set([3])" if pytest.PY2 else "{3}" + assert cvt(set()) == u"set([])" if PY2 else "set()" + assert cvt({3, 3}) == u"set([3])" if PY2 else "{3}" valid_orig = u"DZ" valid_utf8 = valid_orig.encode("utf-8") @@ -320,11 +320,11 @@ def test_list_slicing(): (m.test_memoryview_from_buffer, (True,), 'H', [2, 7, 1, 8]), (m.test_memoryview_from_buffer_nativeformat, (), '@i', [4, 7, 5]), ]) -def test_memoryview(method, args, fmt, expected_view): +def test_memoryview(method, args, fmt, expected_view, PY2): view = method(*args) assert isinstance(view, memoryview) assert view.format == fmt - if isinstance(expected_view, bytes) or not pytest.PY2: + if isinstance(expected_view, bytes) or not PY2: view_as_list = list(view) else: # Using max to pick non-zero byte (big-endian vs little-endian). @@ -348,11 +348,11 @@ def test_memoryview_refcount(method): assert list(view) == list(buf) -def test_memoryview_from_buffer_empty_shape(): +def test_memoryview_from_buffer_empty_shape(PY2): view = m.test_memoryview_from_buffer_empty_shape() assert isinstance(view, memoryview) assert view.format == 'B' - if pytest.PY2: + if PY2: # Python 2 behavior is weird, but Python 3 (the future) is fine. # PyPy3 has 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.unsupported_on_pypy +@pytest.mark.cpython @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 6f45ef22eb379fb7185c9686e9d7c426a3b9b1d4 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 15 Aug 2020 00:06:26 -0400 Subject: [PATCH 02/11] refactor: more consistent --- .github/workflows/ci.yml | 3 +- tests/conftest.py | 73 +++++++--------------------- tests/pytest.ini | 30 ++++++------ tests/test_buffers.py | 10 ++-- tests/test_call_policies.py | 2 +- tests/test_class.py | 6 +-- tests/test_eval.py | 2 +- tests/test_factory_constructors.py | 2 +- tests/test_methods_and_attributes.py | 4 +- tests/test_multiple_inheritance.py | 6 ++- tests/test_numpy_array.py | 6 +-- tests/test_numpy_dtypes.py | 2 +- tests/test_pickling.py | 2 +- tests/test_pytypes.py | 7 ++- tests/test_stl_binders.py | 4 +- tests/test_virtual_functions.py | 2 +- 16 files changed, 63 insertions(+), 98 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92eb11daef..616f2a025e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -311,7 +311,8 @@ jobs: - name: Install requirements run: | apt-get update - apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip python3-pytest + apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip + pip3 install "pytest==3.6" - name: Configure for install run: > diff --git a/tests/conftest.py b/tests/conftest.py index 99783ff099..f41e7dcdf2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -195,13 +195,15 @@ def pytest_configure(): pytest.gc_collect = gc_collect -# Platform tools +# Platform Markers +PLAT = sys.platform.lower() +IMPL = platform.python_implementation().lower() +PYMAJ = str(sys.version_info.major) +PYNAME = "PY" + PYMAJ -PLAT = sys.platform -IMPL = platform.python_implementation() -PYVM = sys.version_info.major -PYN = "py{}".format(PYVM) +CURRENT = {PLAT, IMPL, PYNAME, PLAT + PYMAJ} +START = {"xfail", "skip"} @pytest.fixture @@ -214,53 +216,14 @@ def PY2(): def pytest_collection_modifyitems(items): for item in items: - names = set(mark.name for mark in item.iter_markers()) - if "unix" in names: - if "linux" not in names: - item.add_marker(pytest.mark.linux) - if "darwin" not in names: - item.add_marker(pytest.mark.darwin) - if "pypy" in names: - if "pypy2" not in names: - item.add_marker(pytest.mark.pypy2) - if "pypy3" not in names: - item.add_marker(pytest.mark.pypy3) - - if "xfail_{}".format(IMPL.lower()) in names: - item.add_marker( - pytest.mark.xfail(reason="expected to fail on {}".format(IMPL)) - ) - if "xfail_{}".format(PLAT.lower()) in names: - item.add_marker( - pytest.mark.xfail(reason="expected to fail on {}".format(PLAT)) - ) - if "xfail_{}".format(PYN.lower()) in names: - item.add_marker( - pytest.mark.xfail(reason="expected to fail on Python {}".format(PYVM)) - ) - if "xfail_pypy{}".format(PYVM) in names and PLAT == "PyPy": - item.add_marker( - pytest.mark.xfail(reason="expected to fail on PyPy {}".format(PYVM)) - ) - - -ALL_PLAT = {"darwin", "linux", "win32"} -ALL_IMPL = {"cpython", "pypy2", "pypy3"} -ALL_PY = {"py2", "py3"} - - -def pytest_runtest_setup(item): - names = {mark.name for mark in item.iter_markers()} - - supported_platforms = ALL_PLAT.intersection(names) - if supported_platforms and PLAT not in supported_platforms: - pytest.skip("cannot run on platform {}".format(PLAT)) - - supported_impls = ALL_IMPL.intersection(names) - impl = IMPL.lower() + (str(PYVM) if IMPL == "PyPy" else "") - if supported_impls and impl not in supported_impls: - pytest.skip("cannot run on implementation {}".format(impl)) - - supported_pythons = ALL_PY.intersection(names) - if supported_pythons and PYN.lower() not in supported_pythons: - pytest.skip("cannot run on Python {}".format(PYVM)) + for mark in tuple(item.iter_markers()): + parts = mark.name.split("_") + if len(parts) == 2 and parts[0] in START and parts[1] in CURRENT: + marker = getattr(pytest.mark, parts[0]) + reason = "expected to fail on {}".format(parts[1]) + + item.add_marker( + marker(**mark.kwargs) + if "reason" in mark.kwargs + else marker(reason=reason, **mark.kwargs) + ) diff --git a/tests/pytest.ini b/tests/pytest.ini index 10931afd3b..8850bb6c1f 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -8,10 +8,12 @@ addopts = -rx # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys + # warnings for deprecated features + -Wd # Error if markers are not listed below as well # Requires pytest 4.5, so not added here # --strict-markers -xfail_strict=true +xfail_strict = true filterwarnings = # make warnings into errors but ignore certain third-party extension issues error @@ -21,23 +23,21 @@ filterwarnings = ignore:.*numpy.dtype size changed.*:RuntimeWarning ignore:.*numpy.ufunc size changed.*:RuntimeWarning markers = - linux: runs on Linux - darwin: runs on MacOS - win32: runs on Windows - unix: shortcut for linux + darwin - cpython: runs on CPython - pypy: runs on PyPy (2 or 3) - pypy2: runs on PyPy2 - pypy3: runs on PyPy3 - py2: runs on Python 2 - py3: runs on Python 3 xfail_linux: expected failure on Linux xfail_darwin: expected failure on MacOS xfail_win32: expected failure on Windows - xfail_unix: shortcut for xfail_linux + xfail_darwin xfail_cpython: expected failure on CPython - xfail_pypy: expected failure on PyPy - xfail_py2: expected failure on Python 2 - xfail_py3: expected failure on Python 3 + xfail_pypy: expected failure on PyPy (2+3) xfail_pypy2: expected failure on PyPy 2 xfail_pypy3: expected failure on PyPy 3 + xfail_py2: expected failure on Python 2 + xfail_py3: expected failure on Python 3 + skip_linux: does not run on Linux + skip_darwin: does not run on MacOS + skip_win32: does not run on Windows + skip_cpython: does not run on CPython + skip_pypy: does not run on PyPy (2+3) + skip_pypy2: does not run on PyPy 2 + skip_pypy3: does not run on PyPy 3 + skip_py2: does not run on Python 2 + skip_py3: does not run on Python 3 diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 0b858f9ffd..95a3c8ae53 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -35,7 +35,7 @@ def test_from_python(): # PyPy: Memory leak in the "np.array(m, copy=False)" call # https://bitbucket.org/pypy/pypy/issues/2444 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -70,7 +70,7 @@ def test_to_python(): assert cstats.move_assignments == 0 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_inherited_protocol(): """SquareMatrix is derived from Matrix and inherits the buffer protocol""" @@ -79,7 +79,7 @@ def test_inherited_protocol(): assert np.asarray(matrix).shape == (5, 5) -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_pointer_to_member_fn(): for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: buf = cls() @@ -88,7 +88,7 @@ def test_pointer_to_member_fn(): assert value == 0x12345678 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_readonly_buffer(PY2): buf = m.BufferReadOnly(0x64) view = memoryview(buf) @@ -96,7 +96,7 @@ def test_readonly_buffer(PY2): assert view.readonly -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_selective_readonly_buffer(PY2): buf = m.BufferReadOnlySelect() diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index cc960d105e..1b64cb942d 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -71,7 +71,7 @@ def test_keep_alive_return_value(capture): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index bce7c867c8..ab77d4431e 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -261,7 +261,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -308,7 +308,7 @@ def test_aligned(): # https://bitbucket.org/pypy/pypy/issues/2742 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -317,7 +317,7 @@ class PyFinalChild(m.IsFinal): # https://bitbucket.org/pypy/pypy/issues/2742 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eval.py b/tests/test_eval.py index 273634ebfa..a0a94d09c4 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -15,7 +15,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail_pypy3 +@pytest.mark.xfail_pypy3(raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index a3d15313f0..5f0b7ede63 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -418,7 +418,7 @@ def test_reallocations(capture, msg): """) -@pytest.mark.py3 +@pytest.mark.skip_py2 def test_invalid_self(): """Tests invocation of the pybind-registered base class with an invalid `self` argument. You can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index df0341bc73..9eb19e9bcb 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -258,7 +258,7 @@ def test_property_rvalue_policy(): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -300,7 +300,7 @@ class PythonDerivedDynamicClass(m.DynamicClass): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 16cece6aff..9218ce781b 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -11,7 +11,8 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.mark.xfail_pypy +@pytest.mark.skip_pypy2 +@pytest.mark.xfail_pypy3 def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -50,7 +51,8 @@ def __init__(self, i, j): assert mt.bar() == 4 -@pytest.mark.xfail_pypy +@pytest.mark.skip_pypy2 +@pytest.mark.xfail_pypy3 def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 7c99139d16..681ddab3b7 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -240,7 +240,7 @@ def test_numpy_view(capture): """ -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_cast_numpy_int64_to_uint64(): m.function_taking_uint64(123) m.function_taking_uint64(np.uint64(123)) @@ -421,7 +421,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -433,7 +433,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index d9b36cdf08..04de8993b2 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -291,7 +291,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 0087756ce6..8d78ff5d16 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -22,7 +22,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.mark.cpython +@pytest.mark.skip_pypy @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index bbdad8f1ea..c8ea5a29a0 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -332,9 +332,8 @@ def test_memoryview(method, args, fmt, expected_view, PY2): assert view_as_list == list(expected_view) -@pytest.mark.skipif( - not hasattr(sys, 'getrefcount'), - reason='getrefcount is not available') +# getrefcount missing +@pytest.mark.xfail_pypy @pytest.mark.parametrize('method', [ m.test_memoryview_object, m.test_memoryview_buffer_info, @@ -373,7 +372,7 @@ def test_test_memoryview_from_buffer_nullptr(PY2): m.test_memoryview_from_buffer_nullptr() -@pytest.mark.py3 +@pytest.mark.skip_py2 def test_memoryview_from_memory(): view = m.test_memoryview_from_memory() assert isinstance(view, memoryview) diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index b60af38111..2a35c34c4b 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -66,7 +66,7 @@ def test_vector_int(): # related to the PyPy's buffer protocol. -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_vector_buffer(PY2): b = bytearray([1, 2, 3, 4]) v = m.VectorUChar(b) @@ -91,7 +91,7 @@ def test_vector_buffer(PY2): assert "NumPy type info missing for " in str(excinfo.value) -@pytest.mark.cpython +@pytest.mark.skip_pypy def test_vector_buffer_numpy(): np = pytest.importorskip("numpy") a = np.array([1, 2, 3, 4], dtype=np.int32) diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index 95ff0bbefc..e7566776e2 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -160,7 +160,7 @@ def f(self): # PyPy: Reference count > 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.cpython +@pytest.mark.skip_pypy @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 1b50d01ab16cd3cea6d916df3806e479197517c7 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 15 Aug 2020 00:20:00 -0400 Subject: [PATCH 03/11] tests: vendor six --- .pre-commit-config.yaml | 3 +- setup.cfg | 5 +- tests/conftest.py | 12 +- tests/six.py | 972 ++++++++++++++++++++++++++++++++++ tests/test_buffers.py | 12 +- tests/test_builtin_casters.py | 12 +- tests/test_pytypes.py | 29 +- tests/test_stl_binders.py | 9 +- 8 files changed, 1012 insertions(+), 42 deletions(-) create mode 100644 tests/six.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a046c6fcfe..777ce9d1a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,6 +13,7 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace - id: fix-encoding-pragma + exclude: ^tests/six.py$ - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.1.9 @@ -24,7 +25,7 @@ repos: hooks: - id: flake8 additional_dependencies: [flake8-bugbear, pep8-naming] - exclude: ^(docs/.*|tools/.*)$ + exclude: ^(docs/.*|tools/.*|tests/six.py)$ - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.11 diff --git a/setup.cfg b/setup.cfg index fd6df03601..d79163dc06 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,12 +4,9 @@ universal=1 [flake8] max-line-length = 99 show_source = True -exclude = .git, __pycache__, build, dist, docs, tools, venv +exclude = .git, __pycache__, build, dist, docs, tools, venv, tests/six.py ignore = # required for pretty matrix formatting: multiple spaces after `,` and `[` E201, E241, W504, # camelcase 'cPickle' imported as lowercase 'pickle' N813 -per-file-ignores = - tests/*: N803 - tests/conftest.py: N802 diff --git a/tests/conftest.py b/tests/conftest.py index f41e7dcdf2..0d72121f7a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -200,20 +200,12 @@ def pytest_configure(): PLAT = sys.platform.lower() IMPL = platform.python_implementation().lower() PYMAJ = str(sys.version_info.major) -PYNAME = "PY" + PYMAJ +PYNAME = "py" + PYMAJ -CURRENT = {PLAT, IMPL, PYNAME, PLAT + PYMAJ} +CURRENT = {PLAT, IMPL, PYNAME, IMPL + PYMAJ} START = {"xfail", "skip"} -@pytest.fixture -def PY2(): - return PYVM == 2 - - -# Markers - - def pytest_collection_modifyitems(items): for item in items: for mark in tuple(item.iter_markers()): diff --git a/tests/six.py b/tests/six.py new file mode 100644 index 0000000000..2651720b00 --- /dev/null +++ b/tests/six.py @@ -0,0 +1,972 @@ +# Copyright (c) 2010-2020 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.15.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 95a3c8ae53..7edb4ebfa7 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -4,6 +4,8 @@ import pytest +import six + from pybind11_tests import buffers as m from pybind11_tests import ConstructorStats @@ -89,18 +91,18 @@ def test_pointer_to_member_fn(): @pytest.mark.skip_pypy -def test_readonly_buffer(PY2): +def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == b'd' if PY2 else 0x64 + assert view[0] == b'd' if six.PY2 else 0x64 assert view.readonly @pytest.mark.skip_pypy -def test_selective_readonly_buffer(PY2): +def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = b'd' if PY2 else 0x64 + memoryview(buf)[0] = b'd' if six.PY2 else 0x64 assert buf.value == 0x64 io.BytesIO(b'A').readinto(buf) @@ -108,6 +110,6 @@ def test_selective_readonly_buffer(PY2): buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = b'\0' if PY2 else 0 + memoryview(buf)[0] = b'\0' if six.PY2 else 0 with pytest.raises(TypeError): io.BytesIO(b'1').readinto(buf) diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 09555de312..8dcbf69e30 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import pytest +import six + from pybind11_tests import builtin_casters as m from pybind11_tests import UserType, IncType @@ -111,13 +113,13 @@ def toobig_message(r): assert str(excinfo.value) == toolong_message -def test_bytes_to_string(PY2): +def test_bytes_to_string(): """Tests the ability to pass bytes to C++ string-accepting functions. Note that this is one-way: the only way to return bytes to Python is via the pybind11::bytes class.""" # Issue #816 def to_bytes(s): - b = s if PY2 else s.encode("utf8") + b = s if six.PY2 else s.encode("utf8") assert isinstance(b, bytes) return b @@ -188,13 +190,13 @@ def test_string_view(capture): """ -def test_integer_casting(PY2): +def test_integer_casting(): """Issue #929 - out-of-range integer values shouldn't be accepted""" assert m.i32_str(-1) == "-1" assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if PY2: + if six.PY2: assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-999999999999)) == "-999999999999" # noqa: F821 undefined name @@ -216,7 +218,7 @@ def test_integer_casting(PY2): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if PY2: + if six.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) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index c8ea5a29a0..3031942e9c 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -3,6 +3,8 @@ import pytest import sys +import six + from pybind11_tests import pytypes as m from pybind11_tests import debug_enabled @@ -108,12 +110,12 @@ def __repr__(self): assert s1 == s2 -def test_bytes(doc, PY2): +def test_bytes(doc): assert m.bytes_from_string().decode() == "foo" assert m.bytes_from_str().decode() == "bar" assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "str" if PY2 else "bytes" + "str" if six.PY2 else "bytes" ) @@ -220,11 +222,11 @@ def test_constructors(): assert noconv2[k] is expected[k] -def test_pybind11_str_raw_str(PY2): +def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str assert cvt(u"Str") == u"Str" - assert cvt(b'Bytes') == u"Bytes" if PY2 else "b'Bytes'" + assert cvt(b'Bytes') == u"Bytes" if six.PY2 else "b'Bytes'" assert cvt(None) == u"None" assert cvt(False) == u"False" assert cvt(True) == u"True" @@ -237,8 +239,8 @@ def test_pybind11_str_raw_str(PY2): assert cvt([28]) == u"[28]" assert cvt({}) == u"{}" assert cvt({3: 4}) == u"{3: 4}" - assert cvt(set()) == u"set([])" if PY2 else "set()" - assert cvt({3, 3}) == u"set([3])" if PY2 else "{3}" + assert cvt(set()) == u"set([])" if six.PY2 else "set()" + assert cvt({3, 3}) == u"set([3])" if six.PY2 else "{3}" valid_orig = u"DZ" valid_utf8 = valid_orig.encode("utf-8") @@ -320,11 +322,11 @@ def test_list_slicing(): (m.test_memoryview_from_buffer, (True,), 'H', [2, 7, 1, 8]), (m.test_memoryview_from_buffer_nativeformat, (), '@i', [4, 7, 5]), ]) -def test_memoryview(method, args, fmt, expected_view, PY2): +def test_memoryview(method, args, fmt, expected_view): view = method(*args) assert isinstance(view, memoryview) assert view.format == fmt - if isinstance(expected_view, bytes) or not PY2: + if isinstance(expected_view, bytes) or not six.PY2: view_as_list = list(view) else: # Using max to pick non-zero byte (big-endian vs little-endian). @@ -332,8 +334,7 @@ def test_memoryview(method, args, fmt, expected_view, PY2): assert view_as_list == list(expected_view) -# getrefcount missing -@pytest.mark.xfail_pypy +@pytest.mark.xfail_pypy(reason="getrefcount is not available") @pytest.mark.parametrize('method', [ m.test_memoryview_object, m.test_memoryview_buffer_info, @@ -347,11 +348,11 @@ def test_memoryview_refcount(method): assert list(view) == list(buf) -def test_memoryview_from_buffer_empty_shape(PY2): +def test_memoryview_from_buffer_empty_shape(): view = m.test_memoryview_from_buffer_empty_shape() assert isinstance(view, memoryview) assert view.format == 'B' - if PY2: + if six.PY2: # Python 2 behavior is weird, but Python 3 (the future) is fine. # PyPy3 has Date: Sat, 15 Aug 2020 23:00:55 -0400 Subject: [PATCH 04/11] tests: more xfails, nicer system --- tests/conftest.py | 43 +++++++++++++++++++--------- tests/pytest.ini | 26 +++++++---------- tests/test_buffers.py | 13 ++++----- tests/test_call_policies.py | 4 +-- tests/test_class.py | 10 +++---- tests/test_eval.py | 2 +- tests/test_factory_constructors.py | 2 +- tests/test_kwargs_and_defaults.py | 3 +- tests/test_methods_and_attributes.py | 6 ++-- tests/test_multiple_inheritance.py | 8 +++--- tests/test_numpy_array.py | 6 ++-- tests/test_numpy_dtypes.py | 2 +- tests/test_pickling.py | 2 +- tests/test_pytypes.py | 2 +- tests/test_stl_binders.py | 5 ++-- tests/test_virtual_functions.py | 2 +- 16 files changed, 71 insertions(+), 65 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0d72121f7a..394fd43342 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -197,25 +197,40 @@ def pytest_configure(): # Platform Markers -PLAT = sys.platform.lower() -IMPL = platform.python_implementation().lower() -PYMAJ = str(sys.version_info.major) -PYNAME = "py" + PYMAJ - -CURRENT = {PLAT, IMPL, PYNAME, IMPL + PYMAJ} -START = {"xfail", "skip"} +PLAT = sys.platform.lower() # linux, osx, or win32 +IMPL = platform.python_implementation().lower() # cpython or pypy def pytest_collection_modifyitems(items): + """ + This will find the markers listed in pytest.ini and add + skip or xfail as needed. The second part can be a platform + marker (linux, osx, or win32), or python, cpython, or pypy. + + You can add also add Python version numbers - either 2 or 3. + + Keyword arguments are passed on. + + # Will skip on Python 2 + pytest.mark.skip_python(2) + + # Will xfail on pypy as long as TypeError is raised + pytest.mark.xfail_pypy(reason="Not supported", raises=TypeError) + """ for item in items: for mark in tuple(item.iter_markers()): + # Check for recognised name parts = mark.name.split("_") - if len(parts) == 2 and parts[0] in START and parts[1] in CURRENT: + if len(parts) == 2 and parts[0] in {"xfail", "skip"}: marker = getattr(pytest.mark, parts[0]) - reason = "expected to fail on {}".format(parts[1]) - item.add_marker( - marker(**mark.kwargs) - if "reason" in mark.kwargs - else marker(reason=reason, **mark.kwargs) - ) + if parts[1] in {PLAT, IMPL, "python"}: + # args lets you remove only Py 2 or 3 + if mark.args: + (ver,) = mark.args # Only single argument supported + assert isinstance(ver, int), "should be a version number" + if ver == sys.version_info.major: + item.add_marker(marker(**mark.kwargs)) + else: + assert parts[1] != "python", "version required (otherwise use mark.skip)" + item.add_marker(marker(**mark.kwargs)) diff --git a/tests/pytest.ini b/tests/pytest.ini index 8850bb6c1f..998a053fa2 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -4,8 +4,8 @@ norecursedirs = test_cmake_build test_embed addopts = # show summary of skipped tests -rs - # Report xfails - -rx + # Report xfails (uncomment to use + # -rx # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys # warnings for deprecated features @@ -26,18 +26,12 @@ markers = xfail_linux: expected failure on Linux xfail_darwin: expected failure on MacOS xfail_win32: expected failure on Windows + xfail_python: expected failure on Python (2 or 3 required) xfail_cpython: expected failure on CPython - xfail_pypy: expected failure on PyPy (2+3) - xfail_pypy2: expected failure on PyPy 2 - xfail_pypy3: expected failure on PyPy 3 - xfail_py2: expected failure on Python 2 - xfail_py3: expected failure on Python 3 - skip_linux: does not run on Linux - skip_darwin: does not run on MacOS - skip_win32: does not run on Windows - skip_cpython: does not run on CPython - skip_pypy: does not run on PyPy (2+3) - skip_pypy2: does not run on PyPy 2 - skip_pypy3: does not run on PyPy 3 - skip_py2: does not run on Python 2 - skip_py3: does not run on Python 3 + xfail_pypy: not valid on PyPy (2+3) + skip_linux: not valid on Linux + skip_darwin: not valid on MacOS + skip_win32: not valid on Windows + skip_python: not valid on Python (2 or 3 required) + skip_cpython: not valid on CPython + skip_pypy: not valid on PyPy (2+3) diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 7edb4ebfa7..37cd7db61b 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -35,9 +35,8 @@ def test_from_python(): assert cstats.move_assignments == 0 -# PyPy: Memory leak in the "np.array(m, copy=False)" call -# https://bitbucket.org/pypy/pypy/issues/2444 -@pytest.mark.skip_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2444 +@pytest.mark.xfail_pypy(reason="memory leak in np.array(m, copy=False)") def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -72,7 +71,7 @@ def test_to_python(): assert cstats.move_assignments == 0 -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_inherited_protocol(): """SquareMatrix is derived from Matrix and inherits the buffer protocol""" @@ -81,7 +80,7 @@ def test_inherited_protocol(): assert np.asarray(matrix).shape == (5, 5) -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_pointer_to_member_fn(): for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: buf = cls() @@ -90,7 +89,7 @@ def test_pointer_to_member_fn(): assert value == 0x12345678 -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) @@ -98,7 +97,7 @@ def test_readonly_buffer(): assert view.readonly -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 1b64cb942d..6229a74581 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -70,8 +70,8 @@ def test_keep_alive_return_value(capture): """ -# https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.skip_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2447 +@pytest.mark.xfail_pypy(reason="_PyObject_GetDictPtr is unimplemented") def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index ab77d4431e..220b2b3aa1 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -261,7 +261,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -307,8 +307,8 @@ def test_aligned(): assert p % 1024 == 0 -# https://bitbucket.org/pypy/pypy/issues/2742 -@pytest.mark.skip_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2742 +@pytest.mark.xfail_pypy def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -316,8 +316,8 @@ class PyFinalChild(m.IsFinal): assert str(exc_info.value).endswith("is not an acceptable base type") -# https://bitbucket.org/pypy/pypy/issues/2742 -@pytest.mark.skip_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2742 +@pytest.mark.xfail_pypy def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eval.py b/tests/test_eval.py index a0a94d09c4..27bc50d4a3 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -15,7 +15,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail_pypy3(raises=RuntimeError) +@pytest.mark.xfail_pypy(3, raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 5f0b7ede63..98c11d8036 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -418,7 +418,7 @@ def test_reallocations(capture, msg): """) -@pytest.mark.skip_py2 +@pytest.mark.skip_python(2) def test_invalid_self(): """Tests invocation of the pybind-registered base class with an invalid `self` argument. You can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index aa9b453850..4b8c238e04 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -146,8 +146,7 @@ def test_keyword_only_args(msg): """ -# PyPy2 doesn't seem to double count -@pytest.mark.xfail_pypy2 +@pytest.mark.xfail_pypy(2, reason="PyPy2 doesn't seem to double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 9eb19e9bcb..17ffd5a280 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -258,7 +258,7 @@ def test_property_rvalue_policy(): # https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -299,8 +299,8 @@ class PythonDerivedDynamicClass(m.DynamicClass): assert cstats.alive() == 0 -# https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.skip_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2447 +@pytest.mark.xfail_pypy def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 9218ce781b..07bda4ee48 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -11,8 +11,8 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.mark.skip_pypy2 -@pytest.mark.xfail_pypy3 +@pytest.mark.skip_pypy(2) +@pytest.mark.xfail_pypy(3) def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -51,8 +51,8 @@ def __init__(self, i, j): assert mt.bar() == 4 -@pytest.mark.skip_pypy2 -@pytest.mark.xfail_pypy3 +@pytest.mark.skip_pypy(2) +@pytest.mark.xfail_pypy(3) def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 681ddab3b7..7a33b18b16 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -240,7 +240,7 @@ def test_numpy_view(capture): """ -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_cast_numpy_int64_to_uint64(): m.function_taking_uint64(123) m.function_taking_uint64(np.uint64(123)) @@ -421,7 +421,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -433,7 +433,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 04de8993b2..3245aa6502 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -291,7 +291,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 8d78ff5d16..7347b5c0fa 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -22,7 +22,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 3031942e9c..5a9b934fc8 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -373,7 +373,7 @@ def test_test_memoryview_from_buffer_nullptr(): m.test_memoryview_from_buffer_nullptr() -@pytest.mark.skip_py2 +@pytest.mark.skip_python(2) def test_memoryview_from_memory(): view = m.test_memoryview_from_memory() assert isinstance(view, memoryview) diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 7daec44034..e5bef271d7 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -68,8 +68,7 @@ def test_vector_int(): assert len(v_int2) == 0 -# related to the PyPy's buffer protocol. -@pytest.mark.skip_pypy +# Older PyPy's failed here, related to the PyPy's buffer protocol. def test_vector_buffer(): b = bytearray([1, 2, 3, 4]) v = m.VectorUChar(b) @@ -94,7 +93,7 @@ def test_vector_buffer(): assert "NumPy type info missing for " in str(excinfo.value) -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy def test_vector_buffer_numpy(): np = pytest.importorskip("numpy") a = np.array([1, 2, 3, 4], dtype=np.int32) diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index e7566776e2..1301989d4a 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -160,7 +160,7 @@ def f(self): # PyPy: Reference count > 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.skip_pypy +@pytest.mark.xfail_pypy @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 87b74ff8b3bb8f24508e23cd9a821b34ff8e80c1 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 15 Aug 2020 23:16:34 -0400 Subject: [PATCH 05/11] tests: simplify to info --- tests/conftest.py | 43 -- tests/info.py | 12 + tests/pytest.ini | 20 +- tests/six.py | 972 --------------------------- tests/test_buffers.py | 18 +- tests/test_builtin_casters.py | 8 +- tests/test_call_policies.py | 5 +- tests/test_class.py | 8 +- tests/test_eval.py | 6 +- tests/test_factory_constructors.py | 4 +- tests/test_kwargs_and_defaults.py | 5 +- tests/test_local_bindings.py | 4 +- tests/test_methods_and_attributes.py | 9 +- tests/test_multiple_inheritance.py | 11 +- tests/test_numpy_array.py | 9 +- tests/test_numpy_dtypes.py | 6 +- tests/test_pickling.py | 5 +- tests/test_pytypes.py | 20 +- tests/test_stl_binders.py | 8 +- tests/test_virtual_functions.py | 4 +- 20 files changed, 96 insertions(+), 1081 deletions(-) create mode 100644 tests/info.py delete mode 100644 tests/six.py diff --git a/tests/conftest.py b/tests/conftest.py index 394fd43342..8b6e47dc2e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,9 +8,7 @@ import contextlib import difflib import gc -import platform import re -import sys import textwrap import pytest @@ -193,44 +191,3 @@ def gc_collect(): def pytest_configure(): pytest.suppress = suppress pytest.gc_collect = gc_collect - - -# Platform Markers - -PLAT = sys.platform.lower() # linux, osx, or win32 -IMPL = platform.python_implementation().lower() # cpython or pypy - - -def pytest_collection_modifyitems(items): - """ - This will find the markers listed in pytest.ini and add - skip or xfail as needed. The second part can be a platform - marker (linux, osx, or win32), or python, cpython, or pypy. - - You can add also add Python version numbers - either 2 or 3. - - Keyword arguments are passed on. - - # Will skip on Python 2 - pytest.mark.skip_python(2) - - # Will xfail on pypy as long as TypeError is raised - pytest.mark.xfail_pypy(reason="Not supported", raises=TypeError) - """ - for item in items: - for mark in tuple(item.iter_markers()): - # Check for recognised name - parts = mark.name.split("_") - if len(parts) == 2 and parts[0] in {"xfail", "skip"}: - marker = getattr(pytest.mark, parts[0]) - - if parts[1] in {PLAT, IMPL, "python"}: - # args lets you remove only Py 2 or 3 - if mark.args: - (ver,) = mark.args # Only single argument supported - assert isinstance(ver, int), "should be a version number" - if ver == sys.version_info.major: - item.add_marker(marker(**mark.kwargs)) - else: - assert parts[1] != "python", "version required (otherwise use mark.skip)" - item.add_marker(marker(**mark.kwargs)) diff --git a/tests/info.py b/tests/info.py new file mode 100644 index 0000000000..f246b082bc --- /dev/null +++ b/tests/info.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +import platform +import sys + +LINUX = sys.platform.startswith("linux") +MACOS = sys.platform.startswith("darwin") +WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") + +CPYTHON = platform.python_implementation() == "CPython" +PYPY = platform.python_implementation() == "PyPy" + +PY2 = sys.version_info.major == 2 diff --git a/tests/pytest.ini b/tests/pytest.ini index 998a053fa2..6d93d69018 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,18 +1,15 @@ [pytest] -minversion = 3.0 +minversion = 3.6 norecursedirs = test_cmake_build test_embed addopts = # show summary of skipped tests -rs - # Report xfails (uncomment to use + # Report xfails (uncomment to use) # -rx # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys # warnings for deprecated features -Wd - # Error if markers are not listed below as well - # Requires pytest 4.5, so not added here - # --strict-markers xfail_strict = true filterwarnings = # make warnings into errors but ignore certain third-party extension issues @@ -22,16 +19,3 @@ filterwarnings = # bogus numpy ABI warning (see numpy/#432) ignore:.*numpy.dtype size changed.*:RuntimeWarning ignore:.*numpy.ufunc size changed.*:RuntimeWarning -markers = - xfail_linux: expected failure on Linux - xfail_darwin: expected failure on MacOS - xfail_win32: expected failure on Windows - xfail_python: expected failure on Python (2 or 3 required) - xfail_cpython: expected failure on CPython - xfail_pypy: not valid on PyPy (2+3) - skip_linux: not valid on Linux - skip_darwin: not valid on MacOS - skip_win32: not valid on Windows - skip_python: not valid on Python (2 or 3 required) - skip_cpython: not valid on CPython - skip_pypy: not valid on PyPy (2+3) diff --git a/tests/six.py b/tests/six.py deleted file mode 100644 index 2651720b00..0000000000 --- a/tests/six.py +++ /dev/null @@ -1,972 +0,0 @@ -# Copyright (c) 2010-2020 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.15.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - del io - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" - _assertNotRegex = "assertNotRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -def assertNotRegex(self, *args, **kwargs): - return getattr(self, _assertNotRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] > (3,): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - # This does exactly the same what the :func:`py3:functools.update_wrapper` - # function does on Python versions after 3.2. It sets the ``__wrapped__`` - # attribute on ``wrapper`` object and it doesn't raise an error if any of - # the attributes mentioned in ``assigned`` and ``updated`` are missing on - # ``wrapped`` object. - def _update_wrapper(wrapper, wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - for attr in assigned: - try: - value = getattr(wrapped, attr) - except AttributeError: - continue - else: - setattr(wrapper, attr, value) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - wrapper.__wrapped__ = wrapped - return wrapper - _update_wrapper.__doc__ = functools.update_wrapper.__doc__ - - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - return functools.partial(_update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - wraps.__doc__ = functools.wraps.__doc__ - -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - if sys.version_info[:2] >= (3, 7): - # This version introduced PEP 560 that requires a bit - # of extra care (we mimic what is done by __build_class__). - resolved_bases = types.resolve_bases(bases) - if resolved_bases is not bases: - d['__orig_bases__'] = bases - else: - resolved_bases = bases - return meta(name, resolved_bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, binary_type): - return s - if isinstance(s, text_type): - return s.encode(encoding, errors) - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - # Optimization: Fast return for the common case. - if type(s) is str: - return s - if PY2 and isinstance(s, text_type): - return s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - return s.decode(encoding, errors) - elif not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def python_2_unicode_compatible(klass): - """ - A class decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 37cd7db61b..20f56e755c 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -4,7 +4,7 @@ import pytest -import six +import info # noqa: F401 from pybind11_tests import buffers as m from pybind11_tests import ConstructorStats @@ -36,7 +36,7 @@ def test_from_python(): # https://foss.heptapod.net/pypy/pypy/-/issues/2444 -@pytest.mark.xfail_pypy(reason="memory leak in np.array(m, copy=False)") +@pytest.mark.xfail("info.PYPY", reason="memory leak in np.array(m, copy=False)") def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -71,7 +71,7 @@ def test_to_python(): assert cstats.move_assignments == 0 -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_inherited_protocol(): """SquareMatrix is derived from Matrix and inherits the buffer protocol""" @@ -80,7 +80,7 @@ def test_inherited_protocol(): assert np.asarray(matrix).shape == (5, 5) -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_pointer_to_member_fn(): for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: buf = cls() @@ -89,19 +89,19 @@ def test_pointer_to_member_fn(): assert value == 0x12345678 -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == b'd' if six.PY2 else 0x64 + assert view[0] == b'd' if info.PY2 else 0x64 assert view.readonly -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = b'd' if six.PY2 else 0x64 + memoryview(buf)[0] = b'd' if info.PY2 else 0x64 assert buf.value == 0x64 io.BytesIO(b'A').readinto(buf) @@ -109,6 +109,6 @@ def test_selective_readonly_buffer(): buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = b'\0' if six.PY2 else 0 + memoryview(buf)[0] = b'\0' if info.PY2 else 0 with pytest.raises(TypeError): io.BytesIO(b'1').readinto(buf) diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 8dcbf69e30..0ea9627d4a 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import six +import info # noqa: F401 from pybind11_tests import builtin_casters as m from pybind11_tests import UserType, IncType @@ -119,7 +119,7 @@ def test_bytes_to_string(): # Issue #816 def to_bytes(s): - b = s if six.PY2 else s.encode("utf8") + b = s if info.PY2 else s.encode("utf8") assert isinstance(b, bytes) return b @@ -196,7 +196,7 @@ def test_integer_casting(): assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if six.PY2: + if info.PY2: assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-999999999999)) == "-999999999999" # noqa: F821 undefined name @@ -218,7 +218,7 @@ def test_integer_casting(): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if six.PY2: + if info.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) diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 6229a74581..20cd56e37f 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import pytest + +import info # noqa: F401 + from pybind11_tests import call_policies as m from pybind11_tests import ConstructorStats @@ -71,7 +74,7 @@ def test_keep_alive_return_value(capture): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail_pypy(reason="_PyObject_GetDictPtr is unimplemented") +@pytest.mark.xfail("info.PYPY", reason="_PyObject_GetDictPtr is unimplemented") def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index 220b2b3aa1..cfaf94d256 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import pytest +import info # noqa: F401 + from pybind11_tests import class_ as m from pybind11_tests import UserType, ConstructorStats @@ -261,7 +263,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -308,7 +310,7 @@ def test_aligned(): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -317,7 +319,7 @@ class PyFinalChild(m.IsFinal): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eval.py b/tests/test_eval.py index 27bc50d4a3..b58ae727e2 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- import os + import pytest + +import info # noqa: F401 + from pybind11_tests import eval_ as m @@ -15,7 +19,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail_pypy(3, raises=RuntimeError) +@pytest.mark.xfail("info.PYPY and not info.PY2", raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 98c11d8036..3b8ba87e25 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -2,6 +2,8 @@ import pytest import re +import info # noqa: F401 + from pybind11_tests import factory_constructors as m from pybind11_tests.factory_constructors import tag from pybind11_tests import ConstructorStats @@ -418,7 +420,7 @@ def test_reallocations(capture, msg): """) -@pytest.mark.skip_python(2) +@pytest.mark.skipif("info.PY2") def test_invalid_self(): """Tests invocation of the pybind-registered base class with an invalid `self` argument. You can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index 4b8c238e04..e42b393371 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import pytest + +import info # noqa: F401 + from pybind11_tests import kwargs_and_defaults as m @@ -146,7 +149,7 @@ def test_keyword_only_args(msg): """ -@pytest.mark.xfail_pypy(2, reason="PyPy2 doesn't seem to double count") +@pytest.mark.xfail("info.PYPY and info.PY2", reason="PyPy2 doesn't seem to double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index 56fe9acc91..c0e2d9d6c6 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import pytest +import info # noqa: F401 + from pybind11_tests import local_bindings as m @@ -153,7 +155,7 @@ def test_internal_locals_differ(): assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `` while the other exports `std::vector` via `py:bind_vector` and `py::module_local`""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 17ffd5a280..af1d0dbc98 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import pytest + +import info # noqa: F401 + from pybind11_tests import methods_and_attributes as m from pybind11_tests import ConstructorStats @@ -257,8 +260,8 @@ def test_property_rvalue_policy(): assert os.value == 1 -# https://bitbucket.org/pypy/pypy/issues/2447 -@pytest.mark.xfail_pypy +# https://foss.heptapod.net/pypy/pypy/-/issues/2447 +@pytest.mark.xfail("info.PYPY") def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -300,7 +303,7 @@ class PythonDerivedDynamicClass(m.DynamicClass): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 07bda4ee48..f1f871680b 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import pytest + +import info # noqa: F401 + from pybind11_tests import ConstructorStats from pybind11_tests import multiple_inheritance as m @@ -11,8 +14,8 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.mark.skip_pypy(2) -@pytest.mark.xfail_pypy(3) +@pytest.mark.skipif("info.PYPY and info.PY2") +@pytest.mark.xfail("info.PYPY and not info.PY2") def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -51,8 +54,8 @@ def __init__(self, i, j): assert mt.bar() == 4 -@pytest.mark.skip_pypy(2) -@pytest.mark.xfail_pypy(3) +@pytest.mark.skipif("info.PYPY and info.PY2") +@pytest.mark.xfail("info.PYPY and not info.PY2") def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 7a33b18b16..aa641936f8 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import pytest + +import info # noqa: F401 + from pybind11_tests import numpy_array as m np = pytest.importorskip("numpy") @@ -240,7 +243,7 @@ def test_numpy_view(capture): """ -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_cast_numpy_int64_to_uint64(): m.function_taking_uint64(123) m.function_taking_uint64(np.uint64(123)) @@ -421,7 +424,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -433,7 +436,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 3245aa6502..61ab9a384b 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- import re + import pytest + +import info # noqa: F401 + from pybind11_tests import numpy_dtypes as m np = pytest.importorskip("numpy") @@ -291,7 +295,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 7347b5c0fa..c10460e500 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import pytest + +import info # noqa: F401 + from pybind11_tests import pickling as m try: @@ -22,7 +25,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 5a9b934fc8..fdbe219bca 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -3,7 +3,7 @@ import pytest import sys -import six +import info # noqa: F401 from pybind11_tests import pytypes as m from pybind11_tests import debug_enabled @@ -115,7 +115,7 @@ def test_bytes(doc): assert m.bytes_from_str().decode() == "bar" assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "str" if six.PY2 else "bytes" + "str" if info.PY2 else "bytes" ) @@ -226,7 +226,7 @@ def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str assert cvt(u"Str") == u"Str" - assert cvt(b'Bytes') == u"Bytes" if six.PY2 else "b'Bytes'" + assert cvt(b'Bytes') == u"Bytes" if info.PY2 else "b'Bytes'" assert cvt(None) == u"None" assert cvt(False) == u"False" assert cvt(True) == u"True" @@ -239,8 +239,8 @@ def test_pybind11_str_raw_str(): assert cvt([28]) == u"[28]" assert cvt({}) == u"{}" assert cvt({3: 4}) == u"{3: 4}" - assert cvt(set()) == u"set([])" if six.PY2 else "set()" - assert cvt({3, 3}) == u"set([3])" if six.PY2 else "{3}" + assert cvt(set()) == u"set([])" if info.PY2 else "set()" + assert cvt({3, 3}) == u"set([3])" if info.PY2 else "{3}" valid_orig = u"DZ" valid_utf8 = valid_orig.encode("utf-8") @@ -326,7 +326,7 @@ def test_memoryview(method, args, fmt, expected_view): view = method(*args) assert isinstance(view, memoryview) assert view.format == fmt - if isinstance(expected_view, bytes) or not six.PY2: + if isinstance(expected_view, bytes) or not info.PY2: view_as_list = list(view) else: # Using max to pick non-zero byte (big-endian vs little-endian). @@ -334,7 +334,7 @@ def test_memoryview(method, args, fmt, expected_view): assert view_as_list == list(expected_view) -@pytest.mark.xfail_pypy(reason="getrefcount is not available") +@pytest.mark.xfail("info.PYPY", reason="getrefcount is not available") @pytest.mark.parametrize('method', [ m.test_memoryview_object, m.test_memoryview_buffer_info, @@ -352,7 +352,7 @@ def test_memoryview_from_buffer_empty_shape(): view = m.test_memoryview_from_buffer_empty_shape() assert isinstance(view, memoryview) assert view.format == 'B' - if six.PY2: + if info.PY2: # Python 2 behavior is weird, but Python 3 (the future) is fine. # PyPy3 has 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.xfail_pypy +@pytest.mark.xfail("info.PYPY") @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 85bcf04bdd17a87f09023e6ef0b680f6ba825fce Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 16 Aug 2020 11:30:11 -0400 Subject: [PATCH 06/11] tests: suggestions from @YannickJadoul and @bstaletic --- .pre-commit-config.yaml | 3 +-- setup.cfg | 2 +- tests/pytest.ini | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 777ce9d1a8..a046c6fcfe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,6 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace - id: fix-encoding-pragma - exclude: ^tests/six.py$ - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.1.9 @@ -25,7 +24,7 @@ repos: hooks: - id: flake8 additional_dependencies: [flake8-bugbear, pep8-naming] - exclude: ^(docs/.*|tools/.*|tests/six.py)$ + exclude: ^(docs/.*|tools/.*)$ - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.11 diff --git a/setup.cfg b/setup.cfg index d79163dc06..002f38d10e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ universal=1 [flake8] max-line-length = 99 show_source = True -exclude = .git, __pycache__, build, dist, docs, tools, venv, tests/six.py +exclude = .git, __pycache__, build, dist, docs, tools, venv ignore = # required for pretty matrix formatting: multiple spaces after `,` and `[` E201, E241, W504, diff --git a/tests/pytest.ini b/tests/pytest.ini index 6d93d69018..c185b2b0a2 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -8,8 +8,8 @@ addopts = # -rx # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys - # warnings for deprecated features - -Wd + # Enable all warnings + -Wa xfail_strict = true filterwarnings = # make warnings into errors but ignore certain third-party extension issues From 19cd7598116660547b1f0b100693cea445091e96 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 16 Aug 2020 11:35:00 -0400 Subject: [PATCH 07/11] tests: restore some pypy tests that now pass --- tests/test_buffers.py | 5 ----- tests/test_numpy_array.py | 1 - tests/test_stl_binders.py | 1 - 3 files changed, 7 deletions(-) diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 20f56e755c..cc0e3f467a 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -36,7 +36,6 @@ def test_from_python(): # https://foss.heptapod.net/pypy/pypy/-/issues/2444 -@pytest.mark.xfail("info.PYPY", reason="memory leak in np.array(m, copy=False)") def test_to_python(): mat = m.Matrix(5, 4) assert memoryview(mat).shape == (5, 4) @@ -71,7 +70,6 @@ def test_to_python(): assert cstats.move_assignments == 0 -@pytest.mark.xfail("info.PYPY") def test_inherited_protocol(): """SquareMatrix is derived from Matrix and inherits the buffer protocol""" @@ -80,7 +78,6 @@ def test_inherited_protocol(): assert np.asarray(matrix).shape == (5, 5) -@pytest.mark.xfail("info.PYPY") def test_pointer_to_member_fn(): for cls in [m.Buffer, m.ConstBuffer, m.DerivedBuffer]: buf = cls() @@ -89,7 +86,6 @@ def test_pointer_to_member_fn(): assert value == 0x12345678 -@pytest.mark.xfail("info.PYPY") def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) @@ -97,7 +93,6 @@ def test_readonly_buffer(): assert view.readonly -@pytest.mark.xfail("info.PYPY") def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index aa641936f8..452422825e 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -243,7 +243,6 @@ def test_numpy_view(capture): """ -@pytest.mark.xfail("info.PYPY") def test_cast_numpy_int64_to_uint64(): m.function_taking_uint64(123) m.function_taking_uint64(np.uint64(123)) diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 4256887683..4f36254b77 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -93,7 +93,6 @@ def test_vector_buffer(): assert "NumPy type info missing for " in str(excinfo.value) -@pytest.mark.xfail("info.PYPY") def test_vector_buffer_numpy(): np = pytest.importorskip("numpy") a = np.array([1, 2, 3, 4], dtype=np.int32) From 4dda0f5bfadf55c68614c449309405bb8cc6c20a Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 16 Aug 2020 12:29:47 -0400 Subject: [PATCH 08/11] tests: rename info to env --- tests/{info.py => env.py} | 0 tests/test_buffers.py | 8 ++++---- tests/test_builtin_casters.py | 8 ++++---- tests/test_call_policies.py | 4 ++-- tests/test_class.py | 8 ++++---- tests/test_eval.py | 4 ++-- tests/test_factory_constructors.py | 4 ++-- tests/test_kwargs_and_defaults.py | 4 ++-- tests/test_local_bindings.py | 4 ++-- tests/test_methods_and_attributes.py | 6 +++--- tests/test_multiple_inheritance.py | 10 +++++----- tests/test_numpy_array.py | 6 +++--- tests/test_numpy_dtypes.py | 4 ++-- tests/test_pickling.py | 4 ++-- tests/test_pytypes.py | 20 ++++++++++---------- tests/test_stl_binders.py | 6 +++--- tests/test_virtual_functions.py | 4 ++-- 17 files changed, 52 insertions(+), 52 deletions(-) rename tests/{info.py => env.py} (100%) diff --git a/tests/info.py b/tests/env.py similarity index 100% rename from tests/info.py rename to tests/env.py diff --git a/tests/test_buffers.py b/tests/test_buffers.py index cc0e3f467a..d6adaf1f5e 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -4,7 +4,7 @@ import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import buffers as m from pybind11_tests import ConstructorStats @@ -89,14 +89,14 @@ def test_pointer_to_member_fn(): def test_readonly_buffer(): buf = m.BufferReadOnly(0x64) view = memoryview(buf) - assert view[0] == b'd' if info.PY2 else 0x64 + assert view[0] == b'd' if env.PY2 else 0x64 assert view.readonly def test_selective_readonly_buffer(): buf = m.BufferReadOnlySelect() - memoryview(buf)[0] = b'd' if info.PY2 else 0x64 + memoryview(buf)[0] = b'd' if env.PY2 else 0x64 assert buf.value == 0x64 io.BytesIO(b'A').readinto(buf) @@ -104,6 +104,6 @@ def test_selective_readonly_buffer(): buf.readonly = True with pytest.raises(TypeError): - memoryview(buf)[0] = b'\0' if info.PY2 else 0 + memoryview(buf)[0] = b'\0' if env.PY2 else 0 with pytest.raises(TypeError): io.BytesIO(b'1').readinto(buf) diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index 0ea9627d4a..08d38bc154 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import builtin_casters as m from pybind11_tests import UserType, IncType @@ -119,7 +119,7 @@ def test_bytes_to_string(): # Issue #816 def to_bytes(s): - b = s if info.PY2 else s.encode("utf8") + b = s if env.PY2 else s.encode("utf8") assert isinstance(b, bytes) return b @@ -196,7 +196,7 @@ def test_integer_casting(): assert m.i64_str(-1) == "-1" assert m.i32_str(2000000000) == "2000000000" assert m.u32_str(2000000000) == "2000000000" - if info.PY2: + if env.PY2: assert m.i32_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-1)) == "-1" # noqa: F821 undefined name 'long' assert m.i64_str(long(-999999999999)) == "-999999999999" # noqa: F821 undefined name @@ -218,7 +218,7 @@ def test_integer_casting(): m.i32_str(3000000000) assert "incompatible function arguments" in str(excinfo.value) - if info.PY2: + 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) diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 20cd56e37f..300209e97a 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import call_policies as m from pybind11_tests import ConstructorStats @@ -74,7 +74,7 @@ def test_keep_alive_return_value(capture): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("info.PYPY", reason="_PyObject_GetDictPtr is unimplemented") +@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index cfaf94d256..4214fe79d7 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import class_ as m from pybind11_tests import UserType, ConstructorStats @@ -263,7 +263,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -310,7 +310,7 @@ def test_aligned(): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -319,7 +319,7 @@ class PyFinalChild(m.IsFinal): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eval.py b/tests/test_eval.py index b58ae727e2..b6f9d1881d 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -3,7 +3,7 @@ import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import eval_ as m @@ -19,7 +19,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail("info.PYPY and not info.PY2", raises=RuntimeError) +@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 3b8ba87e25..8465c59e3f 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -2,7 +2,7 @@ import pytest import re -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import factory_constructors as m from pybind11_tests.factory_constructors import tag @@ -420,7 +420,7 @@ def test_reallocations(capture, msg): """) -@pytest.mark.skipif("info.PY2") +@pytest.mark.skipif("env.PY2") def test_invalid_self(): """Tests invocation of the pybind-registered base class with an invalid `self` argument. You can only actually do this on Python 3: Python 2 raises an exception itself if you try.""" diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index e42b393371..b51fea63f8 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import kwargs_and_defaults as m @@ -149,7 +149,7 @@ def test_keyword_only_args(msg): """ -@pytest.mark.xfail("info.PYPY and info.PY2", reason="PyPy2 doesn't seem to double count") +@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't seem to double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index c0e2d9d6c6..5460727e1d 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import local_bindings as m @@ -155,7 +155,7 @@ def test_internal_locals_differ(): assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `` while the other exports `std::vector` via `py:bind_vector` and `py::module_local`""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index af1d0dbc98..c296b6868d 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import methods_and_attributes as m from pybind11_tests import ConstructorStats @@ -261,7 +261,7 @@ def test_property_rvalue_policy(): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -303,7 +303,7 @@ class PythonDerivedDynamicClass(m.DynamicClass): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index f1f871680b..7a0259d214 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import ConstructorStats from pybind11_tests import multiple_inheritance as m @@ -14,8 +14,8 @@ def test_multiple_inheritance_cpp(): assert mt.bar() == 4 -@pytest.mark.skipif("info.PYPY and info.PY2") -@pytest.mark.xfail("info.PYPY and not info.PY2") +@pytest.mark.skipif("env.PYPY and env.PY2") +@pytest.mark.xfail("env.PYPY and not env.PY2") def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -54,8 +54,8 @@ def __init__(self, i, j): assert mt.bar() == 4 -@pytest.mark.skipif("info.PYPY and info.PY2") -@pytest.mark.xfail("info.PYPY and not info.PY2") +@pytest.mark.skipif("env.PYPY and env.PY2") +@pytest.mark.xfail("env.PYPY and not env.PY2") def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 452422825e..ad3ca58c1a 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import numpy_array as m @@ -423,7 +423,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -435,7 +435,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 61ab9a384b..417d6f1cff 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -3,7 +3,7 @@ import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import numpy_dtypes as m @@ -295,7 +295,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index c10460e500..9aee70505d 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import pytest -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import pickling as m @@ -25,7 +25,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index fdbe219bca..c21ad61146 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -3,7 +3,7 @@ import pytest import sys -import info # noqa: F401 +import env # noqa: F401 from pybind11_tests import pytypes as m from pybind11_tests import debug_enabled @@ -115,7 +115,7 @@ def test_bytes(doc): assert m.bytes_from_str().decode() == "bar" assert doc(m.bytes_from_str) == "bytes_from_str() -> {}".format( - "str" if info.PY2 else "bytes" + "str" if env.PY2 else "bytes" ) @@ -226,7 +226,7 @@ def test_pybind11_str_raw_str(): # specifically to exercise pybind11::str::raw_str cvt = m.convert_to_pybind11_str assert cvt(u"Str") == u"Str" - assert cvt(b'Bytes') == u"Bytes" if info.PY2 else "b'Bytes'" + assert cvt(b'Bytes') == u"Bytes" if env.PY2 else "b'Bytes'" assert cvt(None) == u"None" assert cvt(False) == u"False" assert cvt(True) == u"True" @@ -239,8 +239,8 @@ def test_pybind11_str_raw_str(): assert cvt([28]) == u"[28]" assert cvt({}) == u"{}" assert cvt({3: 4}) == u"{3: 4}" - assert cvt(set()) == u"set([])" if info.PY2 else "set()" - assert cvt({3, 3}) == u"set([3])" if info.PY2 else "{3}" + assert cvt(set()) == u"set([])" if env.PY2 else "set()" + assert cvt({3, 3}) == u"set([3])" if env.PY2 else "{3}" valid_orig = u"DZ" valid_utf8 = valid_orig.encode("utf-8") @@ -326,7 +326,7 @@ def test_memoryview(method, args, fmt, expected_view): view = method(*args) assert isinstance(view, memoryview) assert view.format == fmt - if isinstance(expected_view, bytes) or not info.PY2: + if isinstance(expected_view, bytes) or not env.PY2: view_as_list = list(view) else: # Using max to pick non-zero byte (big-endian vs little-endian). @@ -334,7 +334,7 @@ def test_memoryview(method, args, fmt, expected_view): assert view_as_list == list(expected_view) -@pytest.mark.xfail("info.PYPY", reason="getrefcount is not available") +@pytest.mark.xfail("env.PYPY", reason="getrefcount is not available") @pytest.mark.parametrize('method', [ m.test_memoryview_object, m.test_memoryview_buffer_info, @@ -352,7 +352,7 @@ def test_memoryview_from_buffer_empty_shape(): view = m.test_memoryview_from_buffer_empty_shape() assert isinstance(view, memoryview) assert view.format == 'B' - if info.PY2: + if env.PY2: # Python 2 behavior is weird, but Python 3 (the future) is fine. # PyPy3 has 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.xfail("info.PYPY") +@pytest.mark.xfail("env.PYPY") @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 30bc6210a36d6dd4bcab9bf99e18fbaa9e8aa055 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 16 Aug 2020 12:49:57 -0400 Subject: [PATCH 09/11] tests: strict False/True --- tests/pytest.ini | 5 +---- tests/test_call_policies.py | 3 ++- tests/test_class.py | 6 +++--- tests/test_eval.py | 2 +- tests/test_kwargs_and_defaults.py | 2 +- tests/test_local_bindings.py | 2 +- tests/test_methods_and_attributes.py | 4 ++-- tests/test_multiple_inheritance.py | 4 ++-- tests/test_numpy_array.py | 4 ++-- tests/test_numpy_dtypes.py | 2 +- tests/test_pickling.py | 2 +- tests/test_pytypes.py | 2 +- tests/test_virtual_functions.py | 2 +- 13 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tests/pytest.ini b/tests/pytest.ini index c185b2b0a2..7e022b9078 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -4,13 +4,10 @@ norecursedirs = test_cmake_build test_embed addopts = # show summary of skipped tests -rs - # Report xfails (uncomment to use) - # -rx # capture only Python print and C++ py::print, but not C output (low-level Python errors) --capture=sys - # Enable all warnings + # enable all warnings -Wa -xfail_strict = true filterwarnings = # make warnings into errors but ignore certain third-party extension issues error diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 300209e97a..b2b7cf1c96 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -7,6 +7,7 @@ from pybind11_tests import ConstructorStats +@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) def test_keep_alive_argument(capture): n_inst = ConstructorStats.detail_reg_inst() with capture: @@ -74,7 +75,7 @@ def test_keep_alive_return_value(capture): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") +@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented", strict=True) def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index 4214fe79d7..08dbb90cd2 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -263,7 +263,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -310,7 +310,7 @@ def test_aligned(): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -319,7 +319,7 @@ class PyFinalChild(m.IsFinal): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eval.py b/tests/test_eval.py index b6f9d1881d..d222e79e79 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -19,7 +19,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError) +@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError, strict=True) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index b51fea63f8..062e7c2ada 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -149,7 +149,7 @@ def test_keyword_only_args(msg): """ -@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't seem to double count") +@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't double count", strict=True) def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index 5460727e1d..d1d60eb3ae 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -155,7 +155,7 @@ def test_internal_locals_differ(): assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `` while the other exports `std::vector` via `py:bind_vector` and `py::module_local`""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index c296b6868d..8e1234020f 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -261,7 +261,7 @@ def test_property_rvalue_policy(): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -303,7 +303,7 @@ class PythonDerivedDynamicClass(m.DynamicClass): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index 7a0259d214..fab2583ae4 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -15,7 +15,7 @@ def test_multiple_inheritance_cpp(): @pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2") +@pytest.mark.xfail("env.PYPY and not env.PY2", strict=True) def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -55,7 +55,7 @@ def __init__(self, i, j): @pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2") +@pytest.mark.xfail("env.PYPY and not env.PY2", strict=True) def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index ad3ca58c1a..8e9a50eb58 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -423,7 +423,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -435,7 +435,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 417d6f1cff..31ec2c60e5 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -295,7 +295,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index 9aee70505d..a2f2cd8551 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -25,7 +25,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index c21ad61146..630e7e0ffa 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -334,7 +334,7 @@ def test_memoryview(method, args, fmt, expected_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", strict=True) @pytest.mark.parametrize('method', [ m.test_memoryview_object, m.test_memoryview_buffer_info, diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index b7bd5badf0..ebb81a5f3b 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -162,7 +162,7 @@ def f(self): # PyPy: Reference count > 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY", strict=True) @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 3d83321b100ca7807eb8514a244523403a44567b Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 16 Aug 2020 13:27:00 -0400 Subject: [PATCH 10/11] tests: drop explicit strict=True again --- tests/pytest.ini | 1 + tests/test_call_policies.py | 2 +- tests/test_class.py | 6 +++--- tests/test_eval.py | 2 +- tests/test_kwargs_and_defaults.py | 2 +- tests/test_local_bindings.py | 2 +- tests/test_methods_and_attributes.py | 4 ++-- tests/test_multiple_inheritance.py | 4 ++-- tests/test_numpy_array.py | 4 ++-- tests/test_numpy_dtypes.py | 2 +- tests/test_pickling.py | 2 +- tests/test_pytypes.py | 2 +- tests/test_virtual_functions.py | 2 +- 13 files changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/pytest.ini b/tests/pytest.ini index 7e022b9078..af7a97b4f6 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,6 +1,7 @@ [pytest] minversion = 3.6 norecursedirs = test_cmake_build test_embed +xfail_strict = True addopts = # show summary of skipped tests -rs diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index b2b7cf1c96..ec005c132f 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -75,7 +75,7 @@ def test_keep_alive_return_value(capture): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented", strict=True) +@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() diff --git a/tests/test_class.py b/tests/test_class.py index 08dbb90cd2..4214fe79d7 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -263,7 +263,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount @@ -310,7 +310,7 @@ def test_aligned(): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_final(): with pytest.raises(TypeError) as exc_info: class PyFinalChild(m.IsFinal): @@ -319,7 +319,7 @@ class PyFinalChild(m.IsFinal): # https://foss.heptapod.net/pypy/pypy/-/issues/2742 -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_non_final_final(): with pytest.raises(TypeError) as exc_info: class PyNonFinalFinalChild(m.IsNonFinalFinal): diff --git a/tests/test_eval.py b/tests/test_eval.py index d222e79e79..b6f9d1881d 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -19,7 +19,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError, strict=True) +@pytest.mark.xfail("env.PYPY and not env.PY2", raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index 062e7c2ada..5257e0cd30 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -149,7 +149,7 @@ def test_keyword_only_args(msg): """ -@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't double count", strict=True) +@pytest.mark.xfail("env.PYPY and env.PY2", reason="PyPy2 doesn't double count") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_local_bindings.py b/tests/test_local_bindings.py index d1d60eb3ae..5460727e1d 100644 --- a/tests/test_local_bindings.py +++ b/tests/test_local_bindings.py @@ -155,7 +155,7 @@ def test_internal_locals_differ(): assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_stl_caster_vs_stl_bind(msg): """One module uses a generic vector caster from `` while the other exports `std::vector` via `py:bind_vector` and `py::module_local`""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 8e1234020f..c296b6868d 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -261,7 +261,7 @@ def test_property_rvalue_policy(): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_dynamic_attributes(): instance = m.DynamicClass() assert not hasattr(instance, "foo") @@ -303,7 +303,7 @@ class PythonDerivedDynamicClass(m.DynamicClass): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index fab2583ae4..7a0259d214 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -15,7 +15,7 @@ def test_multiple_inheritance_cpp(): @pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2", strict=True) +@pytest.mark.xfail("env.PYPY and not env.PY2") def test_multiple_inheritance_mix1(): class Base1: def __init__(self, i): @@ -55,7 +55,7 @@ def __init__(self, i, j): @pytest.mark.skipif("env.PYPY and env.PY2") -@pytest.mark.xfail("env.PYPY and not env.PY2", strict=True) +@pytest.mark.xfail("env.PYPY and not env.PY2") def test_multiple_inheritance_python(): class MI1(m.Base1, m.Base2): diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 8e9a50eb58..ad3ca58c1a 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -423,7 +423,7 @@ def test_array_resize(msg): assert(b.shape == (8, 8)) -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_array_create_and_resize(msg): a = m.create_and_resize(2) assert(a.size == 4) @@ -435,7 +435,7 @@ def test_index_using_ellipsis(): assert a.shape == (6,) -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_dtype_refcount_leak(): from sys import getrefcount dtype = np.dtype(np.float_) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 31ec2c60e5..417d6f1cff 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -295,7 +295,7 @@ def test_register_dtype(): assert 'dtype is already registered' in str(excinfo.value) -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") def test_str_leak(): from sys import getrefcount fmt = "f4" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index a2f2cd8551..9aee70505d 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -25,7 +25,7 @@ def test_roundtrip(cls_name): assert p2.extra2() == p.extra2() -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") @pytest.mark.parametrize("cls_name", ["PickleableWithDict", "PickleableWithDictNew"]) def test_roundtrip_with_dict(cls_name): cls = getattr(m, cls_name) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 630e7e0ffa..c21ad61146 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -334,7 +334,7 @@ def test_memoryview(method, args, fmt, expected_view): assert view_as_list == list(expected_view) -@pytest.mark.xfail("env.PYPY", reason="getrefcount is not available", strict=True) +@pytest.mark.xfail("env.PYPY", reason="getrefcount is not available") @pytest.mark.parametrize('method', [ m.test_memoryview_object, m.test_memoryview_buffer_info, diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index ebb81a5f3b..b7bd5badf0 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -162,7 +162,7 @@ def f(self): # PyPy: Reference count > 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.xfail("env.PYPY", strict=True) +@pytest.mark.xfail("env.PYPY") @pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC") def test_move_support(): class NCVirtExt(m.NCVirt): From 4039dc1a360e5464431aee41ad540917387cd887 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 16 Aug 2020 13:29:35 -0400 Subject: [PATCH 11/11] tests: reduce minimum PyTest to 3.1 --- .github/workflows/ci.yml | 2 +- tests/CMakeLists.txt | 4 ++-- tests/pytest.ini | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 616f2a025e..30829631d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -312,7 +312,7 @@ jobs: run: | apt-get update apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip - pip3 install "pytest==3.6" + pip3 install "pytest==3.1.*" - name: Configure for install run: > diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6ea3fd6193..2a077c6eb5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -266,8 +266,8 @@ if(NOT PYBIND11_PYTEST_FOUND) if(pytest_not_found) message(FATAL_ERROR "Running the tests requires pytest. Please install it manually" " (try: ${PYTHON_EXECUTABLE} -m pip install pytest)") - elseif(pytest_version VERSION_LESS 3.6) - message(FATAL_ERROR "Running the tests requires pytest >= 3.6. Found: ${pytest_version}" + elseif(pytest_version VERSION_LESS 3.1) + message(FATAL_ERROR "Running the tests requires pytest >= 3.1. Found: ${pytest_version}" "Please update it (try: ${PYTHON_EXECUTABLE} -m pip install -U pytest)") endif() set(PYBIND11_PYTEST_FOUND diff --git a/tests/pytest.ini b/tests/pytest.ini index af7a97b4f6..6d758ea6ac 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,5 +1,5 @@ [pytest] -minversion = 3.6 +minversion = 3.1 norecursedirs = test_cmake_build test_embed xfail_strict = True addopts =