Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions betty/ancestry/event_type/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, final

from betty.data import Data
from betty.locale.localizable.gettext import _, ngettext
from betty.plugin import Plugin, PluginTypeDefinition
from betty.plugin.data import DataPluginDefinition
from betty.plugin.discovery.entry_point import EntryPointDiscovery
from betty.plugin.discovery.project import ProjectDiscovery
from betty.plugin.human_facing import CountableHumanFacingPluginDefinition
Expand All @@ -24,7 +26,7 @@
from betty.project import Project


class EventType(Plugin["EventTypeDefinition"]):
class EventType(Data, Plugin["EventTypeDefinition"]):
"""
Define an :py:class:`betty.ancestry.event.Event` type.
"""
Expand Down Expand Up @@ -58,7 +60,9 @@ async def should_exist(cls, project: Project, person: Person) -> bool:
],
)
class EventTypeDefinition(
CountableHumanFacingPluginDefinition[EventType], OrderedPluginDefinition[EventType]
CountableHumanFacingPluginDefinition[EventType],
OrderedPluginDefinition[EventType],
DataPluginDefinition[EventType],
):
"""
.. plugin_type:: event-type.
Expand Down
8 changes: 6 additions & 2 deletions betty/ancestry/gender/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@

from typing import final

from betty.data import Data
from betty.locale.localizable.gettext import _, ngettext
from betty.plugin import Plugin, PluginTypeDefinition
from betty.plugin.data import DataPluginDefinition
from betty.plugin.discovery.entry_point import EntryPointDiscovery
from betty.plugin.discovery.project import ProjectDiscovery
from betty.plugin.human_facing import CountableHumanFacingPluginDefinition


class Gender(Plugin["GenderDefinition"]):
class Gender(Data, Plugin["GenderDefinition"]):
"""
Define a gender.
"""
Expand All @@ -31,7 +33,9 @@ class Gender(Plugin["GenderDefinition"]):
ProjectDiscovery(lambda project: project.configuration.genders.new_plugins()),
],
)
class GenderDefinition(CountableHumanFacingPluginDefinition[Gender]):
class GenderDefinition(
CountableHumanFacingPluginDefinition[Gender], DataPluginDefinition[Gender]
):
"""
.. plugin_type:: gender.

Expand Down
8 changes: 6 additions & 2 deletions betty/ancestry/place_type/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@

from typing import final

from betty.data import Data
from betty.locale.localizable.gettext import _, ngettext
from betty.plugin import Plugin, PluginTypeDefinition
from betty.plugin.data import DataPluginDefinition
from betty.plugin.discovery.entry_point import EntryPointDiscovery
from betty.plugin.discovery.project import ProjectDiscovery
from betty.plugin.human_facing import (
CountableHumanFacingPluginDefinition,
)


class PlaceType(Plugin["PlaceTypeDefinition"]):
class PlaceType(Data, Plugin["PlaceTypeDefinition"]):
"""
Define a :py:class:`betty.ancestry.place.Place` type.
"""
Expand All @@ -35,7 +37,9 @@ class PlaceType(Plugin["PlaceTypeDefinition"]):
),
],
)
class PlaceTypeDefinition(CountableHumanFacingPluginDefinition[PlaceType]):
class PlaceTypeDefinition(
CountableHumanFacingPluginDefinition[PlaceType], DataPluginDefinition[PlaceType]
):
"""
.. plugin_type:: place-type.
"""
9 changes: 7 additions & 2 deletions betty/ancestry/presence_role/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@

from typing import final

from betty.data import Data
from betty.locale.localizable.gettext import _, ngettext
from betty.plugin import Plugin, PluginTypeDefinition
from betty.plugin.data import DataPluginDefinition
from betty.plugin.discovery.entry_point import EntryPointDiscovery
from betty.plugin.discovery.project import ProjectDiscovery
from betty.plugin.human_facing import CountableHumanFacingPluginDefinition


class PresenceRole(Plugin["PresenceRoleDefinition"]):
class PresenceRole(Data, Plugin["PresenceRoleDefinition"]):
"""
A person's role at an event.
"""
Expand All @@ -33,7 +35,10 @@ class PresenceRole(Plugin["PresenceRoleDefinition"]):
),
],
)
class PresenceRoleDefinition(CountableHumanFacingPluginDefinition[PresenceRole]):
class PresenceRoleDefinition(
CountableHumanFacingPluginDefinition[PresenceRole],
DataPluginDefinition[PresenceRole],
):
"""
.. plugin_type:: presence-role.
"""
10 changes: 8 additions & 2 deletions betty/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from typing_extensions import override

from betty.data import Data
from betty.hashid import hashid
from betty.json.linked_data import (
JsonLdObject,
Expand All @@ -16,6 +17,7 @@
from betty.locale.localizable.gettext import _, ngettext
from betty.locale.localize import DEFAULT_LOCALIZER
from betty.plugin import Plugin, PluginTypeDefinition
from betty.plugin.data import DataPluginDefinition
from betty.plugin.discovery.entry_point import EntryPointDiscovery
from betty.plugin.human_facing import CountableHumanFacingPluginDefinition
from betty.string import kebab_case_to_lower_camel_case
Expand Down Expand Up @@ -51,7 +53,9 @@ def __new__(cls, entity_id: str | None = None, /): # noqa: D102
return super().__new__(cls, entity_id or str(uuid4()))


class Entity(LinkedDataDumpableWithSchemaJsonLdObject, Plugin["EntityDefinition"]):
class Entity(
Data, LinkedDataDumpableWithSchemaJsonLdObject, Plugin["EntityDefinition"]
):
"""
An entity is a uniquely identifiable data container.

Expand Down Expand Up @@ -148,7 +152,9 @@ async def linked_data_schema(cls, project: Project, /) -> JsonLdObject:
),
discovery=EntryPointDiscovery("betty.entity_type"),
)
class EntityDefinition(CountableHumanFacingPluginDefinition[Entity]):
class EntityDefinition(
CountableHumanFacingPluginDefinition[Entity], DataPluginDefinition[Entity]
):
"""
.. plugin_type:: entity.
"""
Expand Down
7 changes: 6 additions & 1 deletion betty/plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,17 @@ def __call__(

:raises ValueError: Raised if the definition was already used to decorate a class.
"""
self._set_cls(cls)
return cls

def _set_cls(
self, cls: builtins.type[Intersection[_BaseClsCoT, Plugin[Self]]]
) -> None:
if self._cls is not None:
raise ValueError("This definition was already used to decorate a class.")
assert self._cls is None
cls.plugin = staticmethod(update_wrapper(lambda: self, cls.plugin))
self._cls = cls
return cls

@property
def reference_label(self) -> Localizable:
Expand Down
25 changes: 21 additions & 4 deletions betty/plugin/data.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
"""
Data types for plugins.
Integrate the plugin API with the data API.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, final
from typing import TYPE_CHECKING, Self, final

from typing_extensions import override
from typing_extensions import TypeVar, override

from betty.data import DataDefinition
from betty.data.aggregate.record.object import ObjectDefinition
from betty.functools import passthrough
from betty.locale.localizable.gettext import _
from betty.machine_name import MachineName, assert_machine_name
from betty.plugin.human_facing import HumanFacingPluginDefinition
from betty.portable import CallbackPorter

if TYPE_CHECKING:
from betty.plugin import PluginDefinition
from ty_extensions import Intersection

from betty.plugin import Plugin, PluginDefinition, _BaseClsCoT
from betty.service.level import ServiceLevel

_DataT = TypeVar("_DataT")


@final
class PluginIdDefinition(DataDefinition[MachineName]):
Expand All @@ -37,3 +43,14 @@ def __init__(self, plugin_type: type[PluginDefinition]):
@override
async def hydrate(self, services: ServiceLevel, data: MachineName, /) -> None:
(await services.plugins(self._plugin_type)).get(data)


class DataPluginDefinition(HumanFacingPluginDefinition[_DataT]):
"""
A definition of a plugin that contains defined object data.
"""

@override
def _set_cls(self, cls: type[Intersection[_BaseClsCoT, Plugin[Self]]]) -> None:
super()._set_cls(cls)
ObjectDefinition(label=self.label, description=self.description)(cls)
3 changes: 2 additions & 1 deletion betty/test_utils/ancestry/event_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

from betty.ancestry.event_type import EventType
from betty.test_utils.data import DataTestBase
from betty.test_utils.plugin import PluginTestBase
from betty.test_utils.plugin.human_facing import HumanFacingPluginDefinitionTestBase
from betty.test_utils.plugin.ordered import OrderedPluginDefinitionTestBase
Expand All @@ -18,7 +19,7 @@ class EventTypeDefinitionTestBase(
"""


class EventTypeTestBase(PluginTestBase[EventType]):
class EventTypeTestBase(PluginTestBase[EventType], DataTestBase[EventType]):
"""
A base class for testing :py:class:`betty.ancestry.event_type.EventType` implementations.
"""
3 changes: 2 additions & 1 deletion betty/test_utils/ancestry/gender.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

from betty.ancestry.gender import Gender
from betty.test_utils.data import DataTestBase
from betty.test_utils.plugin import PluginTestBase
from betty.test_utils.plugin.human_facing import HumanFacingPluginDefinitionTestBase

Expand All @@ -15,7 +16,7 @@ class GenderDefinitionTestBase(HumanFacingPluginDefinitionTestBase):
"""


class GenderTestBase(PluginTestBase[Gender]):
class GenderTestBase(PluginTestBase[Gender], DataTestBase[Gender]):
"""
A base class for testing :py:class:`betty.ancestry.gender.Gender` implementations.
"""
3 changes: 2 additions & 1 deletion betty/test_utils/ancestry/place_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

from betty.ancestry.place_type import PlaceType
from betty.test_utils.data import DataTestBase
from betty.test_utils.plugin import PluginTestBase
from betty.test_utils.plugin.human_facing import HumanFacingPluginDefinitionTestBase

Expand All @@ -15,7 +16,7 @@ class PlaceTypeDefinitionTestBase(HumanFacingPluginDefinitionTestBase):
"""


class PlaceTypeTestBase(PluginTestBase[PlaceType]):
class PlaceTypeTestBase(PluginTestBase[PlaceType], DataTestBase[PlaceType]):
"""
A base class for testing :py:class:`betty.ancestry.place_type.PlaceType` implementations.
"""
3 changes: 2 additions & 1 deletion betty/test_utils/ancestry/presence_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

from betty.ancestry.presence_role import PresenceRole
from betty.test_utils.data import DataTestBase
from betty.test_utils.plugin import PluginTestBase
from betty.test_utils.plugin.human_facing import HumanFacingPluginDefinitionTestBase

Expand All @@ -15,7 +16,7 @@ class PresenceRoleDefinitionTestBase(HumanFacingPluginDefinitionTestBase):
"""


class PresenceRoleTestBase(PluginTestBase[PresenceRole]):
class PresenceRoleTestBase(PluginTestBase[PresenceRole], DataTestBase[PresenceRole]):
"""
A base class for testing :py:class:`betty.ancestry.presence_role.PresenceRole` implementations.
"""
3 changes: 2 additions & 1 deletion betty/test_utils/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
from betty.locale.localizable.static import CountableStaticTranslations
from betty.locale.localize import DEFAULT_LOCALIZER
from betty.model import Entity, EntityDefinition
from betty.test_utils.data import DataTestBase
from betty.test_utils.plugin import PluginTestBase
from betty.test_utils.plugin.human_facing import (
CountableHumanFacingPluginDefinitionTestBase,
)


class EntityTestBase(PluginTestBase[Entity]):
class EntityTestBase(PluginTestBase[Entity], DataTestBase[Entity]):
"""
A base class for testing :py:class:`betty.model.Entity` implementations.
"""
Expand Down
Loading