Compare commits

...

2 Commits

17 changed files with 123 additions and 41 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)

View File

@ -99,3 +99,24 @@ class LazyLogging:
yield yield
finally: finally:
self._package_logger_reset() self._package_logger_reset()
@contextlib.contextmanager
def suppress_logging(self, log_level: int = logging.WARNING) -> Generator[None, None, None]:
"""
silence log messages in context
Args:
log_level(int, optional): the highest log level to keep (Default value = logging.WARNING)
Examples:
This function is designed to be used to suppress all log messages in context, e.g.:
>>> with self.suppress_logging():
>>> do_some_noisy_actions()
"""
current_level = self.logger.manager.disable
try:
logging.disable(log_level)
yield
finally:
logging.disable(current_level)

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

@ -429,13 +429,14 @@ class Package(LazyLogging):
task = Task(self, configuration, repository_id.architecture, paths) task = Task(self, configuration, repository_id.architecture, paths)
try: try:
# create fresh chroot environment, fetch sources and - automagically - update PKGBUILD with self.suppress_logging():
task.init(paths.cache_for(self.base), [], None) # create fresh chroot environment, fetch sources and - automagically - update PKGBUILD
task.build(paths.cache_for(self.base), dry_run=True) task.init(paths.cache_for(self.base), [], None)
task.build(paths.cache_for(self.base), dry_run=True)
pkgbuild = Pkgbuild.from_file(paths.cache_for(self.base) / "PKGBUILD") pkgbuild = Pkgbuild.from_file(paths.cache_for(self.base) / "PKGBUILD")
return full_version(pkgbuild.get("epoch"), pkgbuild["pkgver"], pkgbuild["pkgrel"]) return full_version(pkgbuild.get("epoch"), pkgbuild["pkgver"], pkgbuild["pkgrel"])
except Exception: except Exception:
self.logger.exception("cannot determine version of VCS package") self.logger.exception("cannot determine version of VCS package")
finally: finally:

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

@ -2,6 +2,7 @@ import logging
import pytest import pytest
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.alpm.repo import Repo from ahriman.core.alpm.repo import Repo
from ahriman.core.build_tools.task import Task from ahriman.core.build_tools.task import Task
@ -10,6 +11,17 @@ from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package from ahriman.models.package import Package
def test_logger(database: SQLite, repo: Repo) -> None:
"""
must set logger attribute
"""
assert database.logger
assert database.logger.name == "sql"
assert repo.logger
assert repo.logger.name == "ahriman.core.alpm.repo.Repo"
def test_logger_name(database: SQLite, repo: Repo, task_ahriman: Task) -> None: def test_logger_name(database: SQLite, repo: Repo, task_ahriman: Task) -> None:
""" """
must correctly generate logger name must correctly generate logger name
@ -77,12 +89,11 @@ def test_in_package_context_failed(database: SQLite, package_ahriman: Package, m
reset_mock.assert_called_once_with() reset_mock.assert_called_once_with()
def test_logger(database: SQLite, repo: Repo) -> None: def test_suppress_logging(database: SQLite, mocker: MockerFixture) -> None:
""" """
must set logger attribute must temporary disable log messages
""" """
assert database.logger disable_mock = mocker.patch("ahriman.core.log.lazy_logging.logging.disable")
assert database.logger.name == "sql" with database.suppress_logging():
pass
assert repo.logger disable_mock.assert_has_calls([MockCall(logging.WARNING), MockCall(logging.NOTSET)])
assert repo.logger.name == "ahriman.core.alpm.repo.Repo"

View File

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