refactor: even further improvements for Handler.check_status method

This commit is contained in:
Evgenii Alekseev 2024-09-24 18:34:50 +03:00
parent 5d495fc813
commit b357c96204
14 changed files with 78 additions and 29 deletions

View File

@ -52,6 +52,14 @@ ahriman.core.tree module
:no-undoc-members: :no-undoc-members:
:show-inheritance: :show-inheritance:
ahriman.core.types module
-------------------------
.. automodule:: ahriman.core.types
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.util module ahriman.core.util module
------------------------ ------------------------

View File

@ -27,6 +27,7 @@ from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
from ahriman.core.log.log_loader import LogLoader 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_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -124,13 +125,14 @@ class Handler:
raise NotImplementedError raise NotImplementedError
@staticmethod @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 check condition and flag and raise ExitCode exception in case if it is enabled and condition match
Args: Args:
enabled(bool): if ``False`` no check will be performed 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: Raises:
ExitCode: if result is empty and check is enabled ExitCode: if result is empty and check is enabled
@ -138,12 +140,9 @@ class Handler:
if not enabled: if not enabled:
return return
match status: status = status() if callable(status) else status
case False: if not status:
raise ExitCode raise ExitCode
# https://github.com/python/mypy/issues/14014
case Callable() if not status(): # type: ignore[misc]
raise ExitCode
@staticmethod @staticmethod
def repositories_extract(args: argparse.Namespace) -> list[RepositoryId]: def repositories_extract(args: argparse.Namespace) -> list[RepositoryId]:

View File

@ -136,7 +136,7 @@ class Patch(Handler):
for patch in application.reporter.package_patches_get(package_base, None) for patch in application.reporter.package_patches_get(package_base, None)
if variables is None or patch.key in variables 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=" = ") PatchPrinter(package_base, patches)(verbose=True, separator=" = ")

View File

@ -51,7 +51,7 @@ class Rebuild(Handler):
packages = Rebuild.extract_packages(application, args.status, from_database=args.from_database) packages = Rebuild.extract_packages(application, args.status, from_database=args.from_database)
packages = application.repository.packages_depend_on(packages, args.depends_on) 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: if args.dry_run:
application.print_updates(packages, log_fn=print) application.print_updates(packages, log_fn=print)
return return

View File

@ -61,7 +61,7 @@ class Status(Handler):
else: else:
packages = client.package_get(None) 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 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] =\

View File

@ -54,7 +54,7 @@ class Update(Handler):
application.changes(packages) application.changes(packages)
if args.dry_run: # exit from application if no build requested 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 return
packages = application.with_dependencies(packages, process_dependencies=args.dependencies) packages = application.with_dependencies(packages, process_dependencies=args.dependencies)

View File

@ -61,7 +61,7 @@ class Users(Handler):
users = database.user_list(args.username, args.role) users = database.user_list(args.username, args.role)
for user in users: for user in users:
UserPrinter(user)(verbose=True) UserPrinter(user)(verbose=True)
Users.check_status(args.exit_code, bool(users)) Users.check_status(args.exit_code, users)
case Action.Remove: case Action.Remove:
database.user_remove(args.username) database.user_remove(args.username)

39
src/ahriman/core/types.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#
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

View File

@ -168,7 +168,7 @@ def test_patch_set_list(application: Application, mocker: MockerFixture) -> None
Patch.patch_set_list(application, "ahriman", ["version"], False) Patch.patch_set_list(application, "ahriman", ["version"], False)
get_mock.assert_called_once_with("ahriman", None) get_mock.assert_called_once_with("ahriman", None)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ") 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: 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) Patch.patch_set_list(application, "ahriman", None, False)
get_mock.assert_called_once_with("ahriman", None) get_mock.assert_called_once_with("ahriman", None)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ") 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: 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") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
Patch.patch_set_list(application, "ahriman", [], True) 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: def test_patch_set_create(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -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) 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_packages_mock.assert_called_once_with([package_ahriman], None)
application_mock.assert_called_once_with([package_ahriman], Packagers(args.username), bump_pkgrel=args.increment) 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() 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() _, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False) Rebuild.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called() 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)) 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() _, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False) 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, 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() _, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False) 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: def test_extract_packages(application: Application, mocker: MockerFixture) -> None:

View File

@ -36,11 +36,13 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
must run command must run command
""" """
args = _default_args(args) 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) mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.Client.status_get") application_mock = mocker.patch("ahriman.core.status.Client.status_get")
packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=packages)
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print") 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) Status.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with() application_mock.assert_called_once_with()
packages_mock.assert_called_once_with(None) 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([ print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ") MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
for _ in range(3) for _ in range(3)
@ -69,7 +71,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False) 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, def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository,

View File

@ -85,7 +85,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False) 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, 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) args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
application_mock.assert_not_called() application_mock.assert_not_called()
changes_mock.assert_called_once_with([package_ahriman]) 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, def test_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository,

View File

@ -103,7 +103,7 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, databa
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False) Users.run(args, repository_id, configuration, report=False)
list_mock.assert_called_once_with("user", args.role) 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, 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() _, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False) 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, def test_run_remove(args: argparse.Namespace, configuration: Configuration, database: SQLite,

View File

@ -0,0 +1 @@
# no need to test types explicitly