From 4fa5d55317729f553c46ef84be879a98696eef72 Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Thu, 15 Jan 2026 15:22:03 +0200 Subject: [PATCH] type: replace generator return type with iterator --- CONTRIBUTING.md | 1 + src/ahriman/application/handlers/versions.py | 6 +++--- src/ahriman/application/handlers/web.py | 4 ++-- src/ahriman/core/alpm/pacman.py | 8 ++++---- src/ahriman/core/alpm/pkgbuild_parser.py | 8 ++++---- src/ahriman/core/build_tools/sources.py | 4 ++-- src/ahriman/core/build_tools/task.py | 4 ++-- src/ahriman/core/configuration/shell_interpolator.py | 6 +++--- src/ahriman/core/configuration/shell_template.py | 4 ++-- .../core/database/operations/package_operations.py | 4 ++-- src/ahriman/core/formatters/validation_printer.py | 5 ++--- src/ahriman/core/gitremote/remote_push.py | 4 ++-- src/ahriman/core/log/lazy_logging.py | 4 ++-- src/ahriman/core/module_loader.py | 6 +++--- src/ahriman/core/repository/event_logger.py | 4 ++-- .../core/support/pkgbuild/pkgbuild_generator.py | 4 ++-- src/ahriman/core/triggers/trigger_loader.py | 4 ++-- src/ahriman/core/utils.py | 6 +++--- src/ahriman/models/package.py | 8 ++++---- src/ahriman/models/pkgbuild_patch.py | 4 ++-- src/ahriman/models/repository_paths.py | 10 +++++----- src/ahriman/web/routes.py | 4 ++-- 22 files changed, 56 insertions(+), 56 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ed8f87a..ecac0386 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -215,6 +215,7 @@ Again, the most checks can be performed by `tox` command, though some additional * It is allowed to change web API to add new fields or remove optional ones. However, in case of model changes, new API version must be introduced. * On the other hand, it is allowed to change method signatures, however, it is recommended to add new parameters as optional if possible. Deprecated API can be dropped during major release. * Enumerations (`Enum` classes) are allowed and recommended. However, it is recommended to use `StrEnum` class if there are from/to string conversions and `IntEnum` otherwise. +* `Generator` return type is not allowed. Generator functions must return generic `Iterator` object. Documentation should be described as `Yields`, however, because of pylint checks. Unfortunately, `Iterable` return type is not available for generators also, because of specific `contextlib.contextmanager` case. ### Other checks diff --git a/src/ahriman/application/handlers/versions.py b/src/ahriman/application/handlers/versions.py index 6b60606a..135f190f 100644 --- a/src/ahriman/application/handlers/versions.py +++ b/src/ahriman/application/handlers/versions.py @@ -21,7 +21,7 @@ import argparse import re import sys -from collections.abc import Generator +from collections.abc import Iterator from importlib import metadata from typing import ClassVar @@ -77,7 +77,7 @@ class Versions(Handler): return parser @staticmethod - def package_dependencies(root: str) -> Generator[tuple[str, str], None, None]: + def package_dependencies(root: str) -> Iterator[tuple[str, str]]: """ extract list of ahriman package dependencies installed into system with their versions @@ -87,7 +87,7 @@ class Versions(Handler): Yields: tuple[str, str]: map of installed dependency to its version """ - def dependencies_by_key(key: str) -> Generator[str, None, None]: + def dependencies_by_key(key: str) -> Iterator[str]: # in importlib it returns requires in the following format # ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] try: diff --git a/src/ahriman/application/handlers/web.py b/src/ahriman/application/handlers/web.py index 21863158..7a0482da 100644 --- a/src/ahriman/application/handlers/web.py +++ b/src/ahriman/application/handlers/web.py @@ -19,7 +19,7 @@ # import argparse -from collections.abc import Generator +from collections.abc import Iterator from pathlib import Path from ahriman.application.handlers.handler import Handler, SubParserAction @@ -86,7 +86,7 @@ class Web(Handler): return parser @staticmethod - def extract_arguments(args: argparse.Namespace, configuration: Configuration) -> Generator[str, None, None]: + def extract_arguments(args: argparse.Namespace, configuration: Configuration) -> Iterator[str]: """ extract list of arguments used for current command, except for command specific ones diff --git a/src/ahriman/core/alpm/pacman.py b/src/ahriman/core/alpm/pacman.py index 4fbbd8a5..acf2ec77 100644 --- a/src/ahriman/core/alpm/pacman.py +++ b/src/ahriman/core/alpm/pacman.py @@ -21,7 +21,7 @@ import itertools import shutil import tarfile -from collections.abc import Generator, Iterable +from collections.abc import Iterable, Iterator from functools import cached_property from pathlib import Path from pyalpm import DB, Handle, Package, SIG_DATABASE_OPTIONAL, SIG_PACKAGE_OPTIONAL # type: ignore[import-not-found] @@ -188,7 +188,7 @@ class Pacman(LazyLogging): Returns: dict[str, set[str]]: map of package name to its list of files """ - def extract(tar: tarfile.TarFile, versions: dict[str, str]) -> Generator[tuple[str, set[str]], None, None]: + def extract(tar: tarfile.TarFile, versions: dict[str, str]) -> Iterator[tuple[str, set[str]]]: for package_name, version in versions.items(): path = Path(f"{package_name}-{version}") / "files" try: @@ -223,7 +223,7 @@ class Pacman(LazyLogging): return result - def package(self, package_name: str) -> Generator[Package, None, None]: + def package(self, package_name: str) -> Iterator[Package]: """ retrieve list of the packages from the repository by name @@ -256,7 +256,7 @@ class Pacman(LazyLogging): return result - def provided_by(self, package_name: str) -> Generator[Package, None, None]: + def provided_by(self, package_name: str) -> Iterator[Package]: """ search through databases and emit packages which provides the ``package_name`` diff --git a/src/ahriman/core/alpm/pkgbuild_parser.py b/src/ahriman/core/alpm/pkgbuild_parser.py index b5b3e157..ded0ae69 100644 --- a/src/ahriman/core/alpm/pkgbuild_parser.py +++ b/src/ahriman/core/alpm/pkgbuild_parser.py @@ -21,7 +21,7 @@ import itertools import re import shlex -from collections.abc import Generator +from collections.abc import Iterator from enum import StrEnum from typing import IO @@ -209,7 +209,7 @@ class PkgbuildParser(shlex.shlex): Raises: PkgbuildParserError: if array is not closed """ - def extract() -> Generator[str, None, None]: + def extract() -> Iterator[str]: while token := self.get_token(): match token: case _ if self._is_escaped(): @@ -276,7 +276,7 @@ class PkgbuildParser(shlex.shlex): return content - def _parse_token(self, token: str) -> Generator[PkgbuildPatch, None, None]: + def _parse_token(self, token: str) -> Iterator[PkgbuildPatch]: """ parse single token to the PKGBUILD field @@ -360,7 +360,7 @@ class PkgbuildParser(shlex.shlex): raise PkgbuildParserError("reached starting position, no valid symbols found") - def parse(self) -> Generator[PkgbuildPatch, None, None]: + def parse(self) -> Iterator[PkgbuildPatch]: """ parse source stream and yield parsed entries diff --git a/src/ahriman/core/build_tools/sources.py b/src/ahriman/core/build_tools/sources.py index 7bba7ff7..8808497d 100644 --- a/src/ahriman/core/build_tools/sources.py +++ b/src/ahriman/core/build_tools/sources.py @@ -19,7 +19,7 @@ # import shutil -from collections.abc import Generator +from collections.abc import Iterator from pathlib import Path from typing import ClassVar @@ -347,7 +347,7 @@ class Sources(LazyLogging): """ gitconfig = gitconfig or {} - def configuration_flags() -> Generator[str, None, None]: + def configuration_flags() -> Iterator[str]: for option, value in (self.GITCONFIG | gitconfig).items(): yield "-c" yield f"{option}=\"{value}\"" diff --git a/src/ahriman/core/build_tools/task.py b/src/ahriman/core/build_tools/task.py index 2809635b..b6f8d084 100644 --- a/src/ahriman/core/build_tools/task.py +++ b/src/ahriman/core/build_tools/task.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from collections.abc import Generator +from collections.abc import Iterator from pathlib import Path from ahriman.core.build_tools.sources import Sources @@ -77,7 +77,7 @@ class Task(LazyLogging): Returns: list[Path]: list of file paths which looks like freshly generated archives """ - def files() -> Generator[Path, None, None]: + def files() -> Iterator[Path]: for filepath in sources_dir.iterdir(): if filepath in source_files: continue # skip files which were already there diff --git a/src/ahriman/core/configuration/shell_interpolator.py b/src/ahriman/core/configuration/shell_interpolator.py index 5d99c4c0..df43c7b6 100644 --- a/src/ahriman/core/configuration/shell_interpolator.py +++ b/src/ahriman/core/configuration/shell_interpolator.py @@ -21,7 +21,7 @@ import configparser import os import sys -from collections.abc import Generator, Mapping, MutableMapping +from collections.abc import Iterator, Mapping, MutableMapping from string import Template from typing import Any, ClassVar @@ -37,7 +37,7 @@ class ShellInterpolator(configparser.Interpolation): @staticmethod def _extract_variables(parser: MutableMapping[str, Mapping[str, str]], value: str, - defaults: Mapping[str, str]) -> Generator[tuple[str, str], None, None]: + defaults: Mapping[str, str]) -> Iterator[tuple[str, str]]: """ extract keys and values (if available) from the configuration. In case if a key is not available, it will be silently skipped from the result @@ -50,7 +50,7 @@ class ShellInterpolator(configparser.Interpolation): Yields: tuple[str, str]: variable name used for substitution and its value """ - def identifiers() -> Generator[tuple[str | None, str], None, None]: + def identifiers() -> Iterator[tuple[str | None, str]]: # extract all found identifiers and parse them for identifier in ShellTemplate(value).get_identifiers(): match identifier.rsplit(":", maxsplit=1): diff --git a/src/ahriman/core/configuration/shell_template.py b/src/ahriman/core/configuration/shell_template.py index e2f82ea7..12f6e6e6 100644 --- a/src/ahriman/core/configuration/shell_template.py +++ b/src/ahriman/core/configuration/shell_template.py @@ -20,7 +20,7 @@ import fnmatch import re -from collections.abc import Generator, Mapping +from collections.abc import Iterator, Mapping from string import Template @@ -132,7 +132,7 @@ class ShellTemplate(Template): (self._REPLACE, self._replace, "/"), ) - def generator(variables: dict[str, str]) -> Generator[tuple[str, str], None, None]: + def generator(variables: dict[str, str]) -> Iterator[tuple[str, str]]: for identifier in self.get_identifiers(): for regex, function, greediness in substitutions: if m := regex.match(identifier): diff --git a/src/ahriman/core/database/operations/package_operations.py b/src/ahriman/core/database/operations/package_operations.py index bb004e51..3b806eb2 100644 --- a/src/ahriman/core/database/operations/package_operations.py +++ b/src/ahriman/core/database/operations/package_operations.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from collections.abc import Generator, Iterable +from collections.abc import Iterable, Iterator from sqlite3 import Connection from ahriman.core.database.operations.operations import Operations @@ -263,7 +263,7 @@ class PackageOperations(Operations): """ repository_id = repository_id or self._repository_id - def run(connection: Connection) -> Generator[tuple[Package, BuildStatus], None, None]: + def run(connection: Connection) -> Iterator[tuple[Package, BuildStatus]]: packages = self._packages_get_select_package_bases(connection, repository_id) statuses = self._packages_get_select_statuses(connection, repository_id) per_package_base = self._packages_get_select_packages(connection, packages, repository_id) diff --git a/src/ahriman/core/formatters/validation_printer.py b/src/ahriman/core/formatters/validation_printer.py index bd17a62d..ce23a859 100644 --- a/src/ahriman/core/formatters/validation_printer.py +++ b/src/ahriman/core/formatters/validation_printer.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from collections.abc import Generator +from collections.abc import Iterator from typing import Any from ahriman.core.formatters.string_printer import StringPrinter @@ -44,8 +44,7 @@ class ValidationPrinter(StringPrinter): self.errors = errors @staticmethod - def get_error_messages(node: str, errors: list[str | dict[str, Any]], - current_level: int = 1) -> Generator[Property, None, None]: + def get_error_messages(node: str, errors: list[str | dict[str, Any]], current_level: int = 1) -> Iterator[Property]: """ extract default error message from cerberus class diff --git a/src/ahriman/core/gitremote/remote_push.py b/src/ahriman/core/gitremote/remote_push.py index 60c84338..90a70240 100644 --- a/src/ahriman/core/gitremote/remote_push.py +++ b/src/ahriman/core/gitremote/remote_push.py @@ -19,7 +19,7 @@ # import shutil -from collections.abc import Generator +from collections.abc import Iterator from pathlib import Path from tempfile import TemporaryDirectory @@ -96,7 +96,7 @@ class RemotePush(LazyLogging): # ...and finally return path to the copied directory return package.base - def packages_update(self, result: Result, target_dir: Path) -> Generator[str, None, None]: + def packages_update(self, result: Result, target_dir: Path) -> Iterator[str]: """ update all packages from the build result diff --git a/src/ahriman/core/log/lazy_logging.py b/src/ahriman/core/log/lazy_logging.py index 1fe86e31..2dc930cc 100644 --- a/src/ahriman/core/log/lazy_logging.py +++ b/src/ahriman/core/log/lazy_logging.py @@ -20,7 +20,7 @@ import contextlib import logging -from collections.abc import Generator +from collections.abc import Iterator from functools import cached_property from typing import Any @@ -80,7 +80,7 @@ class LazyLogging: logging.setLogRecordFactory(package_record_factory) @contextlib.contextmanager - def in_package_context(self, package_base: str, version: str | None) -> Generator[None, None, None]: + def in_package_context(self, package_base: str, version: str | None) -> Iterator[None]: """ execute function while setting package context diff --git a/src/ahriman/core/module_loader.py b/src/ahriman/core/module_loader.py index 20257cc3..e45aee62 100644 --- a/src/ahriman/core/module_loader.py +++ b/src/ahriman/core/module_loader.py @@ -19,7 +19,7 @@ # import inspect -from collections.abc import Generator +from collections.abc import Iterator from importlib import import_module from pathlib import Path from pkgutil import ModuleInfo, walk_packages @@ -33,7 +33,7 @@ __all__ = ["implementations"] T = TypeVar("T") -def _modules(module_root: Path, prefix: str) -> Generator[ModuleInfo, None, None]: +def _modules(module_root: Path, prefix: str) -> Iterator[ModuleInfo]: """ extract available modules from package @@ -52,7 +52,7 @@ def _modules(module_root: Path, prefix: str) -> Generator[ModuleInfo, None, None yield module_info -def implementations(root_module: ModuleType, base_class: type[T]) -> Generator[type[T], None, None]: +def implementations(root_module: ModuleType, base_class: type[T]) -> Iterator[type[T]]: """ extract implementations of the ``base_class`` from the module diff --git a/src/ahriman/core/repository/event_logger.py b/src/ahriman/core/repository/event_logger.py index b1d38562..889a10dc 100644 --- a/src/ahriman/core/repository/event_logger.py +++ b/src/ahriman/core/repository/event_logger.py @@ -19,7 +19,7 @@ # import contextlib -from typing import Generator +from collections.abc import Iterator from ahriman.core.status import Client from ahriman.models.event import Event, EventType @@ -55,7 +55,7 @@ class EventLogger: @contextlib.contextmanager def in_event(self, package_base: str, event: EventType, message: str | None = None, - failure: EventType | None = None) -> Generator[None, None, None]: + failure: EventType | None = None) -> Iterator[None]: """ perform action in package context and log event with time elapsed diff --git a/src/ahriman/core/support/pkgbuild/pkgbuild_generator.py b/src/ahriman/core/support/pkgbuild/pkgbuild_generator.py index b673994f..21e60841 100644 --- a/src/ahriman/core/support/pkgbuild/pkgbuild_generator.py +++ b/src/ahriman/core/support/pkgbuild/pkgbuild_generator.py @@ -20,7 +20,7 @@ import hashlib import itertools -from collections.abc import Callable, Generator +from collections.abc import Callable, Iterator from pathlib import Path from typing import ClassVar @@ -187,7 +187,7 @@ class PkgbuildGenerator: Returns: list[PkgbuildPatch]: list of patches to be applied to the PKGBUILD """ - def sources_generator() -> Generator[tuple[str, str], None, None]: + def sources_generator() -> Iterator[tuple[str, str]]: for source, generator in sorted(self.sources().items()): source_path = source_dir / source generator(source_path) diff --git a/src/ahriman/core/triggers/trigger_loader.py b/src/ahriman/core/triggers/trigger_loader.py index f9ac92cd..2ae9fecf 100644 --- a/src/ahriman/core/triggers/trigger_loader.py +++ b/src/ahriman/core/triggers/trigger_loader.py @@ -21,7 +21,7 @@ import atexit import contextlib import os -from collections.abc import Generator +from collections.abc import Iterator from importlib import import_module, machinery from pathlib import Path from types import ModuleType @@ -112,7 +112,7 @@ class TriggerLoader(LazyLogging): return configuration.getlist("build", "triggers", fallback=[]) @contextlib.contextmanager - def __execute_trigger(self, trigger: Trigger) -> Generator[None, None, None]: + def __execute_trigger(self, trigger: Trigger) -> Iterator[None]: """ decorator for calling triggers diff --git a/src/ahriman/core/utils.py b/src/ahriman/core/utils.py index eacf0dff..7e4e2e1d 100644 --- a/src/ahriman/core/utils.py +++ b/src/ahriman/core/utils.py @@ -27,7 +27,7 @@ import re import selectors import subprocess -from collections.abc import Callable, Generator, Iterable, Mapping +from collections.abc import Callable, Iterable, Iterator, Mapping from dataclasses import asdict from enum import Enum from pathlib import Path @@ -113,7 +113,7 @@ def check_output(*args: str, exception: Exception | Callable[[int, list[str], st return channel if channel is not None else io.StringIO() # wrapper around selectors polling - def poll(sel: selectors.BaseSelector) -> Generator[tuple[str, str], None, None]: + def poll(sel: selectors.BaseSelector) -> Iterator[tuple[str, str]]: for key, _ in sel.select(): # we don't need to check mask here because we have only subscribed on reading line = key.fileobj.readline() # type: ignore[union-attr] if not line: # in case of empty line we remove selector as there is no data here anymore @@ -499,7 +499,7 @@ def utcnow() -> datetime.datetime: return datetime.datetime.now(datetime.UTC) -def walk(directory_path: Path) -> Generator[Path, None, None]: +def walk(directory_path: Path) -> Iterator[Path]: """ list all file paths in given directory diff --git a/src/ahriman/models/package.py b/src/ahriman/models/package.py index 14b65c7d..ed7969fc 100644 --- a/src/ahriman/models/package.py +++ b/src/ahriman/models/package.py @@ -22,7 +22,7 @@ from __future__ import annotations import copy -from collections.abc import Callable, Generator, Iterable +from collections.abc import Callable, Iterable, Iterator from dataclasses import dataclass from pathlib import Path from pyalpm import vercmp # type: ignore[import-not-found] @@ -346,7 +346,7 @@ class Package(LazyLogging): ) @staticmethod - def local_files(path: Path) -> Generator[Path, None, None]: + def local_files(path: Path) -> Iterator[Path]: """ extract list of local files @@ -407,7 +407,7 @@ class Package(LazyLogging): Returns: list[str]: combined list of unique entries in properties list """ - def generator() -> Generator[str, None, None]: + def generator() -> Iterator[str]: for package in self.packages.values(): yield from extractor(package) @@ -570,7 +570,7 @@ class Package(LazyLogging): """ return dataclass_view(self) - def with_packages(self, packages: list[Path], pacman: Pacman) -> None: + def with_packages(self, packages: Iterable[Path], pacman: Pacman) -> None: """ replace packages descriptions with ones from archives diff --git a/src/ahriman/models/pkgbuild_patch.py b/src/ahriman/models/pkgbuild_patch.py index 3582f71b..b726d684 100644 --- a/src/ahriman/models/pkgbuild_patch.py +++ b/src/ahriman/models/pkgbuild_patch.py @@ -22,7 +22,7 @@ import shlex from dataclasses import dataclass, fields from pathlib import Path -from typing import Any, Generator, Self +from typing import Any, Iterator, Self from ahriman.core.configuration.shell_template import ShellTemplate from ahriman.core.utils import dataclass_view, filter_json @@ -166,7 +166,7 @@ class PkgbuildPatch: ValueError: if no closing quotation """ - def generator() -> Generator[str, None, None]: + def generator() -> Iterator[str]: token = None for char in source: if token is not None: diff --git a/src/ahriman/models/repository_paths.py b/src/ahriman/models/repository_paths.py index a9ff1e0a..e3cec7a9 100644 --- a/src/ahriman/models/repository_paths.py +++ b/src/ahriman/models/repository_paths.py @@ -21,7 +21,7 @@ import contextlib import os import shutil -from collections.abc import Generator +from collections.abc import Iterator from dataclasses import dataclass, field from functools import cached_property from pathlib import Path @@ -170,7 +170,7 @@ class RepositoryPaths(LazyLogging): Returns: set[str]: list of repository architectures for which there is created tree """ - def walk(repository_dir: Path) -> Generator[str, None, None]: + def walk(repository_dir: Path) -> Iterator[str]: for architecture in filter(lambda path: path.is_dir(), repository_dir.iterdir()): yield architecture.name @@ -197,7 +197,7 @@ class RepositoryPaths(LazyLogging): is loaded in legacy mode """ # simply walk through the root. In case if there are subdirectories, emit the name - def walk(paths: RepositoryPaths) -> Generator[str, None, None]: + def walk(paths: RepositoryPaths) -> Iterator[str]: for repository in filter(lambda path: path.is_dir(), paths._repository_root.iterdir()): if any(path.is_dir() for path in repository.iterdir()): yield repository.name @@ -262,7 +262,7 @@ class RepositoryPaths(LazyLogging): return self.cache / package_base @contextlib.contextmanager - def preserve_owner(self, path: Path | None = None) -> Generator[None, None, None]: + def preserve_owner(self, path: Path | None = None) -> Iterator[None]: """ perform any action preserving owner for any newly created file or directory @@ -281,7 +281,7 @@ class RepositoryPaths(LazyLogging): """ path = path or self.root - def walk(root: Path) -> Generator[Path, None, None]: + def walk(root: Path) -> Iterator[Path]: # basically walk, but skipping some content for child in root.iterdir(): yield child diff --git a/src/ahriman/web/routes.py b/src/ahriman/web/routes.py index dc3f8de9..c4915493 100644 --- a/src/ahriman/web/routes.py +++ b/src/ahriman/web/routes.py @@ -20,7 +20,7 @@ import re from aiohttp.web import Application, View -from collections.abc import Generator +from collections.abc import Iterator import ahriman.web.views @@ -32,7 +32,7 @@ from ahriman.web.views.base import BaseView __all__ = ["setup_routes"] -def _dynamic_routes(configuration: Configuration) -> Generator[tuple[str, type[View]], None, None]: +def _dynamic_routes(configuration: Configuration) -> Iterator[tuple[str, type[View]]]: """ extract dynamic routes based on views