Skip to content

Commit

Permalink
Lazy command privoder
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Nov 27, 2023
1 parent 370f5f7 commit 67c6c51
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 6 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed

- Optimized startup time https://github.com/Textualize/textual/pull/3753

- App.COMMANDS or Screen.COMMANDS can now accept a callable which returns a command palette privoder

## [0.42.0] - 2023-11-22

Expand Down
17 changes: 15 additions & 2 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
from ._context import message_hook as message_hook_context_var
from ._event_broker import NoHandler, extract_handler_actions
from ._path import CSSPathType, _css_path_type_as_list, _make_path_object_relative
from ._system_commands import SystemCommands
from ._wait import wait_for_idle
from ._worker_manager import WorkerManager
from .actions import ActionParseResult, SkipAction
Expand Down Expand Up @@ -107,6 +106,7 @@
from textual_dev.client import DevtoolsClient
from typing_extensions import Coroutine, Literal, TypeAlias

from ._system_commands import SystemCommands
from ._types import MessageTarget

# Unused & ignored imports are needed for the docs to link to these objects:
Expand Down Expand Up @@ -158,6 +158,17 @@
"""Signature for valid callbacks that can be used to control apps."""


def get_system_commands() -> type[SystemCommands]:
"""Callable to lazy load the system commands.
Returns:
System commands class.
"""
from ._system_commands import SystemCommands

return SystemCommands


class AppError(Exception):
"""Base class for general App related exceptions."""

Expand Down Expand Up @@ -330,7 +341,9 @@ class MyApp(App[None]):
ENABLE_COMMAND_PALETTE: ClassVar[bool] = True
"""Should the [command palette][textual.command.CommandPalette] be enabled for the application?"""

COMMANDS: ClassVar[set[type[Provider]]] = {SystemCommands}
COMMANDS: ClassVar[set[type[Provider] | Callable[[], type[Provider]]]] = {
get_system_commands
}
"""Command providers used by the [command palette](/guide/command_palette).
Should be a set of [command.Provider][textual.command.Provider] classes.
Expand Down
22 changes: 20 additions & 2 deletions src/textual/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
from asyncio import CancelledError, Queue, Task, TimeoutError, wait, wait_for
from dataclasses import dataclass
from functools import total_ordering
from inspect import isclass
from time import monotonic
from typing import TYPE_CHECKING, Any, AsyncGenerator, AsyncIterator, ClassVar
from typing import TYPE_CHECKING, Any, AsyncGenerator, AsyncIterator, ClassVar, Iterable

import rich.repr
from rich.align import Align
Expand Down Expand Up @@ -486,10 +487,27 @@ def _provider_classes(self) -> set[type[Provider]]:
application][textual.app.App.COMMANDS] and those [defined in
the current screen][textual.screen.Screen.COMMANDS].
"""

def get_providers(root: App | Screen) -> Iterable[type[Provider]]:
"""Get providers from app or screen.
Args:
root: The app or screen.
Returns:
An iterable of providers.
"""
for provider in root.COMMANDS:
if isclass(provider) and issubclass(provider, Provider):
yield provider
else:
# Lazy loaded providers
yield provider() # type: ignore

return (
set()
if self._calling_screen is None
else self.app.COMMANDS | self._calling_screen.COMMANDS
else {*get_providers(self.app), *get_providers(self._calling_screen)}
)

def compose(self) -> ComposeResult:
Expand Down
2 changes: 1 addition & 1 deletion src/textual/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class Screen(Generic[ScreenResultType], Widget):
title: Reactive[str | None] = Reactive(None, compute=False)
"""Screen title to override [the app title][textual.app.App.title]."""

COMMANDS: ClassVar[set[type[Provider]]] = set()
COMMANDS: ClassVar[set[type[Provider] | Callable[[], type[Provider]]]] = set()
"""Command providers used by the [command palette](/guide/command_palette), associated with the screen.
Should be a set of [`command.Provider`][textual.command.Provider] classes.
Expand Down

0 comments on commit 67c6c51

Please sign in to comment.