type: replace generator return type with iterator

This commit is contained in:
2026-01-15 15:22:03 +02:00
parent a7fa3b90e4
commit 4fa5d55317
22 changed files with 56 additions and 56 deletions

View File

@ -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. * 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. * 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. * 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 ### Other checks

View File

@ -21,7 +21,7 @@ import argparse
import re import re
import sys import sys
from collections.abc import Generator from collections.abc import Iterator
from importlib import metadata from importlib import metadata
from typing import ClassVar from typing import ClassVar
@ -77,7 +77,7 @@ class Versions(Handler):
return parser return parser
@staticmethod @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 extract list of ahriman package dependencies installed into system with their versions
@ -87,7 +87,7 @@ class Versions(Handler):
Yields: Yields:
tuple[str, str]: map of installed dependency to its version 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 # in importlib it returns requires in the following format
# ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] # ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
try: try:

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from collections.abc import Generator from collections.abc import Iterator
from pathlib import Path from pathlib import Path
from ahriman.application.handlers.handler import Handler, SubParserAction from ahriman.application.handlers.handler import Handler, SubParserAction
@ -86,7 +86,7 @@ class Web(Handler):
return parser return parser
@staticmethod @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 extract list of arguments used for current command, except for command specific ones

View File

@ -21,7 +21,7 @@ import itertools
import shutil import shutil
import tarfile import tarfile
from collections.abc import Generator, Iterable from collections.abc import Iterable, Iterator
from functools import cached_property from functools import cached_property
from pathlib import Path from pathlib import Path
from pyalpm import DB, Handle, Package, SIG_DATABASE_OPTIONAL, SIG_PACKAGE_OPTIONAL # type: ignore[import-not-found] 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: Returns:
dict[str, set[str]]: map of package name to its list of files 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(): for package_name, version in versions.items():
path = Path(f"{package_name}-{version}") / "files" path = Path(f"{package_name}-{version}") / "files"
try: try:
@ -223,7 +223,7 @@ class Pacman(LazyLogging):
return result 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 retrieve list of the packages from the repository by name
@ -256,7 +256,7 @@ class Pacman(LazyLogging):
return result 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`` search through databases and emit packages which provides the ``package_name``

View File

@ -21,7 +21,7 @@ import itertools
import re import re
import shlex import shlex
from collections.abc import Generator from collections.abc import Iterator
from enum import StrEnum from enum import StrEnum
from typing import IO from typing import IO
@ -209,7 +209,7 @@ class PkgbuildParser(shlex.shlex):
Raises: Raises:
PkgbuildParserError: if array is not closed PkgbuildParserError: if array is not closed
""" """
def extract() -> Generator[str, None, None]: def extract() -> Iterator[str]:
while token := self.get_token(): while token := self.get_token():
match token: match token:
case _ if self._is_escaped(): case _ if self._is_escaped():
@ -276,7 +276,7 @@ class PkgbuildParser(shlex.shlex):
return content 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 parse single token to the PKGBUILD field
@ -360,7 +360,7 @@ class PkgbuildParser(shlex.shlex):
raise PkgbuildParserError("reached starting position, no valid symbols found") 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 parse source stream and yield parsed entries

View File

@ -19,7 +19,7 @@
# #
import shutil import shutil
from collections.abc import Generator from collections.abc import Iterator
from pathlib import Path from pathlib import Path
from typing import ClassVar from typing import ClassVar
@ -347,7 +347,7 @@ class Sources(LazyLogging):
""" """
gitconfig = gitconfig or {} gitconfig = gitconfig or {}
def configuration_flags() -> Generator[str, None, None]: def configuration_flags() -> Iterator[str]:
for option, value in (self.GITCONFIG | gitconfig).items(): for option, value in (self.GITCONFIG | gitconfig).items():
yield "-c" yield "-c"
yield f"{option}=\"{value}\"" yield f"{option}=\"{value}\""

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from collections.abc import Generator from collections.abc import Iterator
from pathlib import Path from pathlib import Path
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources
@ -77,7 +77,7 @@ class Task(LazyLogging):
Returns: Returns:
list[Path]: list of file paths which looks like freshly generated archives 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(): for filepath in sources_dir.iterdir():
if filepath in source_files: if filepath in source_files:
continue # skip files which were already there continue # skip files which were already there

View File

@ -21,7 +21,7 @@ import configparser
import os import os
import sys import sys
from collections.abc import Generator, Mapping, MutableMapping from collections.abc import Iterator, Mapping, MutableMapping
from string import Template from string import Template
from typing import Any, ClassVar from typing import Any, ClassVar
@ -37,7 +37,7 @@ class ShellInterpolator(configparser.Interpolation):
@staticmethod @staticmethod
def _extract_variables(parser: MutableMapping[str, Mapping[str, str]], value: str, 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 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 silently skipped from the result
@ -50,7 +50,7 @@ class ShellInterpolator(configparser.Interpolation):
Yields: Yields:
tuple[str, str]: variable name used for substitution and its value 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 # extract all found identifiers and parse them
for identifier in ShellTemplate(value).get_identifiers(): for identifier in ShellTemplate(value).get_identifiers():
match identifier.rsplit(":", maxsplit=1): match identifier.rsplit(":", maxsplit=1):

View File

@ -20,7 +20,7 @@
import fnmatch import fnmatch
import re import re
from collections.abc import Generator, Mapping from collections.abc import Iterator, Mapping
from string import Template from string import Template
@ -132,7 +132,7 @@ class ShellTemplate(Template):
(self._REPLACE, self._replace, "/"), (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 identifier in self.get_identifiers():
for regex, function, greediness in substitutions: for regex, function, greediness in substitutions:
if m := regex.match(identifier): if m := regex.match(identifier):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from collections.abc import Generator, Iterable from collections.abc import Iterable, Iterator
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations.operations import Operations
@ -263,7 +263,7 @@ class PackageOperations(Operations):
""" """
repository_id = repository_id or self._repository_id 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) packages = self._packages_get_select_package_bases(connection, repository_id)
statuses = self._packages_get_select_statuses(connection, repository_id) statuses = self._packages_get_select_statuses(connection, repository_id)
per_package_base = self._packages_get_select_packages(connection, packages, repository_id) per_package_base = self._packages_get_select_packages(connection, packages, repository_id)

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from collections.abc import Generator from collections.abc import Iterator
from typing import Any from typing import Any
from ahriman.core.formatters.string_printer import StringPrinter from ahriman.core.formatters.string_printer import StringPrinter
@ -44,8 +44,7 @@ class ValidationPrinter(StringPrinter):
self.errors = errors self.errors = errors
@staticmethod @staticmethod
def get_error_messages(node: str, errors: list[str | dict[str, Any]], def get_error_messages(node: str, errors: list[str | dict[str, Any]], current_level: int = 1) -> Iterator[Property]:
current_level: int = 1) -> Generator[Property, None, None]:
""" """
extract default error message from cerberus class extract default error message from cerberus class

View File

@ -19,7 +19,7 @@
# #
import shutil import shutil
from collections.abc import Generator from collections.abc import Iterator
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
@ -96,7 +96,7 @@ class RemotePush(LazyLogging):
# ...and finally return path to the copied directory # ...and finally return path to the copied directory
return package.base 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 update all packages from the build result

View File

@ -20,7 +20,7 @@
import contextlib import contextlib
import logging import logging
from collections.abc import Generator from collections.abc import Iterator
from functools import cached_property from functools import cached_property
from typing import Any from typing import Any
@ -80,7 +80,7 @@ class LazyLogging:
logging.setLogRecordFactory(package_record_factory) logging.setLogRecordFactory(package_record_factory)
@contextlib.contextmanager @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 execute function while setting package context

View File

@ -19,7 +19,7 @@
# #
import inspect import inspect
from collections.abc import Generator from collections.abc import Iterator
from importlib import import_module from importlib import import_module
from pathlib import Path from pathlib import Path
from pkgutil import ModuleInfo, walk_packages from pkgutil import ModuleInfo, walk_packages
@ -33,7 +33,7 @@ __all__ = ["implementations"]
T = TypeVar("T") 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 extract available modules from package
@ -52,7 +52,7 @@ def _modules(module_root: Path, prefix: str) -> Generator[ModuleInfo, None, None
yield module_info 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 extract implementations of the ``base_class`` from the module

View File

@ -19,7 +19,7 @@
# #
import contextlib import contextlib
from typing import Generator from collections.abc import Iterator
from ahriman.core.status import Client from ahriman.core.status import Client
from ahriman.models.event import Event, EventType from ahriman.models.event import Event, EventType
@ -55,7 +55,7 @@ class EventLogger:
@contextlib.contextmanager @contextlib.contextmanager
def in_event(self, package_base: str, event: EventType, message: str | None = None, 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 perform action in package context and log event with time elapsed

View File

@ -20,7 +20,7 @@
import hashlib import hashlib
import itertools import itertools
from collections.abc import Callable, Generator from collections.abc import Callable, Iterator
from pathlib import Path from pathlib import Path
from typing import ClassVar from typing import ClassVar
@ -187,7 +187,7 @@ class PkgbuildGenerator:
Returns: Returns:
list[PkgbuildPatch]: list of patches to be applied to the PKGBUILD 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()): for source, generator in sorted(self.sources().items()):
source_path = source_dir / source source_path = source_dir / source
generator(source_path) generator(source_path)

View File

@ -21,7 +21,7 @@ import atexit
import contextlib import contextlib
import os import os
from collections.abc import Generator from collections.abc import Iterator
from importlib import import_module, machinery from importlib import import_module, machinery
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
@ -112,7 +112,7 @@ class TriggerLoader(LazyLogging):
return configuration.getlist("build", "triggers", fallback=[]) return configuration.getlist("build", "triggers", fallback=[])
@contextlib.contextmanager @contextlib.contextmanager
def __execute_trigger(self, trigger: Trigger) -> Generator[None, None, None]: def __execute_trigger(self, trigger: Trigger) -> Iterator[None]:
""" """
decorator for calling triggers decorator for calling triggers

View File

@ -27,7 +27,7 @@ import re
import selectors import selectors
import subprocess import subprocess
from collections.abc import Callable, Generator, Iterable, Mapping from collections.abc import Callable, Iterable, Iterator, Mapping
from dataclasses import asdict from dataclasses import asdict
from enum import Enum from enum import Enum
from pathlib import Path 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() return channel if channel is not None else io.StringIO()
# wrapper around selectors polling # 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 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] 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 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) 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 list all file paths in given directory

View File

@ -22,7 +22,7 @@ from __future__ import annotations
import copy import copy
from collections.abc import Callable, Generator, Iterable from collections.abc import Callable, Iterable, Iterator
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from pyalpm import vercmp # type: ignore[import-not-found] from pyalpm import vercmp # type: ignore[import-not-found]
@ -346,7 +346,7 @@ class Package(LazyLogging):
) )
@staticmethod @staticmethod
def local_files(path: Path) -> Generator[Path, None, None]: def local_files(path: Path) -> Iterator[Path]:
""" """
extract list of local files extract list of local files
@ -407,7 +407,7 @@ class Package(LazyLogging):
Returns: Returns:
list[str]: combined list of unique entries in properties list 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(): for package in self.packages.values():
yield from extractor(package) yield from extractor(package)
@ -570,7 +570,7 @@ class Package(LazyLogging):
""" """
return dataclass_view(self) 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 replace packages descriptions with ones from archives

View File

@ -22,7 +22,7 @@ import shlex
from dataclasses import dataclass, fields from dataclasses import dataclass, fields
from pathlib import Path 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.configuration.shell_template import ShellTemplate
from ahriman.core.utils import dataclass_view, filter_json from ahriman.core.utils import dataclass_view, filter_json
@ -166,7 +166,7 @@ class PkgbuildPatch:
ValueError: if no closing quotation ValueError: if no closing quotation
""" """
def generator() -> Generator[str, None, None]: def generator() -> Iterator[str]:
token = None token = None
for char in source: for char in source:
if token is not None: if token is not None:

View File

@ -21,7 +21,7 @@ import contextlib
import os import os
import shutil import shutil
from collections.abc import Generator from collections.abc import Iterator
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import cached_property from functools import cached_property
from pathlib import Path from pathlib import Path
@ -170,7 +170,7 @@ class RepositoryPaths(LazyLogging):
Returns: Returns:
set[str]: list of repository architectures for which there is created tree 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()): for architecture in filter(lambda path: path.is_dir(), repository_dir.iterdir()):
yield architecture.name yield architecture.name
@ -197,7 +197,7 @@ class RepositoryPaths(LazyLogging):
is loaded in legacy mode is loaded in legacy mode
""" """
# simply walk through the root. In case if there are subdirectories, emit the name # 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()): for repository in filter(lambda path: path.is_dir(), paths._repository_root.iterdir()):
if any(path.is_dir() for path in repository.iterdir()): if any(path.is_dir() for path in repository.iterdir()):
yield repository.name yield repository.name
@ -262,7 +262,7 @@ class RepositoryPaths(LazyLogging):
return self.cache / package_base return self.cache / package_base
@contextlib.contextmanager @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 perform any action preserving owner for any newly created file or directory
@ -281,7 +281,7 @@ class RepositoryPaths(LazyLogging):
""" """
path = path or self.root 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 # basically walk, but skipping some content
for child in root.iterdir(): for child in root.iterdir():
yield child yield child

View File

@ -20,7 +20,7 @@
import re import re
from aiohttp.web import Application, View from aiohttp.web import Application, View
from collections.abc import Generator from collections.abc import Iterator
import ahriman.web.views import ahriman.web.views
@ -32,7 +32,7 @@ from ahriman.web.views.base import BaseView
__all__ = ["setup_routes"] __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 extract dynamic routes based on views