PEP-585 complaint: remove type aliases (#93)

This commit is contained in:
Evgenii Alekseev 2023-04-12 04:09:28 +03:00 committed by GitHub
parent 54d36ccf36
commit abf29083c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
137 changed files with 807 additions and 907 deletions

View File

@ -91,6 +91,7 @@ Again, the most checks can be performed by `make check` command, though some add
``` ```
* Type annotations are the must, even for local functions. * Type annotations are the must, even for local functions.
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`).
* Recommended order of function definitions in class: * Recommended order of function definitions in class:
```python ```python
@ -102,7 +103,7 @@ Again, the most checks can be performed by `make check` command, though some add
def property(self) -> Any: ... def property(self) -> Any: ...
@classmethod @classmethod
def class_method(cls: Type[Clazz]) -> Clazz: ... def class_method(cls: type[Clazz]) -> Clazz: ...
@staticmethod @staticmethod
def static_method() -> Any: ... def static_method() -> Any: ...

View File

@ -121,7 +121,7 @@ Type conversions
By default, it parses rows into python dictionary. In addition, the following pseudo-types are supported: By default, it parses rows into python dictionary. In addition, the following pseudo-types are supported:
* ``Dict[str, Any]``, ``List[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` data type and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively. * ``dict[str, Any]``, ``list[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` data type and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
Basic flows Basic flows
----------- -----------
@ -235,7 +235,7 @@ Triggers are extensions which can be used in order to perform any actions on app
The main idea is to load classes by their full path (e.g. ``ahriman.core.upload.UploadTrigger``) by using ``importlib``: get the last part of the import and treat it as class name, join remain part by ``.`` and interpret as module path, import module and extract attribute from it. The main idea is to load classes by their full path (e.g. ``ahriman.core.upload.UploadTrigger``) by using ``importlib``: get the last part of the import and treat it as class name, join remain part by ``.`` and interpret as module path, import module and extract attribute from it.
The loaded triggers will be called with ``ahriman.models.result.Result`` and ``List[Packages]`` arguments, which describes the process result and current repository packages respectively. Any exception raised will be suppressed and will generate an exception message in logs. The loaded triggers will be called with ``ahriman.models.result.Result`` and ``list[Packages]`` arguments, which describes the process result and current repository packages respectively. Any exception raised will be suppressed and will generate an exception message in logs.
In addition triggers can implement ``on_start`` and ``on_stop`` actions which will be called on the application start and right before the application exit. The ``on_start`` action is usually being called from handlers directly in order to make sure that no trigger will be run when it is not required (e.g. on user management). As soon as ``on_start`` action is called, the additional flag will be set; ``ahriman.core.triggers.TriggerLoader`` class implements ``__del__`` method in which, if the flag is set, the ``on_stop`` actions will be called. In addition triggers can implement ``on_start`` and ``on_stop`` actions which will be called on the application start and right before the application exit. The ``on_start`` action is usually being called from handlers directly in order to make sure that no trigger will be run when it is not required (e.g. on user management). As soon as ``on_start`` action is called, the additional flag will be set; ``ahriman.core.triggers.TriggerLoader`` class implements ``__del__`` method in which, if the flag is set, the ``on_stop`` actions will be called.

View File

@ -1,10 +1,10 @@
from pathlib import Path from pathlib import Path
from setuptools import find_packages, setup from setuptools import find_packages, setup
from typing import Any, Dict from typing import Any
metadata_path = Path(__file__).resolve().parent / "src/ahriman/version.py" metadata_path = Path(__file__).resolve().parent / "src/ahriman/version.py"
metadata: Dict[str, Any] = {} metadata: dict[str, Any] = {}
with metadata_path.open() as metadata_file: with metadata_path.open() as metadata_file:
exec(metadata_file.read(), metadata) # pylint: disable=exec-used exec(metadata_file.read(), metadata) # pylint: disable=exec-used

View File

@ -23,7 +23,7 @@ import sys
import tempfile import tempfile
from pathlib import Path from pathlib import Path
from typing import List, TypeVar from typing import TypeVar
from ahriman import version from ahriman import version
from ahriman.application import handlers from ahriman.application import handlers
@ -35,7 +35,7 @@ from ahriman.models.sign_settings import SignSettings
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
__all__: List[str] = [] __all__: list[str] = []
# this workaround is for several things # this workaround is for several things

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 typing import Iterable, List, Set from collections.abc import Iterable
from ahriman.application.application.application_packages import ApplicationPackages from ahriman.application.application.application_packages import ApplicationPackages
from ahriman.application.application.application_repository import ApplicationRepository from ahriman.application.application.application_repository import ApplicationRepository
@ -50,14 +50,14 @@ class Application(ApplicationPackages, ApplicationRepository):
be used instead. be used instead.
""" """
def _known_packages(self) -> Set[str]: def _known_packages(self) -> set[str]:
""" """
load packages from repository and pacman repositories load packages from repository and pacman repositories
Returns: Returns:
Set[str]: list of known packages set[str]: list of known packages
""" """
known_packages: Set[str] = set() known_packages: set[str] = set()
# local set # local set
for base in self.repository.packages(): for base in self.repository.packages():
for package, properties in base.packages.items(): for package, properties in base.packages.items():
@ -89,15 +89,15 @@ class Application(ApplicationPackages, ApplicationRepository):
""" """
self.repository.triggers.on_stop() self.repository.triggers.on_stop()
def with_dependencies(self, packages: List[Package], *, process_dependencies: bool) -> List[Package]: def with_dependencies(self, packages: list[Package], *, process_dependencies: bool) -> list[Package]:
""" """
add missing dependencies to list of packages add missing dependencies to list of packages
Args: Args:
packages(List[Package]): list of source packages of which dependencies have to be processed packages(list[Package]): list of source packages of which dependencies have to be processed
process_dependencies(bool): if no set, dependencies will not be processed process_dependencies(bool): if no set, dependencies will not be processed
""" """
def missing_dependencies(source: Iterable[Package]) -> Set[str]: def missing_dependencies(source: Iterable[Package]) -> set[str]:
# build initial list of dependencies # build initial list of dependencies
result = set() result = set()
for package in source: for package in source:

View File

@ -20,8 +20,9 @@
import requests import requests
import shutil import shutil
from collections.abc import Iterable
from pathlib import Path from pathlib import Path
from typing import Any, Iterable from typing import Any
from ahriman.application.application.application_properties import ApplicationProperties from ahriman.application.application.application_properties import ApplicationProperties
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources

View File

@ -17,8 +17,8 @@
# 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 Callable, Iterable
from pathlib import Path from pathlib import Path
from typing import Callable, Iterable, List
from ahriman.application.application.application_properties import ApplicationProperties from ahriman.application.application.application_properties import ApplicationProperties
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources
@ -89,19 +89,19 @@ class ApplicationRepository(ApplicationProperties):
# process triggers # process triggers
self.on_result(Result()) self.on_result(Result())
def unknown(self) -> List[str]: def unknown(self) -> list[str]:
""" """
get packages which were not found in AUR get packages which were not found in AUR
Returns: Returns:
List[str]: unknown package archive list list[str]: unknown package archive list
""" """
def has_local(probe: Package) -> bool: def has_local(probe: Package) -> bool:
cache_dir = self.repository.paths.cache_for(probe.base) cache_dir = self.repository.paths.cache_for(probe.base)
return cache_dir.is_dir() and not Sources.has_remotes(cache_dir) return cache_dir.is_dir() and not Sources.has_remotes(cache_dir)
def unknown_aur(probe: Package) -> List[str]: def unknown_aur(probe: Package) -> list[str]:
packages: List[str] = [] packages: list[str] = []
for single in probe.packages: for single in probe.packages:
try: try:
_ = Package.from_aur(single, self.repository.pacman) _ = Package.from_aur(single, self.repository.pacman)
@ -109,7 +109,7 @@ class ApplicationRepository(ApplicationProperties):
packages.append(single) packages.append(single)
return packages return packages
def unknown_local(probe: Package) -> List[str]: def unknown_local(probe: Package) -> list[str]:
cache_dir = self.repository.paths.cache_for(probe.base) cache_dir = self.repository.paths.cache_for(probe.base)
local = Package.from_build(cache_dir, self.architecture) local = Package.from_build(cache_dir, self.architecture)
packages = set(probe.packages.keys()).difference(local.packages.keys()) packages = set(probe.packages.keys()).difference(local.packages.keys())
@ -155,7 +155,7 @@ class ApplicationRepository(ApplicationProperties):
return build_result return build_result
def updates(self, filter_packages: Iterable[str], *, def updates(self, filter_packages: Iterable[str], *,
aur: bool, local: bool, manual: bool, vcs: bool, log_fn: Callable[[str], None]) -> List[Package]: aur: bool, local: bool, manual: bool, vcs: bool, log_fn: Callable[[str], None]) -> list[Package]:
""" """
get list of packages to run update process get list of packages to run update process
@ -168,7 +168,7 @@ class ApplicationRepository(ApplicationProperties):
log_fn(Callable[[str], None]): logger function to log updates log_fn(Callable[[str], None]): logger function to log updates
Returns: Returns:
List[Package]: list of out-of-dated packages list[Package]: list of out-of-dated packages
""" """
updates = {} updates = {}

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -32,7 +30,7 @@ class Add(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -22,7 +22,6 @@ import pwd
from pathlib import Path from pathlib import Path
from tarfile import TarFile from tarfile import TarFile
from typing import Set, Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -37,7 +36,7 @@ class Backup(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -55,7 +54,7 @@ class Backup(Handler):
archive.add(backup_path) archive.add(backup_path)
@staticmethod @staticmethod
def get_paths(configuration: Configuration) -> Set[Path]: def get_paths(configuration: Configuration) -> set[Path]:
""" """
extract paths to back up extract paths to back up
@ -63,7 +62,7 @@ class Backup(Handler):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
Set[Path]: map of the filesystem paths set[Path]: map of the filesystem paths
""" """
paths = set(configuration.include.glob("*.ini")) paths = set(configuration.include.glob("*.ini"))

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -32,7 +30,7 @@ class Clean(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -20,8 +20,6 @@
import argparse import argparse
import threading import threading
from typing import Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -32,7 +30,7 @@ class Daemon(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ConfigurationPrinter from ahriman.core.formatters import ConfigurationPrinter
@ -34,7 +32,7 @@ class Dump(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -23,7 +23,6 @@ import argparse
import logging import logging
from multiprocessing import Pool from multiprocessing import Pool
from typing import List, Type
from ahriman.application.lock import Lock from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -53,7 +52,7 @@ class Handler:
ALLOW_MULTI_ARCHITECTURE_RUN = True ALLOW_MULTI_ARCHITECTURE_RUN = True
@classmethod @classmethod
def architectures_extract(cls: Type[Handler], args: argparse.Namespace) -> List[str]: def architectures_extract(cls: type[Handler], args: argparse.Namespace) -> list[str]:
""" """
get known architectures get known architectures
@ -61,7 +60,7 @@ class Handler:
args(argparse.Namespace): command line args args(argparse.Namespace): command line args
Returns: Returns:
List[str]: list of architectures for which tree is created list[str]: list of architectures for which tree is created
Raises: Raises:
MissingArchitecture: if no architecture set and automatic detection is not allowed or failed MissingArchitecture: if no architecture set and automatic detection is not allowed or failed
@ -84,7 +83,7 @@ class Handler:
return sorted(architectures) return sorted(architectures)
@classmethod @classmethod
def call(cls: Type[Handler], args: argparse.Namespace, architecture: str) -> bool: def call(cls: type[Handler], args: argparse.Namespace, architecture: str) -> bool:
""" """
additional function to wrap all calls for multiprocessing library additional function to wrap all calls for multiprocessing library
@ -109,7 +108,7 @@ class Handler:
return False return False
@classmethod @classmethod
def execute(cls: Type[Handler], args: argparse.Namespace) -> int: def execute(cls: type[Handler], args: argparse.Namespace) -> int:
""" """
execute function for all aru execute function for all aru
@ -138,7 +137,7 @@ class Handler:
return 0 if all(result) else 1 return 0 if all(result) else 1
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -33,7 +31,7 @@ class Help(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -34,7 +32,7 @@ class KeyImport(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -21,7 +21,6 @@ import argparse
import sys import sys
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple, Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -39,7 +38,7 @@ class Patch(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -66,17 +65,17 @@ class Patch(Handler):
Patch.patch_set_remove(application, args.package, args.variable) Patch.patch_set_remove(application, args.package, args.variable)
@staticmethod @staticmethod
def patch_create_from_diff(sources_dir: Path, architecture: str, track: List[str]) -> Tuple[str, PkgbuildPatch]: def patch_create_from_diff(sources_dir: Path, architecture: str, track: list[str]) -> tuple[str, PkgbuildPatch]:
""" """
create PKGBUILD plain diff patches from sources directory create PKGBUILD plain diff patches from sources directory
Args: Args:
sources_dir(Path): path to directory with the package sources sources_dir(Path): path to directory with the package sources
architecture(str): repository architecture architecture(str): repository architecture
track(List[str]): track files which match the glob before creating the patch track(list[str]): track files which match the glob before creating the patch
Returns: Returns:
Tuple[str, PkgbuildPatch]: package base and created PKGBUILD patch based on the diff from master HEAD tuple[str, PkgbuildPatch]: package base and created PKGBUILD patch based on the diff from master HEAD
to current files to current files
""" """
package = Package.from_build(sources_dir, architecture) package = Package.from_build(sources_dir, architecture)
@ -84,13 +83,13 @@ class Patch(Handler):
return package.base, PkgbuildPatch(None, patch) return package.base, PkgbuildPatch(None, patch)
@staticmethod @staticmethod
def patch_create_from_function(variable: str, patch_path: Optional[Path]) -> PkgbuildPatch: def patch_create_from_function(variable: str, patch_path: Path | None) -> PkgbuildPatch:
""" """
create single-function patch set for the package base create single-function patch set for the package base
Args: Args:
variable(str): function or variable name inside PKGBUILD variable(str): function or variable name inside PKGBUILD
patch_path(Path): optional path to patch content. If not set, it will be read from stdin patch_path(Path | None): optional path to patch content. If not set, it will be read from stdin
Returns: Returns:
PkgbuildPatch: created patch for the PKGBUILD function PkgbuildPatch: created patch for the PKGBUILD function
@ -116,15 +115,15 @@ class Patch(Handler):
application.database.patches_insert(package_base, patch) application.database.patches_insert(package_base, patch)
@staticmethod @staticmethod
def patch_set_list(application: Application, package_base: Optional[str], variables: List[str], def patch_set_list(application: Application, package_base: str | None, variables: list[str],
exit_code: bool) -> None: exit_code: bool) -> None:
""" """
list patches available for the package base list patches available for the package base
Args: Args:
application(Application): application instance application(Application): application instance
package_base(Optional[str]): package base package_base(str | None): package base
variables(List[str]): extract patches only for specified PKGBUILD variables variables(list[str]): extract patches only for specified PKGBUILD variables
exit_code(bool): exit with error on empty search result exit_code(bool): exit with error on empty search result
""" """
patches = application.database.patches_list(package_base, variables) patches = application.database.patches_list(package_base, variables)
@ -134,13 +133,13 @@ class Patch(Handler):
PatchPrinter(base, patch).print(verbose=True, separator=" = ") PatchPrinter(base, patch).print(verbose=True, separator=" = ")
@staticmethod @staticmethod
def patch_set_remove(application: Application, package_base: str, variables: List[str]) -> None: def patch_set_remove(application: Application, package_base: str, variables: list[str]) -> None:
""" """
remove patch set for the package base remove patch set for the package base
Args: Args:
application(Application): application instance application(Application): application instance
package_base(str): package base package_base(str): package base
variables(List[str]): remove patches only for specified PKGBUILD variables variables(list[str]): remove patches only for specified PKGBUILD variables
""" """
application.database.patches_remove(package_base, variables) application.database.patches_remove(package_base, variables)

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import List, Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -34,7 +32,7 @@ class Rebuild(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -46,13 +44,11 @@ class Rebuild(Handler):
report(bool): force enable or disable reporting report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation unsafe(bool): if set no user check will be performed before path creation
""" """
depends_on = set(args.depends_on) if args.depends_on else None
application = Application(architecture, configuration, report=report, unsafe=unsafe) application = Application(architecture, configuration, report=report, unsafe=unsafe)
application.on_start() application.on_start()
packages = Rebuild.extract_packages(application, from_database=args.from_database) packages = Rebuild.extract_packages(application, from_database=args.from_database)
updates = application.repository.packages_depend_on(packages, depends_on) updates = application.repository.packages_depend_on(packages, args.depends_on or None)
Rebuild.check_if_empty(args.exit_code, not updates) Rebuild.check_if_empty(args.exit_code, not updates)
if args.dry_run: if args.dry_run:
@ -64,7 +60,7 @@ class Rebuild(Handler):
Rebuild.check_if_empty(args.exit_code, result.is_empty) Rebuild.check_if_empty(args.exit_code, result.is_empty)
@staticmethod @staticmethod
def extract_packages(application: Application, *, from_database: bool) -> List[Package]: def extract_packages(application: Application, *, from_database: bool) -> list[Package]:
""" """
extract packages from database file extract packages from database file
@ -73,7 +69,7 @@ class Rebuild(Handler):
from_database(bool): extract packages from database instead of repository filesystem from_database(bool): extract packages from database instead of repository filesystem
Returns: Returns:
List[Package]: list of packages which were stored in database list[Package]: list of packages which were stored in database
""" """
if from_database: if from_database:
return [package for (package, _) in application.database.packages_get()] return [package for (package, _) in application.database.packages_get()]

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -32,7 +30,7 @@ class Remove(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -33,7 +31,7 @@ class RemoveUnknown(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,7 +19,6 @@
# #
import argparse import argparse
from typing import Type
from tarfile import TarFile from tarfile import TarFile
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -34,7 +33,7 @@ class Restore(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -20,7 +20,7 @@
import argparse import argparse
from dataclasses import fields from dataclasses import fields
from typing import Callable, Iterable, List, Tuple, Type from collections.abc import Callable, Iterable
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -36,14 +36,14 @@ class Search(Handler):
packages search handler packages search handler
Attributes: Attributes:
SORT_FIELDS(Set[str]): (class attribute) allowed fields to sort the package list SORT_FIELDS(set[str]): (class attribute) allowed fields to sort the package list
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list} # type: ignore SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list} # type: ignore
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -67,7 +67,7 @@ class Search(Handler):
AurPrinter(package).print(verbose=args.info) AurPrinter(package).print(verbose=args.info)
@staticmethod @staticmethod
def sort(packages: Iterable[AURPackage], sort_by: str) -> List[AURPackage]: def sort(packages: Iterable[AURPackage], sort_by: str) -> list[AURPackage]:
""" """
sort package list by specified field sort package list by specified field
@ -76,7 +76,7 @@ class Search(Handler):
sort_by(str): AUR package field name to sort by sort_by(str): AUR package field name to sort by
Returns: Returns:
List[AURPackage]: sorted list for packages list[AURPackage]: sorted list for packages
Raises: Raises:
InvalidOption: if search fields is not in list of allowed ones InvalidOption: if search fields is not in list of allowed ones
@ -85,6 +85,6 @@ class Search(Handler):
raise OptionError(sort_by) raise OptionError(sort_by)
# always sort by package name at the last # always sort by package name at the last
# well technically it is not a string, but we can deal with it # well technically it is not a string, but we can deal with it
comparator: Callable[[AURPackage], Tuple[str, str]] =\ comparator: Callable[[AURPackage], tuple[str, str]] =\
lambda package: (getattr(package, sort_by), package.name) lambda package: (getattr(package, sort_by), package.name)
return sorted(packages, key=comparator) return sorted(packages, key=comparator)

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman import version from ahriman import version
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -37,7 +35,7 @@ class ServiceUpdates(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -21,7 +21,6 @@ import argparse
from pathlib import Path from pathlib import Path
from pwd import getpwuid from pwd import getpwuid
from typing import Optional, Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -46,7 +45,7 @@ class Setup(Handler):
SUDOERS_DIR_PATH = Path("/etc/sudoers.d") SUDOERS_DIR_PATH = Path("/etc/sudoers.d")
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -132,7 +131,7 @@ class Setup(Handler):
configuration.write(ahriman_configuration) configuration.write(ahriman_configuration)
@staticmethod @staticmethod
def configuration_create_devtools(prefix: str, architecture: str, source: Path, mirror: Optional[str], def configuration_create_devtools(prefix: str, architecture: str, source: Path, mirror: str | None,
multilib: bool, repository: str, paths: RepositoryPaths) -> None: multilib: bool, repository: str, paths: RepositoryPaths) -> None:
""" """
create configuration for devtools based on ``source`` configuration create configuration for devtools based on ``source`` configuration
@ -144,7 +143,7 @@ class Setup(Handler):
prefix(str): command prefix in {prefix}-{architecture}-build prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture architecture(str): repository architecture
source(Path): path to source configuration file source(Path): path to source configuration file
mirror(Optional[str]): link to package server mirror mirror(str | None): link to package server mirror
multilib(bool): add or do not multilib repository to the configuration multilib(bool): add or do not multilib repository to the configuration
repository(str): repository name repository(str): repository name
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance

View File

@ -22,7 +22,6 @@ import code
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -38,7 +37,7 @@ class Shell(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -32,7 +30,7 @@ class Sign(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from typing import Callable, Iterable, Tuple, Type from collections.abc import Callable
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -37,7 +37,7 @@ class Status(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -55,7 +55,7 @@ class Status(Handler):
service_status = client.get_internal() service_status = client.get_internal()
StatusPrinter(service_status.status).print(verbose=args.info) StatusPrinter(service_status.status).print(verbose=args.info)
if args.package: if args.package:
packages: Iterable[Tuple[Package, BuildStatus]] = sum( packages: list[tuple[Package, BuildStatus]] = sum(
(client.get(base) for base in args.package), (client.get(base) for base in args.package),
start=[]) start=[])
else: else:
@ -63,8 +63,8 @@ class Status(Handler):
Status.check_if_empty(args.exit_code, not packages) Status.check_if_empty(args.exit_code, not packages)
comparator: Callable[[Tuple[Package, BuildStatus]], str] = lambda item: item[0].base comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda item: item[0].base
filter_fn: Callable[[Tuple[Package, BuildStatus]], bool] =\ filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\
lambda item: args.status is None or item[1].status == args.status lambda item: args.status is None or item[1].status == args.status
for package, package_status in sorted(filter(filter_fn, packages), key=comparator): for package, package_status in sorted(filter(filter_fn, packages), key=comparator):
PackagePrinter(package, package_status).print(verbose=args.info) PackagePrinter(package, package_status).print(verbose=args.info)

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -35,7 +33,7 @@ class StatusUpdate(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -36,7 +34,7 @@ class Structure(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -33,7 +31,7 @@ class Triggers(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -20,8 +20,6 @@
import argparse import argparse
import shlex import shlex
from typing import List, Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
@ -35,7 +33,7 @@ class UnsafeCommands(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -56,20 +54,20 @@ class UnsafeCommands(Handler):
UnsafeCommands.check_unsafe(args.command, unsafe_commands, parser) UnsafeCommands.check_unsafe(args.command, unsafe_commands, parser)
@staticmethod @staticmethod
def check_unsafe(command: str, unsafe_commands: List[str], parser: argparse.ArgumentParser) -> None: def check_unsafe(command: str, unsafe_commands: list[str], parser: argparse.ArgumentParser) -> None:
""" """
check if command is unsafe check if command is unsafe
Args: Args:
command(str): command to check command(str): command to check
unsafe_commands(List[str]): list of unsafe commands unsafe_commands(list[str]): list of unsafe commands
parser(argparse.ArgumentParser): generated argument parser parser(argparse.ArgumentParser): generated argument parser
""" """
args = parser.parse_args(shlex.split(command)) args = parser.parse_args(shlex.split(command))
UnsafeCommands.check_if_empty(True, args.command in unsafe_commands) UnsafeCommands.check_if_empty(True, args.command in unsafe_commands)
@staticmethod @staticmethod
def get_unsafe_commands(parser: argparse.ArgumentParser) -> List[str]: def get_unsafe_commands(parser: argparse.ArgumentParser) -> list[str]:
""" """
extract unsafe commands from argument parser extract unsafe commands from argument parser
@ -77,7 +75,7 @@ class UnsafeCommands(Handler):
parser(argparse.ArgumentParser): generated argument parser parser(argparse.ArgumentParser): generated argument parser
Returns: Returns:
List[str]: list of commands with default unsafe flag list[str]: list of commands with default unsafe flag
""" """
# should never fail # should never fail
# pylint: disable=protected-access # pylint: disable=protected-access

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from typing import Callable, Type from collections.abc import Callable
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
@ -32,7 +32,7 @@ class Update(Handler):
""" """
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -21,7 +21,6 @@ import argparse
import getpass import getpass
from pathlib import Path from pathlib import Path
from typing import Optional, Tuple, Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -40,7 +39,7 @@ class Users(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -119,7 +118,7 @@ class Users(Handler):
path.chmod(0o600) path.chmod(0o600)
@staticmethod @staticmethod
def get_salt(configuration: Configuration, salt_length: int = 20) -> Tuple[Optional[str], str]: def get_salt(configuration: Configuration, salt_length: int = 20) -> tuple[str | None, str]:
""" """
get salt from configuration or create new string get salt from configuration or create new string
@ -128,7 +127,7 @@ class Users(Handler):
salt_length(int, optional): salt length (Default value = 20) salt_length(int, optional): salt length (Default value = 20)
Returns: Returns:
Tuple[Optional[str], str]: tuple containing salt from configuration if any and actual salt which must be tuple[str | None, str]: tuple containing salt from configuration if any and actual salt which must be
used for password hash used for password hash
""" """
if salt := configuration.get("auth", "salt", fallback=None): if salt := configuration.get("auth", "salt", fallback=None):

View File

@ -20,7 +20,7 @@
import argparse import argparse
import copy import copy
from typing import Any, Dict, Type from typing import Any
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -39,7 +39,7 @@ class Validate(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -112,17 +112,17 @@ class Validate(Handler):
return schema return schema
@staticmethod @staticmethod
def schema_merge(source: Dict[str, Any], schema: Dict[str, Any]) -> Dict[str, Any]: def schema_merge(source: dict[str, Any], schema: dict[str, Any]) -> dict[str, Any]:
""" """
merge child schema into source. In case if source already contains values, new keys will be added merge child schema into source. In case if source already contains values, new keys will be added
(possibly with overrides - in case if such key already set also) (possibly with overrides - in case if such key already set also)
Args: Args:
source(Dict[str, Any]): source (current) schema into which will be merged source(dict[str, Any]): source (current) schema into which will be merged
schema(Dict[str, Any]): new schema to be merged schema(dict[str, Any]): new schema to be merged
Returns: Returns:
Dict[str, Any]: schema with added elements from source schema if they were set before and not presented dict[str, Any]: schema with added elements from source schema if they were set before and not presented
in the new one. Note, that schema will be modified in-place in the new one. Note, that schema will be modified in-place
""" """
for key, value in source.items(): for key, value in source.items():

View File

@ -21,8 +21,6 @@ import argparse
import pkg_resources import pkg_resources
import sys import sys
from typing import Dict, List, Tuple, Type
from ahriman import version from ahriman import version
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -37,7 +35,7 @@ class Versions(Handler):
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line
@ -55,23 +53,23 @@ class Versions(Handler):
VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ") VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ")
@staticmethod @staticmethod
def package_dependencies(root: str, root_extras: Tuple[str, ...] = ()) -> Dict[str, str]: def package_dependencies(root: str, root_extras: tuple[str, ...] = ()) -> dict[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
Args: Args:
root(str): root package name root(str): root package name
root_extras(Tuple[str, ...], optional): extras for the root package (Default value = ()) root_extras(tuple[str, ...], optional): extras for the root package (Default value = ())
Returns: Returns:
Dict[str, str]: map of installed dependency to its version dict[str, str]: map of installed dependency to its version
""" """
resources: Dict[str, pkg_resources.Distribution] = pkg_resources.working_set.by_key # type: ignore resources: dict[str, pkg_resources.Distribution] = pkg_resources.working_set.by_key # type: ignore
def dependencies_by_key(key: str, extras: Tuple[str, ...] = ()) -> List[str]: def dependencies_by_key(key: str, extras: tuple[str, ...] = ()) -> list[str]:
return [entry.key for entry in resources[key].requires(extras)] return [entry.key for entry in resources[key].requires(extras)]
keys: List[str] = [] keys: list[str] = []
portion = {key for key in dependencies_by_key(root, root_extras) if key in resources} portion = {key for key in dependencies_by_key(root, root_extras) if key in resources}
while portion: while portion:
keys.extend(portion) keys.extend(portion)

View File

@ -19,8 +19,6 @@
# #
import argparse import argparse
from typing import Type
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn from ahriman.core.spawn import Spawn
@ -35,7 +33,7 @@ class Web(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None: report: bool, unsafe: bool) -> None:
""" """
callback for command line callback for command line

View File

@ -22,7 +22,7 @@ from __future__ import annotations
import argparse import argparse
from types import TracebackType from types import TracebackType
from typing import Literal, Optional, Type from typing import Literal
from ahriman import version from ahriman import version
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -127,14 +127,14 @@ class Lock(LazyLogging):
self.reporter.update_self(BuildStatusEnum.Building) self.reporter.update_self(BuildStatusEnum.Building)
return self return self
def __exit__(self, exc_type: Optional[Type[Exception]], exc_val: Optional[Exception], def __exit__(self, exc_type: type[Exception] | None, exc_val: Exception | None,
exc_tb: TracebackType) -> Literal[False]: exc_tb: TracebackType) -> Literal[False]:
""" """
remove lock file when done remove lock file when done
Args: Args:
exc_type(Optional[Type[Exception]]): exception type name if any exc_type(type[Exception] | None): exception type name if any
exc_val(Optional[Exception]): exception raised if any exc_val(Exception | None): exception raised if any
exc_tb(TracebackType): exception traceback if any exc_tb(TracebackType): exception traceback if any
Returns: Returns:

View File

@ -17,8 +17,9 @@
# 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 Iterator
from contextvars import ContextVar from contextvars import ContextVar
from typing import Any, Dict, Iterator, TypeVar from typing import Any, TypeVar
from ahriman.models.context_key import ContextKey from ahriman.models.context_key import ContextKey
@ -35,7 +36,7 @@ class _Context:
""" """
default constructor. Must not be used directly default constructor. Must not be used directly
""" """
self._content: Dict[str, Any] = {} self._content: dict[str, Any] = {}
def get(self, key: ContextKey[T]) -> T: def get(self, key: ContextKey[T]) -> T:
""" """

View File

@ -19,9 +19,10 @@
# #
import shutil import shutil
from collections.abc import Callable, Generator
from pathlib import Path from pathlib import Path
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
from typing import Any, Callable, Generator, Set from typing import Any
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -166,14 +167,14 @@ class Pacman(LazyLogging):
continue continue
yield package yield package
def packages(self) -> Set[str]: def packages(self) -> set[str]:
""" """
get list of packages known for alpm get list of packages known for alpm
Returns: Returns:
Set[str]: list of package names set[str]: list of package names
""" """
result: Set[str] = set() result: set[str] = set()
for database in self.handle.get_syncdbs(): for database in self.handle.get_syncdbs():
for package in database.pkgcache: for package in database.pkgcache:
# package itself # package itself

View File

@ -19,7 +19,7 @@
# #
import requests import requests
from typing import Any, Dict, List, Type from typing import Any
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote from ahriman.core.alpm.remote import Remote
@ -45,15 +45,15 @@ class AUR(Remote):
DEFAULT_TIMEOUT = 30 DEFAULT_TIMEOUT = 30
@staticmethod @staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]: def parse_response(response: dict[str, Any]) -> list[AURPackage]:
""" """
parse RPC response to package list parse RPC response to package list
Args: Args:
response(Dict[str, Any]): RPC response json response(dict[str, Any]): RPC response json
Returns: Returns:
List[AURPackage]: list of parsed packages list[AURPackage]: list of parsed packages
Raises: Raises:
InvalidPackageInfo: for error API response InvalidPackageInfo: for error API response
@ -65,7 +65,7 @@ class AUR(Remote):
return [AURPackage.from_json(package) for package in response["results"]] return [AURPackage.from_json(package) for package in response["results"]]
@classmethod @classmethod
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str: def remote_git_url(cls: type[Remote], package_base: str, repository: str) -> str:
""" """
generate remote git url from the package base generate remote git url from the package base
@ -79,7 +79,7 @@ class AUR(Remote):
return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git" return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git"
@classmethod @classmethod
def remote_web_url(cls: Type[Remote], package_base: str) -> str: def remote_web_url(cls: type[Remote], package_base: str) -> str:
""" """
generate remote web url from the package base generate remote web url from the package base
@ -91,7 +91,7 @@ class AUR(Remote):
""" """
return f"{AUR.DEFAULT_AUR_URL}/packages/{package_base}" return f"{AUR.DEFAULT_AUR_URL}/packages/{package_base}"
def make_request(self, request_type: str, *args: str, **kwargs: str) -> List[AURPackage]: def make_request(self, request_type: str, *args: str, **kwargs: str) -> list[AURPackage]:
""" """
perform request to AUR RPC perform request to AUR RPC
@ -101,9 +101,9 @@ class AUR(Remote):
**kwargs(str): list of additional named parameters like by **kwargs(str): list of additional named parameters like by
Returns: Returns:
List[AURPackage]: response parsed to package list list[AURPackage]: response parsed to package list
""" """
query: Dict[str, Any] = { query: dict[str, Any] = {
"type": request_type, "type": request_type,
"v": self.DEFAULT_RPC_VERSION "v": self.DEFAULT_RPC_VERSION
} }
@ -145,7 +145,7 @@ class AUR(Remote):
except StopIteration: except StopIteration:
raise UnknownPackageError(package_name) raise UnknownPackageError(package_name)
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]: def package_search(self, *keywords: str, pacman: Pacman) -> list[AURPackage]:
""" """
search package in AUR web search package in AUR web
@ -154,6 +154,6 @@ class AUR(Remote):
pacman(Pacman): alpm wrapper instance pacman(Pacman): alpm wrapper instance
Returns: Returns:
List[AURPackage]: list of packages which match the criteria list[AURPackage]: list of packages which match the criteria
""" """
return self.make_request("search", *keywords, by="name-desc") return self.make_request("search", *keywords, by="name-desc")

View File

@ -19,7 +19,7 @@
# #
import requests import requests
from typing import Any, Dict, List, Type from typing import Any
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote from ahriman.core.alpm.remote import Remote
@ -34,7 +34,7 @@ class Official(Remote):
Attributes: Attributes:
DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url
DEFAULT_SEARCH_REPOSITORIES(List[str]): (class attribute) default list of repositories to search DEFAULT_SEARCH_REPOSITORIES(list[str]): (class attribute) default list of repositories to search
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
""" """
@ -45,15 +45,15 @@ class Official(Remote):
DEFAULT_TIMEOUT = 30 DEFAULT_TIMEOUT = 30
@staticmethod @staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]: def parse_response(response: dict[str, Any]) -> list[AURPackage]:
""" """
parse RPC response to package list parse RPC response to package list
Args: Args:
response(Dict[str, Any]): RPC response json response(dict[str, Any]): RPC response json
Returns: Returns:
List[AURPackage]: list of parsed packages list[AURPackage]: list of parsed packages
Raises: Raises:
InvalidPackageInfo: for error API response InvalidPackageInfo: for error API response
@ -63,7 +63,7 @@ class Official(Remote):
return [AURPackage.from_repo(package) for package in response["results"]] return [AURPackage.from_repo(package) for package in response["results"]]
@classmethod @classmethod
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str: def remote_git_url(cls: type[Remote], package_base: str, repository: str) -> str:
""" """
generate remote git url from the package base generate remote git url from the package base
@ -79,7 +79,7 @@ class Official(Remote):
return "https://github.com/archlinux/svntogit-community.git" return "https://github.com/archlinux/svntogit-community.git"
@classmethod @classmethod
def remote_web_url(cls: Type[Remote], package_base: str) -> str: def remote_web_url(cls: type[Remote], package_base: str) -> str:
""" """
generate remote web url from the package base generate remote web url from the package base
@ -91,7 +91,7 @@ class Official(Remote):
""" """
return f"{Official.DEFAULT_ARCHLINUX_URL}/packages/{package_base}" return f"{Official.DEFAULT_ARCHLINUX_URL}/packages/{package_base}"
def make_request(self, *args: str, by: str) -> List[AURPackage]: def make_request(self, *args: str, by: str) -> list[AURPackage]:
""" """
perform request to official repositories RPC perform request to official repositories RPC
@ -100,7 +100,7 @@ class Official(Remote):
by(str): search by the field by(str): search by the field
Returns: Returns:
List[AURPackage]: response parsed to package list list[AURPackage]: response parsed to package list
""" """
try: try:
response = requests.get( response = requests.get(
@ -133,7 +133,7 @@ class Official(Remote):
except StopIteration: except StopIteration:
raise UnknownPackageError(package_name) raise UnknownPackageError(package_name)
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]: def package_search(self, *keywords: str, pacman: Pacman) -> list[AURPackage]:
""" """
search package in AUR web search package in AUR web
@ -142,6 +142,6 @@ class Official(Remote):
pacman(Pacman): alpm wrapper instance pacman(Pacman): alpm wrapper instance
Returns: Returns:
List[AURPackage]: list of packages which match the criteria list[AURPackage]: list of packages which match the criteria
""" """
return self.make_request(*keywords, by="q") return self.make_request(*keywords, by="q")

View File

@ -19,8 +19,6 @@
# #
from __future__ import annotations from __future__ import annotations
from typing import Dict, List, Type
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
from ahriman.models.aur_package import AURPackage from ahriman.models.aur_package import AURPackage
@ -44,7 +42,7 @@ class Remote(LazyLogging):
""" """
@classmethod @classmethod
def info(cls: Type[Remote], package_name: str, *, pacman: Pacman) -> AURPackage: def info(cls: type[Remote], package_name: str, *, pacman: Pacman) -> AURPackage:
""" """
get package info by its name get package info by its name
@ -58,7 +56,7 @@ class Remote(LazyLogging):
return cls().package_info(package_name, pacman=pacman) return cls().package_info(package_name, pacman=pacman)
@classmethod @classmethod
def multisearch(cls: Type[Remote], *keywords: str, pacman: Pacman) -> List[AURPackage]: def multisearch(cls: type[Remote], *keywords: str, pacman: Pacman) -> list[AURPackage]:
""" """
search in remote repository by using API with multiple words. This method is required in order to handle search in remote repository by using API with multiple words. This method is required in order to handle
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
@ -68,10 +66,10 @@ class Remote(LazyLogging):
pacman(Pacman): alpm wrapper instance pacman(Pacman): alpm wrapper instance
Returns: Returns:
List[AURPackage]: list of packages each of them matches all search terms list[AURPackage]: list of packages each of them matches all search terms
""" """
instance = cls() instance = cls()
packages: Dict[str, AURPackage] = {} packages: dict[str, AURPackage] = {}
for term in filter(lambda word: len(word) >= 3, keywords): for term in filter(lambda word: len(word) >= 3, keywords):
portion = instance.search(term, pacman=pacman) portion = instance.search(term, pacman=pacman)
packages = { packages = {
@ -82,7 +80,7 @@ class Remote(LazyLogging):
return list(packages.values()) return list(packages.values())
@classmethod @classmethod
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str: def remote_git_url(cls: type[Remote], package_base: str, repository: str) -> str:
""" """
generate remote git url from the package base generate remote git url from the package base
@ -99,7 +97,7 @@ class Remote(LazyLogging):
raise NotImplementedError raise NotImplementedError
@classmethod @classmethod
def remote_web_url(cls: Type[Remote], package_base: str) -> str: def remote_web_url(cls: type[Remote], package_base: str) -> str:
""" """
generate remote web url from the package base generate remote web url from the package base
@ -115,7 +113,7 @@ class Remote(LazyLogging):
raise NotImplementedError raise NotImplementedError
@classmethod @classmethod
def search(cls: Type[Remote], *keywords: str, pacman: Pacman) -> List[AURPackage]: def search(cls: type[Remote], *keywords: str, pacman: Pacman) -> list[AURPackage]:
""" """
search package in AUR web search package in AUR web
@ -124,7 +122,7 @@ class Remote(LazyLogging):
pacman(Pacman): alpm wrapper instance pacman(Pacman): alpm wrapper instance
Returns: Returns:
List[AURPackage]: list of packages which match the criteria list[AURPackage]: list of packages which match the criteria
""" """
return cls().package_search(*keywords, pacman=pacman) return cls().package_search(*keywords, pacman=pacman)
@ -144,7 +142,7 @@ class Remote(LazyLogging):
""" """
raise NotImplementedError raise NotImplementedError
def package_search(self, *keywords: str, pacman: Pacman) -> List[AURPackage]: def package_search(self, *keywords: str, pacman: Pacman) -> list[AURPackage]:
""" """
search package in AUR web search package in AUR web
@ -153,7 +151,7 @@ class Remote(LazyLogging):
pacman(Pacman): alpm wrapper instance pacman(Pacman): alpm wrapper instance
Returns: Returns:
List[AURPackage]: list of packages which match the criteria list[AURPackage]: list of packages which match the criteria
Raises: Raises:
NotImplementedError: not implemented method NotImplementedError: not implemented method

View File

@ -18,7 +18,6 @@
# 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 pathlib import Path from pathlib import Path
from typing import List
from ahriman.core.exceptions import BuildError from ahriman.core.exceptions import BuildError
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -33,20 +32,20 @@ class Repo(LazyLogging):
Attributes: Attributes:
name(str): repository name name(str): repository name
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance
sign_args(List[str]): additional args which have to be used to sign repository archive sign_args(list[str]): additional args which have to be used to sign repository archive
uid(int): uid of the repository owner user uid(int): uid of the repository owner user
""" """
_check_output = check_output _check_output = check_output
def __init__(self, name: str, paths: RepositoryPaths, sign_args: List[str]) -> None: def __init__(self, name: str, paths: RepositoryPaths, sign_args: list[str]) -> None:
""" """
default constructor default constructor
Args: Args:
name(str): repository name name(str): repository name
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance
sign_args(List[str]): additional args which have to be used to sign repository archive sign_args(list[str]): additional args which have to be used to sign repository archive
""" """
self.name = name self.name = name
self.paths = paths self.paths = paths

View File

@ -19,8 +19,6 @@
# #
from __future__ import annotations from __future__ import annotations
from typing import Optional, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -65,7 +63,7 @@ class Auth(LazyLogging):
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#login-modal" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>""" return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#login-modal" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>"""
@classmethod @classmethod
def load(cls: Type[Auth], configuration: Configuration, database: SQLite) -> Auth: def load(cls: type[Auth], configuration: Configuration, database: SQLite) -> Auth:
""" """
load authorization module from settings load authorization module from settings
@ -85,13 +83,13 @@ class Auth(LazyLogging):
return OAuth(configuration, database) return OAuth(configuration, database)
return cls(configuration) return cls(configuration)
async def check_credentials(self, username: Optional[str], password: Optional[str]) -> bool: async def check_credentials(self, username: str | None, password: str | None) -> bool:
""" """
validate user password validate user password
Args: Args:
username(Optional[str]): username username(str | None): username
password(Optional[str]): entered password password(str | None): entered password
Returns: Returns:
bool: True in case if password matches, False otherwise bool: True in case if password matches, False otherwise
@ -99,12 +97,12 @@ class Auth(LazyLogging):
del username, password del username, password
return True return True
async def known_username(self, username: Optional[str]) -> bool: async def known_username(self, username: str | None) -> bool:
""" """
check if user is known check if user is known
Args: Args:
username(Optional[str]): username username(str | None): username
Returns: Returns:
bool: True in case if user is known and can be authorized and False otherwise bool: True in case if user is known and can be authorized and False otherwise
@ -112,14 +110,14 @@ class Auth(LazyLogging):
del username del username
return True return True
async def verify_access(self, username: str, required: UserAccess, context: Optional[str]) -> bool: async def verify_access(self, username: str, required: UserAccess, context: str | None) -> bool:
""" """
validate if user has access to requested resource validate if user has access to requested resource
Args: Args:
username(str): username username(str): username
required(UserAccess): required access level required(UserAccess): required access level
context(Optional[str]): URI request path context(str | None): URI request path
Returns: Returns:
bool: True in case if user is allowed to do this request and False otherwise bool: True in case if user is allowed to do this request and False otherwise

View File

@ -17,8 +17,6 @@
# 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 typing import Optional
from ahriman.core.auth import Auth from ahriman.core.auth import Auth
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
@ -50,13 +48,13 @@ class Mapping(Auth):
self.database = database self.database = database
self.salt = configuration.get("auth", "salt") self.salt = configuration.get("auth", "salt")
async def check_credentials(self, username: Optional[str], password: Optional[str]) -> bool: async def check_credentials(self, username: str | None, password: str | None) -> bool:
""" """
validate user password validate user password
Args: Args:
username(Optional[str]): username username(str | None): username
password(Optional[str]): entered password password(str | None): entered password
Returns: Returns:
bool: True in case if password matches, False otherwise bool: True in case if password matches, False otherwise
@ -66,7 +64,7 @@ class Mapping(Auth):
user = self.get_user(username) user = self.get_user(username)
return user is not None and user.check_credentials(password, self.salt) return user is not None and user.check_credentials(password, self.salt)
def get_user(self, username: str) -> Optional[User]: def get_user(self, username: str) -> User | None:
""" """
retrieve user from in-memory mapping retrieve user from in-memory mapping
@ -74,30 +72,30 @@ class Mapping(Auth):
username(str): username username(str): username
Returns: Returns:
Optional[User]: user descriptor if username is known and None otherwise User | None: user descriptor if username is known and None otherwise
""" """
return self.database.user_get(username) return self.database.user_get(username)
async def known_username(self, username: Optional[str]) -> bool: async def known_username(self, username: str | None) -> bool:
""" """
check if user is known check if user is known
Args: Args:
username(Optional[str]): username username(str | None): username
Returns: Returns:
bool: True in case if user is known and can be authorized and False otherwise bool: True in case if user is known and can be authorized and False otherwise
""" """
return username is not None and self.get_user(username) is not None return username is not None and self.get_user(username) is not None
async def verify_access(self, username: str, required: UserAccess, context: Optional[str]) -> bool: async def verify_access(self, username: str, required: UserAccess, context: str | None) -> bool:
""" """
validate if user has access to requested resource validate if user has access to requested resource
Args: Args:
username(str): username username(str): username
required(UserAccess): required access level required(UserAccess): required access level
context(Optional[str]): URI request path context(str | None): URI request path
Returns: Returns:
bool: True in case if user is allowed to do this request and False otherwise bool: True in case if user is allowed to do this request and False otherwise

View File

@ -19,8 +19,6 @@
# #
import aioauth_client import aioauth_client
from typing import Optional, Type
from ahriman.core.auth.mapping import Mapping from ahriman.core.auth.mapping import Mapping
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
@ -72,7 +70,7 @@ class OAuth(Mapping):
return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-google"></i> login</a>""" return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-google"></i> login</a>"""
@staticmethod @staticmethod
def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]: def get_provider(name: str) -> type[aioauth_client.OAuth2Client]:
""" """
load OAuth2 provider by name load OAuth2 provider by name
@ -80,12 +78,12 @@ class OAuth(Mapping):
name(str): name of the provider. Must be valid class defined in aioauth-client library name(str): name of the provider. Must be valid class defined in aioauth-client library
Returns: Returns:
Type[aioauth_client.OAuth2Client]: loaded provider type type[aioauth_client.OAuth2Client]: loaded provider type
Raises: Raises:
InvalidOption: in case if invalid OAuth provider name supplied InvalidOption: in case if invalid OAuth provider name supplied
""" """
provider: Type[aioauth_client.OAuth2Client] = getattr(aioauth_client, name) provider: type[aioauth_client.OAuth2Client] = getattr(aioauth_client, name)
try: try:
is_oauth2_client = issubclass(provider, aioauth_client.OAuth2Client) is_oauth2_client = issubclass(provider, aioauth_client.OAuth2Client)
except TypeError: # what if it is random string? except TypeError: # what if it is random string?
@ -114,7 +112,7 @@ class OAuth(Mapping):
uri: str = client.get_authorize_url(scope=self.scopes, redirect_uri=self.redirect_uri) uri: str = client.get_authorize_url(scope=self.scopes, redirect_uri=self.redirect_uri)
return uri return uri
async def get_oauth_username(self, code: str) -> Optional[str]: async def get_oauth_username(self, code: str) -> str | None:
""" """
extract OAuth username from remote extract OAuth username from remote
@ -122,7 +120,7 @@ class OAuth(Mapping):
code(str): authorization code provided by external service code(str): authorization code provided by external service
Returns: Returns:
Optional[str]: username as is in OAuth provider str | None: username as is in OAuth provider
""" """
try: try:
client = self.get_client() client = self.get_client()

View File

@ -20,7 +20,6 @@
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import List, Optional
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
from ahriman.core.util import check_output, utcnow, walk from ahriman.core.util import check_output, utcnow, walk
@ -44,7 +43,7 @@ class Sources(LazyLogging):
_check_output = check_output _check_output = check_output
@staticmethod @staticmethod
def extend_architectures(sources_dir: Path, architecture: str) -> List[PkgbuildPatch]: def extend_architectures(sources_dir: Path, architecture: str) -> list[PkgbuildPatch]:
""" """
extend existing PKGBUILD with repository architecture extend existing PKGBUILD with repository architecture
@ -53,7 +52,7 @@ class Sources(LazyLogging):
architecture(str): repository architecture architecture(str): repository architecture
Returns: Returns:
List[PkgbuildPatch]: generated patch for PKGBUILD architectures if required list[PkgbuildPatch]: generated patch for PKGBUILD architectures if required
""" """
architectures = Package.supported_architectures(sources_dir) architectures = Package.supported_architectures(sources_dir)
if "any" in architectures: # makepkg does not like when there is any other arch except for any if "any" in architectures: # makepkg does not like when there is any other arch except for any
@ -62,13 +61,13 @@ class Sources(LazyLogging):
return [PkgbuildPatch("arch", list(architectures))] return [PkgbuildPatch("arch", list(architectures))]
@staticmethod @staticmethod
def fetch(sources_dir: Path, remote: Optional[RemoteSource]) -> None: def fetch(sources_dir: Path, remote: RemoteSource | None) -> None:
""" """
either clone repository or update it to origin/``remote.branch`` either clone repository or update it to origin/``remote.branch``
Args: Args:
sources_dir(Path): local path to fetch sources_dir(Path): local path to fetch
remote(Optional[RemoteSource]): remote target (from where to fetch) remote(RemoteSource | None): remote target (from where to fetch)
""" """
instance = Sources() instance = Sources()
# local directory exists and there is .git directory # local directory exists and there is .git directory
@ -127,14 +126,14 @@ class Sources(LazyLogging):
cwd=sources_dir, logger=instance.logger) cwd=sources_dir, logger=instance.logger)
@staticmethod @staticmethod
def load(sources_dir: Path, package: Package, patches: List[PkgbuildPatch], paths: RepositoryPaths) -> None: def load(sources_dir: Path, package: Package, patches: list[PkgbuildPatch], paths: RepositoryPaths) -> None:
""" """
fetch sources from remote and apply patches fetch sources from remote and apply patches
Args: Args:
sources_dir(Path): local path to fetch sources_dir(Path): local path to fetch
package(Package): package definitions package(Package): package definitions
patches(List[PkgbuildPatch]): optional patch to be applied patches(list[PkgbuildPatch]): optional patch to be applied
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance
""" """
instance = Sources() instance = Sources()
@ -165,7 +164,7 @@ class Sources(LazyLogging):
return f"{diff}\n" # otherwise, patch will be broken return f"{diff}\n" # otherwise, patch will be broken
@staticmethod @staticmethod
def push(sources_dir: Path, remote: RemoteSource, *pattern: str, commit_author: Optional[str] = None) -> None: def push(sources_dir: Path, remote: RemoteSource, *pattern: str, commit_author: str | None = None) -> None:
""" """
commit selected changes and push files to the remote repository commit selected changes and push files to the remote repository
@ -173,7 +172,7 @@ class Sources(LazyLogging):
sources_dir(Path): local path to git repository sources_dir(Path): local path to git repository
remote(RemoteSource): remote target, branch and url remote(RemoteSource): remote target, branch and url
*pattern(str): glob patterns *pattern(str): glob patterns
commit_author(Optional[str], optional): commit author in form of git config (i.e. ``user <user@host>``) commit_author(str | None, optional): commit author in form of git config (i.e. ``user <user@host>``)
(Default value = None) (Default value = None)
""" """
instance = Sources() instance = Sources()
@ -192,7 +191,7 @@ class Sources(LazyLogging):
--intent-to-add git flag (Default value = False) --intent-to-add git flag (Default value = False)
""" """
# glob directory to find files which match the specified patterns # glob directory to find files which match the specified patterns
found_files: List[Path] = [] found_files: list[Path] = []
for glob in pattern: for glob in pattern:
found_files.extend(sources_dir.glob(glob)) found_files.extend(sources_dir.glob(glob))
if not found_files: if not found_files:
@ -203,15 +202,15 @@ class Sources(LazyLogging):
Sources._check_output("git", "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files], Sources._check_output("git", "add", *args, *[str(fn.relative_to(sources_dir)) for fn in found_files],
cwd=sources_dir, logger=self.logger) cwd=sources_dir, logger=self.logger)
def commit(self, sources_dir: Path, message: Optional[str] = None, author: Optional[str] = None) -> None: def commit(self, sources_dir: Path, message: str | None = None, author: str | None = None) -> None:
""" """
commit changes commit changes
Args: Args:
sources_dir(Path): local path to git repository sources_dir(Path): local path to git repository
message(Optional[str], optional): optional commit message if any. If none set, message will be generated message(str | None, optional): optional commit message if any. If none set, message will be generated
according to the current timestamp (Default value = None) according to the current timestamp (Default value = None)
author(Optional[str], optional): optional commit author if any (Default value = None) author(str | None, optional): optional commit author if any (Default value = None)
""" """
if message is None: if message is None:
message = f"Autogenerated commit at {utcnow()}" message = f"Autogenerated commit at {utcnow()}"

View File

@ -18,7 +18,6 @@
# 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 pathlib import Path from pathlib import Path
from typing import List
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -60,7 +59,7 @@ class Task(LazyLogging):
self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[]) self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[])
self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[]) self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[])
def build(self, sources_dir: Path) -> List[Path]: def build(self, sources_dir: Path) -> list[Path]:
""" """
run package build run package build
@ -68,7 +67,7 @@ class Task(LazyLogging):
sources_dir(Path): path to where sources are sources_dir(Path): path to where sources are
Returns: Returns:
List[Path]: paths of produced packages list[Path]: paths of produced packages
""" """
command = [self.build_command, "-r", str(self.paths.chroot)] command = [self.build_command, "-r", str(self.paths.chroot)]
command.extend(self.archbuild_flags) command.extend(self.archbuild_flags)

View File

@ -23,8 +23,9 @@ import configparser
import shlex import shlex
import sys import sys
from collections.abc import Callable
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Tuple, Type from typing import Any
from ahriman.core.exceptions import InitializeError from ahriman.core.exceptions import InitializeError
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -35,11 +36,11 @@ class Configuration(configparser.RawConfigParser):
extension for built-in configuration parser extension for built-in configuration parser
Attributes: Attributes:
ARCHITECTURE_SPECIFIC_SECTIONS(List[str]): (class attribute) known sections which can be architecture specific. ARCHITECTURE_SPECIFIC_SECTIONS(list[str]): (class attribute) known sections which can be architecture specific.
Required by dump and merging functions Required by dump and merging functions
SYSTEM_CONFIGURATION_PATH(Path): (class attribute) default system configuration path distributed by package SYSTEM_CONFIGURATION_PATH(Path): (class attribute) default system configuration path distributed by package
architecture(Optional[str]): repository architecture architecture(str | None): repository architecture
path(Optional[Path]): path to root configuration file path(Path | None): path to root configuration file
Examples: Examples:
Configuration class provides additional method in order to handle application configuration. Since this class is Configuration class provides additional method in order to handle application configuration. Since this class is
@ -63,7 +64,7 @@ class Configuration(configparser.RawConfigParser):
ARCHITECTURE_SPECIFIC_SECTIONS = ["alpm", "build", "sign", "web"] ARCHITECTURE_SPECIFIC_SECTIONS = ["alpm", "build", "sign", "web"]
SYSTEM_CONFIGURATION_PATH = Path(sys.prefix) / "share" / "ahriman" / "settings" / "ahriman.ini" SYSTEM_CONFIGURATION_PATH = Path(sys.prefix) / "share" / "ahriman" / "settings" / "ahriman.ini"
converters: Dict[str, Callable[[str], Any]] # typing guard converters: dict[str, Callable[[str], Any]] # typing guard
def __init__(self, allow_no_value: bool = False) -> None: def __init__(self, allow_no_value: bool = False) -> None:
""" """
@ -77,8 +78,8 @@ class Configuration(configparser.RawConfigParser):
"list": shlex.split, "list": shlex.split,
"path": self._convert_path, "path": self._convert_path,
}) })
self.architecture: Optional[str] = None self.architecture: str | None = None
self.path: Optional[Path] = None self.path: Path | None = None
@property @property
def include(self) -> Path: def include(self) -> Path:
@ -112,7 +113,7 @@ class Configuration(configparser.RawConfigParser):
return RepositoryPaths(self.getpath("repository", "root"), architecture) return RepositoryPaths(self.getpath("repository", "root"), architecture)
@classmethod @classmethod
def from_path(cls: Type[Configuration], path: Path, architecture: str) -> Configuration: def from_path(cls: type[Configuration], path: Path, architecture: str) -> Configuration:
""" """
constructor with full object initialization constructor with full object initialization
@ -157,12 +158,12 @@ class Configuration(configparser.RawConfigParser):
return path return path
return self.path.parent / path return self.path.parent / path
def check_loaded(self) -> Tuple[Path, str]: def check_loaded(self) -> tuple[Path, str]:
""" """
check if service was actually loaded check if service was actually loaded
Returns: Returns:
Tuple[Path, str]: configuration root path and architecture if loaded tuple[Path, str]: configuration root path and architecture if loaded
Raises: Raises:
InitializeError: in case if architecture and/or path are not set InitializeError: in case if architecture and/or path are not set
@ -171,12 +172,12 @@ class Configuration(configparser.RawConfigParser):
raise InitializeError("Configuration path and/or architecture are not set") raise InitializeError("Configuration path and/or architecture are not set")
return self.path, self.architecture return self.path, self.architecture
def dump(self) -> Dict[str, Dict[str, str]]: def dump(self) -> dict[str, dict[str, str]]:
""" """
dump configuration to dictionary dump configuration to dictionary
Returns: Returns:
Dict[str, Dict[str, str]]: configuration dump for specific architecture dict[str, dict[str, str]]: configuration dump for specific architecture
""" """
return { return {
section: dict(self[section]) section: dict(self[section])
@ -185,11 +186,11 @@ class Configuration(configparser.RawConfigParser):
# pylint and mypy are too stupid to find these methods # pylint and mypy are too stupid to find these methods
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument # pylint: disable=missing-function-docstring,multiple-statements,unused-argument
def getlist(self, *args: Any, **kwargs: Any) -> List[str]: ... # type: ignore def getlist(self, *args: Any, **kwargs: Any) -> list[str]: ... # type: ignore
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore
def gettype(self, section: str, architecture: str, *, fallback: Optional[str] = None) -> Tuple[str, str]: def gettype(self, section: str, architecture: str, *, fallback: str | None = None) -> tuple[str, str]:
""" """
get type variable with fallback to old logic. Despite the fact that it has same semantics as other get* methods, get type variable with fallback to old logic. Despite the fact that it has same semantics as other get* methods,
but it has different argument list but it has different argument list
@ -197,11 +198,11 @@ class Configuration(configparser.RawConfigParser):
Args: Args:
section(str): section name section(str): section name
architecture(str): repository architecture architecture(str): repository architecture
fallback(Optional[str], optional): optional fallback type if any. If set, second element of the tuple will fallback(str | None, optional): optional fallback type if any. If set, second element of the tuple will
be always set to this value (Default value = None) be always set to this value (Default value = None)
Returns: Returns:
Tuple[str, str]: section name and found type name tuple[str, str]: section name and found type name
Raises: Raises:
configparser.NoSectionError: in case if no section found configparser.NoSectionError: in case if no section found

View File

@ -17,13 +17,13 @@
# 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 typing import Any, Dict from typing import Any
__all__ = ["CONFIGURATION_SCHEMA", "ConfigurationSchema"] __all__ = ["CONFIGURATION_SCHEMA", "ConfigurationSchema"]
ConfigurationSchema = Dict[str, Dict[str, Any]] ConfigurationSchema = dict[str, dict[str, Any]]
CONFIGURATION_SCHEMA: ConfigurationSchema = { CONFIGURATION_SCHEMA: ConfigurationSchema = {

View File

@ -21,7 +21,7 @@ import ipaddress
from cerberus import TypeDefinition, Validator as RootValidator # type: ignore from cerberus import TypeDefinition, Validator as RootValidator # type: ignore
from pathlib import Path from pathlib import Path
from typing import Any, List from typing import Any
from urllib.parse import urlparse from urllib.parse import urlparse
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -90,7 +90,7 @@ class Validator(RootValidator):
del self del self
return int(value) return int(value)
def _normalize_coerce_list(self, value: str) -> List[str]: def _normalize_coerce_list(self, value: str) -> list[str]:
""" """
extract string list from string value extract string list from string value
@ -98,17 +98,17 @@ class Validator(RootValidator):
value(str): converting value value(str): converting value
Returns: Returns:
List[str]: value converted to string list instance according to configuration rules list[str]: value converted to string list instance according to configuration rules
""" """
converted: List[str] = self.configuration.converters["list"](value) converted: list[str] = self.configuration.converters["list"](value)
return converted return converted
def _validate_is_ip_address(self, constraint: List[str], field: str, value: str) -> None: def _validate_is_ip_address(self, constraint: list[str], field: str, value: str) -> None:
""" """
check if the specified value is valid ip address check if the specified value is valid ip address
Args: Args:
constraint(List[str]): optional list of allowed special words (e.g. ``localhost``) constraint(list[str]): optional list of allowed special words (e.g. ``localhost``)
field(str): field name to be checked field(str): field name to be checked
value(Path): value to be checked value(Path): value to be checked
@ -123,12 +123,12 @@ class Validator(RootValidator):
except ValueError: except ValueError:
self._error(field, f"Value {value} must be valid IP address") self._error(field, f"Value {value} must be valid IP address")
def _validate_is_url(self, constraint: List[str], field: str, value: str) -> None: def _validate_is_url(self, constraint: list[str], field: str, value: str) -> None:
""" """
check if the specified value is a valid url check if the specified value is a valid url
Args: Args:
constraint(List[str]): optional list of supported schemas. If empty, no schema validation will be performed constraint(list[str]): optional list of supported schemas. If empty, no schema validation will be performed
field(str): field name to be checked field(str): field name to be checked
value(str): value to be checked value(str): value to be checked

View File

@ -17,11 +17,11 @@
# 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 Callable
from importlib import import_module from importlib import import_module
from pathlib import Path from pathlib import Path
from pkgutil import iter_modules from pkgutil import iter_modules
from sqlite3 import Connection, Cursor from sqlite3 import Connection, Cursor
from typing import Callable, List
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -83,22 +83,22 @@ class Migrations(LazyLogging):
"data migration %s at index %s has been performed", "data migration %s at index %s has been performed",
migration.name, migration.index) migration.name, migration.index)
def migrations(self) -> List[Migration]: def migrations(self) -> list[Migration]:
""" """
extract all migrations from the current package extract all migrations from the current package
idea comes from https://julienharbulot.com/python-dynamical-import.html idea comes from https://julienharbulot.com/python-dynamical-import.html
Returns: Returns:
List[Migration]: list of found migrations list[Migration]: list of found migrations
""" """
migrations: List[Migration] = [] migrations: list[Migration] = []
package_dir = Path(__file__).resolve().parent package_dir = Path(__file__).resolve().parent
modules = [module_name for (_, module_name, _) in iter_modules([str(package_dir)])] modules = [module_name for (_, module_name, _) in iter_modules([str(package_dir)])]
for index, module_name in enumerate(sorted(modules)): for index, module_name in enumerate(sorted(modules)):
module = import_module(f"{__name__}.{module_name}") module = import_module(f"{__name__}.{module_name}")
steps: List[str] = getattr(module, "steps", []) steps: list[str] = getattr(module, "steps", [])
self.logger.debug("found migration %s at index %s with steps count %s", module_name, index, len(steps)) self.logger.debug("found migration %s at index %s with steps count %s", module_name, index, len(steps))
migrate_data: Callable[[Connection, Configuration], None] = \ migrate_data: Callable[[Connection, Configuration], None] = \

View File

@ -18,7 +18,6 @@
# 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 sqlite3 import Connection from sqlite3 import Connection
from typing import List, Optional
from ahriman.core.database.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.user import User from ahriman.models.user import User
@ -30,7 +29,7 @@ class AuthOperations(Operations):
authorization operations authorization operations
""" """
def user_get(self, username: str) -> Optional[User]: def user_get(self, username: str) -> User | None:
""" """
get user by username get user by username
@ -38,25 +37,25 @@ class AuthOperations(Operations):
username(str): username username(str): username
Returns: Returns:
Optional[User]: user if it was found User | None: user if it was found
""" """
return next(iter(self.user_list(username, None)), None) return next(iter(self.user_list(username, None)), None)
def user_list(self, username: Optional[str], access: Optional[UserAccess]) -> List[User]: def user_list(self, username: str | None, access: UserAccess | None) -> list[User]:
""" """
get users by filter get users by filter
Args: Args:
username(Optional[str]): optional filter by username username(str | None): optional filter by username
access(Optional[UserAccess]): optional filter by role access(UserAccess | None): optional filter by role
Returns: Returns:
List[User]: list of users who match criteria list[User]: list of users who match criteria
""" """
username_filter = username.lower() if username is not None else username username_filter = username.lower() if username is not None else username
access_filter = access.value if access is not None else access access_filter = access.value if access is not None else access
def run(connection: Connection) -> List[User]: def run(connection: Connection) -> list[User]:
return [ return [
User(username=cursor["username"], password=cursor["password"], access=UserAccess(cursor["access"])) User(username=cursor["username"], password=cursor["password"], access=UserAccess(cursor["access"]))
for cursor in connection.execute( for cursor in connection.execute(

View File

@ -18,7 +18,6 @@
# 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 sqlite3 import Connection from sqlite3 import Connection
from typing import List, Optional
from ahriman.core.database.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.package import Package from ahriman.models.package import Package
@ -29,12 +28,12 @@ class BuildOperations(Operations):
operations for build queue functions operations for build queue functions
""" """
def build_queue_clear(self, package_base: Optional[str]) -> None: def build_queue_clear(self, package_base: str | None) -> None:
""" """
remove packages from build queue remove packages from build queue
Args: Args:
package_base(Optional[str]): optional filter by package base package_base(str | None): optional filter by package base
""" """
def run(connection: Connection) -> None: def run(connection: Connection) -> None:
connection.execute( connection.execute(
@ -46,14 +45,14 @@ class BuildOperations(Operations):
return self.with_connection(run, commit=True) return self.with_connection(run, commit=True)
def build_queue_get(self) -> List[Package]: def build_queue_get(self) -> list[Package]:
""" """
retrieve packages from build queue retrieve packages from build queue
Return: Return:
List[Package]: list of packages to be built list[Package]: list of packages to be built
""" """
def run(connection: Connection) -> List[Package]: def run(connection: Connection) -> list[Package]:
return [ return [
Package.from_json(row["properties"]) Package.from_json(row["properties"])
for row in connection.execute("""select * from build_queue""") for row in connection.execute("""select * from build_queue""")

View File

@ -18,7 +18,6 @@
# 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 sqlite3 import Connection from sqlite3 import Connection
from typing import List, Optional
from ahriman.core.database.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.core.util import pretty_datetime from ahriman.core.util import pretty_datetime
@ -40,7 +39,7 @@ class LogsOperations(Operations):
Return: Return:
str: full package log str: full package log
""" """
def run(connection: Connection) -> List[str]: def run(connection: Connection) -> list[str]:
return [ return [
f"""[{pretty_datetime(row["created"])}] {row["record"]}""" f"""[{pretty_datetime(row["created"])}] {row["record"]}"""
for row in connection.execute( for row in connection.execute(
@ -81,13 +80,13 @@ class LogsOperations(Operations):
return self.with_connection(run, commit=True) return self.with_connection(run, commit=True)
def logs_remove(self, package_base: str, current_process_id: Optional[int]) -> None: def logs_remove(self, package_base: str, current_process_id: int | None) -> None:
""" """
remove log records for the specified package remove log records for the specified package
Args: Args:
package_base(str): package base to remove logs package_base(str): package base to remove logs
current_process_id(Optional[int]): current process id. If set it will remove only logs belonging to another current_process_id(int | None): current process id. If set it will remove only logs belonging to another
process process
""" """
def run(connection: Connection) -> None: def run(connection: Connection) -> None:

View File

@ -19,8 +19,9 @@
# #
import sqlite3 import sqlite3
from collections.abc import Callable
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, Tuple, TypeVar from typing import Any, TypeVar
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -46,16 +47,16 @@ class Operations(LazyLogging):
self.path = path self.path = path
@staticmethod @staticmethod
def factory(cursor: sqlite3.Cursor, row: Tuple[Any, ...]) -> Dict[str, Any]: def factory(cursor: sqlite3.Cursor, row: tuple[Any, ...]) -> dict[str, Any]:
""" """
dictionary factory based on official documentation dictionary factory based on official documentation
Args: Args:
cursor(Cursor): cursor descriptor cursor(Cursor): cursor descriptor
row(Tuple[Any, ...]): fetched row row(tuple[Any, ...]): fetched row
Returns: Returns:
Dict[str, Any]: row converted to dictionary dict[str, Any]: row converted to dictionary
""" """
result = {} result = {}
for index, column in enumerate(cursor.description): for index, column in enumerate(cursor.description):

View File

@ -17,8 +17,8 @@
# 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 sqlite3 import Connection from sqlite3 import Connection
from typing import Dict, Generator, Iterable, List, Tuple
from ahriman.core.database.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.build_status import BuildStatus from ahriman.models.build_status import BuildStatus
@ -148,7 +148,7 @@ class PackageOperations(Operations):
{"package_base": package_base, "status": status.status.value, "last_updated": status.timestamp}) {"package_base": package_base, "status": status.status.value, "last_updated": status.timestamp})
@staticmethod @staticmethod
def _packages_get_select_package_bases(connection: Connection) -> Dict[str, Package]: def _packages_get_select_package_bases(connection: Connection) -> dict[str, Package]:
""" """
select package bases from the table select package bases from the table
@ -156,7 +156,7 @@ class PackageOperations(Operations):
connection(Connection): database connection connection(Connection): database connection
Returns: Returns:
Dict[str, Package]: map of the package base to its descriptor (without packages themselves) dict[str, Package]: map of the package base to its descriptor (without packages themselves)
""" """
return { return {
row["package_base"]: Package( row["package_base"]: Package(
@ -168,16 +168,16 @@ class PackageOperations(Operations):
} }
@staticmethod @staticmethod
def _packages_get_select_packages(connection: Connection, packages: Dict[str, Package]) -> Dict[str, Package]: def _packages_get_select_packages(connection: Connection, packages: dict[str, Package]) -> dict[str, Package]:
""" """
select packages from the table select packages from the table
Args: Args:
connection(Connection): database connection connection(Connection): database connection
packages(Dict[str, Package]): packages descriptor map packages(dict[str, Package]): packages descriptor map
Returns: Returns:
Dict[str, Package]: map of the package base to its descriptor including individual packages dict[str, Package]: map of the package base to its descriptor including individual packages
""" """
for row in connection.execute("""select * from packages"""): for row in connection.execute("""select * from packages"""):
if row["package_base"] not in packages: if row["package_base"] not in packages:
@ -186,7 +186,7 @@ class PackageOperations(Operations):
return packages return packages
@staticmethod @staticmethod
def _packages_get_select_statuses(connection: Connection) -> Dict[str, BuildStatus]: def _packages_get_select_statuses(connection: Connection) -> dict[str, BuildStatus]:
""" """
select package build statuses from the table select package build statuses from the table
@ -194,7 +194,7 @@ class PackageOperations(Operations):
connection(Connection): database connection connection(Connection): database connection
Returns: Returns:
Dict[str, BuildStatus]: map of the package base to its status dict[str, BuildStatus]: map of the package base to its status
""" """
return { return {
row["package_base"]: BuildStatus.from_json({"status": row["status"], "timestamp": row["last_updated"]}) row["package_base"]: BuildStatus.from_json({"status": row["status"], "timestamp": row["last_updated"]})
@ -230,14 +230,14 @@ class PackageOperations(Operations):
return self.with_connection(run, commit=True) return self.with_connection(run, commit=True)
def packages_get(self) -> List[Tuple[Package, BuildStatus]]: def packages_get(self) -> list[tuple[Package, BuildStatus]]:
""" """
get package list and their build statuses from database get package list and their build statuses from database
Return: Return:
List[Tuple[Package, BuildStatus]]: list of package properties and their statuses list[tuple[Package, BuildStatus]]: list of package properties and their statuses
""" """
def run(connection: Connection) -> Generator[Tuple[Package, BuildStatus], None, None]: def run(connection: Connection) -> Generator[tuple[Package, BuildStatus], None, None]:
packages = self._packages_get_select_package_bases(connection) packages = self._packages_get_select_package_bases(connection)
statuses = self._packages_get_select_statuses(connection) statuses = self._packages_get_select_statuses(connection)
for package_base, package in self._packages_get_select_packages(connection, packages).items(): for package_base, package in self._packages_get_select_packages(connection, packages).items():
@ -256,12 +256,12 @@ class PackageOperations(Operations):
lambda connection: self._package_update_insert_base(connection, package), lambda connection: self._package_update_insert_base(connection, package),
commit=True) commit=True)
def remotes_get(self) -> Dict[str, RemoteSource]: def remotes_get(self) -> dict[str, RemoteSource]:
""" """
get packages remotes based on current settings get packages remotes based on current settings
Returns: Returns:
Dict[str, RemoteSource]: map of package base to its remote sources dict[str, RemoteSource]: map of package base to its remote sources
""" """
packages = self.with_connection(self._packages_get_select_package_bases) packages = self.with_connection(self._packages_get_select_package_bases)
return { return {

View File

@ -20,7 +20,6 @@
from collections import defaultdict from collections import defaultdict
from sqlite3 import Connection from sqlite3 import Connection
from typing import Dict, List, Optional, Tuple
from ahriman.core.database.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -31,7 +30,7 @@ class PatchOperations(Operations):
operations for patches operations for patches
""" """
def patches_get(self, package_base: str) -> List[PkgbuildPatch]: def patches_get(self, package_base: str) -> list[PkgbuildPatch]:
""" """
retrieve patches for the package retrieve patches for the package
@ -39,7 +38,7 @@ class PatchOperations(Operations):
package_base(str): package base to search for patches package_base(str): package base to search for patches
Returns: Returns:
List[PkgbuildPatch]: plain text patch for the package list[PkgbuildPatch]: plain text patch for the package
""" """
return self.patches_list(package_base, []).get(package_base, []) return self.patches_list(package_base, []).get(package_base, [])
@ -65,18 +64,18 @@ class PatchOperations(Operations):
return self.with_connection(run, commit=True) return self.with_connection(run, commit=True)
def patches_list(self, package_base: Optional[str], variables: List[str]) -> Dict[str, List[PkgbuildPatch]]: def patches_list(self, package_base: str | None, variables: list[str]) -> dict[str, list[PkgbuildPatch]]:
""" """
extract all patches extract all patches
Args: Args:
package_base(Optional[str]): optional filter by package base package_base(str | None): optional filter by package base
variables(List[str]): extract patches only for specified PKGBUILD variables variables(list[str]): extract patches only for specified PKGBUILD variables
Returns: Returns:
Dict[str, List[PkgbuildPatch]]: map of package base to patch content dict[str, list[PkgbuildPatch]]: map of package base to patch content
""" """
def run(connection: Connection) -> List[Tuple[str, PkgbuildPatch]]: def run(connection: Connection) -> list[tuple[str, PkgbuildPatch]]:
return [ return [
(cursor["package_base"], PkgbuildPatch(cursor["variable"], cursor["patch"])) (cursor["package_base"], PkgbuildPatch(cursor["variable"], cursor["patch"]))
for cursor in connection.execute( for cursor in connection.execute(
@ -85,20 +84,20 @@ class PatchOperations(Operations):
] ]
# we could use itertools & operator but why? # we could use itertools & operator but why?
patches: Dict[str, List[PkgbuildPatch]] = defaultdict(list) patches: dict[str, list[PkgbuildPatch]] = defaultdict(list)
for package, patch in self.with_connection(run): for package, patch in self.with_connection(run):
if variables and patch.key not in variables: if variables and patch.key not in variables:
continue continue
patches[package].append(patch) patches[package].append(patch)
return dict(patches) return dict(patches)
def patches_remove(self, package_base: str, variables: List[str]) -> None: def patches_remove(self, package_base: str, variables: list[str]) -> None:
""" """
remove patch set remove patch set
Args: Args:
package_base(str): package base to clear patches package_base(str): package base to clear patches
variables(List[str]): remove patches only for specified PKGBUILD variables variables(list[str]): remove patches only for specified PKGBUILD variables
""" """
def run_many(connection: Connection) -> None: def run_many(connection: Connection) -> None:
connection.executemany( connection.executemany(

View File

@ -23,7 +23,6 @@ import json
import sqlite3 import sqlite3
from pathlib import Path from pathlib import Path
from typing import Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations import Migrations from ahriman.core.database.migrations import Migrations
@ -47,7 +46,7 @@ class SQLite(AuthOperations, BuildOperations, LogsOperations, PackageOperations,
""" """
@classmethod @classmethod
def load(cls: Type[SQLite], configuration: Configuration) -> SQLite: def load(cls: type[SQLite], configuration: Configuration) -> SQLite:
""" """
construct instance from configuration construct instance from configuration

View File

@ -17,8 +17,6 @@
# 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 typing import List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.core.util import pretty_datetime from ahriman.core.util import pretty_datetime
from ahriman.models.aur_package import AURPackage from ahriman.models.aur_package import AURPackage
@ -43,12 +41,12 @@ class AurPrinter(StringPrinter):
StringPrinter.__init__(self, f"{package.name} {package.version} ({package.num_votes})") StringPrinter.__init__(self, f"{package.name} {package.version} ({package.num_votes})")
self.package = package self.package = package
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [ return [
Property("Package base", self.package.package_base), Property("Package base", self.package.package_base),

View File

@ -17,8 +17,6 @@
# 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 typing import Dict, List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.property import Property from ahriman.models.property import Property
@ -28,9 +26,9 @@ class ConfigurationPrinter(StringPrinter):
print content of the configuration section print content of the configuration section
Attributes: Attributes:
HIDE_KEYS(List[str]): (class attribute) hide values for mentioned keys. This list must be used in order to hide HIDE_KEYS(list[str]): (class attribute) hide values for mentioned keys. This list must be used in order to hide
passwords from outputs passwords from outputs
values(Dict[str, str]): configuration values dictionary values(dict[str, str]): configuration values dictionary
""" """
HIDE_KEYS = [ HIDE_KEYS = [
@ -42,23 +40,23 @@ class ConfigurationPrinter(StringPrinter):
"secret_key", # aws secret key "secret_key", # aws secret key
] ]
def __init__(self, section: str, values: Dict[str, str]) -> None: def __init__(self, section: str, values: dict[str, str]) -> None:
""" """
default constructor default constructor
Args: Args:
section(str): section name section(str): section name
values(Dict[str, str]): configuration values dictionary values(dict[str, str]): configuration values dictionary
""" """
StringPrinter.__init__(self, f"[{section}]") StringPrinter.__init__(self, f"[{section}]")
self.values = values self.values = values
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [ return [
Property(key, value, is_required=key not in self.HIDE_KEYS) Property(key, value, is_required=key not in self.HIDE_KEYS)

View File

@ -17,8 +17,6 @@
# 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 typing import List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.build_status import BuildStatus from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package from ahriman.models.package import Package
@ -46,12 +44,12 @@ class PackagePrinter(StringPrinter):
self.package = package self.package = package
self.status = status self.status = status
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [ return [
Property("Version", self.package.version, is_required=True), Property("Version", self.package.version, is_required=True),

View File

@ -17,8 +17,6 @@
# 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 typing import List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.property import Property from ahriman.models.property import Property
@ -29,26 +27,26 @@ class PatchPrinter(StringPrinter):
print content of the PKGBUILD patch print content of the PKGBUILD patch
Attributes: Attributes:
patches(List[PkgbuildPatch]): PKGBUILD patch object patches(list[PkgbuildPatch]): PKGBUILD patch object
""" """
def __init__(self, package_base: str, patches: List[PkgbuildPatch]) -> None: def __init__(self, package_base: str, patches: list[PkgbuildPatch]) -> None:
""" """
default constructor default constructor
Args: Args:
package_base(str): package base package_base(str): package base
patches(List[PkgbuildPatch]): PKGBUILD patch object patches(list[PkgbuildPatch]): PKGBUILD patch object
""" """
StringPrinter.__init__(self, package_base) StringPrinter.__init__(self, package_base)
self.patches = patches self.patches = patches
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [ return [
Property(patch.key or "Full source diff", patch.value, is_required=True) Property(patch.key or "Full source diff", patch.value, is_required=True)

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 typing import Callable, List, Optional from collections.abc import Callable
from ahriman.models.property import Property from ahriman.models.property import Property
@ -44,19 +44,19 @@ class Printer:
indent = "\t" * prop.indent indent = "\t" * prop.indent
log_fn(f"{indent}{prop.name}{separator}{prop.value}") log_fn(f"{indent}{prop.name}{separator}{prop.value}")
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [] return []
def title(self) -> Optional[str]: def title(self) -> str | None:
""" """
generate entry title from content generate entry title from content
Returns: Returns:
Optional[str]: content title if it can be generated and None otherwise str | None: content title if it can be generated and None otherwise
""" """

View File

@ -17,8 +17,6 @@
# 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 typing import Optional
from ahriman.core.formatters import Printer from ahriman.core.formatters import Printer
@ -39,11 +37,11 @@ class StringPrinter(Printer):
""" """
self.content = content self.content = content
def title(self) -> Optional[str]: def title(self) -> str | None:
""" """
generate entry title from content generate entry title from content
Returns: Returns:
Optional[str]: content title if it can be generated and None otherwise str | None: content title if it can be generated and None otherwise
""" """
return self.content return self.content

View File

@ -17,8 +17,6 @@
# 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 typing import Iterable, List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.property import Property from ahriman.models.property import Property
@ -29,25 +27,25 @@ class TreePrinter(StringPrinter):
print content of the package tree level print content of the package tree level
Attributes: Attributes:
packages(Iterable[Package]): packages which belong to this level packages(list[Package]): packages which belong to this level
""" """
def __init__(self, level: int, packages: Iterable[Package]) -> None: def __init__(self, level: int, packages: list[Package]) -> None:
""" """
default constructor default constructor
Args: Args:
level(int): dependencies tree level level(int): dependencies tree level
packages(Iterable[Package]): packages which belong to this level packages(list[Package]): packages which belong to this level
""" """
StringPrinter.__init__(self, f"level {level}") StringPrinter.__init__(self, f"level {level}")
self.packages = packages self.packages = packages
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [Property(package.base, package.version, is_required=True) for package in self.packages] return [Property(package.base, package.version, is_required=True) for package in self.packages]

View File

@ -17,8 +17,6 @@
# 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 typing import List, Optional
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.property import Property from ahriman.models.property import Property
@ -30,26 +28,26 @@ class UpdatePrinter(StringPrinter):
Attributes: Attributes:
package(Package): remote (new) package object package(Package): remote (new) package object
local_version(Optional[str]): local version of the package if any local_version(str | None): local version of the package if any
""" """
def __init__(self, remote: Package, local_version: Optional[str]) -> None: def __init__(self, remote: Package, local_version: str | None) -> None:
""" """
default constructor default constructor
Args: Args:
remote(Package): remote (new) package object remote(Package): remote (new) package object
local_version(Optional[str]): local version of the package if any local_version(str | None): local version of the package if any
""" """
StringPrinter.__init__(self, remote.base) StringPrinter.__init__(self, remote.base)
self.package = remote self.package = remote
self.local_version = local_version or "N/A" self.local_version = local_version or "N/A"
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [Property(self.local_version, self.package.version, is_required=True)] return [Property(self.local_version, self.package.version, is_required=True)]

View File

@ -17,8 +17,6 @@
# 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 typing import List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.property import Property from ahriman.models.property import Property
from ahriman.models.user import User from ahriman.models.user import User
@ -42,11 +40,11 @@ class UserPrinter(StringPrinter):
StringPrinter.__init__(self, user.username) StringPrinter.__init__(self, user.username)
self.user = user self.user = user
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [Property("role", self.user.access.value, is_required=True)] return [Property("role", self.user.access.value, is_required=True)]

View File

@ -17,7 +17,8 @@
# 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 typing import Any, Dict, Generator, List, Union from collections.abc import Generator
from typing import Any
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.property import Property from ahriman.models.property import Property
@ -29,30 +30,30 @@ class ValidationPrinter(StringPrinter):
Attributes: Attributes:
node(str): root level name node(str): root level name
errors(List[Union[str, Dict[str, Any]]]): validation errors errors(list[str | dict[str, Any]]): validation errors
""" """
def __init__(self, node: str, errors: List[Union[str, Dict[str, Any]]]) -> None: def __init__(self, node: str, errors: list[str | dict[str, Any]]) -> None:
""" """
default constructor default constructor
Args: Args:
node(str): root level name node(str): root level name
errors(List[Union[str, Dict[str, Any]]]): validation errors errors(list[str | dict[str, Any]]): validation errors
""" """
StringPrinter.__init__(self, node) StringPrinter.__init__(self, node)
self.node = node self.node = node
self.errors = errors self.errors = errors
@staticmethod @staticmethod
def get_error_messages(node: str, errors: List[Union[str, Dict[str, Any]]], def get_error_messages(node: str, errors: list[str | dict[str, Any]],
current_level: int = 1) -> Generator[Property, None, None]: current_level: int = 1) -> Generator[Property, None, None]:
""" """
extract default error message from cerberus class extract default error message from cerberus class
Args: Args:
node(str): current node level name node(str): current node level name
errors(List[Union[str, Dict[str, Any]]]): current node validation errors errors(list[str | dict[str, Any]]): current node validation errors
current_level(int, optional): current level number (Default value = 1) current_level(int, optional): current level number (Default value = 1)
Yields: Yields:
@ -67,11 +68,11 @@ class ValidationPrinter(StringPrinter):
else: # current node errors else: # current node errors
yield Property(node, error, is_required=True, indent=current_level) yield Property(node, error, is_required=True, indent=current_level)
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return list(self.get_error_messages(self.node, self.errors)) return list(self.get_error_messages(self.node, self.errors))

View File

@ -17,8 +17,6 @@
# 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 typing import Dict, List
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.property import Property from ahriman.models.property import Property
@ -28,26 +26,26 @@ class VersionPrinter(StringPrinter):
print content of the python package versions print content of the python package versions
Attributes: Attributes:
packages(Dict[str, str]): map of package name to its version packages(dict[str, str]): map of package name to its version
""" """
def __init__(self, title: str, packages: Dict[str, str]) -> None: def __init__(self, title: str, packages: dict[str, str]) -> None:
""" """
default constructor default constructor
Args: Args:
title(str): title of the message title(str): title of the message
packages(Dict[str, str]): map of package name to its version packages(dict[str, str]): map of package name to its version
""" """
StringPrinter.__init__(self, title) StringPrinter.__init__(self, title)
self.packages = packages self.packages = packages
def properties(self) -> List[Property]: def properties(self) -> list[Property]:
""" """
convert content into printable data convert content into printable data
Returns: Returns:
List[Property]: list of content properties list[Property]: list of content properties
""" """
return [ return [
Property(package, version, is_required=True) Property(package, version, is_required=True)

View File

@ -17,8 +17,6 @@
# 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 typing import List, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.gitremote.remote_pull import RemotePull from ahriman.core.gitremote.remote_pull import RemotePull
from ahriman.core.triggers import Trigger from ahriman.core.triggers import Trigger
@ -29,7 +27,7 @@ class RemotePullTrigger(Trigger):
trigger based on pulling PKGBUILDs before the actions trigger based on pulling PKGBUILDs before the actions
Attributes: Attributes:
targets(List[str]): git remote target list targets(list[str]): git remote target list
""" """
CONFIGURATION_SCHEMA = { CONFIGURATION_SCHEMA = {
@ -70,7 +68,7 @@ class RemotePullTrigger(Trigger):
self.targets = self.configuration_sections(configuration) self.targets = self.configuration_sections(configuration)
@classmethod @classmethod
def configuration_sections(cls: Type[Trigger], configuration: Configuration) -> List[str]: def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
""" """
extract configuration sections from configuration extract configuration sections from configuration
@ -78,7 +76,7 @@ class RemotePullTrigger(Trigger):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
List[str]: read configuration sections belong to this trigger list[str]: read configuration sections belong to this trigger
""" """
return configuration.getlist("remote-pull", "target", fallback=[]) return configuration.getlist("remote-pull", "target", fallback=[])

View File

@ -19,9 +19,9 @@
# #
import shutil import shutil
from collections.abc import Generator
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Generator
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -39,7 +39,7 @@ class RemotePush(LazyLogging):
sync PKGBUILDs to remote repository after actions sync PKGBUILDs to remote repository after actions
Attributes: Attributes:
commit_author(Optional[str]): optional commit author in form of git config (i.e. ``user <user@host>``) commit_author(str | None): optional commit author in form of git config (i.e. ``user <user@host>``)
database(SQLite): database instance database(SQLite): database instance
remote_source(RemoteSource): repository remote source (remote pull url and branch) remote_source(RemoteSource): repository remote source (remote pull url and branch)
""" """

View File

@ -17,8 +17,6 @@
# 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 typing import Iterable, List, Type
from ahriman.core import context from ahriman.core import context
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
@ -34,7 +32,7 @@ class RemotePushTrigger(Trigger):
trigger for syncing PKGBUILDs to remote repository trigger for syncing PKGBUILDs to remote repository
Attributes: Attributes:
targets(List[str]): git remote target list targets(list[str]): git remote target list
""" """
CONFIGURATION_SCHEMA = { CONFIGURATION_SCHEMA = {
@ -78,7 +76,7 @@ class RemotePushTrigger(Trigger):
self.targets = self.configuration_sections(configuration) self.targets = self.configuration_sections(configuration)
@classmethod @classmethod
def configuration_sections(cls: Type[Trigger], configuration: Configuration) -> List[str]: def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
""" """
extract configuration sections from configuration extract configuration sections from configuration
@ -86,17 +84,17 @@ class RemotePushTrigger(Trigger):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
List[str]: read configuration sections belong to this trigger list[str]: read configuration sections belong to this trigger
""" """
return configuration.getlist("remote-push", "target", fallback=[]) return configuration.getlist("remote-push", "target", fallback=[])
def on_result(self, result: Result, packages: Iterable[Package]) -> None: def on_result(self, result: Result, packages: list[Package]) -> None:
""" """
trigger action which will be called after build process with process result trigger action which will be called after build process with process result
Args: Args:
result(Result): build result result(Result): build result
packages(Iterable[Package]): list of all available packages packages(list[Package]): list of all available packages
Raises: Raises:
GitRemoteError: if database is not set in context GitRemoteError: if database is not set in context

View File

@ -20,7 +20,8 @@
import contextlib import contextlib
import logging import logging
from typing import Any, Generator from collections.abc import Generator
from typing import Any
class LazyLogging: class LazyLogging:

View File

@ -17,8 +17,6 @@
# 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 typing import Iterable
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import BuildPrinter from ahriman.core.formatters import BuildPrinter
from ahriman.core.report.report import Report from ahriman.core.report.report import Report
@ -46,12 +44,12 @@ class Console(Report):
Report.__init__(self, architecture, configuration) Report.__init__(self, architecture, configuration)
self.use_utf = configuration.getboolean(section, "use_utf", fallback=True) self.use_utf = configuration.getboolean(section, "use_utf", fallback=True)
def generate(self, packages: Iterable[Package], result: Result) -> None: def generate(self, packages: list[Package], result: Result) -> None:
""" """
generate report for the specified packages generate report for the specified packages
Args: Args:
packages(Iterable[Package]): list of packages to generate report packages(list[Package]): list of packages to generate report
result(Result): build result result(Result): build result
""" """
for package in result.success: for package in result.success:

View File

@ -21,7 +21,6 @@ import smtplib
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from typing import Dict, Iterable
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.report.jinja_template import JinjaTemplate from ahriman.core.report.jinja_template import JinjaTemplate
@ -40,13 +39,13 @@ class Email(Report, JinjaTemplate):
full_template_path(Path): path to template for full package list full_template_path(Path): path to template for full package list
host(str): SMTP host to connect host(str): SMTP host to connect
no_empty_report(bool): skip empty report generation no_empty_report(bool): skip empty report generation
password(Optional[str]): password to authenticate via SMTP password(str | None): password to authenticate via SMTP
port(int): SMTP port to connect port(int): SMTP port to connect
receivers(List[str]): list of receivers emails receivers(list[str]): list of receivers emails
sender(str): sender email address sender(str): sender email address
ssl(SmtpSSLSettings): SSL mode for SMTP connection ssl(SmtpSSLSettings): SSL mode for SMTP connection
template_path(Path): path to template for built packages template_path(Path): path to template for built packages
user(Optional[str]): username to authenticate via SMTP user(str | None): username to authenticate via SMTP
""" """
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None: def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
@ -74,13 +73,13 @@ class Email(Report, JinjaTemplate):
self.ssl = SmtpSSLSettings.from_option(configuration.get(section, "ssl", fallback="disabled")) self.ssl = SmtpSSLSettings.from_option(configuration.get(section, "ssl", fallback="disabled"))
self.user = configuration.get(section, "user", fallback=None) self.user = configuration.get(section, "user", fallback=None)
def _send(self, text: str, attachment: Dict[str, str]) -> None: def _send(self, text: str, attachment: dict[str, str]) -> None:
""" """
send email callback send email callback
Args: Args:
text(str): email body text text(str): email body text
attachment(Dict[str, str]): map of attachment filename to attachment content attachment(dict[str, str]): map of attachment filename to attachment content
""" """
message = MIMEMultipart() message = MIMEMultipart()
message["From"] = self.sender message["From"] = self.sender
@ -104,12 +103,12 @@ class Email(Report, JinjaTemplate):
session.sendmail(self.sender, self.receivers, message.as_string()) session.sendmail(self.sender, self.receivers, message.as_string())
session.quit() session.quit()
def generate(self, packages: Iterable[Package], result: Result) -> None: def generate(self, packages: list[Package], result: Result) -> None:
""" """
generate report for the specified packages generate report for the specified packages
Args: Args:
packages(Iterable[Package]): list of packages to generate report packages(list[Package]): list of packages to generate report
result(Result): build result result(Result): build result
""" """
if self.no_empty_report and not result.success: if self.no_empty_report and not result.success:

View File

@ -17,8 +17,6 @@
# 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 typing import Iterable
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.report.jinja_template import JinjaTemplate from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.core.report.report import Report from ahriman.core.report.report import Report
@ -50,12 +48,12 @@ class HTML(Report, JinjaTemplate):
self.report_path = configuration.getpath(section, "path") self.report_path = configuration.getpath(section, "path")
self.template_path = configuration.getpath(section, "template_path") self.template_path = configuration.getpath(section, "template_path")
def generate(self, packages: Iterable[Package], result: Result) -> None: def generate(self, packages: list[Package], result: Result) -> None:
""" """
generate report for the specified packages generate report for the specified packages
Args: Args:
packages(Iterable[Package]): list of packages to generate report packages(list[Package]): list of packages to generate report
result(Result): build result result(Result): build result
""" """
html = self.make_html(Result(success=packages), self.template_path) html = self.make_html(Result(success=packages), self.template_path)

View File

@ -19,8 +19,8 @@
# #
import jinja2 import jinja2
from collections.abc import Callable
from pathlib import Path from pathlib import Path
from typing import Callable, Dict
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.sign.gpg import GPG from ahriman.core.sign.gpg import GPG
@ -56,11 +56,11 @@ class JinjaTemplate:
* repository - repository name, string, required * repository - repository name, string, required
Attributes: Attributes:
homepage(Optional[str]): homepage link if any (for footer) homepage(str | None): homepage link if any (for footer)
link_path(str): prefix fo packages to download link_path(str): prefix fo packages to download
name(str): repository name name(str): repository name
default_pgp_key(Optional[str]): default PGP key default_pgp_key(str | None): default PGP key
sign_targets(Set[SignSettings]): targets to sign enabled in configuration sign_targets(set[SignSettings]): targets to sign enabled in configuration
""" """
def __init__(self, section: str, configuration: Configuration) -> None: def __init__(self, section: str, configuration: Configuration) -> None:
@ -108,7 +108,7 @@ class JinjaTemplate:
"version": base.version "version": base.version
} for base in result.success for package, properties in base.packages.items() } for base in result.success for package, properties in base.packages.items()
] ]
comparator: Callable[[Dict[str, str]], str] = lambda item: item["filename"] comparator: Callable[[dict[str, str]], str] = lambda item: item["filename"]
return template.render( return template.render(
homepage=self.homepage, homepage=self.homepage,

View File

@ -19,8 +19,6 @@
# #
from __future__ import annotations from __future__ import annotations
from typing import Iterable, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ReportError from ahriman.core.exceptions import ReportError
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -69,7 +67,7 @@ class Report(LazyLogging):
self.configuration = configuration self.configuration = configuration
@classmethod @classmethod
def load(cls: Type[Report], architecture: str, configuration: Configuration, target: str) -> Report: def load(cls: type[Report], architecture: str, configuration: Configuration, target: str) -> Report:
""" """
load client from settings load client from settings
@ -97,22 +95,22 @@ class Report(LazyLogging):
return Telegram(architecture, configuration, section) return Telegram(architecture, configuration, section)
return cls(architecture, configuration) # should never happen return cls(architecture, configuration) # should never happen
def generate(self, packages: Iterable[Package], result: Result) -> None: def generate(self, packages: list[Package], result: Result) -> None:
""" """
generate report for the specified packages generate report for the specified packages
Args: Args:
packages(Iterable[Package]): list of packages to generate report packages(list[Package]): list of packages to generate report
result(Result): build result result(Result): build result
""" """
def run(self, result: Result, packages: Iterable[Package]) -> None: def run(self, result: Result, packages: list[Package]) -> None:
""" """
run report generation run report generation
Args: Args:
result(Result): build result result(Result): build result
packages(Iterable[Package]): list of packages to generate report packages(list[Package]): list of packages to generate report
Raises: Raises:
ReportFailed: in case of any report unmatched exception ReportFailed: in case of any report unmatched exception

View File

@ -17,8 +17,6 @@
# 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 typing import Iterable, List, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.triggers import Trigger from ahriman.core.triggers import Trigger
from ahriman.core.report.report import Report from ahriman.core.report.report import Report
@ -31,7 +29,7 @@ class ReportTrigger(Trigger):
report trigger report trigger
Attributes: Attributes:
targets(List[str]): report target list targets(list[str]): report target list
""" """
CONFIGURATION_SCHEMA = { CONFIGURATION_SCHEMA = {
@ -207,7 +205,7 @@ class ReportTrigger(Trigger):
self.targets = self.configuration_sections(configuration) self.targets = self.configuration_sections(configuration)
@classmethod @classmethod
def configuration_sections(cls: Type[Trigger], configuration: Configuration) -> List[str]: def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
""" """
extract configuration sections from configuration extract configuration sections from configuration
@ -215,17 +213,17 @@ class ReportTrigger(Trigger):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
List[str]: read configuration sections belong to this trigger list[str]: read configuration sections belong to this trigger
""" """
return configuration.getlist("report", "target", fallback=[]) return configuration.getlist("report", "target", fallback=[])
def on_result(self, result: Result, packages: Iterable[Package]) -> None: def on_result(self, result: Result, packages: list[Package]) -> None:
""" """
run trigger run trigger
Args: Args:
result(Result): build result result(Result): build result
packages(Iterable[Package]): list of all available packages packages(list[Package]): list of all available packages
""" """
for target in self.targets: for target in self.targets:
runner = Report.load(self.architecture, self.configuration, target) runner = Report.load(self.architecture, self.configuration, target)

View File

@ -19,8 +19,6 @@
# #
import requests # technically we could use python-telegram-bot, but it is just a single request, c'mon import requests # technically we could use python-telegram-bot, but it is just a single request, c'mon
from typing import Iterable
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.report.jinja_template import JinjaTemplate from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.core.report.report import Report from ahriman.core.report.report import Report
@ -84,12 +82,12 @@ class Telegram(Report, JinjaTemplate):
self.logger.exception("could not perform request") self.logger.exception("could not perform request")
raise raise
def generate(self, packages: Iterable[Package], result: Result) -> None: def generate(self, packages: list[Package], result: Result) -> None:
""" """
generate report for the specified packages generate report for the specified packages
Args: Args:
packages(Iterable[Package]): list of packages to generate report packages(list[Package]): list of packages to generate report
result(Result): build result result(Result): build result
""" """
if not result.success: if not result.success:

View File

@ -20,7 +20,6 @@
import shutil import shutil
from pathlib import Path from pathlib import Path
from typing import List
from ahriman.core.repository.repository_properties import RepositoryProperties from ahriman.core.repository.repository_properties import RepositoryProperties
@ -69,12 +68,12 @@ class Cleaner(RepositoryProperties):
self.logger.info("clear build queue") self.logger.info("clear build queue")
self.database.build_queue_clear(None) self.database.build_queue_clear(None)
def packages_built(self) -> List[Path]: def packages_built(self) -> list[Path]:
""" """
get list of files in built packages directory get list of files in built packages directory
Returns: Returns:
List[Path]: list of filenames from the directory list[Path]: list of filenames from the directory
Raises: Raises:
NotImplementedError: not implemented method NotImplementedError: not implemented method

View File

@ -19,9 +19,9 @@
# #
import shutil import shutil
from collections.abc import Iterable
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Dict, Iterable, List, Optional
from ahriman.core.build_tools.task import Task from ahriman.core.build_tools.task import Task
from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.cleaner import Cleaner
@ -36,7 +36,7 @@ class Executor(Cleaner):
trait for common repository update processes trait for common repository update processes
""" """
def load_archives(self, packages: Iterable[Path]) -> List[Package]: def load_archives(self, packages: Iterable[Path]) -> list[Package]:
""" """
load packages from list of archives load packages from list of archives
@ -44,19 +44,19 @@ class Executor(Cleaner):
packages(Iterable[Path]): paths to package archives packages(Iterable[Path]): paths to package archives
Returns: Returns:
List[Package]: list of read packages list[Package]: list of read packages
Raises: Raises:
NotImplementedError: not implemented method NotImplementedError: not implemented method
""" """
raise NotImplementedError raise NotImplementedError
def packages(self) -> List[Package]: def packages(self) -> list[Package]:
""" """
generate list of repository packages generate list of repository packages
Returns: Returns:
List[Package]: list of packages properties list[Package]: list of packages properties
Raises: Raises:
NotImplementedError: not implemented method NotImplementedError: not implemented method
@ -122,8 +122,8 @@ class Executor(Cleaner):
except Exception: except Exception:
self.logger.exception("could not remove %s", package) self.logger.exception("could not remove %s", package)
packages_to_remove: Dict[str, Path] = {} packages_to_remove: dict[str, Path] = {}
bases_to_remove: List[str] = [] bases_to_remove: list[str] = []
# build package list based on user input # build package list based on user input
requested = set(packages) requested = set(packages)
@ -177,7 +177,7 @@ class Executor(Cleaner):
shutil.move(self.paths.packages / archive.filename, self.paths.packages / safe) shutil.move(self.paths.packages / archive.filename, self.paths.packages / safe)
archive.filename = safe archive.filename = safe
def update_single(name: Optional[str], package_base: str) -> None: def update_single(name: str | None, package_base: str) -> None:
if name is None: if name is None:
self.logger.warning("received empty package name for base %s", package_base) self.logger.warning("received empty package name for base %s", package_base)
return # suppress type checking, it never can be none actually return # suppress type checking, it never can be none actually
@ -191,7 +191,7 @@ class Executor(Cleaner):
self.repo.add(package_path) self.repo.add(package_path)
current_packages = self.packages() current_packages = self.packages()
removed_packages: List[str] = [] # list of packages which have been removed from the base removed_packages: list[str] = [] # list of packages which have been removed from the base
updates = self.load_archives(packages) updates = self.load_archives(packages)
result = Result() result = Result()

View File

@ -19,8 +19,8 @@
# #
from __future__ import annotations from __future__ import annotations
from collections.abc import Iterable
from pathlib import Path from pathlib import Path
from typing import Dict, Iterable, List, Optional, Type
from ahriman.core import _Context, context from ahriman.core import _Context, context
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
@ -58,7 +58,7 @@ class Repository(Executor, UpdateHandler):
""" """
@classmethod @classmethod
def load(cls: Type[Repository], architecture: str, configuration: Configuration, database: SQLite, *, def load(cls: type[Repository], architecture: str, configuration: Configuration, database: SQLite, *,
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> Repository: report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> Repository:
""" """
load instance from argument list load instance from argument list
@ -99,7 +99,7 @@ class Repository(Executor, UpdateHandler):
context.set(ctx) context.set(ctx)
def load_archives(self, packages: Iterable[Path]) -> List[Package]: def load_archives(self, packages: Iterable[Path]) -> list[Package]:
""" """
load packages from list of archives load packages from list of archives
@ -107,11 +107,11 @@ class Repository(Executor, UpdateHandler):
packages(Iterable[Path]): paths to package archives packages(Iterable[Path]): paths to package archives
Returns: Returns:
List[Package]: list of read packages list[Package]: list of read packages
""" """
sources = self.database.remotes_get() sources = self.database.remotes_get()
result: Dict[str, Package] = {} result: dict[str, Package] = {}
# we are iterating over bases, not single packages # we are iterating over bases, not single packages
for full_path in packages: for full_path in packages:
try: try:
@ -130,34 +130,34 @@ class Repository(Executor, UpdateHandler):
self.logger.exception("could not load package from %s", full_path) self.logger.exception("could not load package from %s", full_path)
return list(result.values()) return list(result.values())
def packages(self) -> List[Package]: def packages(self) -> list[Package]:
""" """
generate list of repository packages generate list of repository packages
Returns: Returns:
List[Package]: list of packages properties list[Package]: list of packages properties
""" """
return self.load_archives(filter(package_like, self.paths.repository.iterdir())) return self.load_archives(filter(package_like, self.paths.repository.iterdir()))
def packages_built(self) -> List[Path]: def packages_built(self) -> list[Path]:
""" """
get list of files in built packages directory get list of files in built packages directory
Returns: Returns:
List[Path]: list of filenames from the directory list[Path]: list of filenames from the directory
""" """
return list(filter(package_like, self.paths.packages.iterdir())) return list(filter(package_like, self.paths.packages.iterdir()))
def packages_depend_on(self, packages: List[Package], depends_on: Optional[Iterable[str]]) -> List[Package]: def packages_depend_on(self, packages: list[Package], depends_on: Iterable[str] | None) -> list[Package]:
""" """
extract list of packages which depends on specified package extract list of packages which depends on specified package
Args: Args:
packages(List[Package]): list of packages to be filtered packages(list[Package]): list of packages to be filtered
depends_on(Optional[Iterable[str]]): dependencies of the packages depends_on(Iterable[str] | None): dependencies of the packages
Returns: Returns:
List[Package]: list of repository packages which depend on specified packages list[Package]: list of repository packages which depend on specified packages
""" """
if depends_on is None: if depends_on is None:
return packages # no list provided extract everything by default return packages # no list provided extract everything by default

View File

@ -37,7 +37,7 @@ class RepositoryProperties(LazyLogging):
architecture(str): repository architecture architecture(str): repository architecture
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
database(SQLite): database instance database(SQLite): database instance
ignore_list(List[str]): package bases which will be ignored during auto updates ignore_list(list[str]): package bases which will be ignored during auto updates
name(str): repository name name(str): repository name
pacman(Pacman): alpm wrapper instance pacman(Pacman): alpm wrapper instance
paths(RepositoryPaths): repository paths instance paths(RepositoryPaths): repository paths instance

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 typing import Iterable, List from collections.abc import Iterable
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources
from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.cleaner import Cleaner
@ -30,19 +30,19 @@ class UpdateHandler(Cleaner):
trait to get package update list trait to get package update list
""" """
def packages(self) -> List[Package]: def packages(self) -> list[Package]:
""" """
generate list of repository packages generate list of repository packages
Returns: Returns:
List[Package]: list of packages properties list[Package]: list of packages properties
Raises: Raises:
NotImplementedError: not implemented method NotImplementedError: not implemented method
""" """
raise NotImplementedError raise NotImplementedError
def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> List[Package]: def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> list[Package]:
""" """
check AUR for updates check AUR for updates
@ -51,9 +51,9 @@ class UpdateHandler(Cleaner):
vcs(bool): enable or disable checking of VCS packages vcs(bool): enable or disable checking of VCS packages
Returns: Returns:
List[Package]: list of packages which are out-of-dated list[Package]: list of packages which are out-of-dated
""" """
result: List[Package] = [] result: list[Package] = []
for local in self.packages(): for local in self.packages():
with self.in_package_context(local.base): with self.in_package_context(local.base):
@ -81,7 +81,7 @@ class UpdateHandler(Cleaner):
return result return result
def updates_local(self, *, vcs: bool) -> List[Package]: def updates_local(self, *, vcs: bool) -> list[Package]:
""" """
check local packages for updates check local packages for updates
@ -89,9 +89,9 @@ class UpdateHandler(Cleaner):
vcs(bool): enable or disable checking of VCS packages vcs(bool): enable or disable checking of VCS packages
Returns: Returns:
List[Package]: list of local packages which are out-of-dated list[Package]: list of local packages which are out-of-dated
""" """
result: List[Package] = [] result: list[Package] = []
packages = {local.base: local for local in self.packages()} packages = {local.base: local for local in self.packages()}
for cache_dir in self.paths.cache.iterdir(): for cache_dir in self.paths.cache.iterdir():
@ -114,14 +114,14 @@ class UpdateHandler(Cleaner):
return result return result
def updates_manual(self) -> List[Package]: def updates_manual(self) -> list[Package]:
""" """
check for packages for which manual update has been requested check for packages for which manual update has been requested
Returns: Returns:
List[Package]: list of packages which are out-of-dated list[Package]: list of packages which are out-of-dated
""" """
result: List[Package] = [] result: list[Package] = []
known_bases = {package.base for package in self.packages()} known_bases = {package.base for package in self.packages()}
try: try:

View File

@ -20,7 +20,6 @@
import requests import requests
from pathlib import Path from pathlib import Path
from typing import List, Optional, Set, Tuple
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import BuildError from ahriman.core.exceptions import BuildError
@ -37,8 +36,8 @@ class GPG(LazyLogging):
DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
architecture(str): repository architecture architecture(str): repository architecture
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
default_key(Optional[str]): default PGP key ID to use default_key(str | None): default PGP key ID to use
targets(Set[SignSettings]): list of targets to sign (repository, package etc) targets(set[SignSettings]): list of targets to sign (repository, package etc)
""" """
_check_output = check_output _check_output = check_output
@ -57,12 +56,12 @@ class GPG(LazyLogging):
self.targets, self.default_key = self.sign_options(configuration) self.targets, self.default_key = self.sign_options(configuration)
@property @property
def repository_sign_args(self) -> List[str]: def repository_sign_args(self) -> list[str]:
""" """
get command line arguments based on settings get command line arguments based on settings
Returns: Returns:
List[str]: command line arguments for repo-add command to sign database list[str]: command line arguments for repo-add command to sign database
""" """
if SignSettings.Repository not in self.targets: if SignSettings.Repository not in self.targets:
return [] return []
@ -72,7 +71,7 @@ class GPG(LazyLogging):
return ["--sign", "--key", self.default_key] return ["--sign", "--key", self.default_key]
@staticmethod @staticmethod
def sign_command(path: Path, key: str) -> List[str]: def sign_command(path: Path, key: str) -> list[str]:
""" """
gpg command to run gpg command to run
@ -81,12 +80,12 @@ class GPG(LazyLogging):
key(str): PGP key ID key(str): PGP key ID
Returns: Returns:
List[str]: gpg command with all required arguments list[str]: gpg command with all required arguments
""" """
return ["gpg", "-u", key, "-b", str(path)] return ["gpg", "-u", key, "-b", str(path)]
@staticmethod @staticmethod
def sign_options(configuration: Configuration) -> Tuple[Set[SignSettings], Optional[str]]: def sign_options(configuration: Configuration) -> tuple[set[SignSettings], str | None]:
""" """
extract default sign options from configuration extract default sign options from configuration
@ -94,9 +93,9 @@ class GPG(LazyLogging):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
Tuple[Set[SignSettings], Optional[str]]: tuple of sign targets and default PGP key tuple[set[SignSettings], str | None]: tuple of sign targets and default PGP key
""" """
targets: Set[SignSettings] = set() targets: set[SignSettings] = set()
for option in configuration.getlist("sign", "target", fallback=[]): for option in configuration.getlist("sign", "target", fallback=[]):
target = SignSettings.from_option(option) target = SignSettings.from_option(option)
if target == SignSettings.Disabled: if target == SignSettings.Disabled:
@ -140,7 +139,7 @@ class GPG(LazyLogging):
key_body = self.key_download(server, key) key_body = self.key_download(server, key)
GPG._check_output("gpg", "--import", input_data=key_body, logger=self.logger) GPG._check_output("gpg", "--import", input_data=key_body, logger=self.logger)
def process(self, path: Path, key: str) -> List[Path]: def process(self, path: Path, key: str) -> list[Path]:
""" """
gpg command wrapper gpg command wrapper
@ -149,7 +148,7 @@ class GPG(LazyLogging):
key(str): PGP key ID key(str): PGP key ID
Returns: Returns:
List[Path]: list of generated files including original file list[Path]: list of generated files including original file
""" """
GPG._check_output( GPG._check_output(
*GPG.sign_command(path, key), *GPG.sign_command(path, key),
@ -157,7 +156,7 @@ class GPG(LazyLogging):
logger=self.logger) logger=self.logger)
return [path, path.parent / f"{path.name}.sig"] return [path, path.parent / f"{path.name}.sig"]
def process_sign_package(self, path: Path, package_base: str) -> List[Path]: def process_sign_package(self, path: Path, package_base: str) -> list[Path]:
""" """
sign package if required by configuration sign package if required by configuration
@ -166,7 +165,7 @@ class GPG(LazyLogging):
package_base(str): package base required to check for key overrides package_base(str): package base required to check for key overrides
Returns: Returns:
List[Path]: list of generated files including original file list[Path]: list of generated files including original file
""" """
if SignSettings.Packages not in self.targets: if SignSettings.Packages not in self.targets:
return [path] return [path]
@ -176,7 +175,7 @@ class GPG(LazyLogging):
return [path] return [path]
return self.process(path, key) return self.process(path, key)
def process_sign_repository(self, path: Path) -> List[Path]: def process_sign_repository(self, path: Path) -> list[Path]:
""" """
sign repository if required by configuration sign repository if required by configuration
@ -187,7 +186,7 @@ class GPG(LazyLogging):
path(Path): path to repository database path(Path): path to repository database
Returns: Returns:
List[Path]: list of generated files including original file list[Path]: list of generated files including original file
""" """
if SignSettings.Repository not in self.targets: if SignSettings.Repository not in self.targets:
return [path] return [path]

View File

@ -22,9 +22,9 @@ from __future__ import annotations
import argparse import argparse
import uuid import uuid
from collections.abc import Callable, Iterable
from multiprocessing import Process, Queue from multiprocessing import Process, Queue
from threading import Lock, Thread from threading import Lock, Thread
from typing import Callable, Dict, Iterable, Optional, Tuple
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -37,10 +37,10 @@ class Spawn(Thread, LazyLogging):
MUST NOT be used directly, the only one usage allowed is to spawn process from web services MUST NOT be used directly, the only one usage allowed is to spawn process from web services
Attributes: Attributes:
active(Dict[str, Process]): map of active child processes required to avoid zombies active(dict[str, Process]): map of active child processes required to avoid zombies
architecture(str): repository architecture architecture(str): repository architecture
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
queue(Queue[Tuple[str, bool]]): multiprocessing queue to read updates from processes queue(Queue[tuple[str, bool]]): multiprocessing queue to read updates from processes
""" """
def __init__(self, args_parser: argparse.ArgumentParser, architecture: str, configuration: Configuration) -> None: def __init__(self, args_parser: argparse.ArgumentParser, architecture: str, configuration: Configuration) -> None:
@ -58,13 +58,13 @@ class Spawn(Thread, LazyLogging):
self.configuration = configuration self.configuration = configuration
self.lock = Lock() self.lock = Lock()
self.active: Dict[str, Process] = {} self.active: dict[str, Process] = {}
# stupid pylint does not know that it is possible # stupid pylint does not know that it is possible
self.queue: Queue[Tuple[str, bool] | None] = Queue() # pylint: disable=unsubscriptable-object self.queue: Queue[tuple[str, bool] | None] = Queue() # pylint: disable=unsubscriptable-object
@staticmethod @staticmethod
def process(callback: Callable[[argparse.Namespace, str], bool], args: argparse.Namespace, architecture: str, def process(callback: Callable[[argparse.Namespace, str], bool], args: argparse.Namespace, architecture: str,
process_id: str, queue: Queue[Tuple[str, bool]]) -> None: # pylint: disable=unsubscriptable-object process_id: str, queue: Queue[tuple[str, bool]]) -> None: # pylint: disable=unsubscriptable-object
""" """
helper to run external process helper to run external process
@ -73,7 +73,7 @@ class Spawn(Thread, LazyLogging):
args(argparse.Namespace): command line arguments args(argparse.Namespace): command line arguments
architecture(str): repository architecture architecture(str): repository architecture
process_id(str): process unique identifier process_id(str): process unique identifier
queue(Queue[Tuple[str, bool]]): output queue queue(Queue[tuple[str, bool]]): output queue
""" """
result = callback(args, architecture) result = callback(args, architecture)
queue.put((process_id, result)) queue.put((process_id, result))
@ -113,13 +113,13 @@ class Spawn(Thread, LazyLogging):
with self.lock: with self.lock:
self.active[process_id] = process self.active[process_id] = process
def key_import(self, key: str, server: Optional[str]) -> None: def key_import(self, key: str, server: str | None) -> None:
""" """
import key to service cache import key to service cache
Args: Args:
key(str): key to import key(str): key to import
server(str): PGP key server server(str | None): PGP key server
""" """
kwargs = {} if server is None else {"key-server": server} kwargs = {} if server is None else {"key-server": server}
self._spawn_process("service-key-import", key, **kwargs) self._spawn_process("service-key-import", key, **kwargs)
@ -155,7 +155,7 @@ class Spawn(Thread, LazyLogging):
""" """
self._spawn_process("package-remove", *packages) self._spawn_process("package-remove", *packages)
def packages_update(self, ) -> None: def packages_update(self) -> None:
""" """
run full repository update run full repository update
""" """

View File

@ -21,8 +21,6 @@ from __future__ import annotations
import logging import logging
from typing import List, Optional, Tuple, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.build_status import BuildStatus, BuildStatusEnum from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.internal_status import InternalStatus from ahriman.models.internal_status import InternalStatus
@ -35,7 +33,7 @@ class Client:
""" """
@classmethod @classmethod
def load(cls: Type[Client], configuration: Configuration, *, report: bool) -> Client: def load(cls: type[Client], configuration: Configuration, *, report: bool) -> Client:
""" """
load client from settings load client from settings
@ -71,15 +69,15 @@ class Client:
status(BuildStatusEnum): current package build status status(BuildStatusEnum): current package build status
""" """
def get(self, package_base: Optional[str]) -> List[Tuple[Package, BuildStatus]]: def get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
""" """
get package status get package status
Args: Args:
package_base(Optional[str]): package base to get package_base(str | None): package base to get
Returns: Returns:
List[Tuple[Package, BuildStatus]]: list of current package description and status if it has been found list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
""" """
del package_base del package_base
return [] return []

View File

@ -19,8 +19,6 @@
# #
import os import os
from typing import Dict, List, Optional, Tuple
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
from ahriman.core.exceptions import UnknownPackageError from ahriman.core.exceptions import UnknownPackageError
@ -38,7 +36,7 @@ class Watcher(LazyLogging):
Attributes: Attributes:
architecture(str): repository architecture architecture(str): repository architecture
database(SQLite): database instance database(SQLite): database instance
known(Dict[str, Tuple[Package, BuildStatus]]): list of known packages. For the most cases ``packages`` should known(dict[str, tuple[Package, BuildStatus]]): list of known packages. For the most cases ``packages`` should
be used instead be used instead
repository(Repository): repository object repository(Repository): repository object
status(BuildStatus): daemon status status(BuildStatus): daemon status
@ -57,23 +55,23 @@ class Watcher(LazyLogging):
self.database = database self.database = database
self.repository = Repository.load(architecture, configuration, database, report=False, unsafe=False) self.repository = Repository.load(architecture, configuration, database, report=False, unsafe=False)
self.known: Dict[str, Tuple[Package, BuildStatus]] = {} self.known: dict[str, tuple[Package, BuildStatus]] = {}
self.status = BuildStatus() self.status = BuildStatus()
# special variables for updating logs # special variables for updating logs
self._last_log_record_id = LogRecordId("", os.getpid()) self._last_log_record_id = LogRecordId("", os.getpid())
@property @property
def packages(self) -> List[Tuple[Package, BuildStatus]]: def packages(self) -> list[tuple[Package, BuildStatus]]:
""" """
get current known packages list get current known packages list
Returns: Returns:
List[Tuple[Package, BuildStatus]]: list of packages together with their statuses list[tuple[Package, BuildStatus]]: list of packages together with their statuses
""" """
return list(self.known.values()) return list(self.known.values())
def get(self, package_base: str) -> Tuple[Package, BuildStatus]: def get(self, package_base: str) -> tuple[Package, BuildStatus]:
""" """
get current package base build status get current package base build status
@ -81,7 +79,7 @@ class Watcher(LazyLogging):
package_base(str): package base package_base(str): package base
Returns: Returns:
Tuple[Package, BuildStatus]: package and its status tuple[Package, BuildStatus]: package and its status
Raises: Raises:
UnknownPackage: if no package found UnknownPackage: if no package found
@ -130,24 +128,24 @@ class Watcher(LazyLogging):
self.database.package_remove(package_base) self.database.package_remove(package_base)
self.remove_logs(package_base, None) self.remove_logs(package_base, None)
def remove_logs(self, package_base: str, current_process_id: Optional[int]) -> None: def remove_logs(self, package_base: str, current_process_id: int | None) -> None:
""" """
remove package related logs remove package related logs
Args: Args:
package_base(str): package base package_base(str): package base
current_process_id(int): current process id current_process_id(int | None): current process id
""" """
self.database.logs_remove(package_base, current_process_id) self.database.logs_remove(package_base, current_process_id)
def update(self, package_base: str, status: BuildStatusEnum, package: Optional[Package]) -> None: def update(self, package_base: str, status: BuildStatusEnum, package: Package | None) -> None:
""" """
update package status and description update package status and description
Args: Args:
package_base(str): package base to update package_base(str): package base to update
status(BuildStatusEnum): new build status status(BuildStatusEnum): new build status
package(Optional[Package]): optional new package description. In case if not set current properties will be used package(Package | None): optional package description. In case if not set current properties will be used
Raises: Raises:
UnknownPackage: if no package found UnknownPackage: if no package found

View File

@ -21,7 +21,7 @@ import contextlib
import logging import logging
import requests import requests
from typing import Generator, List, Optional, Tuple from collections.abc import Generator
from urllib.parse import quote_plus as urlencode from urllib.parse import quote_plus as urlencode
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -40,7 +40,7 @@ class WebClient(Client, LazyLogging):
Attributes: Attributes:
address(str): address of the web service address(str): address of the web service
user(Optional[User]): web service user descriptor user(User | None): web service user descriptor
""" """
def __init__(self, configuration: Configuration) -> None: def __init__(self, configuration: Configuration) -> None:
@ -78,7 +78,7 @@ class WebClient(Client, LazyLogging):
return f"{self.address}/api/v1/status" return f"{self.address}/api/v1/status"
@staticmethod @staticmethod
def parse_address(configuration: Configuration) -> Tuple[str, bool]: def parse_address(configuration: Configuration) -> tuple[str, bool]:
""" """
parse address from configuration parse address from configuration
@ -86,7 +86,7 @@ class WebClient(Client, LazyLogging):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
Tuple[str, bool]: tuple of server address and socket flag (True in case if unix socket must be used) tuple[str, bool]: tuple of server address and socket flag (True in case if unix socket must be used)
""" """
if (unix_socket := configuration.get("web", "unix_socket", fallback=None)) is not None: if (unix_socket := configuration.get("web", "unix_socket", fallback=None)) is not None:
# special pseudo-protocol which is used for unix sockets # special pseudo-protocol which is used for unix sockets
@ -190,15 +190,15 @@ class WebClient(Client, LazyLogging):
response = self.__session.post(self._package_url(package.base), json=payload) response = self.__session.post(self._package_url(package.base), json=payload)
response.raise_for_status() response.raise_for_status()
def get(self, package_base: Optional[str]) -> List[Tuple[Package, BuildStatus]]: def get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
""" """
get package status get package status
Args: Args:
package_base(Optional[str]): package base to get package_base(str | None): package base to get
Returns: Returns:
List[Tuple[Package, BuildStatus]]: list of current package description and status if it has been found list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
""" """
with self.__execute_request(): with self.__execute_request():
response = self.__session.get(self._package_url(package_base or "")) response = self.__session.get(self._package_url(package_base or ""))

View File

@ -21,7 +21,7 @@ from __future__ import annotations
import functools import functools
from typing import Callable, Iterable, List from collections.abc import Callable, Iterable
from ahriman.core.util import partition from ahriman.core.util import partition
from ahriman.models.package import Package from ahriman.models.package import Package
@ -32,7 +32,7 @@ class Leaf:
tree leaf implementation tree leaf implementation
Attributes: Attributes:
dependencies(Set[str]): list of package dependencies dependencies(set[str]): list of package dependencies
package(Package): leaf package properties package(Package): leaf package properties
""" """
@ -92,7 +92,7 @@ class Tree:
dependency tree implementation dependency tree implementation
Attributes: Attributes:
leaves[List[Leaf]): list of tree leaves leaves[list[Leaf]): list of tree leaves
Examples: Examples:
The most important feature here is to generate tree levels one by one which can be achieved by using class The most important feature here is to generate tree levels one by one which can be achieved by using class
@ -119,17 +119,17 @@ class Tree:
>>> tree = Tree(leaves) >>> tree = Tree(leaves)
""" """
def __init__(self, leaves: List[Leaf]) -> None: def __init__(self, leaves: list[Leaf]) -> None:
""" """
default constructor default constructor
Args: Args:
leaves(List[Leaf]): leaves to build the tree leaves(list[Leaf]): leaves to build the tree
""" """
self.leaves = leaves self.leaves = leaves
@staticmethod @staticmethod
def resolve(packages: Iterable[Package]) -> List[List[Package]]: def resolve(packages: Iterable[Package]) -> list[list[Package]]:
""" """
resolve dependency tree resolve dependency tree
@ -137,20 +137,20 @@ class Tree:
packages(Iterable[Package]): packages list packages(Iterable[Package]): packages list
Returns: Returns:
List[List[Package]]: list of packages lists based on their dependencies list[list[Package]]: list of packages lists based on their dependencies
""" """
leaves = [Leaf(package) for package in packages] leaves = [Leaf(package) for package in packages]
instance = Tree(leaves) instance = Tree(leaves)
return instance.levels() return instance.levels()
def levels(self) -> List[List[Package]]: def levels(self) -> list[list[Package]]:
""" """
get build levels starting from the packages which do not require any other package to build get build levels starting from the packages which do not require any other package to build
Returns: Returns:
List[List[Package]]: sorted list of packages lists based on their dependencies list[list[Package]]: sorted list of packages lists based on their dependencies
""" """
unsorted: List[List[Leaf]] = [] unsorted: list[list[Leaf]] = []
# build initial tree # build initial tree
unprocessed = self.leaves[:] unprocessed = self.leaves[:]

View File

@ -19,8 +19,6 @@
# #
from __future__ import annotations from __future__ import annotations
from typing import Iterable, List, Optional, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import ConfigurationSchema from ahriman.core.configuration.schema import ConfigurationSchema
from ahriman.core.log import LazyLogging from ahriman.core.log import LazyLogging
@ -34,7 +32,7 @@ class Trigger(LazyLogging):
Attributes: Attributes:
CONFIGURATION_SCHEMA(ConfigurationSchema): (class attribute) configuration schema template CONFIGURATION_SCHEMA(ConfigurationSchema): (class attribute) configuration schema template
CONFIGURATION_SCHEMA_FALLBACK(Optional[str]): (class attribute) optional fallback option for defining CONFIGURATION_SCHEMA_FALLBACK(str | None): (class attribute) optional fallback option for defining
configuration schema type used configuration schema type used
architecture(str): repository architecture architecture(str): repository architecture
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
@ -43,7 +41,7 @@ class Trigger(LazyLogging):
This class must be used in order to create own extension. Basically idea is the following:: This class must be used in order to create own extension. Basically idea is the following::
>>> class CustomTrigger(Trigger): >>> class CustomTrigger(Trigger):
>>> def on_result(self, result: Result, packages: Iterable[Package]) -> None: >>> def on_result(self, result: Result, packages: list[Package]) -> None:
>>> perform_some_action() >>> perform_some_action()
Having this class you can pass it to ``configuration`` and it will be run on action:: Having this class you can pass it to ``configuration`` and it will be run on action::
@ -58,7 +56,7 @@ class Trigger(LazyLogging):
""" """
CONFIGURATION_SCHEMA: ConfigurationSchema = {} CONFIGURATION_SCHEMA: ConfigurationSchema = {}
CONFIGURATION_SCHEMA_FALLBACK: Optional[str] = None CONFIGURATION_SCHEMA_FALLBACK: str | None = None
def __init__(self, architecture: str, configuration: Configuration) -> None: def __init__(self, architecture: str, configuration: Configuration) -> None:
""" """
@ -72,8 +70,8 @@ class Trigger(LazyLogging):
self.configuration = configuration self.configuration = configuration
@classmethod @classmethod
def configuration_schema(cls: Type[Trigger], architecture: str, def configuration_schema(cls: type[Trigger], architecture: str,
configuration: Optional[Configuration]) -> ConfigurationSchema: configuration: Configuration | None) -> ConfigurationSchema:
""" """
configuration schema based on supplied service configuration configuration schema based on supplied service configuration
@ -82,7 +80,7 @@ class Trigger(LazyLogging):
Args: Args:
architecture(str): repository architecture architecture(str): repository architecture
configuration(Optional[Configuration]): configuration instance. If set to None, the default schema configuration(Configuration | None): configuration instance. If set to None, the default schema
should be returned should be returned
Returns: Returns:
@ -104,7 +102,7 @@ class Trigger(LazyLogging):
return result return result
@classmethod @classmethod
def configuration_sections(cls: Type[Trigger], configuration: Configuration) -> List[str]: def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
""" """
extract configuration sections from configuration extract configuration sections from configuration
@ -112,26 +110,26 @@ class Trigger(LazyLogging):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
List[str]: read configuration sections belong to this trigger list[str]: read configuration sections belong to this trigger
Examples: Examples:
This method can be used in order to extract specific configuration sections which are set by user, e.g. This method can be used in order to extract specific configuration sections which are set by user, e.g.
from sources:: from sources::
>>> @staticmethod >>> @staticmethod
>>> def configuration_sections(cls: Type[Trigger], configuration: Configuration) -> List[str]: >>> def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
>>> return configuration.getlist("report", "target", fallback=[]) >>> return configuration.getlist("report", "target", fallback=[])
""" """
del configuration del configuration
return [] return []
def on_result(self, result: Result, packages: Iterable[Package]) -> None: def on_result(self, result: Result, packages: list[Package]) -> None:
""" """
trigger action which will be called after build process with process result trigger action which will be called after build process with process result
Args: Args:
result(Result): build result result(Result): build result
packages(Iterable[Package]): list of all available packages packages(list[Package]): list of all available packages
""" """
if (run := getattr(self, "run", None)) is not None: if (run := getattr(self, "run", None)) is not None:
run(result, packages) # compatibility with old triggers run(result, packages) # compatibility with old triggers

View File

@ -23,9 +23,9 @@ import contextlib
import importlib import importlib
import os import os
from collections.abc import Generator
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import Generator, Iterable, List, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExtensionError from ahriman.core.exceptions import ExtensionError
@ -40,7 +40,7 @@ class TriggerLoader(LazyLogging):
trigger loader class trigger loader class
Attributes: Attributes:
triggers(List[Trigger]): list of loaded triggers according to the configuration triggers(list[Trigger]): list of loaded triggers according to the configuration
Examples: Examples:
This class more likely must not be used directly, but the usual workflow is the following:: This class more likely must not be used directly, but the usual workflow is the following::
@ -63,10 +63,10 @@ class TriggerLoader(LazyLogging):
default constructor default constructor
""" """
self._on_stop_requested = False self._on_stop_requested = False
self.triggers: List[Trigger] = [] self.triggers: list[Trigger] = []
@classmethod @classmethod
def load(cls: Type[TriggerLoader], architecture: str, configuration: Configuration) -> TriggerLoader: def load(cls: type[TriggerLoader], architecture: str, configuration: Configuration) -> TriggerLoader:
""" """
create instance from configuration create instance from configuration
@ -86,7 +86,7 @@ class TriggerLoader(LazyLogging):
return instance return instance
@staticmethod @staticmethod
def selected_triggers(configuration: Configuration) -> List[str]: def selected_triggers(configuration: Configuration) -> list[str]:
""" """
read configuration and return triggers which are set by settings read configuration and return triggers which are set by settings
@ -94,7 +94,7 @@ class TriggerLoader(LazyLogging):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
List[str]: list of triggers according to configuration list[str]: list of triggers according to configuration
""" """
return configuration.getlist("build", "triggers", fallback=[]) return configuration.getlist("build", "triggers", fallback=[])
@ -176,7 +176,7 @@ class TriggerLoader(LazyLogging):
return trigger return trigger
def load_trigger_class(self, module_path: str) -> Type[Trigger]: def load_trigger_class(self, module_path: str) -> type[Trigger]:
""" """
load trigger class by module path load trigger class by module path
@ -184,7 +184,7 @@ class TriggerLoader(LazyLogging):
module_path(str): module import path to load module_path(str): module import path to load
Returns: Returns:
Type[Trigger]: loaded trigger type by module path type[Trigger]: loaded trigger type by module path
Raises: Raises:
InvalidExtension: in case if module cannot be loaded from the specified module path or is not a trigger InvalidExtension: in case if module cannot be loaded from the specified module path or is not a trigger
@ -208,13 +208,13 @@ class TriggerLoader(LazyLogging):
self.logger.info("loaded type %s of package %s", class_name, package_or_path) self.logger.info("loaded type %s of package %s", class_name, package_or_path)
return trigger_type return trigger_type
def on_result(self, result: Result, packages: Iterable[Package]) -> None: def on_result(self, result: Result, packages: list[Package]) -> None:
""" """
run trigger with result of application run run trigger with result of application run
Args: Args:
result(Result): build result result(Result): build result
packages(Iterable[Package]): list of all available packages packages(list[Package]): list of all available packages
""" """
self.logger.debug("executing triggers on result") self.logger.debug("executing triggers on result")
for trigger in self.triggers: for trigger in self.triggers:

View File

@ -21,7 +21,7 @@ import mimetypes
import requests import requests
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Iterable, Optional from typing import Any
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.upload.http_upload import HttpUpload from ahriman.core.upload.http_upload import HttpUpload
@ -51,12 +51,12 @@ class Github(HttpUpload):
self.github_owner = configuration.get(section, "owner") self.github_owner = configuration.get(section, "owner")
self.github_repository = configuration.get(section, "repository") self.github_repository = configuration.get(section, "repository")
def asset_remove(self, release: Dict[str, Any], name: str) -> None: def asset_remove(self, release: dict[str, Any], name: str) -> None:
""" """
remove asset from the release by name remove asset from the release by name
Args: Args:
release(Dict[str, Any]): release object release(dict[str, Any]): release object
name(str): asset name name(str): asset name
""" """
try: try:
@ -65,12 +65,12 @@ class Github(HttpUpload):
except StopIteration: except StopIteration:
self.logger.info("no asset %s found in release %s", name, release["name"]) self.logger.info("no asset %s found in release %s", name, release["name"])
def asset_upload(self, release: Dict[str, Any], path: Path) -> None: def asset_upload(self, release: dict[str, Any], path: Path) -> None:
""" """
upload asset to the release upload asset to the release
Args: Args:
release(Dict[str, Any]): release object release(dict[str, Any]): release object
path(Path): path to local file path(Path): path to local file
""" """
exists = any(path.name == asset["name"] for asset in release["assets"]) exists = any(path.name == asset["name"] for asset in release["assets"])
@ -81,7 +81,7 @@ class Github(HttpUpload):
headers = {"Content-Type": mime} if mime is not None else {"Content-Type": "application/octet-stream"} headers = {"Content-Type": mime} if mime is not None else {"Content-Type": "application/octet-stream"}
self._request("POST", url, params={"name": path.name}, data=path.open("rb"), headers=headers) self._request("POST", url, params={"name": path.name}, data=path.open("rb"), headers=headers)
def get_local_files(self, path: Path) -> Dict[Path, str]: def get_local_files(self, path: Path) -> dict[Path, str]:
""" """
get all local files and their calculated checksums get all local files and their calculated checksums
@ -89,21 +89,21 @@ class Github(HttpUpload):
path(Path): local path to sync path(Path): local path to sync
Returns: Returns:
Dict[Path, str]: map of path objects to its checksum dict[Path, str]: map of path objects to its checksum
""" """
return { return {
local_file: self.calculate_hash(local_file) local_file: self.calculate_hash(local_file)
for local_file in walk(path) for local_file in walk(path)
} }
def files_remove(self, release: Dict[str, Any], local_files: Dict[Path, str], remote_files: Dict[str, str]) -> None: def files_remove(self, release: dict[str, Any], local_files: dict[Path, str], remote_files: dict[str, str]) -> None:
""" """
remove files from github remove files from github
Args: Args:
release(Dict[str, Any]): release object release(dict[str, Any]): release object
local_files(Dict[Path, str]): map of local file paths to its checksum local_files(dict[Path, str]): map of local file paths to its checksum
remote_files(Dict[str, str]): map of the remote files and its checksum remote_files(dict[str, str]): map of the remote files and its checksum
""" """
local_filenames = {local_file.name for local_file in local_files} local_filenames = {local_file.name for local_file in local_files}
for remote_file in remote_files: for remote_file in remote_files:
@ -111,14 +111,14 @@ class Github(HttpUpload):
continue continue
self.asset_remove(release, remote_file) self.asset_remove(release, remote_file)
def files_upload(self, release: Dict[str, Any], local_files: Dict[Path, str], remote_files: Dict[str, str]) -> None: def files_upload(self, release: dict[str, Any], local_files: dict[Path, str], remote_files: dict[str, str]) -> None:
""" """
upload files to github upload files to github
Args: Args:
release(Dict[str, Any]): release object release(dict[str, Any]): release object
local_files(Dict[Path, str]): map of local file paths to its checksum local_files(dict[Path, str]): map of local file paths to its checksum
remote_files(Dict[str, str]): map of the remote files and its checksum remote_files(dict[str, str]): map of the remote files and its checksum
""" """
for local_file, checksum in local_files.items(): for local_file, checksum in local_files.items():
remote_checksum = remote_files.get(local_file.name) remote_checksum = remote_files.get(local_file.name)
@ -126,29 +126,29 @@ class Github(HttpUpload):
continue continue
self.asset_upload(release, local_file) self.asset_upload(release, local_file)
def release_create(self) -> Dict[str, Any]: def release_create(self) -> dict[str, Any]:
""" """
create empty release create empty release
Returns: Returns:
Dict[str, Any]: github API release object for the new release dict[str, Any]: github API release object for the new release
""" """
url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases" url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases"
response = self._request("POST", url, json={"tag_name": self.architecture, "name": self.architecture}) response = self._request("POST", url, json={"tag_name": self.architecture, "name": self.architecture})
release: Dict[str, Any] = response.json() release: dict[str, Any] = response.json()
return release return release
def release_get(self) -> Optional[Dict[str, Any]]: def release_get(self) -> dict[str, Any] | None:
""" """
get release object if any get release object if any
Returns: Returns:
Optional[Dict[str, Any]]: github API release object if release found and None otherwise dict[str, Any] | None: github API release object if release found and None otherwise
""" """
url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases/tags/{self.architecture}" url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases/tags/{self.architecture}"
try: try:
response = self._request("GET", url) response = self._request("GET", url)
release: Dict[str, Any] = response.json() release: dict[str, Any] = response.json()
return release return release
except requests.HTTPError as e: except requests.HTTPError as e:
status_code = e.response.status_code if e.response is not None else None status_code = e.response.status_code if e.response is not None else None
@ -156,23 +156,23 @@ class Github(HttpUpload):
return None return None
raise raise
def release_update(self, release: Dict[str, Any], body: str) -> None: def release_update(self, release: dict[str, Any], body: str) -> None:
""" """
update release update release
Args: Args:
release(Dict[str, Any]): release object release(dict[str, Any]): release object
body(str): new release body body(str): new release body
""" """
self._request("POST", release["url"], json={"body": body}) self._request("POST", release["url"], json={"body": body})
def sync(self, path: Path, built_packages: Iterable[Package]) -> None: def sync(self, path: Path, built_packages: list[Package]) -> None:
""" """
sync data to remote server sync data to remote server
Args: Args:
path(Path): local path to sync path(Path): local path to sync
built_packages(Iterable[Package]): list of packages which has just been built built_packages(list[Package]): list of packages which has just been built
""" """
release = self.release_get() release = self.release_get()
if release is None: if release is None:

View File

@ -21,7 +21,7 @@ import hashlib
import requests import requests
from pathlib import Path from pathlib import Path
from typing import Any, Dict from typing import Any
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.upload.upload import Upload from ahriman.core.upload.upload import Upload
@ -33,7 +33,7 @@ class HttpUpload(Upload):
helper for the http based uploads helper for the http based uploads
Attributes: Attributes:
auth(Optional[Tuple[str, str]]): HTTP auth object if set auth(tuple[str, str] | None): HTTP auth object if set
timeout(int): HTTP request timeout in seconds timeout(int): HTTP request timeout in seconds
""" """
@ -68,12 +68,12 @@ class HttpUpload(Upload):
return md5.hexdigest() return md5.hexdigest()
@staticmethod @staticmethod
def get_body(local_files: Dict[Path, str]) -> str: def get_body(local_files: dict[Path, str]) -> str:
""" """
generate release body from the checksums as returned from HttpUpload.get_hashes method generate release body from the checksums as returned from HttpUpload.get_hashes method
Args: Args:
local_files(Dict[Path, str]): map of the paths to its checksum local_files(dict[Path, str]): map of the paths to its checksum
Returns: Returns:
str: body to be inserted into release str: body to be inserted into release
@ -81,7 +81,7 @@ class HttpUpload(Upload):
return "\n".join(f"{file.name} {md5}" for file, md5 in sorted(local_files.items())) return "\n".join(f"{file.name} {md5}" for file, md5 in sorted(local_files.items()))
@staticmethod @staticmethod
def get_hashes(body: str) -> Dict[str, str]: def get_hashes(body: str) -> dict[str, str]:
""" """
get checksums of the content from the repository get checksums of the content from the repository
@ -89,7 +89,7 @@ class HttpUpload(Upload):
body(str): release string body object body(str): release string body object
Returns: Returns:
Dict[str, str]: map of the filename to its checksum as it is written in body dict[str, str]: map of the filename to its checksum as it is written in body
""" """
files = {} files = {}
for line in body.splitlines(): for line in body.splitlines():

View File

@ -18,7 +18,6 @@
# 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 pathlib import Path from pathlib import Path
from typing import Iterable
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.upload.upload import Upload from ahriman.core.upload.upload import Upload
@ -31,7 +30,7 @@ class Rsync(Upload):
rsync wrapper rsync wrapper
Attributes: Attributes:
command(List[str]): command arguments for sync command(list[str]): command arguments for sync
remote(str): remote address to sync remote(str): remote address to sync
""" """
@ -50,12 +49,12 @@ class Rsync(Upload):
self.command = configuration.getlist(section, "command") self.command = configuration.getlist(section, "command")
self.remote = configuration.get(section, "remote") self.remote = configuration.get(section, "remote")
def sync(self, path: Path, built_packages: Iterable[Package]) -> None: def sync(self, path: Path, built_packages: list[Package]) -> None:
""" """
sync data to remote server sync data to remote server
Args: Args:
path(Path): local path to sync path(Path): local path to sync
built_packages(Iterable[Package]): list of packages which has just been built built_packages(list[Package]): list of packages which has just been built
""" """
Rsync._check_output(*self.command, str(path), self.remote, logger=self.logger) Rsync._check_output(*self.command, str(path), self.remote, logger=self.logger)

View File

@ -22,7 +22,7 @@ import hashlib
import mimetypes import mimetypes
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Iterable from typing import Any
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.upload.upload import Upload from ahriman.core.upload.upload import Upload
@ -97,27 +97,27 @@ class S3(Upload):
return client.Bucket(configuration.get(section, "bucket")) return client.Bucket(configuration.get(section, "bucket"))
@staticmethod @staticmethod
def files_remove(local_files: Dict[Path, str], remote_objects: Dict[Path, Any]) -> None: def files_remove(local_files: dict[Path, str], remote_objects: dict[Path, Any]) -> None:
""" """
remove files which have been removed locally remove files which have been removed locally
Args: Args:
local_files(Dict[Path, str]): map of local path object to its checksum local_files(dict[Path, str]): map of local path object to its checksum
remote_objects(Dict[Path, Any]): map of remote path object to the remote s3 object remote_objects(dict[Path, Any]): map of remote path object to the remote s3 object
""" """
for local_file, remote_object in remote_objects.items(): for local_file, remote_object in remote_objects.items():
if local_file in local_files: if local_file in local_files:
continue continue
remote_object.delete() remote_object.delete()
def files_upload(self, path: Path, local_files: Dict[Path, str], remote_objects: Dict[Path, Any]) -> None: def files_upload(self, path: Path, local_files: dict[Path, str], remote_objects: dict[Path, Any]) -> None:
""" """
upload changed files to s3 upload changed files to s3
Args: Args:
path(Path): local path to sync path(Path): local path to sync
local_files(Dict[Path, str]): map of local path object to its checksum local_files(dict[Path, str]): map of local path object to its checksum
remote_objects(Dict[Path, Any]): map of remote path object to the remote s3 object remote_objects(dict[Path, Any]): map of remote path object to the remote s3 object
""" """
for local_file, checksum in local_files.items(): for local_file, checksum in local_files.items():
remote_object = remote_objects.get(local_file) remote_object = remote_objects.get(local_file)
@ -133,7 +133,7 @@ class S3(Upload):
self.bucket.upload_file(Filename=str(local_path), Key=str(remote_path), ExtraArgs=extra_args) self.bucket.upload_file(Filename=str(local_path), Key=str(remote_path), ExtraArgs=extra_args)
def get_local_files(self, path: Path) -> Dict[Path, str]: def get_local_files(self, path: Path) -> dict[Path, str]:
""" """
get all local files and their calculated checksums get all local files and their calculated checksums
@ -141,30 +141,30 @@ class S3(Upload):
path(Path): local path to sync path(Path): local path to sync
Returns: Returns:
Dict[Path, str]: map of path object to its checksum dict[Path, str]: map of path object to its checksum
""" """
return { return {
local_file.relative_to(path): self.calculate_etag(local_file, self.chunk_size) local_file.relative_to(path): self.calculate_etag(local_file, self.chunk_size)
for local_file in walk(path) for local_file in walk(path)
} }
def get_remote_objects(self) -> Dict[Path, Any]: def get_remote_objects(self) -> dict[Path, Any]:
""" """
get all remote objects and their checksums get all remote objects and their checksums
Returns: Returns:
Dict[Path, Any]: map of path object to the remote s3 object dict[Path, Any]: map of path object to the remote s3 object
""" """
objects = self.bucket.objects.filter(Prefix=self.architecture) objects = self.bucket.objects.filter(Prefix=self.architecture)
return {Path(item.key).relative_to(self.architecture): item for item in objects} return {Path(item.key).relative_to(self.architecture): item for item in objects}
def sync(self, path: Path, built_packages: Iterable[Package]) -> None: def sync(self, path: Path, built_packages: list[Package]) -> None:
""" """
sync data to remote server sync data to remote server
Args: Args:
path(Path): local path to sync path(Path): local path to sync
built_packages(Iterable[Package]): list of packages which has just been built built_packages(list[Package]): list of packages which has just been built
""" """
remote_objects = self.get_remote_objects() remote_objects = self.get_remote_objects()
local_files = self.get_local_files(path) local_files = self.get_local_files(path)

View File

@ -20,7 +20,6 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import Iterable, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import SynchronizationError from ahriman.core.exceptions import SynchronizationError
@ -68,7 +67,7 @@ class Upload(LazyLogging):
self.configuration = configuration self.configuration = configuration
@classmethod @classmethod
def load(cls: Type[Upload], architecture: str, configuration: Configuration, target: str) -> Upload: def load(cls: type[Upload], architecture: str, configuration: Configuration, target: str) -> Upload:
""" """
load client from settings load client from settings
@ -93,13 +92,13 @@ class Upload(LazyLogging):
return Github(architecture, configuration, section) return Github(architecture, configuration, section)
return cls(architecture, configuration) # should never happen return cls(architecture, configuration) # should never happen
def run(self, path: Path, built_packages: Iterable[Package]) -> None: def run(self, path: Path, built_packages: list[Package]) -> None:
""" """
run remote sync run remote sync
Args: Args:
path(Path): local path to sync path(Path): local path to sync
built_packages(Iterable[Package]): list of packages which has just been built built_packages(list[Package]): list of packages which has just been built
Raises: Raises:
SyncFailed: in case of any synchronization unmatched exception SyncFailed: in case of any synchronization unmatched exception
@ -110,11 +109,11 @@ class Upload(LazyLogging):
self.logger.exception("remote sync failed") self.logger.exception("remote sync failed")
raise SynchronizationError() raise SynchronizationError()
def sync(self, path: Path, built_packages: Iterable[Package]) -> None: def sync(self, path: Path, built_packages: list[Package]) -> None:
""" """
sync data to remote server sync data to remote server
Args: Args:
path(Path): local path to sync path(Path): local path to sync
built_packages(Iterable[Package]): list of packages which has just been built built_packages(list[Package]): list of packages which has just been built
""" """

View File

@ -17,8 +17,6 @@
# 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 typing import Iterable, List, Type
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.triggers import Trigger from ahriman.core.triggers import Trigger
from ahriman.core.upload.upload import Upload from ahriman.core.upload.upload import Upload
@ -31,7 +29,7 @@ class UploadTrigger(Trigger):
synchronization trigger synchronization trigger
Attributes: Attributes:
targets(List[str]): upload target list targets(list[str]): upload target list
""" """
CONFIGURATION_SCHEMA = { CONFIGURATION_SCHEMA = {
@ -138,7 +136,7 @@ class UploadTrigger(Trigger):
self.targets = self.configuration_sections(configuration) self.targets = self.configuration_sections(configuration)
@classmethod @classmethod
def configuration_sections(cls: Type[Trigger], configuration: Configuration) -> List[str]: def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
""" """
extract configuration sections from configuration extract configuration sections from configuration
@ -146,17 +144,17 @@ class UploadTrigger(Trigger):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
List[str]: read configuration sections belong to this trigger list[str]: read configuration sections belong to this trigger
""" """
return configuration.getlist("upload", "target", fallback=[]) return configuration.getlist("upload", "target", fallback=[])
def on_result(self, result: Result, packages: Iterable[Package]) -> None: def on_result(self, result: Result, packages: list[Package]) -> None:
""" """
run trigger run trigger
Args: Args:
result(Result): build result result(Result): build result
packages(Iterable[Package]): list of all available packages packages(list[Package]): list of all available packages
""" """
for target in self.targets: for target in self.targets:
runner = Upload.load(self.architecture, self.configuration, target) runner = Upload.load(self.architecture, self.configuration, target)

View File

@ -26,10 +26,11 @@ import re
import requests import requests
import subprocess import subprocess
from collections.abc import Callable, Generator, Iterable
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from pwd import getpwuid from pwd import getpwuid
from typing import Any, Callable, Dict, Generator, IO, Iterable, List, Optional, Tuple, Type, TypeVar, Union from typing import Any, IO, TypeVar
from ahriman.core.exceptions import OptionError, UnsafeRunError from ahriman.core.exceptions import OptionError, UnsafeRunError
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -56,20 +57,19 @@ __all__ = [
T = TypeVar("T") T = TypeVar("T")
def check_output(*args: str, exception: Optional[Exception] = None, cwd: Optional[Path] = None, def check_output(*args: str, exception: Exception | None = None, cwd: Path | None = None, input_data: str | None = None,
input_data: Optional[str] = None, logger: Optional[logging.Logger] = None, logger: logging.Logger | None = None, user: int | None = None) -> str:
user: Optional[int] = None) -> str:
""" """
subprocess wrapper subprocess wrapper
Args: Args:
*args(str): command line arguments *args(str): command line arguments
exception(Optional[Exception], optional): exception which has to be reraised instead of default subprocess exception(Exception | None, optional): exception which has to be reraised instead of default subprocess
exception (Default value = None) exception (Default value = None)
cwd(Optional[Path], optional): current working directory (Default value = None) cwd(Path | None, optional): current working directory (Default value = None)
input_data(Optional[str], optional): data which will be written to command stdin (Default value = None) input_data(str | None, optional): data which will be written to command stdin (Default value = None)
logger(Optional[logging.Logger], optional): logger to log command result if required (Default value = None) logger(logging.Logger | None, optional): logger to log command result if required (Default value = None)
user(Optional[int], optional): run process as specified user (Default value = None) user(int | None, optional): run process as specified user (Default value = None)
Returns: Returns:
str: command output str: command output
@ -94,9 +94,9 @@ def check_output(*args: str, exception: Optional[Exception] = None, cwd: Optiona
>>> check_output("false", exception=RuntimeError("An exception occurred")) >>> check_output("false", exception=RuntimeError("An exception occurred"))
""" """
# hack for Optional[IO[str]] handle # hack for IO[str] handle
def get_io(proc: subprocess.Popen[str], channel_name: str) -> IO[str]: def get_io(proc: subprocess.Popen[str], channel_name: str) -> IO[str]:
channel: Optional[IO[str]] = getattr(proc, channel_name, None) channel: IO[str] | None = getattr(proc, channel_name, None)
return channel if channel is not None else io.StringIO() return channel if channel is not None else io.StringIO()
def log(single: str) -> None: def log(single: str) -> None:
@ -114,7 +114,7 @@ def check_output(*args: str, exception: Optional[Exception] = None, cwd: Optiona
input_channel.close() input_channel.close()
# read stdout and append to output result # read stdout and append to output result
result: List[str] = [] result: list[str] = []
for line in iter(get_io(process, "stdout").readline, ""): for line in iter(get_io(process, "stdout").readline, ""):
line = line.strip() line = line.strip()
if not line: # skip empty lines if not line: # skip empty lines
@ -162,15 +162,15 @@ def check_user(paths: RepositoryPaths, *, unsafe: bool) -> None:
raise UnsafeRunError(current_uid, root_uid) raise UnsafeRunError(current_uid, root_uid)
def enum_values(enum: Type[Enum]) -> List[str]: def enum_values(enum: type[Enum]) -> list[str]:
""" """
generate list of enumeration values from the source generate list of enumeration values from the source
Args: Args:
enum(Type[Enum]): source enumeration class enum(type[Enum]): source enumeration class
Returns: Returns:
List[str]: available enumeration values as string list[str]: available enumeration values as string
""" """
return [str(key.value) for key in enum] # explicit str conversion for typing return [str(key.value) for key in enum] # explicit str conversion for typing
@ -189,16 +189,16 @@ def exception_response_text(exception: requests.exceptions.HTTPError) -> str:
return result return result
def filter_json(source: Dict[str, Any], known_fields: Iterable[str]) -> Dict[str, Any]: def filter_json(source: dict[str, Any], known_fields: Iterable[str]) -> dict[str, Any]:
""" """
filter json object by fields used for json-to-object conversion filter json object by fields used for json-to-object conversion
Args: Args:
source(Dict[str, Any]): raw json object source(dict[str, Any]): raw json object
known_fields(Iterable[str]): list of fields which have to be known for the target object known_fields(Iterable[str]): list of fields which have to be known for the target object
Returns: Returns:
Dict[str, Any]: json object without unknown and empty fields dict[str, Any]: json object without unknown and empty fields
Examples: Examples:
This wrapper is mainly used for the dataclasses, thus the flow must be something like this:: This wrapper is mainly used for the dataclasses, thus the flow must be something like this::
@ -213,12 +213,12 @@ def filter_json(source: Dict[str, Any], known_fields: Iterable[str]) -> Dict[str
return {key: value for key, value in source.items() if key in known_fields and value is not None} return {key: value for key, value in source.items() if key in known_fields and value is not None}
def full_version(epoch: Union[str, int, None], pkgver: str, pkgrel: str) -> str: def full_version(epoch: str | int | None, pkgver: str, pkgrel: str) -> str:
""" """
generate full version from components generate full version from components
Args: Args:
epoch(Union[str, int, None]): package epoch if any epoch(str | int | None): package epoch if any
pkgver(str): package version pkgver(str): package version
pkgrel(str): package release version (arch linux specific) pkgrel(str): package release version (arch linux specific)
@ -243,27 +243,27 @@ def package_like(filename: Path) -> bool:
return ".pkg." in name and not name.endswith(".sig") return ".pkg." in name and not name.endswith(".sig")
def partition(source: List[T], predicate: Callable[[T], bool]) -> Tuple[List[T], List[T]]: def partition(source: list[T], predicate: Callable[[T], bool]) -> tuple[list[T], list[T]]:
""" """
partition list into two based on predicate, based on # https://docs.python.org/dev/library/itertools.html#itertools-recipes partition list into two based on predicate, based on # https://docs.python.org/dev/library/itertools.html#itertools-recipes
Args: Args:
source(List[T]): source list to be partitioned source(list[T]): source list to be partitioned
predicate(Callable[[T], bool]): filter function predicate(Callable[[T], bool]): filter function
Returns: Returns:
Tuple[List[T], List[T]]: two lists, first is which ``predicate`` is ``True``, second is ``False`` tuple[list[T], list[T]]: two lists, first is which ``predicate`` is ``True``, second is ``False``
""" """
first_iter, second_iter = itertools.tee(source) first_iter, second_iter = itertools.tee(source)
return list(filter(predicate, first_iter)), list(itertools.filterfalse(predicate, second_iter)) return list(filter(predicate, first_iter)), list(itertools.filterfalse(predicate, second_iter))
def pretty_datetime(timestamp: Optional[Union[datetime.datetime, float, int]]) -> str: def pretty_datetime(timestamp: datetime.datetime | float | int | None) -> str:
""" """
convert datetime object to string convert datetime object to string
Args: Args:
timestamp(Optional[Union[datetime.datetime, float, int]]): datetime to convert timestamp(datetime.datetime | float | int | None): datetime to convert
Returns: Returns:
str: pretty printable datetime as string str: pretty printable datetime as string
@ -275,12 +275,12 @@ def pretty_datetime(timestamp: Optional[Union[datetime.datetime, float, int]]) -
return timestamp.strftime("%Y-%m-%d %H:%M:%S") return timestamp.strftime("%Y-%m-%d %H:%M:%S")
def pretty_size(size: Optional[float], level: int = 0) -> str: def pretty_size(size: float | None, level: int = 0) -> str:
""" """
convert size to string convert size to string
Args: Args:
size(Optional[float]): size to convert size(float | None): size to convert
level(int, optional): represents current units, 0 is B, 1 is KiB, etc (Default value = 0) level(int, optional): represents current units, 0 is B, 1 is KiB, etc (Default value = 0)
Returns: Returns:

Some files were not shown because too many files have changed in this diff Show More