Skip to content

Commit 06c1bc4

Browse files
authored
feat: add registered_actions to Application (#242)
feat: add registered_actions to Application
1 parent 166be68 commit 06c1bc4

File tree

3 files changed

+59
-16
lines changed

3 files changed

+59
-16
lines changed

src/app_model/_app.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import sys
66
from collections.abc import Iterable, MutableMapping
7+
from types import MappingProxyType
78
from typing import (
89
TYPE_CHECKING,
910
ClassVar,
@@ -126,6 +127,7 @@ def __init__(
126127

127128
self.injection_store.on_unannotated_required_args = "ignore"
128129

130+
self._registered_actions: dict[str, Action] = {}
129131
self._disposers: list[tuple[str, DisposeCallable]] = []
130132

131133
@property
@@ -291,3 +293,42 @@ def _dispose() -> None:
291293
d.pop()()
292294

293295
return _dispose
296+
297+
@property
298+
def registered_actions(self) -> MappingProxyType[str, Action]:
299+
"""Return a Mapping of id->Action object for all registered actions.
300+
301+
Note that this only includes actions that were registered using
302+
`register_action`. Commands registered directly via
303+
`Application.commands.register_action` will not be included in this mapping.
304+
"""
305+
return MappingProxyType(self._registered_actions)
306+
307+
def _register_action_obj(self, action: Action) -> DisposeCallable:
308+
"""Register an Action object. Return a function that unregisters the action.
309+
310+
Helper for `register_action()`.
311+
"""
312+
# register commands
313+
disposers = [self.commands.register_action(action)]
314+
# register menus
315+
if dm := self.menus.append_action_menus(action):
316+
disposers.append(dm)
317+
# register keybindings
318+
if dk := self.keybindings.register_action_keybindings(action):
319+
disposers.append(dk)
320+
321+
# remember the action object as a whole.
322+
# note that commands.register_action will have raised an exception
323+
# if the action.id is already registered, so we can assume that
324+
# the keys are unique.
325+
self._registered_actions[action.id] = action
326+
327+
# create a function that will dispose of all the disposers
328+
def _dispose() -> None:
329+
self._registered_actions.pop(action.id, None)
330+
for d in disposers:
331+
d()
332+
333+
self._disposers.append((action.id, _dispose))
334+
return _dispose

src/app_model/registries/_register.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -269,19 +269,4 @@ def _register_action_obj(app: Application | str, action: Action) -> DisposeCalla
269269
from app_model._app import Application
270270

271271
app = app if isinstance(app, Application) else Application.get_or_create(app)
272-
273-
# commands
274-
disposers = [app.commands.register_action(action)]
275-
# menus
276-
if dm := app.menus.append_action_menus(action):
277-
disposers.append(dm)
278-
# keybindings
279-
if dk := app.keybindings.register_action_keybindings(action):
280-
disposers.append(dk)
281-
282-
def _dispose() -> None:
283-
for d in disposers:
284-
d()
285-
286-
app._disposers.append((action.id, _dispose))
287-
return _dispose
272+
return app._register_action_obj(action)

tests/test_app.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
from app_model import Application
1010
from app_model.expressions import Context
11+
from app_model.types import Action
12+
from app_model.types._menu_rule import MenuRule
1113

1214
if TYPE_CHECKING:
1315
from conftest import FullApp
@@ -115,3 +117,18 @@ def test_app_context() -> None:
115117

116118
with pytest.raises(TypeError, match="context must be a Context or MutableMapping"):
117119
Application("app4", context=1) # type: ignore[arg-type]
120+
121+
122+
def test_register_actions() -> None:
123+
app = Application("app5")
124+
actions = app.registered_actions
125+
assert not actions
126+
dispose = app.register_action(
127+
"my_action", title="My Action", callback=lambda: None, menus=["Window"]
128+
)
129+
assert "my_action" in actions
130+
assert isinstance(action := actions["my_action"], Action)
131+
assert action.menus == [MenuRule(id="Window")]
132+
dispose()
133+
assert "my_action" not in actions
134+
assert not actions

0 commit comments

Comments
 (0)