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

* `#error BYE_BYE_GOLDEN_SNAKE`

* Removing everything related to 2.7 from ci.yml

* Commenting-out Centos7

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

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

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

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

* Removing obsolete Python 2 mention in compiling.rst

* Proper `#error` for Python 2.

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

* chore: bump pre-commit

* style: run pre-commit for pyupgrade 3+

* tests: use sys.version_info, not PY

* chore: more Python 2 removal

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

* Update pre-commit hooks

* Fix pre-commit hook

* refactor: remove Python 2 from CMake

* refactor: remove Python 2 from setup code

* refactor: simplify, better static typing

* feat: fail with nice messages

* refactor: drop Python 2 C++ code

* docs: cleanup for Python 3

* revert: intree

revert: intree

* docs: minor touchup to py2 statement

Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com>
This commit is contained in:
Ralf W. Grosse-Kunstleve
2022-02-10 18:28:08 -08:00
committed by GitHub
parent 46dcd9bc75
commit 6493f496e3
102 changed files with 529 additions and 1334 deletions

View File

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