Skip to content

Commit 19675a1

Browse files
committed
meh
1 parent 1fd8c65 commit 19675a1

File tree

19 files changed

+147
-256
lines changed

19 files changed

+147
-256
lines changed

betty/collections.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,15 @@ def __delitem__(self, key: _ResolvableKeyT) -> None:
8080
class _DictKeyedCollection(KeyedCollection[_KeyT, _ResolvableKeyT, _ValueT]):
8181
def __init__(
8282
self,
83-
values: Mapping[_KeyT, _ValueT] | None = None,
83+
values: Mapping[_KeyT | _ResolvableKeyT, _ValueT] | None = None,
8484
*,
8585
key_resolver: Callable[[_ResolvableKeyT | _KeyT], _KeyT] = passthrough,
8686
):
87-
self._values = {} if values is None else dict(values)
87+
self._values = (
88+
{}
89+
if values is None
90+
else {key_resolver(key): value for key, value in values.items()}
91+
)
8892
self._key_resolver = key_resolver
8993

9094
@override

betty/extension/raspberry_mint/content_provider.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
)
5151
from betty.plugin.config.property import PluginConfigurationSequenceProperty
5252
from betty.plugin.data import PluginConfigurationSequenceDefinition, PluginIdDefinition
53-
from betty.plugin.resolve import resolve_id
53+
from betty.plugin.resolve import ResolvableDefinition, resolve_id
5454
from betty.portable import CallbackPorter
5555
from betty.service.level.factory import (
5656
ServiceLevelDependentSelfFactory,
@@ -66,7 +66,7 @@
6666
from betty.document import Document
6767
from betty.jinja2 import Environment
6868
from betty.locale.localizable import ResolvableLocalizable
69-
from betty.plugin.repository import PluginRepository
69+
from betty.plugin.collections import PluginDefinitions
7070
from betty.plugin.resolve import ResolvableId
7171

7272

@@ -189,7 +189,7 @@ def __init__(
189189
*,
190190
ancestry: Ancestry,
191191
configuration: EntityReference,
192-
entity_types: PluginRepository[EntityDefinition],
192+
entity_types: PluginDefinitions[EntityDefinition],
193193
jinja2_environment: Environment,
194194
):
195195
super().__init__(
@@ -482,7 +482,7 @@ def __init__(
482482
self,
483483
*,
484484
jinja2_environment: Environment,
485-
presence_roles: PluginRepository[PresenceRoleDefinition],
485+
presence_roles: Iterable[ResolvableDefinition[PresenceRoleDefinition]],
486486
configuration: PresencesConfiguration | None = None,
487487
):
488488
super().__init__(

betty/extension/spdx/__init__.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@
1010
from betty.license import LicenseDefinition
1111
from betty.license.licenses import SpdxLicenseBuilder
1212
from betty.locale.localizable.gettext import _
13-
from betty.plugin.repository.static import StaticPluginRepository
13+
from betty.plugin.collections import PluginDefinitions, new_plugin_definitions
1414
from betty.service.container import service
1515
from betty.service.level.factory import ServiceLevelDependentSelfFactory
1616
from betty.service.requirement.extension import require_extension
1717
from betty.service.requirement.project import require_project
1818

1919
if TYPE_CHECKING:
20-
from betty.plugin.repository import PluginRepository
2120
from betty.project import Project
2221

2322

@@ -41,13 +40,12 @@ async def new_for_services(cls, *, project: Project) -> Self:
4140
return cls(project=project)
4241

4342
@service
44-
async def license_repository(self) -> PluginRepository[LicenseDefinition]:
43+
async def licenses(self) -> PluginDefinitions[LicenseDefinition]:
4544
"""
4645
The SPDX licenses.
4746
"""
48-
return StaticPluginRepository(
49-
LicenseDefinition,
50-
*[
47+
return new_plugin_definitions(
48+
*(
5149
license
5250
async for license in SpdxLicenseBuilder( # noqa: A001
5351
binary_file_cache=self._project.app.binary_file_cache.with_scope(
@@ -56,10 +54,10 @@ async def license_repository(self) -> PluginRepository[LicenseDefinition]:
5654
http_client=await self._project.app.http_client,
5755
user=self._project.app.user,
5856
).build()
59-
],
57+
),
6058
)
6159

6260

6361
LicenseDefinition.type().discoverer.add(
64-
require_extension(Spdx)(lambda *, extension: extension.license_repository),
62+
require_extension(Spdx)(lambda *, extension: extension.licenses),
6563
)

betty/plugin/assertion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
from betty.locale.localizable.gettext import _
1010
from betty.locale.localizable.markup import Paragraph, do_you_mean
1111
from betty.plugin import PluginDefinition
12+
from betty.plugin.collections import PluginDefinitions
1213
from betty.plugin.error import PluginNotFound
13-
from betty.plugin.repository import PluginRepository
1414

1515
_PluginDefinitionT = TypeVar("_PluginDefinitionT", bound=PluginDefinition)
1616

1717

1818
def assert_plugin(
19-
plugins: PluginRepository[_PluginDefinitionT],
19+
plugins: PluginDefinitions[_PluginDefinitionT],
2020
) -> AssertionChain[Any, _PluginDefinitionT]:
2121
"""
2222
Assert that a value is a plugin ID.

betty/plugin/collections.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
Plugin collections.
3+
"""
4+
5+
from typing import TypeAlias
6+
7+
from typing_extensions import TypeVar
8+
9+
from betty.collections import DictKeyedCollection, KeyedCollection
10+
from betty.machine_name import MachineName
11+
from betty.plugin import PluginDefinition, PluginTypeDefinition
12+
from betty.plugin.resolve import ResolvableDefinition, ResolvableId, resolve_id
13+
14+
_PluginDefinitionT = TypeVar(
15+
"_PluginDefinitionT", bound=PluginDefinition, default=PluginDefinition
16+
)
17+
18+
PluginDefinitions: TypeAlias = KeyedCollection[
19+
MachineName, ResolvableId[_PluginDefinitionT], _PluginDefinitionT
20+
]
21+
PluginTypeDefinitions: TypeAlias = KeyedCollection[
22+
MachineName, MachineName, PluginTypeDefinition
23+
]
24+
25+
26+
def new_plugin_definitions(
27+
*plugins: ResolvableDefinition[_PluginDefinitionT],
28+
) -> PluginDefinitions[_PluginDefinitionT]:
29+
"""
30+
Create a new collection of plugin definitions.
31+
"""
32+
return DictKeyedCollection(
33+
{resolve_id(plugin.id): plugin for plugin in plugins}, key_resolver=resolve_id
34+
)
35+
36+
37+
def new_plugin_type_definitions(
38+
*plugin_types: PluginTypeDefinition,
39+
) -> PluginTypeDefinitions:
40+
"""
41+
Create a new collection of plugin type definitions.
42+
"""
43+
return DictKeyedCollection(
44+
{plugin_type.id: plugin_type for plugin_type in plugin_types}
45+
)

betty/plugin/dependent.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
from collections.abc import Iterable, Set
1616
from graphlib import TopologicalSorter
1717

18+
from betty.collections import KeyedCollection
1819
from betty.machine_name import MachineName
19-
from betty.plugin.repository import PluginRepository
2020

2121

2222
_BaseClsCoT = TypeVar("_BaseClsCoT", default=object, covariant=True)
@@ -62,7 +62,7 @@ def depends_on(self) -> Set[MachineName]:
6262

6363

6464
async def expand_plugin_dependencies(
65-
plugin_repository: PluginRepository[_DependentPluginDefinitionT],
65+
available_plugins: KeyedCollection[Any, ResolvableId, _DependentPluginDefinitionT],
6666
plugins: Iterable[_DependentPluginDefinitionT],
6767
/,
6868
) -> Set[_DependentPluginDefinitionT]:
@@ -74,21 +74,21 @@ async def expand_plugin_dependencies(
7474
dependencies.add(plugin)
7575
dependencies.update(
7676
await expand_plugin_dependencies(
77-
plugin_repository,
78-
[plugin_repository.get(depends_on) for depends_on in plugin.depends_on],
77+
available_plugins,
78+
[available_plugins[depends_on] for depends_on in plugin.depends_on],
7979
)
8080
)
8181
return dependencies
8282

8383

8484
async def sort_dependent_plugin_graph(
85-
plugin_repository: PluginRepository[_DependentPluginDefinitionT],
85+
available_plugins: KeyedCollection[Any, ResolvableId, _DependentPluginDefinitionT],
8686
plugins: Iterable[_DependentPluginDefinitionT],
8787
/,
8888
) -> TopologicalSorter[MachineName]:
8989
"""
9090
Sort a dependent plugin graph.
9191
"""
9292
return sort_ordered_plugin_graph(
93-
plugin_repository, await expand_plugin_dependencies(plugin_repository, plugins)
93+
available_plugins, await expand_plugin_dependencies(available_plugins, plugins)
9494
)

betty/plugin/manager/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from typing_extensions import TypeVar
1111

12-
from betty.plugin import PluginDefinition, PluginTypeRepository
12+
from betty.plugin import PluginDefinition, PluginTypeDefinition
1313
from betty.typing import threadsafe
1414

1515
if TYPE_CHECKING:
@@ -40,7 +40,7 @@ async def plugins(
4040

4141
@property
4242
@abstractmethod
43-
def types(self) -> PluginTypeRepository:
43+
def types(self) -> KeyedCollection[MachineName, MachineName, PluginTypeDefinition]:
4444
"""
4545
Get the available plugin types.
4646
"""

betty/plugin/manager/service.py

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@
55
from __future__ import annotations
66

77
from collections import defaultdict
8-
from typing import TYPE_CHECKING, Any, cast, final
8+
from importlib import metadata
9+
from typing import TYPE_CHECKING, cast, final
910

1011
from typing_extensions import TypeVar, override
1112

12-
from betty import plugin
1313
from betty.concurrent import AsynchronizedLock, Ledger
14-
from betty.plugin import PluginDefinition
14+
from betty.plugin import PluginDefinition, PluginTypeDefinition
15+
from betty.plugin.collections import (
16+
PluginDefinitions,
17+
new_plugin_definitions,
18+
new_plugin_type_definitions,
19+
)
1520
from betty.plugin.manager import PluginManager
16-
from betty.plugin.repository.static import StaticPluginRepository
1721

1822
if TYPE_CHECKING:
1923
from collections.abc import MutableMapping
2024

2125
from betty.collections import KeyedCollection
2226
from betty.machine_name import MachineName
23-
from betty.plugin.repository import PluginRepository
24-
from betty.plugin.resolve import ResolvableId
2527
from betty.service.level import ServiceLevel
2628

2729

@@ -39,52 +41,54 @@ class ServiceLevelPluginManager(PluginManager):
3941

4042
def __init__(self, services: ServiceLevel, /):
4143
self._services = services
42-
self._types = plugin.PluginTypeRepository()
43-
self._plugin_repositories: MutableMapping[
44-
type[PluginDefinition], PluginRepository[Any] | None
44+
self._types = new_plugin_type_definitions(
45+
*(
46+
entry_point.load()
47+
for entry_point in metadata.entry_points(group="betty.plugin")
48+
)
49+
)
50+
self._plugins: MutableMapping[
51+
type[PluginDefinition], PluginDefinitions | None
4552
] = defaultdict(None)
4653
self._ledger = Ledger(AsynchronizedLock.new_threadsafe())
4754

4855
@override
4956
@property
50-
def types(self) -> plugin.PluginTypeRepository:
57+
def types(self) -> KeyedCollection[MachineName, MachineName, PluginTypeDefinition]:
5158
return self._types
5259

5360
@override
5461
async def plugins(
5562
self, plugin_type: type[_PluginDefinitionT] | MachineName, /
56-
) -> KeyedCollection[
57-
MachineName, ResolvableId[_PluginDefinitionT], _PluginDefinitionT
58-
]:
63+
) -> PluginDefinitions[_PluginDefinitionT]:
5964
if isinstance(plugin_type, str):
6065
plugin_type = cast(type[_PluginDefinitionT], self.types[plugin_type])
61-
repository: PluginRepository[_PluginDefinitionT] | None
66+
plugins: PluginDefinitions[_PluginDefinitionT] | None
6267
if plugin_type.type().discoverer.overridden:
6368
return await self._new(plugin_type)
64-
# If the repository exists already, return it immediately so we avoid acquiring locks.
65-
repository = self._get(plugin_type)
66-
if repository:
67-
return repository
69+
# If the definitions exist already, return them immediately so we avoid acquiring locks.
70+
plugins = self._get(plugin_type)
71+
if plugins:
72+
return plugins
6873
async with self._ledger.ledger(f"{plugin_type.type().id}"):
69-
# The repository may have been created since we first checked.
70-
repository = self._get(plugin_type)
71-
if repository:
72-
return repository
73-
repository = await self._new(plugin_type)
74-
self._plugin_repositories[plugin_type] = repository
75-
return repository
74+
# The definitions may have been created since we first checked.
75+
plugins = self._get(plugin_type)
76+
if plugins:
77+
return plugins
78+
plugins = await self._new(plugin_type)
79+
self._plugins[plugin_type] = plugins
80+
return plugins
7681

7782
def _get(
7883
self, plugin_type: type[_PluginDefinitionT]
79-
) -> PluginRepository[_PluginDefinitionT] | None:
80-
if plugin_type not in self._plugin_repositories:
84+
) -> PluginDefinitions[_PluginDefinitionT] | None:
85+
if plugin_type not in self._plugins:
8186
return None
82-
return self._plugin_repositories[plugin_type]
87+
return self._plugins[plugin_type]
8388

8489
async def _new(
8590
self, plugin_type: type[_PluginDefinitionT]
86-
) -> PluginRepository[_PluginDefinitionT]:
87-
return StaticPluginRepository(
88-
plugin_type,
89-
*await plugin_type.type().discoverer.discover(services=self._services),
91+
) -> PluginDefinitions[_PluginDefinitionT]:
92+
return new_plugin_definitions(
93+
*await plugin_type.type().discoverer.discover(services=self._services)
9094
)

0 commit comments

Comments
 (0)