From b4404b42e6721842b407515c2ce6212462b13869 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Sun, 4 Oct 2026 16:24:40 +0000 Subject: [PATCH] Fix ruff 0.16 findings; bump dependency minimums to match - pyproject [tool.ruff.lint]: ignore RUF022/RUF023 (deliberately grouped __all__/__slots__), SIM117, BLE001 (blind except is intentional at CFFI callback boundaries) - Legit fixes: dict/tuple instead of typing.Dict/Tuple in generate.py, unquoted annotations in raf.py, Self return for __enter__ methods, explicit check=False on subprocess.run, collapsed nested ifs, sorted test imports, unused-unpacked-variable in test_raises - Bump minimum versions to the synced toolchain: cffi 2.1.1, ruff 0.16.10, pytest 9.1.1, auditwheel 6.8.2, setuptools 84.0.0, setuptools-scm 10.3.4 (project deps and build-system requires) --- pyproject.toml | 26 ++++++++++++++++++------- src/aeg/raf.py | 18 +++++++++--------- src/aeg/securebuf.py | 3 ++- tests/test_fuzz_raf_merkle.py | 3 +-- tests/test_raf.py | 3 +-- tests/test_raf_pyaegis.py | 3 +-- tests/test_raises.py | 1 + tests/util.py | 1 - tools/generate.py | 36 ++++++++++++++++++----------------- tools/release.py | 2 +- 10 files changed, 54 insertions(+), 42 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2a1d826..0028712 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=61.0", "cffi>=2.0.0", "setuptools-scm>=8.0"] +requires = ["setuptools>=84.0.0", "cffi>=2.1.1", "setuptools-scm>=10.3.4"] build-backend = "build_backend" backend-path = ["tools"] @@ -16,7 +16,7 @@ classifiers = [ "Topic :: Security :: Cryptography", ] dependencies = [ - "cffi>=2.0.0", + "cffi>=2.1.1", ] [project.urls] @@ -25,11 +25,11 @@ Repository = "https://github.com/LeoVasanko/aegis-python" [dependency-groups] dev = [ - "auditwheel>=6.5.0", - "pytest>=8.4.2", - "ruff>=0.14.4", - "setuptools>=80.9.0", - "setuptools-scm>=9.2.2", + "auditwheel>=6.8.2", + "pytest>=9.1.1", + "ruff>=0.16.10", + "setuptools>=84.0.0", + "setuptools-scm>=10.3.4", ] [tool.setuptools] @@ -39,4 +39,16 @@ packages = ["aeg"] [tool.setuptools.package-data] aeg = ["*.h"] +[tool.ruff] +# Disable formatting in Markdown files +extend-exclude = ["*.md"] + +[tool.ruff.lint] +ignore = [ + "RUF022", # __all__ is deliberately grouped with comments + "RUF023", # __slots__ is deliberately grouped + "SIM117", # nested with-statements are sometimes clearer + "BLE001", # blind except is intentional at CFFI callback boundaries +] + [tool.setuptools_scm] diff --git a/src/aeg/raf.py b/src/aeg/raf.py index 07b56c2..d18ac49 100644 --- a/src/aeg/raf.py +++ b/src/aeg/raf.py @@ -18,7 +18,7 @@ import hashlib import io as io_module import os import threading -from typing import NamedTuple, Protocol, runtime_checkable +from typing import NamedTuple, Protocol, Self, runtime_checkable from . import cipher as _cipher_for_name from . import random as _random @@ -163,7 +163,7 @@ class FileStorage: os.close(self._fd) self._closed = True - def __enter__(self) -> FileStorage: + def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: @@ -213,7 +213,7 @@ class BytesIOStorage: """Return the entire buffer contents.""" return bytes(self._data) - def __enter__(self) -> BytesIOStorage: + def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: @@ -272,7 +272,7 @@ class StreamStorage: if hasattr(self._stream, "flush"): self._stream.flush() - def __enter__(self) -> StreamStorage: + def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: @@ -588,8 +588,8 @@ def create( truncate: bool = False, merkle: bool | MerkleHasher = False, merkle_max_chunks: int = 16384, - rng: "_random.RandomSource | None" = None, -) -> "Raf": + rng: _random.RandomSource | None = None, +) -> Raf: """Create a new encrypted file. Args: @@ -641,8 +641,8 @@ def open( *, merkle: bool | MerkleHasher = False, merkle_max_chunks: int = 16384, - rng: "_random.RandomSource | None" = None, -) -> "Raf": + rng: _random.RandomSource | None = None, +) -> Raf: """Open an existing encrypted file. The header is probed to size internal buffers and its alg_id is checked @@ -922,7 +922,7 @@ class Raf: """True if the file has been closed.""" return self._closed - def __enter__(self) -> "Raf": + def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: diff --git a/src/aeg/securebuf.py b/src/aeg/securebuf.py index 7b44c87..cb51ed7 100644 --- a/src/aeg/securebuf.py +++ b/src/aeg/securebuf.py @@ -17,6 +17,7 @@ import ctypes import mmap import os from contextlib import suppress +from typing import Self from .util import wipe @@ -104,7 +105,7 @@ class SecureBuffer: def __len__(self) -> int: return len(self._mmap) - def __enter__(self) -> "SecureBuffer": + def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: diff --git a/tests/test_fuzz_raf_merkle.py b/tests/test_fuzz_raf_merkle.py index 3271f26..ccdb869 100644 --- a/tests/test_fuzz_raf_merkle.py +++ b/tests/test_fuzz_raf_merkle.py @@ -12,8 +12,7 @@ import random import pytest -from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 -from aeg import raf +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, raf from aeg.raf import BytesIOStorage, FileStorage CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4] diff --git a/tests/test_raf.py b/tests/test_raf.py index 984a820..65300ca 100644 --- a/tests/test_raf.py +++ b/tests/test_raf.py @@ -1,7 +1,6 @@ import pytest -from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 -from aeg import raf +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, raf from aeg import random as aeg_random CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4] diff --git a/tests/test_raf_pyaegis.py b/tests/test_raf_pyaegis.py index aecd2b4..720baa8 100644 --- a/tests/test_raf_pyaegis.py +++ b/tests/test_raf_pyaegis.py @@ -12,8 +12,7 @@ import hashlib import pytest -from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 -from aeg import raf +from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, raf from aeg.raf import ( BytesIOStorage, FileStorage, diff --git a/tests/test_raises.py b/tests/test_raises.py index 73386fd..3323ae6 100644 --- a/tests/test_raises.py +++ b/tests/test_raises.py @@ -252,6 +252,7 @@ class TestDecryptorFinalization: # Encrypt empty message ct, tag = aegis256x4.encrypt_detached(key, nonce, b"") + del ct # Decrypt without any updates decryptor = aegis256x4.Decryptor(key, nonce) diff --git a/tests/util.py b/tests/util.py index 95d542c..a2709df 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,6 +1,5 @@ import random - _rng = random.SystemRandom() diff --git a/tools/generate.py b/tools/generate.py index 6b74b7d..fff240a 100755 --- a/tools/generate.py +++ b/tools/generate.py @@ -5,7 +5,6 @@ import pathlib import re import subprocess import sys -from typing import Dict, Tuple def preprocess_content(content: str) -> str: @@ -58,16 +57,18 @@ def format_declaration(decl: str, max_width: int = 100) -> str: if len(decl) <= max_width: return decl - if "(" in decl and ")" in decl: - if match := re.match(r"(.*?\s+\w+\s*)\((.*)\)(.*)", decl): - prefix, params, suffix = match.groups() - if len(prefix) + len(params) + 2 > max_width: - param_list = [p.strip() for p in params.split(",")] - if len(param_list) > 1: - formatted_params = (",\n" + " " * (len(prefix) + 1)).join( - param_list - ) - return f"{prefix}({formatted_params}){suffix}" + if ( + "(" in decl + and ")" in decl + and (match := re.match(r"(.*?\s+\w+\s*)\((.*)\)(.*)", decl)) + ): + prefix, params, suffix = match.groups() + if ( + len(prefix) + len(params) + 2 > max_width + and len(param_list := [p.strip() for p in params.split(",")]) > 1 + ): + formatted_params = (",\n" + " " * (len(prefix) + 1)).join(param_list) + return f"{prefix}({formatted_params}){suffix}" return decl @@ -108,7 +109,7 @@ def generate_cdef(include_dir: pathlib.Path) -> str: def extract_constants( common_h_path: pathlib.Path, header_path: pathlib.Path, raf_h_path: pathlib.Path -) -> Dict[str, int]: +) -> dict[str, int]: """Extract constants from common.h (ALIGNMENT, RATE), main header (KEYBYTES, NPUBBYTES, ABYTES_*), and aegis_raf.h (RAF_ALG_ID).""" constants = {} @@ -151,7 +152,7 @@ def extract_constants( def extract_all_constants( libaegis_src_dir: pathlib.Path, include_dir: pathlib.Path -) -> Dict[str, Dict[str, int]]: +) -> dict[str, dict[str, int]]: variants = [ "aegis128l", "aegis128x2", @@ -195,7 +196,7 @@ def algo_label(name: str) -> str: return "AEGIS-" + name[5:].upper() -def generate_variant(template_src: str, variant: str, constants: Dict[str, int]) -> str: +def generate_variant(template_src: str, variant: str, constants: dict[str, int]) -> str: """Generate a variant module from the template with substituted constants.""" s = template_src.replace("aegis256x4", variant).replace( "AEGIS-256X4", algo_label(variant) @@ -238,8 +239,8 @@ def generate_variant(template_src: str, variant: str, constants: Dict[str, int]) def generate_python_modules( template_path: pathlib.Path, output_dir: pathlib.Path, - constants: Dict[str, Dict[str, int]], -) -> Tuple[list[pathlib.Path], list[pathlib.Path]]: + constants: dict[str, dict[str, int]], +) -> tuple[list[pathlib.Path], list[pathlib.Path]]: if not template_path.exists(): raise FileNotFoundError(f"Template not found: {template_path}") @@ -295,7 +296,7 @@ def generate_python_modules( return updated, unchanged -def generate_ciphers_module(constants: Dict[str, Dict[str, int]]) -> str: +def generate_ciphers_module(constants: dict[str, dict[str, int]]) -> str: labels = [algo_label(variant) for variant in constants] literal_items = ", ".join(f'"{label}"' for label in labels) lines = [ @@ -381,6 +382,7 @@ def main() -> int: ["uv", "run", "ruff", "format", str(pyaegis_dir)], capture_output=True, text=True, + check=False, ) if result.returncode != 0: print(f"Error formatting generated files: {result.stderr}", file=sys.stderr) diff --git a/tools/release.py b/tools/release.py index 7ab02fe..a6de867 100755 --- a/tools/release.py +++ b/tools/release.py @@ -77,7 +77,7 @@ def get_next_version(current_version): def is_working_copy_clean(): """Check if git working copy is clean.""" result = subprocess.run( - ["git", "status", "--porcelain"], capture_output=True, text=True + ["git", "status", "--porcelain"], capture_output=True, text=True, check=False ) return result.returncode == 0 and not result.stdout.strip()