Unverified Commit 522c59ce authored by Henry Schreiner's avatar Henry Schreiner Committed by GitHub
Browse files

chore: drop Python 3.5 (#3719)



* chore: drop Python 3.5 support

* chore: more fstrings with flynt's help

* ci: drop Python 3.5

* chore: bump dependency versions

* docs: touch up py::args

* tests: remove deprecation warning

* Ban smartquotes

* Very minor tweaks (by-product of reviewing PR #3719).
Co-authored-by: default avatarAaron Gokaslan <skylion.aaron@gmail.com>
Co-authored-by: default avatarRalf W. Grosse-Kunstleve <rwgk@google.com>
parent 1a432b42
......@@ -2,7 +2,7 @@ import nox
nox.options.sessions = ["lint", "tests", "tests_packaging"]
PYTHON_VERSIONS = ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
@nox.session(reuse_venv=True)
......
import sys
if sys.version_info < (3, 5):
msg = "pybind11 does not support Python < 3.5. 2.9 was the last release supporting older Pythons."
if sys.version_info < (3, 6):
msg = "pybind11 does not support Python < 3.6. 2.9 was the last release supporting Python 2.7 and 3.5."
raise ImportError(msg)
......
......@@ -200,7 +200,7 @@ class Pybind11Extension(_Extension): # type: ignore[misc]
current_macos = tuple(int(x) for x in platform.mac_ver()[0].split(".")[:2])
desired_macos = (10, 9) if level < 17 else (10, 14)
macos_string = ".".join(str(x) for x in min(current_macos, desired_macos))
macosx_min = "-mmacosx-version-min={}".format(macos_string)
macosx_min = f"-mmacosx-version-min={macos_string}"
cflags += [macosx_min]
ldflags += [macosx_min]
......@@ -322,9 +322,9 @@ def intree_extensions(
if not exts:
msg = (
"path {path} is not a child of any of the directories listed "
"in 'package_dir' ({package_dir})"
).format(path=path, package_dir=package_dir)
f"path {path} is not a child of any of the directories listed "
f"in 'package_dir' ({package_dir})"
)
raise ValueError(msg)
return exts
......@@ -419,7 +419,7 @@ class ParallelCompile:
self.default = default
self.max = max
self.needs_recompile = needs_recompile
self._old = [] # type: List[CCompilerMethod]
self._old: List[CCompilerMethod] = []
def function(self) -> CCompilerMethod:
"""
......
......@@ -14,7 +14,6 @@ classifiers =
Topic :: Utilities
Programming Language :: C++
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
......@@ -38,7 +37,7 @@ project_urls =
Chat = https://gitter.im/pybind/Lobby
[options]
python_requires = >=3.5
python_requires = >=3.6
zip_safe = False
......@@ -46,14 +45,5 @@ zip_safe = False
max-line-length = 120
show_source = True
exclude = .git, __pycache__, build, dist, docs, tools, venv
ignore =
# required for pretty matrix formatting: multiple spaces after `,` and `[`
E201, E241, W504,
# camelcase 'cPickle' imported as lowercase 'pickle'
N813
# Black conflict
W503, E203
[tool:pytest]
timeout = 300
extend-ignore = E203, E722, B950
select = C,E,F,N,W,B,B9
#!/usr/bin/env python
#!/usr/bin/env python3
# Setup script for PyPI; use CMakeFile.txt to build extension modules
......@@ -41,12 +41,10 @@ def build_expected_version_hex(matches: Dict[str, str]) -> str:
serial = int(level_serial[len(level) :])
break
if serial is None:
msg = 'Invalid PYBIND11_VERSION_PATCH: "{}"'.format(patch_level_serial)
msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
raise RuntimeError(msg)
version_hex_str = "{:02x}{:02x}{:02x}{}{:x}".format(
major, minor, patch, level[:1], serial
)
return "0x{}".format(version_hex_str.upper())
version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
return f"0x{version_hex_str.upper()}"
# PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
......@@ -66,7 +64,7 @@ to_src = (
# Read the listed version
loc = {} # type: Dict[str, str]
loc: Dict[str, str] = {}
code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
exec(code, loc)
version = loc["__version__"]
......@@ -75,17 +73,13 @@ version = loc["__version__"]
matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
if version != cpp_version:
msg = "Python version {} does not match C++ version {}!".format(
version, cpp_version
)
msg = f"Python version {version} does not match C++ version {cpp_version}!"
raise RuntimeError(msg)
version_hex = matches.get("HEX", "MISSING")
exp_version_hex = build_expected_version_hex(matches)
if version_hex != exp_version_hex:
msg = "PYBIND11_VERSION_HEX {} does not match expected value {}!".format(
version_hex, exp_version_hex
)
msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
raise RuntimeError(msg)
......
......@@ -145,9 +145,9 @@ def test_build_sdist(monkeypatch, tmpdir):
contents = f.read().decode("utf8")
assert 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")' in contents
files = {"pybind11/{}".format(n) for n in all_files}
files = {f"pybind11/{n}" for n in all_files}
files |= sdist_files
files |= {"pybind11{}".format(n) for n in local_sdist_files}
files |= {f"pybind11{n}" for n in local_sdist_files}
files.add("pybind11.egg-info/entry_points.txt")
files.add("pybind11.egg-info/requires.txt")
assert simpler == files
......@@ -200,9 +200,9 @@ def test_build_global_dist(monkeypatch, tmpdir):
) as f:
pyproject_toml = f.read()
files = {"pybind11/{}".format(n) for n in all_files}
files = {f"pybind11/{n}" for n in all_files}
files |= sdist_files
files |= {"pybind11_global{}".format(n) for n in local_sdist_files}
files |= {f"pybind11_global{n}" for n in local_sdist_files}
assert simpler == files
with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
......@@ -227,7 +227,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl")
files = {"pybind11/{}".format(n) for n in all_files}
files = {f"pybind11/{n}" for n in all_files}
files |= {
"dist-info/LICENSE",
"dist-info/METADATA",
......@@ -241,9 +241,7 @@ def tests_build_wheel(monkeypatch, tmpdir):
names = z.namelist()
trimmed = {n for n in names if "dist-info" not in n}
trimmed |= {
"dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
}
trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n}
assert files == trimmed
......@@ -257,8 +255,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
(wheel,) = tmpdir.visit("*.whl")
files = {"data/data/{}".format(n) for n in src_files}
files |= {"data/headers/{}".format(n[8:]) for n in headers}
files = {f"data/data/{n}" for n in src_files}
files |= {f"data/headers/{n[8:]}" for n in headers}
files |= {
"dist-info/LICENSE",
"dist-info/METADATA",
......
......@@ -18,7 +18,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
(tmpdir / "setup.py").write_text(
dedent(
"""\
f"""\
import sys
sys.path.append({MAIN_DIR!r})
......@@ -51,7 +51,7 @@ def test_simple_setup_py(monkeypatch, tmpdir, parallel, std):
ext_modules=ext_modules,
)
"""
).format(MAIN_DIR=MAIN_DIR, std=std, parallel=parallel),
),
encoding="ascii",
)
......
[pytest]
minversion = 3.1
minversion = 3.10
norecursedirs = test_* extra_*
xfail_strict = True
addopts =
# show summary of skipped tests
-rs
# show summary of tests
-ra
# capture only Python print and C++ py::print, but not C output (low-level Python errors)
--capture=sys
# Show local info when a failure occurs
--showlocals
log_cli_level = info
filterwarnings =
# make warnings into errors but ignore certain third-party extension issues
error
......
numpy==1.16.6; python_version<"3.6" and sys_platform!="win32" and platform_python_implementation!="PyPy"
numpy==1.19.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.6"
numpy==1.20.0; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.21.5; platform_python_implementation=="PyPy" and sys_platform=="linux" and python_version=="3.7"
numpy==1.19.3; platform_python_implementation!="PyPy" and python_version=="3.6"
numpy==1.21.3; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.11"
py @ git+https://github.com/pytest-dev/py; python_version>="3.11"
pytest==4.6.9; python_version<"3.5"
pytest==6.1.2; python_version=="3.5"
pytest==6.2.4; python_version>="3.6"
numpy==1.21.5; platform_python_implementation!="PyPy" and python_version>="3.7" and python_version<"3.10"
numpy==1.22.2; platform_python_implementation!="PyPy" and python_version>="3.10" and python_version<"3.11"
pytest==7.0.0
pytest-timeout
scipy==1.2.3; platform_python_implementation!="PyPy" and python_version<"3.6"
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version>="3.6" and python_version<"3.10"
scipy==1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10"
scipy==1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10"
......@@ -50,7 +50,7 @@ def test_single_char_arguments():
"""Tests failures for passing invalid inputs to char-accepting functions"""
def toobig_message(r):
return "Character code point not in range({:#x})".format(r)
return f"Character code point not in range({r:#x})"
toolong_message = "Expected a character, but multi-character string found"
......
......@@ -17,7 +17,7 @@ def test_callbacks():
return "func2", a, b, c, d
def func3(a):
return "func3({})".format(a)
return f"func3({a})"
assert m.test_callback1(func1) == "func1"
assert m.test_callback2(func2) == ("func2", "Hello", "x", True, 5)
......@@ -188,14 +188,8 @@ def test_callback_num_times():
if not rep:
print()
print(
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format(
num_millions, td, rate
)
f"callback_num_times: {num_millions:d} million / {td:.3f} seconds = {rate:.3f} million / second"
)
if len(rates) > 1:
print("Min Mean Max")
print(
"{:6.3f} {:6.3f} {:6.3f}".format(
min(rates), sum(rates) / len(rates), max(rates)
)
)
print(f"{min(rates):6.3f} {sum(rates) / len(rates):6.3f} {max(rates):6.3f}")
......@@ -100,7 +100,7 @@ SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif(
)
def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
if tz is not None:
monkeypatch.setenv("TZ", "/usr/share/zoneinfo/{}".format(tz))
monkeypatch.setenv("TZ", f"/usr/share/zoneinfo/{tz}")
# Roundtrip the time
datetime2 = m.test_chrono2(time1)
......
......@@ -5,4 +5,4 @@ import test_cmake_build
assert isinstance(__file__, str) # Test this is properly set
assert test_cmake_build.add(1, 2) == 3
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))
print(f"{sys.argv[1]} imports, runs, and adds: 1 + 2 = 3")
......@@ -221,9 +221,7 @@ def test_nonunit_stride_to_python():
assert np.all(m.diagonal(ref) == ref.diagonal())
assert np.all(m.diagonal_1(ref) == ref.diagonal(1))
for i in range(-5, 7):
assert np.all(
m.diagonal_n(ref, i) == ref.diagonal(i)
), "m.diagonal_n({})".format(i)
assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), f"m.diagonal_n({i})"
assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4])
assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:])
......@@ -236,7 +234,7 @@ def test_eigen_ref_to_python():
mymat = chol(np.array([[1.0, 2, 4], [2, 13, 23], [4, 23, 77]]))
assert np.all(
mymat == np.array([[1, 0, 0], [2, 3, 0], [4, 5, 6]])
), "cholesky{}".format(i)
), f"cholesky{i}"
def assign_both(a1, a2, r, c, v):
......
......@@ -88,7 +88,7 @@ def test_python_call_in_catch():
def ignore_pytest_unraisable_warning(f):
unraisable = "PytestUnraisableExceptionWarning"
if hasattr(pytest, unraisable): # Python >= 3.8 and pytest >= 6
dec = pytest.mark.filterwarnings("ignore::pytest.{}".format(unraisable))
dec = pytest.mark.filterwarnings(f"ignore::pytest.{unraisable}")
return dec(f)
else:
return f
......
......@@ -18,9 +18,7 @@ def test_dtypes():
assert check.numpy == check.pybind11, check
if check.numpy.num != check.pybind11.num:
print(
"NOTE: typenum mismatch for {}: {} != {}".format(
check, check.numpy.num, check.pybind11.num
)
f"NOTE: typenum mismatch for {check}: {check.numpy.num} != {check.pybind11.num}"
)
......@@ -116,9 +114,7 @@ def test_at_fail(arr, dim):
for func in m.at_t, m.mutate_at_t:
with pytest.raises(IndexError) as excinfo:
func(arr, *([0] * dim))
assert str(excinfo.value) == "index dimension mismatch: {} (ndim = 2)".format(
dim
)
assert str(excinfo.value) == f"index dimension mismatch: {dim} (ndim = 2)"
def test_at(arr):
......@@ -192,8 +188,6 @@ def test_make_empty_shaped_array():
def test_wrap():
def assert_references(a, b, base=None):
from distutils.version import LooseVersion
if base is None:
base = a
assert a is not b
......@@ -204,7 +198,8 @@ def test_wrap():
assert a.flags.f_contiguous == b.flags.f_contiguous
assert a.flags.writeable == b.flags.writeable
assert a.flags.aligned == b.flags.aligned
if LooseVersion(np.__version__) >= LooseVersion("1.14.0"):
# 1.13 supported Python 3.6
if tuple(int(x) for x in np.__version__.split(".")[:2]) >= (1, 14):
assert a.flags.writebackifcopy == b.flags.writebackifcopy
else:
assert a.flags.updateifcopy == b.flags.updateifcopy
......
......@@ -14,7 +14,7 @@ def simple_dtype():
return np.dtype(
{
"names": ["bool_", "uint_", "float_", "ldbl_"],
"formats": ["?", "u4", "f4", "f{}".format(ld.itemsize)],
"formats": ["?", "u4", "f4", f"f{ld.itemsize}"],
"offsets": [0, 4, 8, (16 if ld.alignment > 4 else 12)],
}
)
......@@ -125,7 +125,7 @@ def test_dtype(simple_dtype):
assert [x.replace(" ", "") for x in m.print_dtypes()] == [
simple_dtype_fmt(),
packed_dtype_fmt(),
"[('a',{}),('b',{})]".format(simple_dtype_fmt(), packed_dtype_fmt()),
f"[('a',{simple_dtype_fmt()}),('b',{packed_dtype_fmt()})]",
partial_dtype_fmt(),
partial_nested_fmt(),
"[('a','S3'),('b','S3')]",
......
......@@ -12,7 +12,7 @@ def test_string_list():
assert lst.back() == "Element 2"
for i, k in enumerate(lst, start=1):
assert k == "Element {}".format(i)
assert k == f"Element {i}"
lst.pop_back()
assert m.print_opaque_list(lst) == "Opaque list: [Element 1]"
......
......@@ -305,8 +305,8 @@ def test_non_converting_constructors():
for move in [True, False]:
with pytest.raises(TypeError) as excinfo:
m.nonconverting_constructor(t, v, move)
expected_error = "Object of type '{}' is not an instance of '{}'".format(
type(v).__name__, t
expected_error = (
f"Object of type '{type(v).__name__}' is not an instance of '{t}'"
)
assert str(excinfo.value) == expected_error
......
import pytest
from pytest import approx
from pybind11_tests import ConstructorStats
from pybind11_tests import sequences_and_iterators as m
def isclose(a, b, rel_tol=1e-05, abs_tol=0.0):
"""Like math.isclose() from Python 3.5"""
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
def allclose(a_list, b_list, rel_tol=1e-05, abs_tol=0.0):
return all(
isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol) for a, b in zip(a_list, b_list)
)
def test_slice_constructors():
assert m.make_forward_slice_size_t() == slice(0, -1, 1)
assert m.make_reversed_slice_object() == slice(None, None, -1)
......@@ -117,7 +107,8 @@ def test_sequence():
assert 12.34 not in s
s[0], s[3] = 12.34, 56.78
assert 12.34 in s
assert isclose(s[0], 12.34) and isclose(s[3], 56.78)
assert s[0] == approx(12.34, rel=1e-05)
assert s[3] == approx(56.78, rel=1e-05)
rev = reversed(s)
assert cstats.values() == ["of size", "5"]
......@@ -132,14 +123,14 @@ def test_sequence():
assert cstats.values() == ["of size", "0"]
expected = [0, 56.78, 0, 0, 12.34]
assert allclose(rev, expected)
assert allclose(rev2, expected)
assert rev == approx(expected, rel=1e-05)
assert rev2 == approx(expected, rel=1e-05)
assert rev == rev2
rev[0::2] = m.Sequence([2.0, 2.0, 2.0])
assert cstats.values() == ["of size", "3", "from std::vector"]
assert allclose(rev, [2, 56.78, 2, 0, 2])
assert rev == approx([2, 56.78, 2, 0, 2], rel=1e-05)
assert cstats.alive() == 4
del it
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment