From ac19c407d3423e9f95a408e608e434314ac87b8b Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Fri, 10 May 2024 17:31:45 +0300 Subject: [PATCH] feat: allow to use simplified keys for context Initial implementation requires explicit context key name to be set. Though it is still useful sometimes (e.g. if there should be two variables with the same type), in the most used scenarios internally only type is required. This commit extends set and get methods to allow to construct ContextKey from type directly Also it breaks old keys, since - in order to reduce amount of possible mistakes - internal classes uses this generation method --- src/ahriman/core/__init__.py | 16 ++++++++++++---- .../core/gitremote/remote_push_trigger.py | 3 +-- src/ahriman/core/repository/repository.py | 11 +++++------ src/ahriman/core/support/keyring_trigger.py | 5 ++--- src/ahriman/core/support/package_creator.py | 3 +-- src/ahriman/models/context_key.py | 15 ++++++++++++++- .../core/gitremote/test_remote_push_trigger.py | 3 +-- tests/ahriman/core/repository/test_repository.py | 11 +++++------ .../ahriman/core/support/test_keyring_trigger.py | 3 +-- .../ahriman/core/support/test_package_creator.py | 3 +-- tests/ahriman/core/test_context_init.py | 12 ++++++++++++ tests/ahriman/models/test_context_key.py | 9 +++++++++ 12 files changed, 64 insertions(+), 30 deletions(-) diff --git a/src/ahriman/core/__init__.py b/src/ahriman/core/__init__.py index 53d4d036..5e72ecec 100644 --- a/src/ahriman/core/__init__.py +++ b/src/ahriman/core/__init__.py @@ -38,12 +38,12 @@ class _Context: """ self._content: dict[str, Any] = {} - def get(self, key: ContextKey[T]) -> T: + def get(self, key: ContextKey[T] | type[T]) -> T: """ get value for the specified key Args: - key(ContextKey[T]): context key name + key(ContextKey[T] | type[T]): context key name Returns: T: value associated with the key @@ -52,29 +52,37 @@ class _Context: KeyError: in case if the specified context variable was not found ValueError: in case if type of value is not an instance of specified return type """ + if not isinstance(key, ContextKey): + key = ContextKey.from_type(key) + if key.key not in self._content: raise KeyError(key.key) value = self._content[key.key] if not isinstance(value, key.return_type): raise ValueError(f"Value {value} is not an instance of {key.return_type}") + return value - def set(self, key: ContextKey[T], value: T) -> None: + def set(self, key: ContextKey[T] | type[T], value: T) -> None: """ set value for the specified key Args: - key(ContextKey[T]): context key name + key(ContextKey[T] | type[T]): context key name value(T): context value associated with the specified key Raises: KeyError: in case if the specified context variable already exists ValueError: in case if type of value is not an instance of specified return type """ + if not isinstance(key, ContextKey): + key = ContextKey.from_type(key) + if key.key in self._content: raise KeyError(key.key) if not isinstance(value, key.return_type): raise ValueError(f"Value {value} is not an instance of {key.return_type}") + self._content[key.key] = value def __iter__(self) -> Iterator[str]: diff --git a/src/ahriman/core/gitremote/remote_push_trigger.py b/src/ahriman/core/gitremote/remote_push_trigger.py index 5114ea4a..44aa9166 100644 --- a/src/ahriman/core/gitremote/remote_push_trigger.py +++ b/src/ahriman/core/gitremote/remote_push_trigger.py @@ -22,7 +22,6 @@ from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite from ahriman.core.gitremote.remote_push import RemotePush from ahriman.core.triggers import Trigger -from ahriman.models.context_key import ContextKey from ahriman.models.package import Package from ahriman.models.repository_id import RepositoryId from ahriman.models.result import Result @@ -111,7 +110,7 @@ class RemotePushTrigger(Trigger): GitRemoteError: if database is not set in context """ ctx = context.get() - database = ctx.get(ContextKey("database", SQLite)) + database = ctx.get(SQLite) for target in self.targets: section, _ = self.configuration.gettype( diff --git a/src/ahriman/core/repository/repository.py b/src/ahriman/core/repository/repository.py index 9fe6936b..f9c97907 100644 --- a/src/ahriman/core/repository/repository.py +++ b/src/ahriman/core/repository/repository.py @@ -26,7 +26,6 @@ from ahriman.core.database import SQLite from ahriman.core.repository.executor import Executor from ahriman.core.repository.update_handler import UpdateHandler from ahriman.core.sign.gpg import GPG -from ahriman.models.context_key import ContextKey from ahriman.models.pacman_synchronization import PacmanSynchronization from ahriman.models.repository_id import RepositoryId @@ -89,11 +88,11 @@ class Repository(Executor, UpdateHandler): # directly without loader ctx = _Context() - ctx.set(ContextKey("database", SQLite), self.database) - ctx.set(ContextKey("configuration", Configuration), self.configuration) - ctx.set(ContextKey("pacman", Pacman), self.pacman) - ctx.set(ContextKey("sign", GPG), self.sign) + ctx.set(SQLite, self.database) + ctx.set(Configuration, self.configuration) + ctx.set(Pacman, self.pacman) + ctx.set(GPG, self.sign) - ctx.set(ContextKey("repository", type(self)), self) + ctx.set(type(self), self) context.set(ctx) diff --git a/src/ahriman/core/support/keyring_trigger.py b/src/ahriman/core/support/keyring_trigger.py index 5fad5b2a..cbe24694 100644 --- a/src/ahriman/core/support/keyring_trigger.py +++ b/src/ahriman/core/support/keyring_trigger.py @@ -24,7 +24,6 @@ from ahriman.core.sign.gpg import GPG from ahriman.core.support.package_creator import PackageCreator from ahriman.core.support.pkgbuild.keyring_generator import KeyringGenerator from ahriman.core.triggers import Trigger -from ahriman.models.context_key import ContextKey from ahriman.models.repository_id import RepositoryId @@ -134,8 +133,8 @@ class KeyringTrigger(Trigger): trigger action which will be called at the start of the application """ ctx = context.get() - sign = ctx.get(ContextKey("sign", GPG)) - database = ctx.get(ContextKey("database", SQLite)) + sign = ctx.get(GPG) + database = ctx.get(SQLite) for target in self.targets: generator = KeyringGenerator(database, sign, self.repository_id, self.configuration, target) diff --git a/src/ahriman/core/support/package_creator.py b/src/ahriman/core/support/package_creator.py index a95cbcba..d33248ca 100644 --- a/src/ahriman/core/support/package_creator.py +++ b/src/ahriman/core/support/package_creator.py @@ -25,7 +25,6 @@ from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator from ahriman.models.build_status import BuildStatus -from ahriman.models.context_key import ContextKey from ahriman.models.package import Package @@ -65,7 +64,7 @@ class PackageCreator: # register package ctx = context.get() - database: SQLite = ctx.get(ContextKey("database", SQLite)) + database = ctx.get(SQLite) _, repository_id = self.configuration.check_loaded() package = Package.from_build(local_path, repository_id.architecture, None) database.package_update(package, BuildStatus()) diff --git a/src/ahriman/models/context_key.py b/src/ahriman/models/context_key.py index 4e1af459..ec46c67f 100644 --- a/src/ahriman/models/context_key.py +++ b/src/ahriman/models/context_key.py @@ -18,7 +18,7 @@ # along with this program. If not, see . # from dataclasses import dataclass -from typing import Generic, TypeVar +from typing import Generic, Self, TypeVar T = TypeVar("T") @@ -35,3 +35,16 @@ class ContextKey(Generic[T]): """ key: str return_type: type[T] + + @classmethod + def from_type(cls, return_type: type[T]) -> Self: + """ + construct key from type + + Args: + return_type(type[T]): return type used for the specified context key + + Returns: + Self: context key with autogenerated + """ + return cls(return_type.__name__, return_type) diff --git a/tests/ahriman/core/gitremote/test_remote_push_trigger.py b/tests/ahriman/core/gitremote/test_remote_push_trigger.py index bfe35173..d0458397 100644 --- a/tests/ahriman/core/gitremote/test_remote_push_trigger.py +++ b/tests/ahriman/core/gitremote/test_remote_push_trigger.py @@ -3,7 +3,6 @@ from pytest_mock import MockerFixture from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite from ahriman.core.gitremote import RemotePushTrigger -from ahriman.models.context_key import ContextKey from ahriman.models.package import Package from ahriman.models.result import Result @@ -30,5 +29,5 @@ def test_on_result(configuration: Configuration, result: Result, package_ahriman trigger = RemotePushTrigger(repository_id, configuration) trigger.on_result(result, [package_ahriman]) - database_mock.assert_called_once_with(ContextKey("database", SQLite)) + database_mock.assert_called_once_with(SQLite) run_mock.assert_called_once_with(result) diff --git a/tests/ahriman/core/repository/test_repository.py b/tests/ahriman/core/repository/test_repository.py index 654f89d9..0983c1fd 100644 --- a/tests/ahriman/core/repository/test_repository.py +++ b/tests/ahriman/core/repository/test_repository.py @@ -6,7 +6,6 @@ from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite from ahriman.core.repository import Repository from ahriman.core.sign.gpg import GPG -from ahriman.models.context_key import ContextKey def test_load(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None: @@ -29,9 +28,9 @@ def test_set_context(configuration: Configuration, database: SQLite, mocker: Moc instance = Repository.load(repository_id, configuration, database, report=False) set_mock.assert_has_calls([ - MockCall(ContextKey("database", SQLite), instance.database), - MockCall(ContextKey("configuration", Configuration), instance.configuration), - MockCall(ContextKey("pacman", Pacman), instance.pacman), - MockCall(ContextKey("sign", GPG), instance.sign), - MockCall(ContextKey("repository", Repository), instance), + MockCall(SQLite, instance.database), + MockCall(Configuration, instance.configuration), + MockCall(Pacman, instance.pacman), + MockCall(GPG, instance.sign), + MockCall(Repository, instance), ]) diff --git a/tests/ahriman/core/support/test_keyring_trigger.py b/tests/ahriman/core/support/test_keyring_trigger.py index bcc2fbfa..13631858 100644 --- a/tests/ahriman/core/support/test_keyring_trigger.py +++ b/tests/ahriman/core/support/test_keyring_trigger.py @@ -5,7 +5,6 @@ from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite from ahriman.core.sign.gpg import GPG from ahriman.core.support import KeyringTrigger -from ahriman.models.context_key import ContextKey def test_configuration_sections(configuration: Configuration) -> None: @@ -29,5 +28,5 @@ def test_on_start(configuration: Configuration, mocker: MockerFixture) -> None: trigger = KeyringTrigger(repository_id, configuration) trigger.on_start() - context_mock.assert_has_calls([MockCall(ContextKey("sign", GPG)), MockCall(ContextKey("database", SQLite))]) + context_mock.assert_has_calls([MockCall(GPG), MockCall(SQLite)]) run_mock.assert_called_once_with() diff --git a/tests/ahriman/core/support/test_package_creator.py b/tests/ahriman/core/support/test_package_creator.py index 61ae71d5..acb32316 100644 --- a/tests/ahriman/core/support/test_package_creator.py +++ b/tests/ahriman/core/support/test_package_creator.py @@ -4,7 +4,6 @@ from pytest_mock import MockerFixture from ahriman.core.database import SQLite from ahriman.core.support.package_creator import PackageCreator -from ahriman.models.context_key import ContextKey from ahriman.models.package import Package from ahriman.models.package_description import PackageDescription from ahriman.models.package_source import PackageSource @@ -38,5 +37,5 @@ def test_run(package_creator: PackageCreator, database: SQLite, mocker: MockerFi init_mock.assert_called_once_with(local_path) package_mock.assert_called_once_with(local_path, "x86_64", None) - database_mock.assert_called_once_with(ContextKey("database", SQLite)) + database_mock.assert_called_once_with(SQLite) insert_mock.assert_called_once_with(package, pytest.helpers.anyvar(int)) diff --git a/tests/ahriman/core/test_context_init.py b/tests/ahriman/core/test_context_init.py index ba4425f2..e7791d91 100644 --- a/tests/ahriman/core/test_context_init.py +++ b/tests/ahriman/core/test_context_init.py @@ -15,6 +15,18 @@ def test_get_set() -> None: assert ctx.get(key) == value +def test_get_set_type() -> None: + """ + must set and get variable by type + """ + key, value = int, 42 + ctx = _Context() + + ctx.set(key, value) + assert ctx.get(key) == value + assert ctx.get(ContextKey.from_type(int)) == value + + def test_get_key_exception() -> None: """ must raise KeyError in case if key was not found diff --git a/tests/ahriman/models/test_context_key.py b/tests/ahriman/models/test_context_key.py index e69de29b..555928c4 100644 --- a/tests/ahriman/models/test_context_key.py +++ b/tests/ahriman/models/test_context_key.py @@ -0,0 +1,9 @@ +from ahriman.models.context_key import ContextKey + + +def test_from_type() -> None: + """ + must construct key from type + """ + assert ContextKey.from_type(int) == ContextKey("int", int) + assert ContextKey.from_type(ContextKey) == ContextKey("ContextKey", ContextKey)