diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aff695437f..1934c2d7b4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,14 +25,14 @@ repos: # Clang format the codebase automatically - repo: https://github.com/pre-commit/mirrors-clang-format - rev: "v22.1.5" + rev: "v22.1.8" hooks: - id: clang-format types_or: [c++, c, cuda] # Ruff, the Python auto-correcting linter/formatter written in Rust - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.20 + rev: v0.16.1 hooks: - id: ruff-check args: ["--fix", "--show-fixes"] @@ -40,7 +40,7 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v2.1.0" + rev: "v2.3.0" hooks: - id: mypy args: [] @@ -112,7 +112,7 @@ repos: # Use tools/codespell_ignore_lines_from_errors.py # to rebuild .codespell-ignore-lines - repo: https://github.com/codespell-project/codespell - rev: "v2.4.2" + rev: "v2.4.3" hooks: - id: codespell exclude: "(.supp|^pyproject.toml)$" diff --git a/docs/conf.py b/docs/conf.py index 5f216bffac..481e04c0c8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # pybind11 documentation build configuration file, created by # sphinx-quickstart on Sun Oct 11 19:23:48 2015. # @@ -13,6 +11,7 @@ # serve to show the default. from __future__ import annotations +import importlib.util import os import re import subprocess @@ -69,13 +68,14 @@ # Read the listed version version_file = DIR.parent / "pybind11/_version.py" -with version_file.open(encoding="utf-8") as f: - code = compile(f.read(), version_file, "exec") -loc = {"__file__": str(version_file)} -exec(code, loc) +spec = importlib.util.spec_from_file_location("pybind11_version", version_file) +assert spec is not None +assert spec.loader is not None +version_module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(version_module) # The full version, including alpha/beta/rc tags. -version = loc["__version__"] +version = version_module.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/noxfile.py b/noxfile.py old mode 100644 new mode 100755 diff --git a/pybind11/__init__.py b/pybind11/__init__.py index df5e8ee92a..3882b2b17b 100644 --- a/pybind11/__init__.py +++ b/pybind11/__init__.py @@ -11,9 +11,9 @@ from .commands import get_cmake_dir, get_include, get_pkgconfig_dir __all__ = ( - "version_info", "__version__", - "get_include", "get_cmake_dir", + "get_include", "get_pkgconfig_dir", + "version_info", ) diff --git a/pybind11/setup_helpers.py b/pybind11/setup_helpers.py index 8f42605245..66a3eec0d4 100644 --- a/pybind11/setup_helpers.py +++ b/pybind11/setup_helpers.py @@ -52,10 +52,10 @@ from functools import lru_cache from pathlib import Path from typing import ( + TYPE_CHECKING, Any, Callable, Optional, - TypeVar, Union, ) @@ -71,6 +71,9 @@ import distutils.ccompiler import distutils.errors +if TYPE_CHECKING: + from typing_extensions import Self + WIN = sys.platform.startswith("win32") and "mingw" not in sysconfig.get_platform() MACOS = sys.platform.startswith("darwin") STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" @@ -338,8 +341,6 @@ def no_recompile(obj: str, src: str) -> bool: # noqa: ARG001 return True -S = TypeVar("S", bound="ParallelCompile") - CCompilerMethod = Callable[ [ distutils.ccompiler.CCompiler, @@ -397,7 +398,7 @@ class ParallelCompile: called. """ - __slots__ = ("envvar", "default", "max", "_old", "needs_recompile") + __slots__ = ("_old", "default", "envvar", "max", "needs_recompile") def __init__( self, @@ -477,16 +478,16 @@ def _single_compile(obj: Any) -> None: return compile_function - def install(self: S) -> S: + def install(self) -> Self: """ Installs the compile function into distutils.ccompiler.CCompiler.compile. """ distutils.ccompiler.CCompiler.compile = self.function() # type: ignore[assignment] return self - def __enter__(self: S) -> S: + def __enter__(self) -> Self: self._old.append(distutils.ccompiler.CCompiler.compile) return self.install() - def __exit__(self, *args: Any) -> None: + def __exit__(self, *args: object) -> None: distutils.ccompiler.CCompiler.compile = self._old.pop() # type: ignore[assignment] diff --git a/pyproject.toml b/pyproject.toml index caf0a8b902..cb5d728b3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -181,8 +181,14 @@ isort.required-imports = ["from __future__ import annotations"] "EM", "N", "E721", + "BLE001", # Tests capture and re-report exceptions from workers and callbacks + "DTZ", # test_chrono.py checks naive local-time round-tripping on purpose + "FLY002", # Joining a list keeps long signatures one-per-line + "RUF012", # ClassVar annotations are noise in test fixtures + "RUF063", # test_pytypes.py reads __annotations__ from __dict__ deliberately ] "tests/test_call_policies.py" = ["PLC1901"] +"docs/benchmark.py" = ["DTZ"] [tool.repo-review] ignore = ["PP"] diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index e2c18565dc..164611db34 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -25,7 +25,7 @@ # Newer pytest has global path setting, but keeping old pytest for now sys.path.append(str(MAIN_DIR / "tools")) -from make_global import get_global # noqa: E402 +from make_global import get_global HAS_UV = shutil.which("uv") is not None UV_ARGS = ["--installer=uv"] if HAS_UV else [] diff --git a/tests/test_enum.py b/tests/test_enum.py index 53dcc09cb9..81170c91dd 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -71,8 +71,8 @@ def test_unscoped_enum(): assert y != 3 assert 3 != y # Compare with None - assert y != None # noqa: E711 - assert not (y == None) # noqa: E711 + assert y != None + assert not (y == None) # Compare with an object assert y != object() assert not (y == object()) @@ -137,8 +137,8 @@ def test_scoped_enum(): assert z != 3 assert 3 != z # Compare with None - assert z != None # noqa: E711 - assert not (z == None) # noqa: E711 + assert z != None + assert not (z == None) # Compare with an object assert z != object() assert not (z == object()) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index c52a295b36..aeea5d86d3 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -112,7 +112,7 @@ def test_python_alreadyset_in_destructor(monkeypatch, capsys): default_hook = sys.__unraisablehook__ def hook(unraisable_hook_args): - exc_type, exc_value, exc_tb, err_msg, obj = unraisable_hook_args + _exc_type, _exc_value, _exc_tb, _err_msg, obj = unraisable_hook_args if obj == "already_set demo": nonlocal triggered triggered = True @@ -344,8 +344,10 @@ def _test_flaky_exception_failure_point_init_before_py_3_12(): lines = str(excinfo.value).splitlines() # PyErr_NormalizeException replaces the original FlakyException with ValueError: assert lines[:3] == [ - "pybind11::error_already_set: MISMATCH of original and normalized active exception types:" - " ORIGINAL FlakyException REPLACED BY ValueError: triggered_failure_point_init", + ( + "pybind11::error_already_set: MISMATCH of original and normalized active exception types:" + " ORIGINAL FlakyException REPLACED BY ValueError: triggered_failure_point_init" + ), "", "At:", ] diff --git a/tests/test_iostream.py b/tests/test_iostream.py index 857e0b5f73..8b11997007 100644 --- a/tests/test_iostream.py +++ b/tests/test_iostream.py @@ -160,16 +160,16 @@ def test_flush(capfd): with m.ostream_redirect(): m.noisy_function(msg, flush=False) - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert not stdout m.noisy_function(msg2, flush=True) - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert stdout == msg + msg2 m.noisy_function(msg, flush=False) - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert stdout == msg @@ -218,7 +218,7 @@ def test_multi_captured(capfd): m.raw_output("b") m.captured_output("c") m.raw_output("d") - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert stdout == "bd" assert stream.getvalue() == "ac" @@ -235,21 +235,21 @@ def test_redirect(capfd): stream = StringIO() with redirect_stdout(stream): m.raw_output(msg) - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert stdout == msg assert not stream.getvalue() stream = StringIO() with redirect_stdout(stream), m.ostream_redirect(): m.raw_output(msg) - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert not stdout assert stream.getvalue() == msg stream = StringIO() with redirect_stdout(stream): m.raw_output(msg) - stdout, stderr = capfd.readouterr() + stdout, _stderr = capfd.readouterr() assert stdout == msg assert not stream.getvalue() diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 22814aba5a..ba45d8bf63 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -316,12 +316,18 @@ def test_array_array(): "'offsets':[0,12,20,24],'itemsize':56}" ) assert m.print_array_array(arr) == [ - "a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1}," - "c={0,1,2},d={{0,1},{10,11},{20,21},{30,31}}", - "a={{W,X,Y,Z},{G,H,I,J},{Q,R,S,T}},b={1000,1001}," - "c={10,11,12},d={{100,101},{110,111},{120,121},{130,131}}", - "a={{S,T,U,V},{C,D,E,F},{M,N,O,P}},b={2000,2001}," - "c={20,21,22},d={{200,201},{210,211},{220,221},{230,231}}", + ( + "a={{A,B,C,D},{K,L,M,N},{U,V,W,X}},b={0,1}," + "c={0,1,2},d={{0,1},{10,11},{20,21},{30,31}}" + ), + ( + "a={{W,X,Y,Z},{G,H,I,J},{Q,R,S,T}},b={1000,1001}," + "c={10,11,12},d={{100,101},{110,111},{120,121},{130,131}}" + ), + ( + "a={{S,T,U,V},{C,D,E,F},{M,N,O,P}},b={2000,2001}," + "c={20,21,22},d={{200,201},{210,211},{220,221},{230,231}}" + ), ] assert arr["a"].tolist() == [ [b"ABCD", b"KLMN", b"UVWX"], diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index 7ee4b78ed5..326b768c40 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -5,7 +5,7 @@ import env # noqa: F401 m = pytest.importorskip("pybind11_tests.smart_ptr") -from pybind11_tests import ConstructorStats # noqa: E402 +from pybind11_tests import ConstructorStats @pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") diff --git a/tests/test_stl.py b/tests/test_stl.py index c75ccb8d24..f3f4ccc736 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -818,7 +818,7 @@ class FormalMappingLike(BareMappingLike, Mapping): def test_set_caster_protocol(doc): - from collections.abc import Set + from collections.abc import Set as AbstractSet # Implements the Set protocol without explicitly inheriting from collections.abc.Set. class BareSetLike: @@ -836,7 +836,7 @@ def __iter__(self): # Implements the Set protocol by reusing BareSetLike's implementation. # Additionally, inherits from collections.abc.Set. - class FormalSetLike(BareSetLike, Set): + class FormalSetLike(BareSetLike, AbstractSet): pass # convert mode diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index 617c87b8e0..c2bba47f78 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -7,7 +7,7 @@ import env m = pytest.importorskip("pybind11_tests.virtual_functions") -from pybind11_tests import ConstructorStats # noqa: E402 +from pybind11_tests import ConstructorStats def test_override(capture, msg): diff --git a/tools/make_changelog.py b/tools/make_changelog.py index f872546294..8d2c7b08ef 100755 --- a/tools/make_changelog.py +++ b/tools/make_changelog.py @@ -94,8 +94,7 @@ def get_token() -> str | None: if not msg: missing.append(issue) continue - if msg.startswith("* "): - msg = msg[2:] + msg = msg.removeprefix("* ") if not msg.startswith("- "): msg = "- " + msg if not msg.endswith("."):