Fix ruff 0.16 findings; bump dependency minimums to match
release / build-wheels (windows, cmd) (push) Failing after 11s
release / build-wheels (linux, bash) (push) Failing after 10s
release / build-wheels (macos, bash) (push) Failing after 11s
release / publish-pypi (push) Skipped

- 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)
This commit is contained in:
LeoVasanko committed 2026-10-04 16:24:40 +00:00
1 parent 1eb0b6e8c0
commit b4404b42e6
10 files changed
+54 -42

No files matched your search

+19 -7
View File
@@ -1,5 +1,5 @@
[build-system] [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" build-backend = "build_backend"
backend-path = ["tools"] backend-path = ["tools"]
@@ -16,7 +16,7 @@ classifiers = [
"Topic :: Security :: Cryptography", "Topic :: Security :: Cryptography",
] ]
dependencies = [ dependencies = [
"cffi>=2.0.0", "cffi>=2.1.1",
] ]
[project.urls] [project.urls]
@@ -25,11 +25,11 @@ Repository = "https://github.com/LeoVasanko/aegis-python"
[dependency-groups] [dependency-groups]
dev = [ dev = [
"auditwheel>=6.5.0", "auditwheel>=6.8.2",
"pytest>=8.4.2", "pytest>=9.1.1",
"ruff>=0.14.4", "ruff>=0.16.10",
"setuptools>=80.9.0", "setuptools>=84.0.0",
"setuptools-scm>=9.2.2", "setuptools-scm>=10.3.4",
] ]
[tool.setuptools] [tool.setuptools]
@@ -39,4 +39,16 @@ packages = ["aeg"]
[tool.setuptools.package-data] [tool.setuptools.package-data]
aeg = ["*.h"] 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] [tool.setuptools_scm]
+9 -9
View File
@@ -18,7 +18,7 @@ import hashlib
import io as io_module import io as io_module
import os import os
import threading 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 cipher as _cipher_for_name
from . import random as _random from . import random as _random
@@ -163,7 +163,7 @@ class FileStorage:
os.close(self._fd) os.close(self._fd)
self._closed = True self._closed = True
def __enter__(self) -> FileStorage: def __enter__(self) -> Self:
return self return self
def __exit__(self, *args) -> None: def __exit__(self, *args) -> None:
@@ -213,7 +213,7 @@ class BytesIOStorage:
"""Return the entire buffer contents.""" """Return the entire buffer contents."""
return bytes(self._data) return bytes(self._data)
def __enter__(self) -> BytesIOStorage: def __enter__(self) -> Self:
return self return self
def __exit__(self, *args) -> None: def __exit__(self, *args) -> None:
@@ -272,7 +272,7 @@ class StreamStorage:
if hasattr(self._stream, "flush"): if hasattr(self._stream, "flush"):
self._stream.flush() self._stream.flush()
def __enter__(self) -> StreamStorage: def __enter__(self) -> Self:
return self return self
def __exit__(self, *args) -> None: def __exit__(self, *args) -> None:
@@ -588,8 +588,8 @@ def create(
truncate: bool = False, truncate: bool = False,
merkle: bool | MerkleHasher = False, merkle: bool | MerkleHasher = False,
merkle_max_chunks: int = 16384, merkle_max_chunks: int = 16384,
rng: "_random.RandomSource | None" = None, rng: _random.RandomSource | None = None,
) -> "Raf": ) -> Raf:
"""Create a new encrypted file. """Create a new encrypted file.
Args: Args:
@@ -641,8 +641,8 @@ def open(
*, *,
merkle: bool | MerkleHasher = False, merkle: bool | MerkleHasher = False,
merkle_max_chunks: int = 16384, merkle_max_chunks: int = 16384,
rng: "_random.RandomSource | None" = None, rng: _random.RandomSource | None = None,
) -> "Raf": ) -> Raf:
"""Open an existing encrypted file. """Open an existing encrypted file.
The header is probed to size internal buffers and its alg_id is checked 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.""" """True if the file has been closed."""
return self._closed return self._closed
def __enter__(self) -> "Raf": def __enter__(self) -> Self:
return self return self
def __exit__(self, *args) -> None: def __exit__(self, *args) -> None:
+2 -1
View File
@@ -17,6 +17,7 @@ import ctypes
import mmap import mmap
import os import os
from contextlib import suppress from contextlib import suppress
from typing import Self
from .util import wipe from .util import wipe
@@ -104,7 +105,7 @@ class SecureBuffer:
def __len__(self) -> int: def __len__(self) -> int:
return len(self._mmap) return len(self._mmap)
def __enter__(self) -> "SecureBuffer": def __enter__(self) -> Self:
return self return self
def __exit__(self, *args) -> None: def __exit__(self, *args) -> None:
+1 -2
View File
@@ -12,8 +12,7 @@ import random
import pytest import pytest
from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, raf
from aeg import raf
from aeg.raf import BytesIOStorage, FileStorage from aeg.raf import BytesIOStorage, FileStorage
CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4] CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4]
+1 -2
View File
@@ -1,7 +1,6 @@
import pytest import pytest
from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, raf
from aeg import raf
from aeg import random as aeg_random from aeg import random as aeg_random
CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4] CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4]
+1 -2
View File
@@ -12,8 +12,7 @@ import hashlib
import pytest import pytest
from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4 from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, raf
from aeg import raf
from aeg.raf import ( from aeg.raf import (
BytesIOStorage, BytesIOStorage,
FileStorage, FileStorage,
+1
View File
@@ -252,6 +252,7 @@ class TestDecryptorFinalization:
# Encrypt empty message # Encrypt empty message
ct, tag = aegis256x4.encrypt_detached(key, nonce, b"") ct, tag = aegis256x4.encrypt_detached(key, nonce, b"")
del ct
# Decrypt without any updates # Decrypt without any updates
decryptor = aegis256x4.Decryptor(key, nonce) decryptor = aegis256x4.Decryptor(key, nonce)
-1
View File
@@ -1,6 +1,5 @@
import random import random
_rng = random.SystemRandom() _rng = random.SystemRandom()
+19 -17
View File
@@ -5,7 +5,6 @@ import pathlib
import re import re
import subprocess import subprocess
import sys import sys
from typing import Dict, Tuple
def preprocess_content(content: str) -> str: 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: if len(decl) <= max_width:
return decl return decl
if "(" in decl and ")" in decl: if (
if match := re.match(r"(.*?\s+\w+\s*)\((.*)\)(.*)", decl): "(" in decl
prefix, params, suffix = match.groups() and ")" in decl
if len(prefix) + len(params) + 2 > max_width: and (match := re.match(r"(.*?\s+\w+\s*)\((.*)\)(.*)", decl))
param_list = [p.strip() for p in params.split(",")] ):
if len(param_list) > 1: prefix, params, suffix = match.groups()
formatted_params = (",\n" + " " * (len(prefix) + 1)).join( if (
param_list len(prefix) + len(params) + 2 > max_width
) and len(param_list := [p.strip() for p in params.split(",")]) > 1
return f"{prefix}({formatted_params}){suffix}" ):
formatted_params = (",\n" + " " * (len(prefix) + 1)).join(param_list)
return f"{prefix}({formatted_params}){suffix}"
return decl return decl
@@ -108,7 +109,7 @@ def generate_cdef(include_dir: pathlib.Path) -> str:
def extract_constants( def extract_constants(
common_h_path: pathlib.Path, header_path: pathlib.Path, raf_h_path: pathlib.Path 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).""" """Extract constants from common.h (ALIGNMENT, RATE), main header (KEYBYTES, NPUBBYTES, ABYTES_*), and aegis_raf.h (RAF_ALG_ID)."""
constants = {} constants = {}
@@ -151,7 +152,7 @@ def extract_constants(
def extract_all_constants( def extract_all_constants(
libaegis_src_dir: pathlib.Path, include_dir: pathlib.Path libaegis_src_dir: pathlib.Path, include_dir: pathlib.Path
) -> Dict[str, Dict[str, int]]: ) -> dict[str, dict[str, int]]:
variants = [ variants = [
"aegis128l", "aegis128l",
"aegis128x2", "aegis128x2",
@@ -195,7 +196,7 @@ def algo_label(name: str) -> str:
return "AEGIS-" + name[5:].upper() 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.""" """Generate a variant module from the template with substituted constants."""
s = template_src.replace("aegis256x4", variant).replace( s = template_src.replace("aegis256x4", variant).replace(
"AEGIS-256X4", algo_label(variant) "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( def generate_python_modules(
template_path: pathlib.Path, template_path: pathlib.Path,
output_dir: pathlib.Path, output_dir: pathlib.Path,
constants: Dict[str, Dict[str, int]], constants: dict[str, dict[str, int]],
) -> Tuple[list[pathlib.Path], list[pathlib.Path]]: ) -> tuple[list[pathlib.Path], list[pathlib.Path]]:
if not template_path.exists(): if not template_path.exists():
raise FileNotFoundError(f"Template not found: {template_path}") raise FileNotFoundError(f"Template not found: {template_path}")
@@ -295,7 +296,7 @@ def generate_python_modules(
return updated, unchanged 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] labels = [algo_label(variant) for variant in constants]
literal_items = ", ".join(f'"{label}"' for label in labels) literal_items = ", ".join(f'"{label}"' for label in labels)
lines = [ lines = [
@@ -381,6 +382,7 @@ def main() -> int:
["uv", "run", "ruff", "format", str(pyaegis_dir)], ["uv", "run", "ruff", "format", str(pyaegis_dir)],
capture_output=True, capture_output=True,
text=True, text=True,
check=False,
) )
if result.returncode != 0: if result.returncode != 0:
print(f"Error formatting generated files: {result.stderr}", file=sys.stderr) print(f"Error formatting generated files: {result.stderr}", file=sys.stderr)
+1 -1
View File
@@ -77,7 +77,7 @@ def get_next_version(current_version):
def is_working_copy_clean(): def is_working_copy_clean():
"""Check if git working copy is clean.""" """Check if git working copy is clean."""
result = subprocess.run( 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() return result.returncode == 0 and not result.stdout.strip()