diff --git a/docs/ahriman.core.rst b/docs/ahriman.core.rst index b33a3c54..36551728 100644 --- a/docs/ahriman.core.rst +++ b/docs/ahriman.core.rst @@ -52,6 +52,14 @@ ahriman.core.tree module :no-undoc-members: :show-inheritance: +ahriman.core.types module +------------------------- + +.. automodule:: ahriman.core.types + :members: + :no-undoc-members: + :show-inheritance: + ahriman.core.util module ------------------------ diff --git a/src/ahriman/application/handlers/handler.py b/src/ahriman/application/handlers/handler.py index 6dd6c597..615a01a5 100644 --- a/src/ahriman/application/handlers/handler.py +++ b/src/ahriman/application/handlers/handler.py @@ -27,6 +27,7 @@ from ahriman.application.lock import Lock from ahriman.core.configuration import Configuration from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError from ahriman.core.log.log_loader import LogLoader +from ahriman.core.types import ExplicitBool from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_paths import RepositoryPaths @@ -124,13 +125,14 @@ class Handler: raise NotImplementedError @staticmethod - def check_status(enabled: bool, status: bool | Callable[[], bool]) -> None: + def check_status(enabled: bool, status: ExplicitBool | Callable[[], ExplicitBool]) -> None: """ check condition and flag and raise ExitCode exception in case if it is enabled and condition match Args: enabled(bool): if ``False`` no check will be performed - status(bool | Callable[[], bool]): return status or function to check. ``True`` means success and vice versa + status(ExplicitBool | Callable[[], ExplicitBool]): return status or function to check. + ``True`` means success and vice versa Raises: ExitCode: if result is empty and check is enabled @@ -138,12 +140,9 @@ class Handler: if not enabled: return - match status: - case False: - raise ExitCode - # https://github.com/python/mypy/issues/14014 - case Callable() if not status(): # type: ignore[misc] - raise ExitCode + status = status() if callable(status) else status + if not status: + raise ExitCode @staticmethod def repositories_extract(args: argparse.Namespace) -> list[RepositoryId]: diff --git a/src/ahriman/application/handlers/patch.py b/src/ahriman/application/handlers/patch.py index b2635c47..d24a7753 100644 --- a/src/ahriman/application/handlers/patch.py +++ b/src/ahriman/application/handlers/patch.py @@ -136,7 +136,7 @@ class Patch(Handler): for patch in application.reporter.package_patches_get(package_base, None) if variables is None or patch.key in variables ] - Patch.check_status(exit_code, bool(patches)) + Patch.check_status(exit_code, patches) PatchPrinter(package_base, patches)(verbose=True, separator=" = ") diff --git a/src/ahriman/application/handlers/rebuild.py b/src/ahriman/application/handlers/rebuild.py index 3f2033e7..40f20f34 100644 --- a/src/ahriman/application/handlers/rebuild.py +++ b/src/ahriman/application/handlers/rebuild.py @@ -51,7 +51,7 @@ class Rebuild(Handler): packages = Rebuild.extract_packages(application, args.status, from_database=args.from_database) packages = application.repository.packages_depend_on(packages, args.depends_on) - Rebuild.check_status(args.exit_code, bool(packages)) + Rebuild.check_status(args.exit_code, packages) if args.dry_run: application.print_updates(packages, log_fn=print) return diff --git a/src/ahriman/application/handlers/status.py b/src/ahriman/application/handlers/status.py index 3302ea53..ddbfca3d 100644 --- a/src/ahriman/application/handlers/status.py +++ b/src/ahriman/application/handlers/status.py @@ -61,7 +61,7 @@ class Status(Handler): else: packages = client.package_get(None) - Status.check_status(args.exit_code, bool(packages)) + Status.check_status(args.exit_code, packages) comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda item: item[0].base filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\ diff --git a/src/ahriman/application/handlers/update.py b/src/ahriman/application/handlers/update.py index f8a6da43..dafcac61 100644 --- a/src/ahriman/application/handlers/update.py +++ b/src/ahriman/application/handlers/update.py @@ -54,7 +54,7 @@ class Update(Handler): application.changes(packages) if args.dry_run: # exit from application if no build requested - Update.check_status(args.exit_code, bool(packages)) # status code check + Update.check_status(args.exit_code, packages) # status code check return packages = application.with_dependencies(packages, process_dependencies=args.dependencies) diff --git a/src/ahriman/application/handlers/users.py b/src/ahriman/application/handlers/users.py index 1681c33a..c34d6201 100644 --- a/src/ahriman/application/handlers/users.py +++ b/src/ahriman/application/handlers/users.py @@ -61,7 +61,7 @@ class Users(Handler): users = database.user_list(args.username, args.role) for user in users: UserPrinter(user)(verbose=True) - Users.check_status(args.exit_code, bool(users)) + Users.check_status(args.exit_code, users) case Action.Remove: database.user_remove(args.username) diff --git a/src/ahriman/core/types.py b/src/ahriman/core/types.py new file mode 100644 index 00000000..c570fd8a --- /dev/null +++ b/src/ahriman/core/types.py @@ -0,0 +1,39 @@ +# +# Copyright (c) 2021-2024 ahriman team. +# +# This file is part of ahriman +# (see https://github.com/arcan1s/ahriman). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +from typing import Protocol + + +class HasBool(Protocol): + """ + class which defines :func:`bool()` method + """ + + def __bool__(self) -> bool: ... + + +class HasLength(Protocol): + """ + class which defines :func:`len()` method + """ + + def __len__(self) -> int: ... + + +ExplicitBool = HasBool | HasLength | int diff --git a/tests/ahriman/application/handlers/test_handler_patch.py b/tests/ahriman/application/handlers/test_handler_patch.py index 56c501ad..59307cdd 100644 --- a/tests/ahriman/application/handlers/test_handler_patch.py +++ b/tests/ahriman/application/handlers/test_handler_patch.py @@ -168,7 +168,7 @@ def test_patch_set_list(application: Application, mocker: MockerFixture) -> None Patch.patch_set_list(application, "ahriman", ["version"], False) get_mock.assert_called_once_with("ahriman", None) print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ") - check_mock.assert_called_once_with(False, True) + check_mock.assert_called_once_with(False, [PkgbuildPatch(key='version', value='value')]) def test_patch_set_list_all(application: Application, mocker: MockerFixture) -> None: @@ -183,7 +183,7 @@ def test_patch_set_list_all(application: Application, mocker: MockerFixture) -> Patch.patch_set_list(application, "ahriman", None, False) get_mock.assert_called_once_with("ahriman", None) print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ") - check_mock.assert_called_once_with(False, True) + check_mock.assert_called_once_with(False, [PkgbuildPatch(key=None, value='patch')]) def test_patch_set_list_empty_exception(application: Application, mocker: MockerFixture) -> None: @@ -194,7 +194,7 @@ def test_patch_set_list_empty_exception(application: Application, mocker: Mocker check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status") Patch.patch_set_list(application, "ahriman", [], True) - check_mock.assert_called_once_with(True, False) + check_mock.assert_called_once_with(True, []) def test_patch_set_create(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: diff --git a/tests/ahriman/application/handlers/test_handler_rebuild.py b/tests/ahriman/application/handlers/test_handler_rebuild.py index 0aa7e0f5..c0d95012 100644 --- a/tests/ahriman/application/handlers/test_handler_rebuild.py +++ b/tests/ahriman/application/handlers/test_handler_rebuild.py @@ -55,7 +55,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: extract_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.status, from_database=args.from_database) application_packages_mock.assert_called_once_with([package_ahriman], None) application_mock.assert_called_once_with([package_ahriman], Packagers(args.username), bump_pkgrel=args.increment) - check_mock.assert_has_calls([MockCall(False, True), MockCall(False, True)]) + check_mock.assert_has_calls([MockCall(False, [package_ahriman]), MockCall(False, True)]) on_start_mock.assert_called_once_with() @@ -93,7 +93,7 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep _, repository_id = configuration.check_loaded() Rebuild.run(args, repository_id, configuration, report=False) application_mock.assert_not_called() - check_mock.assert_called_once_with(False, True) + check_mock.assert_called_once_with(False, [package_ahriman]) print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int)) @@ -146,7 +146,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con _, repository_id = configuration.check_loaded() Rebuild.run(args, repository_id, configuration, report=False) - check_mock.assert_called_once_with(True, False) + check_mock.assert_called_once_with(True, []) def test_run_build_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository, @@ -164,7 +164,7 @@ def test_run_build_empty_exception(args: argparse.Namespace, configuration: Conf _, repository_id = configuration.check_loaded() Rebuild.run(args, repository_id, configuration, report=False) - check_mock.assert_has_calls([MockCall(True, True), MockCall(True, False)]) + check_mock.assert_has_calls([MockCall(True, [package_ahriman]), MockCall(True, False)]) def test_extract_packages(application: Application, mocker: MockerFixture) -> None: diff --git a/tests/ahriman/application/handlers/test_handler_status.py b/tests/ahriman/application/handlers/test_handler_status.py index f5c3e104..4ad84e25 100644 --- a/tests/ahriman/application/handlers/test_handler_status.py +++ b/tests/ahriman/application/handlers/test_handler_status.py @@ -36,11 +36,13 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository: must run command """ args = _default_args(args) + packages = [ + (package_ahriman, BuildStatus(BuildStatusEnum.Success)), + (package_python_schedule, BuildStatus(BuildStatusEnum.Failed)), + ] mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) application_mock = mocker.patch("ahriman.core.status.Client.status_get") - packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", - return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)), - (package_python_schedule, BuildStatus(BuildStatusEnum.Failed))]) + packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=packages) check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status") print_mock = mocker.patch("ahriman.core.formatters.Printer.print") @@ -48,7 +50,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository: Status.run(args, repository_id, configuration, report=False) application_mock.assert_called_once_with() packages_mock.assert_called_once_with(None) - check_mock.assert_called_once_with(False, True) + check_mock.assert_called_once_with(False, packages) print_mock.assert_has_calls([ MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ") for _ in range(3) @@ -69,7 +71,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat _, repository_id = configuration.check_loaded() Status.run(args, repository_id, configuration, report=False) - check_mock.assert_called_once_with(True, False) + check_mock.assert_called_once_with(True, []) def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository, diff --git a/tests/ahriman/application/handlers/test_handler_update.py b/tests/ahriman/application/handlers/test_handler_update.py index 78428081..8cf49132 100644 --- a/tests/ahriman/application/handlers/test_handler_update.py +++ b/tests/ahriman/application/handlers/test_handler_update.py @@ -85,7 +85,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat _, repository_id = configuration.check_loaded() Update.run(args, repository_id, configuration, report=False) - check_mock.assert_called_once_with(True, False) + check_mock.assert_called_once_with(True, []) def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration, @@ -127,7 +127,7 @@ def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configu args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files) application_mock.assert_not_called() changes_mock.assert_called_once_with([package_ahriman]) - check_mock.assert_called_once_with(False, True) + check_mock.assert_called_once_with(False, [package_ahriman]) def test_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository, diff --git a/tests/ahriman/application/handlers/test_handler_users.py b/tests/ahriman/application/handlers/test_handler_users.py index 03574369..607d4459 100644 --- a/tests/ahriman/application/handlers/test_handler_users.py +++ b/tests/ahriman/application/handlers/test_handler_users.py @@ -103,7 +103,7 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, databa _, repository_id = configuration.check_loaded() Users.run(args, repository_id, configuration, report=False) list_mock.assert_called_once_with("user", args.role) - check_mock.assert_called_once_with(False, True) + check_mock.assert_called_once_with(False, [user]) def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, database: SQLite, @@ -120,7 +120,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat _, repository_id = configuration.check_loaded() Users.run(args, repository_id, configuration, report=False) - check_mock.assert_called_once_with(True, False) + check_mock.assert_called_once_with(True, []) def test_run_remove(args: argparse.Namespace, configuration: Configuration, database: SQLite, diff --git a/tests/ahriman/core/test_types.py b/tests/ahriman/core/test_types.py new file mode 100644 index 00000000..13aa7ddd --- /dev/null +++ b/tests/ahriman/core/test_types.py @@ -0,0 +1 @@ +# no need to test types explicitly