diff --git a/betty/app/__init__.py b/betty/app/__init__.py index b2b2f43bf..b10ec0d97 100644 --- a/betty/app/__init__.py +++ b/betty/app/__init__.py @@ -65,9 +65,9 @@ from betty.serde.load import ( AssertionFailed, Fields, - Assertions, OptionalField, Asserter, + AssertionChain, ) from betty.warnings import deprecate @@ -164,7 +164,7 @@ def load( Fields( OptionalField( "locale", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "locale"), ), ), diff --git a/betty/config.py b/betty/config.py index ea618e2ea..bd1da64b9 100644 --- a/betty/config.py +++ b/betty/config.py @@ -41,7 +41,7 @@ from betty.serde.dump import Dumpable, Dump, minimize, VoidableDump, Void from betty.serde.error import SerdeErrorCollection from betty.serde.format import FormatRepository -from betty.serde.load import Asserter, Assertion, Assertions +from betty.serde.load import Asserter, Assertion if TYPE_CHECKING: from _weakref import ReferenceType @@ -507,11 +507,7 @@ def load( configuration._clear_without_dispatch() asserter = Asserter() with SerdeErrorCollection().assert_valid(): - configuration.append( - *asserter.assert_sequence(Assertions(cls._item_type().assert_load()))( - dump - ) - ) + configuration.append(*asserter.assert_sequence(cls._item_type().load)(dump)) return configuration @override @@ -666,7 +662,7 @@ def load( configuration = cls() asserter = Asserter() dict_dump = asserter.assert_dict()(dump) - mapping = asserter.assert_mapping(Assertions(cls._item_type().load))( + mapping = asserter.assert_mapping(cls._item_type().load)( {key: cls._load_key(value, key) for key, value in dict_dump.items()} ) configuration.replace(*mapping.values()) diff --git a/betty/extension/cotton_candy/__init__.py b/betty/extension/cotton_candy/__init__.py index af1b9e7ee..74ada8738 100644 --- a/betty/extension/cotton_candy/__init__.py +++ b/betty/extension/cotton_candy/__init__.py @@ -37,9 +37,9 @@ from betty.serde.load import ( AssertionFailed, Fields, - Assertions, OptionalField, Asserter, + AssertionChain, ) if TYPE_CHECKING: @@ -204,47 +204,37 @@ def load( Fields( OptionalField( "featured_entities", - Assertions( - configuration._featured_entities.assert_load( - configuration._featured_entities - ) + configuration._featured_entities.assert_load( + configuration._featured_entities ), ), OptionalField( "primary_inactive_color", - Assertions( - configuration._primary_inactive_color.assert_load( - configuration._primary_inactive_color - ) + configuration._primary_inactive_color.assert_load( + configuration._primary_inactive_color ), ), OptionalField( "primary_active_color", - Assertions( - configuration._primary_active_color.assert_load( - configuration._primary_active_color - ) + configuration._primary_active_color.assert_load( + configuration._primary_active_color ), ), OptionalField( "link_inactive_color", - Assertions( - configuration._link_inactive_color.assert_load( - configuration._link_inactive_color - ) + configuration._link_inactive_color.assert_load( + configuration._link_inactive_color ), ), OptionalField( "link_active_color", - Assertions( - configuration._link_active_color.assert_load( - configuration._link_active_color - ) + configuration._link_active_color.assert_load( + configuration._link_active_color ), ), OptionalField( "logo", - Assertions(asserter.assert_path()) + AssertionChain(asserter.assert_path()) | asserter.assert_setattr(configuration, "logo"), ), ) diff --git a/betty/extension/gramps/config.py b/betty/extension/gramps/config.py index 1aa212baf..4959c90ae 100644 --- a/betty/extension/gramps/config.py +++ b/betty/extension/gramps/config.py @@ -10,7 +10,13 @@ from betty.config import Configuration, ConfigurationSequence from betty.serde.dump import minimize, Dump, VoidableDump -from betty.serde.load import Asserter, Fields, RequiredField, Assertions, OptionalField +from betty.serde.load import ( + Asserter, + Fields, + RequiredField, + OptionalField, + AssertionChain, +) if TYPE_CHECKING: from pathlib import Path @@ -60,7 +66,7 @@ def load( Fields( RequiredField( "file", - Assertions(asserter.assert_path()) + AssertionChain(asserter.assert_path()) | asserter.assert_setattr(configuration, "file_path"), ), ) @@ -127,11 +133,7 @@ def load( Fields( OptionalField( "family_trees", - Assertions( - configuration._family_trees.assert_load( - configuration.family_trees - ) - ), + configuration._family_trees.assert_load(configuration.family_trees), ), ) )(dump) diff --git a/betty/extension/nginx/config.py b/betty/extension/nginx/config.py index e71aac297..bd78d91ed 100644 --- a/betty/extension/nginx/config.py +++ b/betty/extension/nginx/config.py @@ -6,7 +6,7 @@ from betty.config import Configuration from betty.serde.dump import Dump, VoidableDump, minimize, Void, VoidableDictDump -from betty.serde.load import Asserter, Fields, OptionalField, Assertions +from betty.serde.load import Asserter, Fields, OptionalField, AssertionChain class NginxConfiguration(Configuration): @@ -71,7 +71,7 @@ def load( Fields( OptionalField( "https", - Assertions( + AssertionChain( asserter.assert_or( asserter.assert_bool(), asserter.assert_none() ) @@ -80,7 +80,7 @@ def load( ), OptionalField( "www_directory_path", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "www_directory_path"), ), ) diff --git a/betty/extension/wikipedia/config.py b/betty/extension/wikipedia/config.py index 1333b82eb..33dbeece8 100644 --- a/betty/extension/wikipedia/config.py +++ b/betty/extension/wikipedia/config.py @@ -8,7 +8,7 @@ from betty.config import Configuration from betty.serde.dump import Dump, VoidableDump, minimize, VoidableDictDump -from betty.serde.load import Asserter, Fields, OptionalField, Assertions +from betty.serde.load import Asserter, Fields, OptionalField, AssertionChain class WikipediaConfiguration(Configuration): @@ -50,7 +50,7 @@ def load( Fields( OptionalField( "populate_images", - Assertions(asserter.assert_bool()) + AssertionChain(asserter.assert_bool()) | asserter.assert_setattr(configuration, "populate_images"), ), ) diff --git a/betty/project.py b/betty/project.py index e4e02c2b6..932282ea3 100644 --- a/betty/project.py +++ b/betty/project.py @@ -39,11 +39,11 @@ from betty.serde.load import ( AssertionFailed, Fields, - Assertions, Assertion, RequiredField, OptionalField, Asserter, + AssertionChain, ) from betty.warnings import deprecate @@ -132,12 +132,12 @@ def load( Fields( RequiredField( "entity_type", - Assertions(asserter.assert_entity_type()) + AssertionChain(asserter.assert_entity_type()) | asserter.assert_setattr(configuration, "entity_type"), ), OptionalField( "entity_id", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "entity_id"), ), ) @@ -322,7 +322,7 @@ def load( extension_type = asserter.assert_field( RequiredField( "extension", - Assertions(asserter.assert_extension_type()), + asserter.assert_extension_type(), ) )(dump) if configuration is None: @@ -337,15 +337,13 @@ def load( ), OptionalField( "enabled", - Assertions(asserter.assert_bool()) + AssertionChain(asserter.assert_bool()) | asserter.assert_setattr(configuration, "enabled"), ), OptionalField( "configuration", - Assertions( - configuration._assert_load_extension_configuration( - configuration.extension_type - ) + configuration._assert_load_extension_configuration( + configuration.extension_type ), ), ) @@ -521,7 +519,7 @@ def load( entity_type = asserter.assert_field( RequiredField[Any, type[Entity]]( "entity_type", - Assertions(asserter.assert_str()) | asserter.assert_entity_type(), + AssertionChain(asserter.assert_str()) | asserter.assert_entity_type(), ), )(dump) configuration = cls(entity_type) @@ -532,7 +530,7 @@ def load( ), OptionalField( "generate_html_list", - Assertions(asserter.assert_bool()) + AssertionChain(asserter.assert_bool()) | asserter.assert_setattr(configuration, "generate_html_list"), ), ) @@ -668,7 +666,7 @@ def load( ) -> Self: asserter = Asserter() locale = asserter.assert_field( - RequiredField("locale", Assertions(asserter.assert_locale())), + RequiredField("locale", asserter.assert_locale()), )(dump) if configuration is None: configuration = cls(locale) @@ -677,7 +675,7 @@ def load( RequiredField("locale"), OptionalField( "alias", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "alias"), ), ) @@ -1039,59 +1037,59 @@ def load( Fields( OptionalField( "name", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "name"), ), RequiredField( "base_url", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "base_url"), ), OptionalField( "title", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "title"), ), OptionalField( "author", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "author"), ), OptionalField( "root_path", - Assertions(asserter.assert_str()) + AssertionChain(asserter.assert_str()) | asserter.assert_setattr(configuration, "root_path"), ), OptionalField( "clean_urls", - Assertions(asserter.assert_bool()) + AssertionChain(asserter.assert_bool()) | asserter.assert_setattr(configuration, "clean_urls"), ), OptionalField( "debug", - Assertions(asserter.assert_bool()) + AssertionChain(asserter.assert_bool()) | asserter.assert_setattr(configuration, "debug"), ), OptionalField( "lifetime_threshold", - Assertions(asserter.assert_int()) + AssertionChain(asserter.assert_int()) | asserter.assert_setattr(configuration, "lifetime_threshold"), ), OptionalField( "locales", - Assertions( + AssertionChain( configuration._locales.assert_load(configuration.locales) ), ), OptionalField( "extensions", - Assertions( + AssertionChain( configuration._extensions.assert_load(configuration.extensions) ), ), OptionalField( "entity_types", - Assertions( + AssertionChain( configuration._entity_types.assert_load( configuration.entity_types ) diff --git a/betty/serde/load.py b/betty/serde/load.py index cda2922b7..302c9e48b 100644 --- a/betty/serde/load.py +++ b/betty/serde/load.py @@ -4,7 +4,6 @@ from __future__ import annotations -from dataclasses import dataclass from pathlib import Path from typing import ( Iterator, @@ -20,8 +19,6 @@ TypeAlias, ) -from typing_extensions import override - from betty.functools import _Result from betty.locale import LocaleNotFoundError, get_data, Str from betty.model import ( @@ -33,6 +30,7 @@ ) from betty.serde.dump import DumpType, DumpTypeT, Void from betty.serde.error import SerdeError, SerdeErrorCollection +from betty.warnings import deprecated if TYPE_CHECKING: from betty.app.extension import Extension @@ -81,27 +79,9 @@ class FormatError(LoadError): _AssertionsIntermediateValueReturnT = TypeVar("_AssertionsIntermediateValueReturnT") -class _Assertions(Generic[_AssertionValueT, _AssertionReturnT]): - def extend( - self, _assertion: Assertion[_AssertionReturnT, _AssertionsExtendReturnT] - ) -> _Assertions[_AssertionValueT, _AssertionsExtendReturnT]: - return _AssertionExtension(_assertion, self) - - def __or__( - self, _assertion: Assertion[_AssertionReturnT, _AssertionsExtendReturnT] - ) -> _Assertions[_AssertionValueT, _AssertionsExtendReturnT]: - return self.extend(_assertion) - - def __call__(self, value: _AssertionValueT) -> _Result[_AssertionReturnT]: - raise NotImplementedError(repr(self)) - - -class Assertions( - _Assertions[_AssertionValueT, _AssertionReturnT], - Generic[_AssertionValueT, _AssertionReturnT], -): +class AssertionChain(Generic[_AssertionValueT, _AssertionReturnT]): """ - Start an assertions chain. + An assertion chain. Assertion chains let you chain/link/combine assertions into pipelines that take an input value and, if the assertions pass, return an output value. Each chain may be (re)used as many @@ -115,37 +95,109 @@ class Assertions( def __init__(self, _assertion: Assertion[_AssertionValueT, _AssertionReturnT]): self._assertion = _assertion - @override + def extend( + self, _assertion: Assertion[_AssertionReturnT, _AssertionsExtendReturnT] + ) -> AssertionChain[_AssertionValueT, _AssertionsExtendReturnT]: + """ + Extend the chain with the given assertion. + """ + return _AssertionChainExtension(_assertion, self) + + def __or__( + self, _assertion: Assertion[_AssertionReturnT, _AssertionsExtendReturnT] + ) -> AssertionChain[_AssertionValueT, _AssertionsExtendReturnT]: + return self.extend(_assertion) + def __call__(self, value: _AssertionValueT) -> _Result[_AssertionReturnT]: + """ + Invoke the chain with a value. + + This method may be called more than once. + """ return _Result(value).map(self._assertion) + @property + def assertion(self) -> Assertion[_AssertionValueT, _AssertionReturnT]: + """ + The assertion for this chain. + """ + return lambda value: self(value).value + -class _AssertionExtension( - _Assertions[_AssertionValueT, _AssertionReturnT], +class _AssertionChainExtension( + AssertionChain[_AssertionValueT, _AssertionReturnT], Generic[_AssertionValueT, _AssertionReturnT], ): def __init__( self, - assertion: Assertion[_AssertionsIntermediateValueReturnT, _AssertionReturnT], - extended_assertion: _Assertions[ + assertion_extension: Assertion[ + _AssertionsIntermediateValueReturnT, _AssertionReturnT + ], + assertion_chain: AssertionChain[ _AssertionValueT, _AssertionsIntermediateValueReturnT ], ): - self._assertion = assertion - self._extended_assertion = extended_assertion + super().__init__( + lambda value: assertion_chain(value).map(assertion_extension).value + ) + + +@deprecated( + "This class is deprecated as of Betty 0.3.8, and will be removed in Betty 0.4.x. Instead, use :py:class:`betty.serde.load.AssertionChain`." +) +class Assertions( # noqa: D101 + AssertionChain[_AssertionValueT, _AssertionReturnT], + Generic[_AssertionValueT, _AssertionReturnT], +): + pass - @override - def __call__(self, value: _AssertionValueT) -> _Result[_AssertionReturnT]: - return self._extended_assertion(value).map(self._assertion) + +AssertionType: TypeAlias = ( + AssertionChain[_AssertionValueT, _AssertionReturnT] + | Assertion[_AssertionValueT, _AssertionReturnT] +) -@dataclass(frozen=True) class _Field(Generic[_AssertionValueT, _AssertionReturnT]): - name: str - assertion: _Assertions[_AssertionValueT, _AssertionReturnT] | None = None + @overload + def __init__( + self, + name: str, + assertion: AssertionChain[_AssertionValueT, _AssertionReturnT] | None = None, + ): + pass + + @overload + def __init__( + self, + name: str, + assertion: Assertion[_AssertionValueT, _AssertionReturnT] | None = None, + ): + pass + + def __init__( + self, + name: str, + assertion: AssertionChain[_AssertionValueT, _AssertionReturnT] + | Assertion[_AssertionValueT, _AssertionReturnT] + | None = None, + ): + self._name = name + self._assertion = ( + assertion + if assertion is None or isinstance(assertion, AssertionChain) + else AssertionChain(assertion) + ) + + @property + def name(self) -> str: + return self._name + + @property + def assertion(self) -> AssertionChain[_AssertionValueT, _AssertionReturnT] | None: + return self._assertion -@dataclass(frozen=True) class RequiredField( Generic[_AssertionValueT, _AssertionReturnT], _Field[_AssertionValueT, _AssertionReturnT], @@ -157,7 +209,6 @@ class RequiredField( pass # pragma: no cover -@dataclass(frozen=True) class OptionalField( Generic[_AssertionValueT, _AssertionReturnT], _Field[_AssertionValueT, _AssertionReturnT], @@ -337,19 +388,33 @@ def _assert_dict(value: Any) -> dict[str, Any]: return _assert_dict def assert_assertions( - self, assertions: _Assertions[_AssertionValueT, _AssertionReturnT] + self, assertions: AssertionType[_AssertionValueT, _AssertionReturnT] ) -> Assertion[_AssertionValueT, _AssertionReturnT]: """ Assert that an assertions chain passes, and return the chain's output. """ def _assert_assertions(value: _AssertionValueT) -> _AssertionReturnT: - return assertions(value).value + if isinstance(assertions, AssertionChain): + return assertions(value).value + return assertions(value) return _assert_assertions + @overload def assert_sequence( - self, item_assertion: Assertions[Any, _AssertionReturnT] + self, item_assertion: AssertionChain[Any, _AssertionReturnT] + ) -> Assertion[Any, MutableSequence[_AssertionReturnT]]: + pass + + @overload + def assert_sequence( + self, item_assertion: Assertion[Any, _AssertionReturnT] + ) -> Assertion[Any, MutableSequence[_AssertionReturnT]]: + pass + + def assert_sequence( + self, item_assertion: AssertionType[Any, _AssertionReturnT] ) -> Assertion[Any, MutableSequence[_AssertionReturnT]]: """ Assert that a value is a sequence and that all item values are of the given type. @@ -357,7 +422,7 @@ def assert_sequence( def _assert_sequence(value: Any) -> MutableSequence[_AssertionReturnT]: list_value = self.assert_list()(value) - sequence = [] + sequence: MutableSequence[_AssertionReturnT] = [] with SerdeErrorCollection().assert_valid() as errors: for value_item_index, value_item_value in enumerate(list_value): with errors.catch(Str.plain(value_item_index)): @@ -368,16 +433,26 @@ def _assert_sequence(value: Any) -> MutableSequence[_AssertionReturnT]: return _assert_sequence + @overload + def assert_mapping( + self, item_assertion: AssertionChain[Any, _AssertionReturnT] + ) -> Assertion[Any, MutableMapping[str, _AssertionReturnT]]: + pass + + @overload def assert_mapping( - self, item_assertion: Assertions[Any, _AssertionReturnT] + self, item_assertion: Assertion[Any, _AssertionReturnT] ) -> Assertion[Any, MutableMapping[str, _AssertionReturnT]]: + pass + + def assert_mapping(self, item_assertion): """ Assert that a value is a key-value mapping and assert that all item values are of the given type. """ def _assert_mapping(value: Any) -> MutableMapping[str, _AssertionReturnT]: dict_value = self.assert_dict()(value) - mapping = {} + mapping: MutableMapping[str, _AssertionReturnT] = {} with SerdeErrorCollection().assert_valid() as errors: for value_item_key, value_item_value in dict_value.items(): with errors.catch(Str.plain(value_item_key)): @@ -395,7 +470,7 @@ def assert_fields(self, fields: Fields) -> Assertion[Any, MutableMapping[str, An def _assert_fields(value: Any) -> MutableMapping[str, Any]: value_dict = self.assert_dict()(value) - mapping = {} + mapping: MutableMapping[str, Any] = {} with SerdeErrorCollection().assert_valid() as errors: for field in fields: with errors.catch(Str.plain(field.name)): diff --git a/betty/tests/app/test___init__.py b/betty/tests/app/test___init__.py index 00cb00f29..5292ba0fd 100644 --- a/betty/tests/app/test___init__.py +++ b/betty/tests/app/test___init__.py @@ -13,7 +13,7 @@ from betty.config import Configuration from betty.model import Entity from betty.project import ExtensionConfiguration -from betty.serde.load import Fields, Assertions, RequiredField, Asserter +from betty.serde.load import Fields, RequiredField, Asserter, AssertionChain if TYPE_CHECKING: from betty.serde.dump import Dump, VoidableDump @@ -55,7 +55,7 @@ def load( Fields( RequiredField( "check", - Assertions(asserter.assert_int()) + AssertionChain(asserter.assert_int()) | asserter.assert_setattr(configuration, "check"), ), ) diff --git a/betty/tests/coverage/test_coverage.py b/betty/tests/coverage/test_coverage.py index 9dfdf1532..ba82fa298 100644 --- a/betty/tests/coverage/test_coverage.py +++ b/betty/tests/coverage/test_coverage.py @@ -752,6 +752,7 @@ class TestKnownToBeMissing: }, # This is an empty class. "AssertionFailed": TestKnownToBeMissing, + # This is a deprecated class. "Assertions": TestKnownToBeMissing, "Fields": TestKnownToBeMissing, # This is an empty class. diff --git a/betty/tests/serde/test_load.py b/betty/tests/serde/test_load.py index 132d05a47..1a5ba8858 100644 --- a/betty/tests/serde/test_load.py +++ b/betty/tests/serde/test_load.py @@ -14,16 +14,35 @@ Number, Fields, OptionalField, - Assertions, RequiredField, Assertion, + AssertionChain, ) from betty.tests.serde import raises_error - _T = TypeVar("_T") +class TestAssertionChain: + async def test___call__(self) -> None: + sut = AssertionChain[int, int](lambda value: value) + assert sut(123).value == 123 + + async def test___or__(self) -> None: + sut = AssertionChain[int, int](lambda value: value) + sut |= lambda value: 2 * value + assert sut.assertion(123) == 246 + + async def test_assertion(self) -> None: + sut = AssertionChain[int, int](lambda value: value) + assert sut.assertion(123) == 123 + + async def test_extend(self) -> None: + sut = AssertionChain[int, int](lambda value: value) + sut = sut.extend(lambda value: 2 * value) + assert sut.assertion(123) == 246 + + def _always_valid(value: _T) -> _T: return value @@ -41,7 +60,7 @@ class TestAsserter: (_always_invalid, _always_valid, 123), ], ) - async def test_assert_or_with_valid_assertions( + async def test_assert_or_with_valid_AssertionChain( self, if_assertion: Assertion[Any, bool], else_assertion: Assertion[Any, bool], @@ -50,7 +69,7 @@ async def test_assert_or_with_valid_assertions( sut = Asserter() assert sut.assert_or(if_assertion, else_assertion)(value) == value - async def test_assert_or_with_invalid_assertions(self) -> None: + async def test_assert_or_with_invalid_AssertionChain(self) -> None: sut = Asserter() with raises_error(error_type=AssertionFailed): sut.assert_or(_always_invalid, _always_invalid)(123) @@ -149,20 +168,20 @@ async def test_assert_list_without_list(self) -> None: async def test_assert_sequence_without_list(self) -> None: sut = Asserter() with raises_error(error_type=AssertionFailed): - sut.assert_sequence(Assertions(sut.assert_str()))(False) + sut.assert_sequence(AssertionChain(sut.assert_str()))(False) async def test_assert_sequence_with_invalid_item(self) -> None: sut = Asserter() with raises_error(error_type=AssertionFailed, error_contexts=["0"]): - sut.assert_sequence(Assertions(sut.assert_str()))([123]) + sut.assert_sequence(AssertionChain(sut.assert_str()))([123]) async def test_assert_sequence_with_empty_list(self) -> None: sut = Asserter() - sut.assert_sequence(Assertions(sut.assert_str()))([]) + sut.assert_sequence(AssertionChain(sut.assert_str()))([]) async def test_assert_sequence_with_valid_sequence(self) -> None: sut = Asserter() - sut.assert_sequence(Assertions(sut.assert_str()))(["Hello!"]) + sut.assert_sequence(AssertionChain(sut.assert_str()))(["Hello!"]) async def test_assert_dict_with_dict(self) -> None: sut = Asserter() @@ -180,7 +199,7 @@ async def test_assert_fields_with_invalid_value(self) -> None: Fields( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) ) )(None) @@ -192,7 +211,7 @@ async def test_assert_fields_required_without_key(self) -> None: Fields( RequiredField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) ) )({}) @@ -204,7 +223,7 @@ async def test_assert_fields_optional_without_key(self) -> None: Fields( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) ) )({}) @@ -219,7 +238,7 @@ async def test_assert_fields_required_key_with_key(self) -> None: Fields( RequiredField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) ) )({"hello": "World!"}) @@ -234,7 +253,7 @@ async def test_assert_fields_optional_key_with_key(self) -> None: Fields( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) ) )({"hello": "World!"}) @@ -246,7 +265,7 @@ async def test_assert_field_with_invalid_value(self) -> None: sut.assert_field( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) )(None) @@ -256,7 +275,7 @@ async def test_assert_field_required_without_key(self) -> None: sut.assert_field( RequiredField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) )({}) @@ -266,7 +285,7 @@ async def test_assert_field_optional_without_key(self) -> None: actual = sut.assert_field( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) )({}) assert expected == actual @@ -277,7 +296,7 @@ async def test_assert_field_required_key_with_key(self) -> None: actual = sut.assert_field( RequiredField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) )({"hello": "World!"}) assert expected == actual @@ -288,7 +307,7 @@ async def test_assert_field_optional_key_with_key(self) -> None: actual = sut.assert_field( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ) )({"hello": "World!"}) assert expected == actual @@ -296,20 +315,20 @@ async def test_assert_field_optional_key_with_key(self) -> None: async def test_assert_mapping_without_mapping(self) -> None: sut = Asserter() with raises_error(error_type=AssertionFailed): - sut.assert_mapping(Assertions(sut.assert_str()))(None) + sut.assert_mapping(AssertionChain(sut.assert_str()))(None) async def test_assert_mapping_with_invalid_item(self) -> None: sut = Asserter() with raises_error(error_type=AssertionFailed, error_contexts=["hello"]): - sut.assert_mapping(Assertions(sut.assert_str()))({"hello": False}) + sut.assert_mapping(AssertionChain(sut.assert_str()))({"hello": False}) async def test_assert_mapping_with_empty_dict(self) -> None: sut = Asserter() - sut.assert_mapping(Assertions(sut.assert_str()))({}) + sut.assert_mapping(AssertionChain(sut.assert_str()))({}) async def test_assert_mapping_with_valid_mapping(self) -> None: sut = Asserter() - sut.assert_mapping(Assertions(sut.assert_str()))({"hello": "World!"}) + sut.assert_mapping(AssertionChain(sut.assert_str()))({"hello": "World!"}) async def test_assert_record_with_optional_fields_without_items(self) -> None: sut = Asserter() @@ -318,7 +337,7 @@ async def test_assert_record_with_optional_fields_without_items(self) -> None: Fields( OptionalField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ), ) )({}) @@ -333,7 +352,7 @@ async def test_assert_record_with_optional_fields_with_items(self) -> None: Fields( OptionalField( "hello", - Assertions(sut.assert_str()) | (lambda x: x.upper()), + AssertionChain(sut.assert_str()) | (lambda x: x.upper()), ), ) )({"hello": "World!"}) @@ -346,7 +365,7 @@ async def test_assert_record_with_required_fields_without_items(self) -> None: Fields( RequiredField( "hello", - Assertions(sut.assert_str()), + AssertionChain(sut.assert_str()), ), ) )({}) @@ -360,7 +379,7 @@ async def test_assert_record_with_required_fields_with_items(self) -> None: Fields( RequiredField( "hello", - Assertions(sut.assert_str()) | (lambda x: x.upper()), + AssertionChain(sut.assert_str()) | (lambda x: x.upper()), ), ) )( diff --git a/betty/tests/test_project.py b/betty/tests/test_project.py index 391451d56..9026c7d93 100644 --- a/betty/tests/test_project.py +++ b/betty/tests/test_project.py @@ -23,7 +23,6 @@ AssertionFailed, Asserter, Fields, - Assertions, RequiredField, ) from betty.tests.serde import raises_error @@ -977,7 +976,7 @@ def load( asserter = Asserter() asserter.assert_record( Fields( - RequiredField("check", Assertions(asserter.assert_bool())), + RequiredField("check", asserter.assert_bool()), ), )(dump) return configuration