diff --git a/betty/app/__init__.py b/betty/app/__init__.py index b10ec0d97..1ee90e39b 100644 --- a/betty/app/__init__.py +++ b/betty/app/__init__.py @@ -151,25 +151,17 @@ def update(self, other: Self) -> None: self._dispatch_change() @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( OptionalField( "locale", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "locale"), + | asserter.assert_setattr(self, "locale"), ), ), )(dump) - return configuration @override def dump(self) -> VoidableDump: diff --git a/betty/config.py b/betty/config.py index 3a509747b..8cb545d5f 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 +from betty.serde.load import Asserter if TYPE_CHECKING: from _weakref import ReferenceType @@ -99,32 +99,11 @@ def update(self, other: Self) -> None: """ raise NotImplementedError(repr(self)) - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - """ - Load dumped configuration into a new configuration instance. - """ - raise NotImplementedError(repr(cls)) - - @classmethod - def assert_load( - cls: type[ConfigurationT], configuration: ConfigurationT | None = None - ) -> Assertion[Dump, ConfigurationT]: + def load(self, dump: Dump) -> None: """ - Assert that the dumped configuration can be loaded. + Load dumped configuration. """ - - def _assert_load(dump: Dump) -> ConfigurationT: - return cls.load(dump, configuration) - - _assert_load.__qualname__ = ( - f"{_assert_load.__qualname__} for {cls.__module__}.{cls.__qualname__}.load" - ) - return _assert_load + raise NotImplementedError(repr(self)) ConfigurationT = TypeVar("ConfigurationT", bound=Configuration) @@ -380,9 +359,8 @@ def to_keys(self, *indices: int | slice) -> Iterator[ConfigurationKeyT]: for index in sorted(unique_indices): yield self.to_key(index) - @classmethod - def _item_type(cls) -> type[ConfigurationT]: - raise NotImplementedError(repr(cls)) + def _item_type(self) -> type[ConfigurationT]: + raise NotImplementedError(repr(self)) def keys(self) -> Iterator[ConfigurationKeyT]: """ @@ -495,20 +473,11 @@ def update(self, other: Self) -> None: self.append(*other) @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() - else: - configuration._clear_without_dispatch() + def load(self, dump: Dump) -> None: + self._clear_without_dispatch() asserter = Asserter() with SerdeErrorCollection().assert_valid(): - configuration.append(*asserter.assert_sequence(cls._item_type().load)(dump)) - return configuration + self.append(*asserter.assert_sequence(self._item_type().load)(dump)) @override def dump(self) -> VoidableDump: @@ -652,21 +621,13 @@ def replace(self, *values: ConfigurationT) -> None: self.move_to_beginning(*other_keys) @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() dict_dump = asserter.assert_dict()(dump) - mapping = asserter.assert_mapping(cls._item_type().load)( - {key: cls._load_key(value, key) for key, value in dict_dump.items()} + mapping = asserter.assert_mapping(self._item_type().load)( + {key: self._load_key(value, key) for key, value in dict_dump.items()} ) - configuration.replace(*mapping.values()) - return configuration + self.replace(*mapping.values()) @override def dump(self) -> VoidableDump: @@ -758,13 +719,12 @@ def _move_by_offset( def _get_key(self, configuration: ConfigurationT) -> ConfigurationKeyT: raise NotImplementedError(repr(self)) - @classmethod def _load_key( - cls, + self, item_dump: Dump, key_dump: str, ) -> Dump: - raise NotImplementedError(repr(cls)) + raise NotImplementedError(repr(self)) def _dump_key(self, item_dump: VoidableDump) -> tuple[VoidableDump, str]: raise NotImplementedError(repr(self)) diff --git a/betty/extension/cotton_candy/__init__.py b/betty/extension/cotton_candy/__init__.py index fe1b2ab6f..869e232f9 100644 --- a/betty/extension/cotton_candy/__init__.py +++ b/betty/extension/cotton_candy/__init__.py @@ -89,19 +89,9 @@ def update(self, other: Self) -> None: self.hex = other.hex @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: + def load(self, dump: Dump) -> None: asserter = Asserter() - hex_value = asserter.assert_str()(dump) - if configuration is None: - configuration = cls(hex_value) - else: - configuration.hex = hex_value - return configuration + self.hex = asserter.assert_str()(dump) @override def dump(self) -> VoidableDump: @@ -193,55 +183,37 @@ def logo(self, logo: Path | None) -> None: self._dispatch_change() @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( OptionalField( "featured_entities", - configuration._featured_entities.assert_load( - configuration._featured_entities - ), + self.featured_entities.load, ), OptionalField( "primary_inactive_color", - configuration._primary_inactive_color.assert_load( - configuration._primary_inactive_color - ), + self._primary_inactive_color.load, ), OptionalField( "primary_active_color", - configuration._primary_active_color.assert_load( - configuration._primary_active_color - ), + self._primary_active_color.load, ), OptionalField( "link_inactive_color", - configuration._link_inactive_color.assert_load( - configuration._link_inactive_color - ), + self._link_inactive_color.load, ), OptionalField( "link_active_color", - configuration._link_active_color.assert_load( - configuration._link_active_color - ), + self._link_active_color.load, ), OptionalField( "logo", AssertionChain(asserter.assert_path()) - | asserter.assert_setattr(configuration, "logo"), + | asserter.assert_setattr(self, "logo"), ), ) )(dump) - return configuration @override def dump(self) -> VoidableDump: diff --git a/betty/extension/gramps/config.py b/betty/extension/gramps/config.py index 4959c90ae..71905c319 100644 --- a/betty/extension/gramps/config.py +++ b/betty/extension/gramps/config.py @@ -53,25 +53,17 @@ def file_path(self, file_path: Path | None) -> None: self._file_path = file_path @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( RequiredField( "file", AssertionChain(asserter.assert_path()) - | asserter.assert_setattr(configuration, "file_path"), + | asserter.assert_setattr(self, "file_path"), ), ) )(dump) - return configuration @override def dump(self) -> VoidableDump: @@ -89,8 +81,7 @@ class FamilyTreeConfigurationSequence(ConfigurationSequence[FamilyTreeConfigurat """ @override - @classmethod - def _item_type(cls) -> type[FamilyTreeConfiguration]: + def _item_type(self) -> type[FamilyTreeConfiguration]: return FamilyTreeConfiguration @@ -120,24 +111,13 @@ def update(self, other: Self) -> None: self._family_trees.update(other._family_trees) @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( - OptionalField( - "family_trees", - configuration._family_trees.assert_load(configuration.family_trees), - ), + OptionalField("family_trees", self.family_trees.load), ) )(dump) - return configuration @override def dump(self) -> VoidableDump: diff --git a/betty/extension/nginx/config.py b/betty/extension/nginx/config.py index bd78d91ed..ed953d45f 100644 --- a/betty/extension/nginx/config.py +++ b/betty/extension/nginx/config.py @@ -58,14 +58,7 @@ def update(self, other: Self) -> None: self._dispatch_change() @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( @@ -76,16 +69,15 @@ def load( asserter.assert_bool(), asserter.assert_none() ) ) - | asserter.assert_setattr(configuration, "https"), + | asserter.assert_setattr(self, "https"), ), OptionalField( "www_directory_path", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "www_directory_path"), + | asserter.assert_setattr(self, "www_directory_path"), ), ) )(dump) - return configuration @override def dump(self) -> VoidableDump: diff --git a/betty/extension/wikipedia/config.py b/betty/extension/wikipedia/config.py index 33dbeece8..f3188a6c1 100644 --- a/betty/extension/wikipedia/config.py +++ b/betty/extension/wikipedia/config.py @@ -37,25 +37,17 @@ def update(self, other: Self) -> None: self._dispatch_change() @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( OptionalField( "populate_images", AssertionChain(asserter.assert_bool()) - | asserter.assert_setattr(configuration, "populate_images"), + | asserter.assert_setattr(self, "populate_images"), ), ) )(dump) - return configuration @override def dump(self) -> VoidableDump: diff --git a/betty/project.py b/betty/project.py index 5cfd290cb..7e2912dc0 100644 --- a/betty/project.py +++ b/betty/project.py @@ -118,34 +118,29 @@ def update(self, other: Self) -> None: self._dispatch_change() @override - @classmethod def load( - cls, + self, dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + ) -> None: asserter = Asserter() - if isinstance(dump, dict) or not configuration.entity_type_is_constrained: + if isinstance(dump, dict) or not self.entity_type_is_constrained: asserter.assert_record( Fields( RequiredField( "entity_type", AssertionChain(asserter.assert_entity_type()) - | asserter.assert_setattr(configuration, "entity_type"), + | asserter.assert_setattr(self, "entity_type"), ), OptionalField( "entity_id", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "entity_id"), + | asserter.assert_setattr(self, "entity_id"), ), ) )(dump) else: asserter.assert_str()(dump) - asserter.assert_setattr(configuration, "entity_id")(dump) # type: ignore[arg-type] - return configuration + asserter.assert_setattr(self, "entity_id")(dump) # type: ignore[arg-type] @override def dump(self) -> VoidableDump: @@ -190,8 +185,7 @@ def __init__( super().__init__(entity_references) @override - @classmethod - def _item_type(cls) -> type[EntityReference[EntityT]]: + def _item_type(self) -> type[EntityReference[EntityT]]: return EntityReference @override @@ -312,12 +306,7 @@ def update(self, other: Self) -> None: self._set_extension_configuration(other._extension_configuration) @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: + def load(self, dump: Dump) -> None: asserter = Asserter() extension_type = asserter.assert_field( RequiredField( @@ -325,11 +314,8 @@ def load( asserter.assert_extension_type(), ) )(dump) - if configuration is None: - configuration = cls(extension_type) - else: - # This MUST NOT fail. If it does, this is a bug in the calling code that must be fixed. - assert extension_type is configuration.extension_type + # This MUST NOT fail. If it does, this is a bug in the calling code that must be fixed. + assert extension_type is self.extension_type asserter.assert_record( Fields( RequiredField( @@ -338,17 +324,14 @@ def load( OptionalField( "enabled", AssertionChain(asserter.assert_bool()) - | asserter.assert_setattr(configuration, "enabled"), + | asserter.assert_setattr(self, "enabled"), ), OptionalField( "configuration", - configuration._assert_load_extension_configuration( - configuration.extension_type - ), + self._assert_load_extension_configuration(self.extension_type), ), ) )(dump) - return configuration def _assert_load_extension_configuration( self, extension_type: type[Extension] @@ -356,7 +339,7 @@ def _assert_load_extension_configuration( def _assertion(value: Any) -> Configuration: extension_configuration = self._extension_configuration if isinstance(extension_configuration, Configuration): - return extension_configuration.load(value, extension_configuration) + return extension_configuration.load(value) raise AssertionFailed( Str._( "{extension_type} is not configurable.", @@ -407,8 +390,7 @@ def __init__( super().__init__(configurations) @override - @classmethod - def _item_type(cls) -> type[ExtensionConfiguration]: + def _item_type(self) -> type[ExtensionConfiguration]: return ExtensionConfiguration @override @@ -416,9 +398,8 @@ def _get_key(self, configuration: ExtensionConfiguration) -> type[Extension]: return configuration.extension_type @override - @classmethod def _load_key( - cls, + self, item_dump: Dump, key_dump: str, ) -> Dump: @@ -509,20 +490,16 @@ def update(self, other: Self) -> None: self._dispatch_change() @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: + def load(self, dump: Dump) -> None: asserter = Asserter() + # @todo Do something with this entity type + assert False entity_type = asserter.assert_field( RequiredField[Any, type[Entity]]( "entity_type", AssertionChain(asserter.assert_str()) | asserter.assert_entity_type(), ), )(dump) - configuration = cls(entity_type) asserter.assert_record( Fields( OptionalField( @@ -565,9 +542,8 @@ def _get_key(self, configuration: EntityTypeConfiguration) -> type[Entity]: return configuration.entity_type @override - @classmethod def _load_key( - cls, + self, item_dump: Dump, key_dump: str, ) -> Dump: @@ -583,8 +559,7 @@ def _dump_key(self, item_dump: VoidableDump) -> tuple[VoidableDump, str]: return dict_dump, dict_dump.pop("entity_type") @override - @classmethod - def _item_type(cls) -> type[EntityTypeConfiguration]: + def _item_type(self) -> type[EntityTypeConfiguration]: return EntityTypeConfiguration @override @@ -658,29 +633,23 @@ def update(self, other: Self) -> None: self._alias = other._alias @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: + def load(self, dump: Dump) -> None: asserter = Asserter() + # @todo Do something with this locale + assert False locale = asserter.assert_field( RequiredField("locale", asserter.assert_locale()), )(dump) - if configuration is None: - configuration = cls(locale) asserter.assert_record( Fields( RequiredField("locale"), OptionalField( "alias", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "alias"), + | asserter.assert_setattr(self, "alias"), ), ) )(dump) - return configuration @override def dump(self) -> VoidableDump: @@ -710,9 +679,8 @@ def _get_key(self, configuration: LocaleConfiguration) -> str: return configuration.locale @override - @classmethod def _load_key( - cls, + self, item_dump: Dump, key_dump: str, ) -> Dump: @@ -727,8 +695,7 @@ def _dump_key(self, item_dump: VoidableDump) -> tuple[VoidableDump, str]: return dict_item_dump, dict_item_dump.pop("locale") @override - @classmethod - def _item_type(cls) -> type[LocaleConfiguration]: + def _item_type(self) -> type[LocaleConfiguration]: return LocaleConfiguration @override @@ -1024,80 +991,55 @@ def update(self, other: Self) -> None: self._dispatch_change() @override - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( OptionalField( "name", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "name"), + | asserter.assert_setattr(self, "name"), ), RequiredField( "base_url", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "base_url"), + | asserter.assert_setattr(self, "base_url"), ), OptionalField( "title", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "title"), + | asserter.assert_setattr(self, "title"), ), OptionalField( "author", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "author"), + | asserter.assert_setattr(self, "author"), ), OptionalField( "root_path", AssertionChain(asserter.assert_str()) - | asserter.assert_setattr(configuration, "root_path"), + | asserter.assert_setattr(self, "root_path"), ), OptionalField( "clean_urls", AssertionChain(asserter.assert_bool()) - | asserter.assert_setattr(configuration, "clean_urls"), + | asserter.assert_setattr(self, "clean_urls"), ), OptionalField( "debug", AssertionChain(asserter.assert_bool()) - | asserter.assert_setattr(configuration, "debug"), + | asserter.assert_setattr(self, "debug"), ), OptionalField( "lifetime_threshold", AssertionChain(asserter.assert_int()) - | asserter.assert_setattr(configuration, "lifetime_threshold"), - ), - OptionalField( - "locales", - AssertionChain( - configuration._locales.assert_load(configuration.locales) - ), - ), - OptionalField( - "extensions", - AssertionChain( - configuration._extensions.assert_load(configuration.extensions) - ), - ), - OptionalField( - "entity_types", - AssertionChain( - configuration._entity_types.assert_load( - configuration.entity_types - ) - ), + | asserter.assert_setattr(self, "lifetime_threshold"), ), + OptionalField("locales", self.locales.load), + OptionalField("extensions", self._extensions.load), + OptionalField("entity_types", self._entity_types.load), ) )(dump) - return configuration @override def dump(self) -> VoidableDictDump[Dump]: diff --git a/betty/tests/app/test___init__.py b/betty/tests/app/test___init__.py index 5292ba0fd..934f18d15 100644 --- a/betty/tests/app/test___init__.py +++ b/betty/tests/app/test___init__.py @@ -42,25 +42,17 @@ def __init__(self, check: int = 0): super().__init__() self.check = check - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( RequiredField( "check", AssertionChain(asserter.assert_int()) - | asserter.assert_setattr(configuration, "check"), + | asserter.assert_setattr(self, "check"), ), ) )(dump) - return configuration def dump(self) -> VoidableDump: return {"check": self.check} diff --git a/betty/tests/extension/cotton_candy/test___init__.py b/betty/tests/extension/cotton_candy/test___init__.py index 04c6a8a0a..42c2fcabf 100644 --- a/betty/tests/extension/cotton_candy/test___init__.py +++ b/betty/tests/extension/cotton_candy/test___init__.py @@ -57,7 +57,8 @@ async def test_hex_with_invalid_value(self, hex_value: str) -> None: async def test_load_with_valid_hex_value(self) -> None: hex_value = "#000000" dump = hex_value - sut = _ColorConfiguration("#ffffff").load(dump) + sut = _ColorConfiguration("#ffffff") + sut.load(dump) assert hex_value == sut.hex @pytest.mark.parametrize( @@ -119,7 +120,8 @@ async def test_load_with_featured_entities(self) -> None: }, ], } - sut = CottonCandyConfiguration.load(dump) + sut = CottonCandyConfiguration() + sut.load(dump) assert entity_type is sut.featured_entities[0].entity_type assert entity_id == sut.featured_entities[0].entity_id @@ -128,7 +130,8 @@ async def test_load_with_primary_inactive_color(self) -> None: dump: Dump = { "primary_inactive_color": hex_value, } - sut = CottonCandyConfiguration.load(dump) + sut = CottonCandyConfiguration() + sut.load(dump) assert hex_value == sut.primary_inactive_color.hex async def test_load_with_primary_active_color(self) -> None: @@ -136,7 +139,8 @@ async def test_load_with_primary_active_color(self) -> None: dump: Dump = { "primary_active_color": hex_value, } - sut = CottonCandyConfiguration.load(dump) + sut = CottonCandyConfiguration() + sut.load(dump) assert hex_value == sut.primary_active_color.hex async def test_load_with_link_inactive_color(self) -> None: @@ -144,7 +148,8 @@ async def test_load_with_link_inactive_color(self) -> None: dump: Dump = { "link_inactive_color": hex_value, } - sut = CottonCandyConfiguration.load(dump) + sut = CottonCandyConfiguration() + sut.load(dump) assert hex_value == sut.link_inactive_color.hex async def test_load_with_link_active_color(self) -> None: @@ -152,7 +157,8 @@ async def test_load_with_link_active_color(self) -> None: dump: Dump = { "link_active_color": hex_value, } - sut = CottonCandyConfiguration.load(dump) + sut = CottonCandyConfiguration() + sut.load(dump) assert hex_value == sut.link_active_color.hex async def test_load_with_logo(self, tmp_path: Path) -> None: @@ -160,7 +166,8 @@ async def test_load_with_logo(self, tmp_path: Path) -> None: dump: Dump = { "logo": str(logo), } - sut = CottonCandyConfiguration.load(dump) + sut = CottonCandyConfiguration() + sut.load(dump) assert sut.logo == logo async def test_dump_with_minimal_configuration(self) -> None: diff --git a/betty/tests/extension/nginx/test_config.py b/betty/tests/extension/nginx/test_config.py index fde2373b7..a1b000452 100644 --- a/betty/tests/extension/nginx/test_config.py +++ b/betty/tests/extension/nginx/test_config.py @@ -33,7 +33,8 @@ async def test_load_with_https(self, https: bool | None) -> None: dump: Dump = { "https": https, } - sut = NginxConfiguration.load(dump) + sut = NginxConfiguration() + sut.load(dump) assert sut.https == https async def test_load_with_www_directory_path(self, tmp_path: Path) -> None: @@ -41,7 +42,8 @@ async def test_load_with_www_directory_path(self, tmp_path: Path) -> None: dump: Dump = { "www_directory_path": www_directory_path, } - sut = NginxConfiguration.load(dump) + sut = NginxConfiguration() + sut.load(dump) assert sut.www_directory_path == www_directory_path async def test_dump_with_minimal_configuration(self) -> None: diff --git a/betty/tests/extension/wikipedia/test_config.py b/betty/tests/extension/wikipedia/test_config.py index 44881a435..14e6b1e83 100644 --- a/betty/tests/extension/wikipedia/test_config.py +++ b/betty/tests/extension/wikipedia/test_config.py @@ -34,7 +34,8 @@ async def test_load_with_populate_images( dump: Dump = { "populate_images": populate_images, } - sut = WikipediaConfiguration.load(dump) + sut = WikipediaConfiguration() + sut.load(dump) assert sut.populate_images == populate_images async def test_dump_with_minimal_configuration(self) -> None: diff --git a/betty/tests/test_config.py b/betty/tests/test_config.py index af0bba00b..0ca918b20 100644 --- a/betty/tests/test_config.py +++ b/betty/tests/test_config.py @@ -219,8 +219,7 @@ async def test_iter(self) -> None: class ConfigurationSequenceTestConfigurationSequence( ConfigurationSequence[ConfigurationCollectionTestConfiguration[int]] ): - @classmethod - def _item_type(cls) -> type[ConfigurationCollectionTestConfiguration[int]]: + def _item_type(self) -> type[ConfigurationCollectionTestConfiguration[int]]: return ConfigurationCollectionTestConfiguration @@ -291,9 +290,8 @@ def _get_key( ) -> str: return configuration.key - @classmethod def _load_key( - cls, + self, item_dump: Dump, key_dump: str, ) -> Dump: diff --git a/betty/tests/test_project.py b/betty/tests/test_project.py index 9026c7d93..115df82ab 100644 --- a/betty/tests/test_project.py +++ b/betty/tests/test_project.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Iterable, Self, TYPE_CHECKING +from typing import Any, Iterable, TYPE_CHECKING import pytest @@ -68,12 +68,12 @@ async def test_entity_id(self) -> None: assert entity_id == sut.entity_id async def test_load_with_constraint(self) -> None: - configuration = EntityReference( + sut = EntityReference( EntityReferenceTestEntityOne, entity_type_is_constrained=True ) entity_id = "123" dump = entity_id - sut = EntityReference[EntityReferenceTestEntityOne].load(dump, configuration) + sut.load(dump) assert entity_id == sut.entity_id @pytest.mark.parametrize( @@ -94,11 +94,11 @@ async def test_load_with_constraint(self) -> None: async def test_load_with_constraint_without_string_should_error( self, dump: Dump ) -> None: - configuration = EntityReference( + sut = EntityReference( EntityReferenceTestEntityOne, entity_type_is_constrained=True ) with raises_error(error_type=AssertionFailed): - EntityReference.load(dump, configuration) + sut.load(dump) async def test_load_without_constraint(self) -> None: entity_type = EntityReferenceTestEntityOne @@ -107,7 +107,8 @@ async def test_load_without_constraint(self) -> None: "entity_type": get_entity_type_name(entity_type), "entity_id": entity_id, } - sut = EntityReference[EntityReferenceTestEntityOne].load(dump) + sut = EntityReference[EntityReferenceTestEntityOne]() + sut.load(dump) assert entity_type == sut.entity_type assert entity_id == sut.entity_id @@ -119,44 +120,45 @@ async def test_load_without_constraint_without_entity_type_should_error( dump: Dump = { "entity_id": entity_id, } + sut = EntityReference[EntityReferenceTestEntityOne]() with raises_error(error_type=AssertionFailed): - EntityReference.load(dump, sut) + sut.load(dump) async def test_load_without_constraint_without_string_entity_type_should_error( self, ) -> None: - sut = EntityReference[EntityReferenceTestEntityOne]() entity_id = "123" dump: Dump = { "entity_type": None, "entity_id": entity_id, } + sut = EntityReference[EntityReferenceTestEntityOne]() with raises_error(error_type=AssertionFailed): - EntityReference.load(dump, sut) + sut.load(dump) async def test_load_without_constraint_without_importable_entity_type_should_error( self, ) -> None: - sut = EntityReference[EntityReferenceTestEntityOne]() entity_id = "123" dump: Dump = { "entity_type": "betty.non_existent.Entity", "entity_id": entity_id, } + sut = EntityReference[EntityReferenceTestEntityOne]() with raises_error(error_type=AssertionFailed): - EntityReference.load(dump, sut) + sut.load(dump) async def test_load_without_constraint_without_string_entity_id_should_error( self, ) -> None: - sut = EntityReference[EntityReferenceTestEntityOne]() entity_type = EntityReferenceTestEntityOne dump: Dump = { "entity_type": get_entity_type_name(entity_type), "entity_id": None, } + sut = EntityReference[EntityReferenceTestEntityOne]() with raises_error(error_type=AssertionFailed): - EntityReference.load(dump, sut) + sut.load(dump) async def test_dump_with_constraint(self) -> None: sut = EntityReference[Entity](Entity, None, entity_type_is_constrained=True) @@ -687,7 +689,8 @@ async def test_author_with_author(self) -> None: async def test_load_should_load_minimal(self) -> None: dump: Any = ProjectConfiguration().dump() - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert dump["base_url"] == sut.base_url assert sut.title == "Betty" assert sut.author is None @@ -699,21 +702,24 @@ async def test_load_should_load_name(self) -> None: name = "MyFirstBettySite" dump: Any = ProjectConfiguration().dump() dump["name"] = name - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert sut.name == name async def test_load_should_load_title(self) -> None: title = "My first Betty site" dump: Any = ProjectConfiguration().dump() dump["title"] = title - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert title == sut.title async def test_load_should_load_author(self) -> None: author = "Bart" dump: Any = ProjectConfiguration().dump() dump["author"] = author - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert author == sut.author async def test_load_should_load_locale_locale(self) -> None: @@ -722,7 +728,8 @@ async def test_load_should_load_locale_locale(self) -> None: dump["locales"] = { locale: {}, } - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert LocaleConfigurationMapping([LocaleConfiguration(locale)]) == sut.locales async def test_load_should_load_locale_alias(self) -> None: @@ -735,7 +742,8 @@ async def test_load_should_load_locale_alias(self) -> None: dump["locales"] = { locale: locale_config, } - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert ( LocaleConfigurationMapping( [ @@ -753,14 +761,16 @@ async def test_load_should_root_path(self) -> None: expected_root_path = "betty" dump: Any = ProjectConfiguration().dump() dump["root_path"] = configured_root_path - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert expected_root_path == sut.root_path async def test_load_should_clean_urls(self) -> None: clean_urls = True dump: Any = ProjectConfiguration().dump() dump["clean_urls"] = clean_urls - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert clean_urls == sut.clean_urls @pytest.mark.parametrize( @@ -773,7 +783,8 @@ async def test_load_should_clean_urls(self) -> None: async def test_load_should_load_debug(self, debug: bool) -> None: dump: Any = ProjectConfiguration().dump() dump["debug"] = debug - sut = ProjectConfiguration.load(dump) + sut = ProjectConfiguration() + sut.load(dump) assert debug == sut.debug async def test_load_should_load_one_extension_with_configuration(self) -> None: @@ -786,11 +797,12 @@ async def test_load_should_load_one_extension_with_configuration(self) -> None: "configuration": extension_configuration, }, } + sut = ProjectConfiguration() expected = ExtensionConfiguration( DummyConfigurableExtension, extension_configuration=DummyConfigurableExtensionConfiguration(), ) - sut = ProjectConfiguration.load(dump) + sut.load(dump) assert expected == sut.extensions[DummyConfigurableExtension] async def test_load_should_load_one_extension_without_configuration(self) -> None: @@ -798,8 +810,9 @@ async def test_load_should_load_one_extension_without_configuration(self) -> Non dump["extensions"] = { DummyNonConfigurableExtension.name(): {}, } + sut = ProjectConfiguration() expected = ExtensionConfiguration(DummyNonConfigurableExtension) - sut = ProjectConfiguration.load(dump) + sut.load(dump) assert expected == sut.extensions[DummyNonConfigurableExtension] async def test_load_extension_with_invalid_configuration_should_raise_error( @@ -809,29 +822,33 @@ async def test_load_extension_with_invalid_configuration_should_raise_error( dump["extensions"] = { DummyConfigurableExtension.name(): 1337, } + sut = ProjectConfiguration() with raises_error(error_type=AssertionFailed): - ProjectConfiguration.load(dump) + sut.load(dump) async def test_load_unknown_extension_type_name_should_error(self) -> None: dump: Any = ProjectConfiguration().dump() dump["extensions"] = { "non.existent.type": {}, } + sut = ProjectConfiguration() with raises_error(error_type=AssertionFailed): - ProjectConfiguration.load(dump) + sut.load(dump) async def test_load_not_an_extension_type_name_should_error(self) -> None: dump: Any = ProjectConfiguration().dump() dump["extensions"] = { "%s.%s" % (self.__class__.__module__, self.__class__.__name__): {}, } + sut = ProjectConfiguration() with raises_error(error_type=AssertionFailed): - ProjectConfiguration.load(dump) + sut.load(dump) async def test_load_should_error_if_invalid_config(self) -> None: dump: Dump = {} + sut = ProjectConfiguration() with raises_error(error_type=AssertionFailed): - ProjectConfiguration.load(dump) + sut.load(dump) async def test_dump_should_dump_minimal(self) -> None: sut = ProjectConfiguration() @@ -947,8 +964,9 @@ async def test_dump_should_dump_one_extension_without_configuration(self) -> Non async def test_dump_should_error_if_invalid_config(self) -> None: dump: Dump = {} + sut = ProjectConfiguration() with raises_error(error_type=AssertionFailed): - ProjectConfiguration.load(dump) + sut.load(dump) class DummyNonConfigurableExtension(Extension): @@ -965,21 +983,13 @@ def __eq__(self, other: Any) -> bool: return NotImplemented return self.check == other.check - @classmethod - def load( - cls, - dump: Dump, - configuration: Self | None = None, - ) -> Self: - if configuration is None: - configuration = cls() + def load(self, dump: Dump) -> None: asserter = Asserter() asserter.assert_record( Fields( RequiredField("check", asserter.assert_bool()), ), )(dump) - return configuration def dump(self) -> VoidableDump: return {