From e2739be4f30b00856b3696facad0bed152766794 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 17 Jul 2024 13:34:00 +0100 Subject: [PATCH 1/6] docs tweaks --- docs/api/dom_node.md | 4 ++++ mkdocs-common.yml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/api/dom_node.md b/docs/api/dom_node.md index 569986baed..41233c5b62 100644 --- a/docs/api/dom_node.md +++ b/docs/api/dom_node.md @@ -1 +1,5 @@ +--- +title: "textual.dom" +--- + ::: textual.dom diff --git a/mkdocs-common.yml b/mkdocs-common.yml index 924179a95d..883653f6a1 100644 --- a/mkdocs-common.yml +++ b/mkdocs-common.yml @@ -67,11 +67,12 @@ plugins: search: autorefs: mkdocstrings: - custom_templates: docs/_templates default_handler: python handlers: python: options: + show_symbol_type_heading: true + show_symbol_type_toc: true show_signature_annotations: false separate_signature: true merge_init_into_class: true From 54cec67bd8037de5f50a5e64bb42956c5eb6684d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 17 Jul 2024 17:16:40 +0100 Subject: [PATCH 2/6] titles --- docs/api/app.md | 4 + docs/api/await_complete.md | 4 + docs/api/await_remove.md | 4 + docs/api/binding.md | 4 + docs/api/cache.md | 4 + docs/api/color.md | 4 + docs/api/command.md | 4 + docs/api/constants.md | 5 + docs/api/containers.md | 7 +- docs/api/content_switcher.md | 1 - docs/api/coordinate.md | 5 + docs/api/errors.md | 4 + docs/api/events.md | 5 + docs/api/filter.md | 5 + docs/api/fuzzy_matcher.md | 5 + docs/api/geometry.md | 5 + docs/api/lazy.md | 5 + docs/api/logger.md | 5 + docs/api/logging.md | 3 + docs/api/map_geometry.md | 7 +- docs/api/message.md | 4 + docs/api/message_pump.md | 4 + docs/api/on.md | 4 + docs/api/pilot.md | 4 + docs/api/query.md | 4 + docs/api/reactive.md | 4 + docs/api/renderables.md | 4 + docs/api/screen.md | 5 + docs/api/scroll_view.md | 5 + docs/api/scrollbar.md | 4 + docs/api/signal.md | 5 + docs/api/strip.md | 5 + docs/api/suggester.md | 5 + docs/api/system_commands_source.md | 8 +- docs/api/timer.md | 4 + docs/api/types.md | 5 + docs/api/validation.md | 5 + docs/api/walk.md | 5 + docs/api/widget.md | 5 + docs/api/work.md | 4 + docs/api/worker.md | 4 + docs/api/worker_manager.md | 6 +- mkdocs-nav.yml | 1 - src/textual/_compositor.py | 30 +-- src/textual/_system_commands.py | 75 -------- src/textual/_worker_manager.py | 181 ------------------ src/textual/app.py | 6 +- src/textual/dom.py | 2 +- tests/command_palette/test_declare_sources.py | 2 +- 49 files changed, 193 insertions(+), 297 deletions(-) delete mode 100644 docs/api/content_switcher.md delete mode 100644 src/textual/_system_commands.py delete mode 100644 src/textual/_worker_manager.py diff --git a/docs/api/app.md b/docs/api/app.md index 3a83ec5f02..058468ff84 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -1,3 +1,7 @@ +--- +title: "textual.app" +--- + ::: textual.app options: filters: diff --git a/docs/api/await_complete.md b/docs/api/await_complete.md index 523cb8a289..8a1eb58ac6 100644 --- a/docs/api/await_complete.md +++ b/docs/api/await_complete.md @@ -1 +1,5 @@ +--- +title: "textual.await_complete" +--- + ::: textual.await_complete diff --git a/docs/api/await_remove.md b/docs/api/await_remove.md index 502cf81008..68b8d92ac5 100644 --- a/docs/api/await_remove.md +++ b/docs/api/await_remove.md @@ -1 +1,5 @@ +--- +title: "textual.await_remove" +--- + ::: textual.await_remove diff --git a/docs/api/binding.md b/docs/api/binding.md index 6d28660f7a..4250154f0b 100644 --- a/docs/api/binding.md +++ b/docs/api/binding.md @@ -1 +1,5 @@ +--- +title: "textual.binding" +--- + ::: textual.binding diff --git a/docs/api/cache.md b/docs/api/cache.md index fe144a64c6..ecb866720c 100644 --- a/docs/api/cache.md +++ b/docs/api/cache.md @@ -1 +1,5 @@ +--- +title: "textual.cache" +--- + ::: textual.cache diff --git a/docs/api/color.md b/docs/api/color.md index 0d1d717596..a4507741c1 100644 --- a/docs/api/color.md +++ b/docs/api/color.md @@ -1 +1,5 @@ +--- +title: "textual.color" +--- + ::: textual.color diff --git a/docs/api/command.md b/docs/api/command.md index 865a605910..1700530ac6 100644 --- a/docs/api/command.md +++ b/docs/api/command.md @@ -1 +1,5 @@ +--- +title: "textual.command" +--- + ::: textual.command diff --git a/docs/api/constants.md b/docs/api/constants.md index 88aa35b2f9..ed6b671d9d 100644 --- a/docs/api/constants.md +++ b/docs/api/constants.md @@ -1 +1,6 @@ +--- +title: "textual.constants" +--- + + ::: textual.constants diff --git a/docs/api/containers.md b/docs/api/containers.md index d44d570d01..d791c26fd8 100644 --- a/docs/api/containers.md +++ b/docs/api/containers.md @@ -1,3 +1,6 @@ -::: textual.containers +--- +title: "textual.containers" +--- + -::: textual.widgets.ContentSwitcher +::: textual.containers diff --git a/docs/api/content_switcher.md b/docs/api/content_switcher.md deleted file mode 100644 index ef2cfe930b..0000000000 --- a/docs/api/content_switcher.md +++ /dev/null @@ -1 +0,0 @@ -::: textual.widgets.ContentSwitcher diff --git a/docs/api/coordinate.md b/docs/api/coordinate.md index 6131ff9b38..b0d22cd890 100644 --- a/docs/api/coordinate.md +++ b/docs/api/coordinate.md @@ -1 +1,6 @@ +--- +title: "textual.coordinate" +--- + + ::: textual.coordinate diff --git a/docs/api/errors.md b/docs/api/errors.md index 5ee969dd88..d074d170df 100644 --- a/docs/api/errors.md +++ b/docs/api/errors.md @@ -1 +1,5 @@ +--- +title: "textual.errors" +--- + ::: textual.errors diff --git a/docs/api/events.md b/docs/api/events.md index 0be7c28687..c8654e97bf 100644 --- a/docs/api/events.md +++ b/docs/api/events.md @@ -1 +1,6 @@ +--- +title: "textual.events" +--- + + ::: textual.events diff --git a/docs/api/filter.md b/docs/api/filter.md index 37b6966b93..86576002d9 100644 --- a/docs/api/filter.md +++ b/docs/api/filter.md @@ -1 +1,6 @@ +--- +title: "textual.filter" +--- + + ::: textual.filter diff --git a/docs/api/fuzzy_matcher.md b/docs/api/fuzzy_matcher.md index 0269ad2db0..dce3db089f 100644 --- a/docs/api/fuzzy_matcher.md +++ b/docs/api/fuzzy_matcher.md @@ -1 +1,6 @@ +--- +title: "textual.fuzzy" +--- + + ::: textual.fuzzy diff --git a/docs/api/geometry.md b/docs/api/geometry.md index 6f31de5e4d..c998b5c17e 100644 --- a/docs/api/geometry.md +++ b/docs/api/geometry.md @@ -1 +1,6 @@ +--- +title: "textual.geometry" +--- + + ::: textual.geometry diff --git a/docs/api/lazy.md b/docs/api/lazy.md index 1b5039f136..4d243fa489 100644 --- a/docs/api/lazy.md +++ b/docs/api/lazy.md @@ -1 +1,6 @@ +--- +title: "textual.lazy" +--- + + ::: textual.lazy diff --git a/docs/api/logger.md b/docs/api/logger.md index 096ca3011c..046af43498 100644 --- a/docs/api/logger.md +++ b/docs/api/logger.md @@ -1,3 +1,8 @@ +--- +title: "textual.Logger" +--- + + # Logger A [logger class](/guide/devtools/#logging-handler) that logs to the Textual [console](/guide/devtools#console). diff --git a/docs/api/logging.md b/docs/api/logging.md index 5fbf833e15..c0ea0c0ee1 100644 --- a/docs/api/logging.md +++ b/docs/api/logging.md @@ -1 +1,4 @@ +--- +title: "textual.logging" +--- ::: textual.logging diff --git a/docs/api/map_geometry.md b/docs/api/map_geometry.md index 009ae45f37..57188eb291 100644 --- a/docs/api/map_geometry.md +++ b/docs/api/map_geometry.md @@ -1,3 +1,8 @@ +--- +title: "textual.map_geometry" +--- + + A data structure returned by [screen.find_widget][textual.screen.Screen.find_widget]. -::: textual._compositor.MapGeometry +::: textual.map_geometry diff --git a/docs/api/message.md b/docs/api/message.md index 78e9796211..21022eebad 100644 --- a/docs/api/message.md +++ b/docs/api/message.md @@ -1 +1,5 @@ +--- +title: "textual.message" +--- + ::: textual.message diff --git a/docs/api/message_pump.md b/docs/api/message_pump.md index a5aceb7754..c2fb51a90f 100644 --- a/docs/api/message_pump.md +++ b/docs/api/message_pump.md @@ -1 +1,5 @@ +--- +title: "textual.message_pump" +--- + ::: textual.message_pump diff --git a/docs/api/on.md b/docs/api/on.md index 8d59ae19fe..6790654f5d 100644 --- a/docs/api/on.md +++ b/docs/api/on.md @@ -1,3 +1,7 @@ +--- +title: "textual.on" +--- + # On ::: textual.on diff --git a/docs/api/pilot.md b/docs/api/pilot.md index e1db65812f..19856059b1 100644 --- a/docs/api/pilot.md +++ b/docs/api/pilot.md @@ -1 +1,5 @@ +--- +title: "textual.pilot" +--- + ::: textual.pilot diff --git a/docs/api/query.md b/docs/api/query.md index 313755809d..8c281ef096 100644 --- a/docs/api/query.md +++ b/docs/api/query.md @@ -1 +1,5 @@ +--- +title: "textual.css.query" +--- + ::: textual.css.query diff --git a/docs/api/reactive.md b/docs/api/reactive.md index 759c7af264..7789cb202d 100644 --- a/docs/api/reactive.md +++ b/docs/api/reactive.md @@ -1 +1,5 @@ +--- +title: "textual.reactive" +--- + ::: textual.reactive diff --git a/docs/api/renderables.md b/docs/api/renderables.md index 08f3861e71..5add63e086 100644 --- a/docs/api/renderables.md +++ b/docs/api/renderables.md @@ -1,3 +1,7 @@ +--- +title: "textual.renderables" +--- + A collection of Rich renderables which may be returned from a widget's `render()` method. ::: textual.renderables.bar diff --git a/docs/api/screen.md b/docs/api/screen.md index c7054aef5d..2c5f81f7cf 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -1 +1,6 @@ +--- +title: "textual.screen" +--- + + ::: textual.screen diff --git a/docs/api/scroll_view.md b/docs/api/scroll_view.md index 8c81e14dc0..37610e6b6d 100644 --- a/docs/api/scroll_view.md +++ b/docs/api/scroll_view.md @@ -1 +1,6 @@ +--- +title: "textual.scroll_view" +--- + + ::: textual.scroll_view diff --git a/docs/api/scrollbar.md b/docs/api/scrollbar.md index 8945dcd895..3b29b3b8cb 100644 --- a/docs/api/scrollbar.md +++ b/docs/api/scrollbar.md @@ -1 +1,5 @@ +--- +title: "textual.scrollbar" +--- + ::: textual.scrollbar diff --git a/docs/api/signal.md b/docs/api/signal.md index 36727f7f2b..b3d242615b 100644 --- a/docs/api/signal.md +++ b/docs/api/signal.md @@ -1 +1,6 @@ +--- +title: "textual.signal" +--- + + ::: textual.signal diff --git a/docs/api/strip.md b/docs/api/strip.md index 9b1296cd7c..69b09a1be0 100644 --- a/docs/api/strip.md +++ b/docs/api/strip.md @@ -1 +1,6 @@ +--- +title: "textual.strip" +--- + + ::: textual.strip diff --git a/docs/api/suggester.md b/docs/api/suggester.md index b8e4de8ad1..074eb29403 100644 --- a/docs/api/suggester.md +++ b/docs/api/suggester.md @@ -1 +1,6 @@ +--- +title: "textual.suggester" +--- + + ::: textual.suggester diff --git a/docs/api/system_commands_source.md b/docs/api/system_commands_source.md index 4778761810..a9f34b398e 100644 --- a/docs/api/system_commands_source.md +++ b/docs/api/system_commands_source.md @@ -1 +1,7 @@ -::: textual._system_commands +--- +title: "textual.system_commands" +--- + + + +::: textual.system_commands diff --git a/docs/api/timer.md b/docs/api/timer.md index 01b5fa1acb..11489f6c98 100644 --- a/docs/api/timer.md +++ b/docs/api/timer.md @@ -1 +1,5 @@ +--- +title: "textual.timer" +--- + ::: textual.timer diff --git a/docs/api/types.md b/docs/api/types.md index 8d89c89a6a..32f8971921 100644 --- a/docs/api/types.md +++ b/docs/api/types.md @@ -1 +1,6 @@ +--- +title: "textual.types" +--- + + ::: textual.types diff --git a/docs/api/validation.md b/docs/api/validation.md index 4c0e63b60f..2bb3df264d 100644 --- a/docs/api/validation.md +++ b/docs/api/validation.md @@ -1 +1,6 @@ +--- +title: "textual.validation" +--- + + ::: textual.validation diff --git a/docs/api/walk.md b/docs/api/walk.md index 7781c55149..64cdf9ce63 100644 --- a/docs/api/walk.md +++ b/docs/api/walk.md @@ -1 +1,6 @@ +--- +title: "textual.walk" +--- + + ::: textual.walk diff --git a/docs/api/widget.md b/docs/api/widget.md index 072e9625e0..b9d561803a 100644 --- a/docs/api/widget.md +++ b/docs/api/widget.md @@ -1,3 +1,8 @@ +--- +title: "textual.widget" +--- + + ::: textual.widget options: filters: diff --git a/docs/api/work.md b/docs/api/work.md index 539fbfa8ea..5dba82fdf3 100644 --- a/docs/api/work.md +++ b/docs/api/work.md @@ -1,2 +1,6 @@ +--- +title: "textual.work" +--- + ::: textual.work diff --git a/docs/api/worker.md b/docs/api/worker.md index 0889e0f5a1..009e39b104 100644 --- a/docs/api/worker.md +++ b/docs/api/worker.md @@ -1 +1,5 @@ +--- +title: "textual.worker" +--- + ::: textual.worker diff --git a/docs/api/worker_manager.md b/docs/api/worker_manager.md index 80e847b6c6..e7cc5ac6b8 100644 --- a/docs/api/worker_manager.md +++ b/docs/api/worker_manager.md @@ -1 +1,5 @@ -::: textual._worker_manager +--- +title: "textual.worker_manager" +--- + +::: textual.worker_manager diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index 169f3ef0d0..5544e28ad6 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -187,7 +187,6 @@ nav: - "api/command.md" - "api/constants.md" - "api/containers.md" - - "api/content_switcher.md" - "api/coordinate.md" - "api/dom_node.md" - "api/events.md" diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index f1ce3caa1d..9ccb73099f 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -34,6 +34,7 @@ from ._context import visible_screen_stack from ._loop import loop_last from .geometry import NULL_OFFSET, NULL_SPACING, Offset, Region, Size, Spacing +from .map_geometry import MapGeometry from .strip import Strip, StripRenderable if TYPE_CHECKING: @@ -52,35 +53,6 @@ class ReflowResult(NamedTuple): resized: set[Widget] # Widgets that have been resized -class MapGeometry(NamedTuple): - """Defines the absolute location of a Widget.""" - - region: Region - """The (screen) [region][textual.geometry.Region] occupied by the widget.""" - order: tuple[tuple[int, int, int], ...] - """Tuple of tuples defining the painting order of the widget. - - Each successive triple represents painting order information with regards to - ancestors in the DOM hierarchy and the last triple provides painting order - information for this specific widget. - """ - clip: Region - """A [region][textual.geometry.Region] to clip the widget by (if a Widget is within a container).""" - virtual_size: Size - """The virtual [size][textual.geometry.Size] (scrollable area) of a widget if it is a container.""" - container_size: Size - """The container [size][textual.geometry.Size] (area not occupied by scrollbars).""" - virtual_region: Region - """The [region][textual.geometry.Region] relative to the container (but not necessarily visible).""" - dock_gutter: Spacing - """Space from the container reserved by docked widgets.""" - - @property - def visible_region(self) -> Region: - """The Widget region after clipping.""" - return self.clip.intersection(self.region) - - # Maps a widget on to its geometry (information that describes its position in the composition) CompositorMap: TypeAlias = "dict[Widget, MapGeometry]" diff --git a/src/textual/_system_commands.py b/src/textual/_system_commands.py deleted file mode 100644 index ffa73b263a..0000000000 --- a/src/textual/_system_commands.py +++ /dev/null @@ -1,75 +0,0 @@ -"""A command palette command provider for Textual system commands. - -This is a simple command provider that makes the most obvious application -actions available via the [command palette][textual.command.CommandPalette]. -""" - -from __future__ import annotations - -from .command import DiscoveryHit, Hit, Hits, Provider -from .types import IgnoreReturnCallbackType - - -class SystemCommands(Provider): - """A [source][textual.command.Provider] of command palette commands that run app-wide tasks. - - Used by default in [`App.COMMANDS`][textual.app.App.COMMANDS]. - """ - - @property - def _system_commands(self) -> tuple[tuple[str, IgnoreReturnCallbackType, str], ...]: - """The system commands to reveal to the command palette.""" - return ( - ( - "Toggle light/dark mode", - self.app.action_toggle_dark, - "Toggle the application between light and dark mode", - ), - ( - "Quit the application", - self.app.action_quit, - "Quit the application as soon as possible", - ), - ( - "Ring the bell", - self.app.action_bell, - "Ring the terminal's 'bell'", - ), - ) - - async def discover(self) -> Hits: - """Handle a request for the discovery commands for this provider. - - Yields: - Commands that can be discovered. - """ - for name, runnable, help_text in self._system_commands: - yield DiscoveryHit( - name, - runnable, - help=help_text, - ) - - async def search(self, query: str) -> Hits: - """Handle a request to search for system commands that match the query. - - Args: - query: The user input to be matched. - - Yields: - Command hits for use in the command palette. - """ - # We're going to use Textual's builtin fuzzy matcher to find - # matching commands. - matcher = self.matcher(query) - - # Loop over all applicable commands, find those that match and offer - # them up to the command palette. - for name, runnable, help_text in self._system_commands: - if (match := matcher.match(name)) > 0: - yield Hit( - match, - matcher.highlight(name), - runnable, - help=help_text, - ) diff --git a/src/textual/_worker_manager.py b/src/textual/_worker_manager.py deleted file mode 100644 index 6c90b9641a..0000000000 --- a/src/textual/_worker_manager.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -A class to manage [workers](/guide/workers) for an app. - -You access this object via [App.workers][textual.app.App.workers] or [Widget.workers][textual.dom.DOMNode.workers]. -""" - -from __future__ import annotations - -import asyncio -from collections import Counter -from operator import attrgetter -from typing import TYPE_CHECKING, Any, Iterable, Iterator - -import rich.repr - -from .worker import Worker, WorkerState, WorkType - -if TYPE_CHECKING: - from .app import App - from .dom import DOMNode - - -@rich.repr.auto(angular=True) -class WorkerManager: - """An object to manager a number of workers. - - You will not have to construct this class manually, as widgets, screens, and apps - have a worker manager accessibly via a `workers` attribute. - """ - - def __init__(self, app: App) -> None: - """Initialize a worker manager. - - Args: - app: An App instance. - """ - self._app = app - """A reference to the app.""" - self._workers: set[Worker] = set() - """The workers being managed.""" - - def __rich_repr__(self) -> rich.repr.Result: - counter: Counter[WorkerState] = Counter() - counter.update(worker.state for worker in self._workers) - for state, count in sorted(counter.items()): - yield state.name, count - - def __iter__(self) -> Iterator[Worker[Any]]: - return iter(sorted(self._workers, key=attrgetter("_created_time"))) - - def __reversed__(self) -> Iterator[Worker[Any]]: - return iter( - sorted(self._workers, key=attrgetter("_created_time"), reverse=True) - ) - - def __bool__(self) -> bool: - return bool(self._workers) - - def __len__(self) -> int: - return len(self._workers) - - def __contains__(self, worker: object) -> bool: - return worker in self._workers - - def add_worker( - self, worker: Worker, start: bool = True, exclusive: bool = True - ) -> None: - """Add a new worker. - - Args: - worker: A Worker instance. - start: Start the worker if True, otherwise the worker must be started manually. - exclusive: Cancel all workers in the same group as `worker`. - """ - if exclusive and worker.group: - self.cancel_group(worker.node, worker.group) - self._workers.add(worker) - if start: - worker._start(self._app, self._remove_worker) - - def _new_worker( - self, - work: WorkType, - node: DOMNode, - *, - name: str | None = "", - group: str = "default", - description: str = "", - exit_on_error: bool = True, - start: bool = True, - exclusive: bool = False, - thread: bool = False, - ) -> Worker: - """Create a worker from a function, coroutine, or awaitable. - - Args: - work: A callable, a coroutine, or other awaitable. - name: A name to identify the worker. - group: The worker group. - description: A description of the worker. - exit_on_error: Exit the app if the worker raises an error. Set to `False` to suppress exceptions. - start: Automatically start the worker. - exclusive: Cancel all workers in the same group. - thread: Mark the worker as a thread worker. - - Returns: - A Worker instance. - """ - worker: Worker[Any] = Worker( - node, - work, - name=name or getattr(work, "__name__", "") or "", - group=group, - description=description or repr(work), - exit_on_error=exit_on_error, - thread=thread, - ) - self.add_worker(worker, start=start, exclusive=exclusive) - return worker - - def _remove_worker(self, worker: Worker) -> None: - """Remove a worker from the manager. - - Args: - worker: A Worker instance. - """ - self._workers.discard(worker) - - def start_all(self) -> None: - """Start all the workers.""" - for worker in self._workers: - worker._start(self._app, self._remove_worker) - - def cancel_all(self) -> None: - """Cancel all workers.""" - for worker in self._workers: - worker.cancel() - - def cancel_group(self, node: DOMNode, group: str) -> list[Worker]: - """Cancel a single group. - - Args: - node: Worker DOM node. - group: A group name. - - Returns: - A list of workers that were cancelled. - """ - workers = [ - worker - for worker in self._workers - if (worker.group == group and worker.node == node) - ] - for worker in workers: - worker.cancel() - return workers - - def cancel_node(self, node: DOMNode) -> list[Worker]: - """Cancel all workers associated with a given node - - Args: - node: A DOM node (widget, screen, or App). - - Returns: - List of cancelled workers. - """ - workers = [worker for worker in self._workers if worker.node == node] - for worker in workers: - worker.cancel() - return workers - - async def wait_for_complete(self, workers: Iterable[Worker] | None = None) -> None: - """Wait for workers to complete. - - Args: - workers: An iterable of workers or None to wait for all workers in the manager. - """ - try: - await asyncio.gather(*[worker.wait() for worker in (workers or self)]) - except asyncio.CancelledError: - pass diff --git a/src/textual/app.py b/src/textual/app.py index 160e75d55a..3687c308c4 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -76,7 +76,6 @@ from ._path import CSSPathType, _css_path_type_as_list, _make_path_object_relative from ._types import AnimationLevel from ._wait import wait_for_idle -from ._worker_manager import WorkerManager from .actions import ActionParseResult, SkipAction from .await_complete import AwaitComplete from .await_remove import AwaitRemove @@ -115,12 +114,12 @@ from .widget import AwaitMount, Widget from .widgets._toast import ToastRack from .worker import NoActiveWorker, get_current_worker +from .worker_manager import WorkerManager if TYPE_CHECKING: from textual_dev.client import DevtoolsClient from typing_extensions import Coroutine, Literal, Self, TypeAlias - from ._system_commands import SystemCommands from ._types import MessageTarget # Unused & ignored imports are needed for the docs to link to these objects: @@ -128,6 +127,7 @@ from .filter import LineFilter from .message import Message from .pilot import Pilot + from .system_commands import SystemCommands from .widget import MountError # type: ignore # noqa: F401 WINDOWS = sys.platform == "win32" @@ -177,7 +177,7 @@ def get_system_commands() -> type[SystemCommands]: Returns: System commands class. """ - from ._system_commands import SystemCommands + from .system_commands import SystemCommands return SystemCommands diff --git a/src/textual/dom.py b/src/textual/dom.py index bc75cadd86..1e9ea22570 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -31,7 +31,6 @@ from ._context import NoActiveAppError, active_message_pump from ._node_list import NodeList from ._types import WatchCallbackType -from ._worker_manager import WorkerManager from .binding import Binding, BindingType, _Bindings from .color import BLACK, WHITE, Color from .css._error_tools import friendly_list @@ -44,6 +43,7 @@ from .reactive import Reactive, ReactiveError, _watch from .timer import Timer from .walk import walk_breadth_first, walk_depth_first +from .worker_manager import WorkerManager if TYPE_CHECKING: from typing_extensions import Self, TypeAlias diff --git a/tests/command_palette/test_declare_sources.py b/tests/command_palette/test_declare_sources.py index e547fc4eb1..340a9b1666 100644 --- a/tests/command_palette/test_declare_sources.py +++ b/tests/command_palette/test_declare_sources.py @@ -1,7 +1,7 @@ -from textual._system_commands import SystemCommands from textual.app import App from textual.command import CommandPalette, Hit, Hits, Provider from textual.screen import Screen +from textual.system_commands import SystemCommands async def test_sources_with_no_known_screen() -> None: From 2f2f3142bc4dbb9ea1d0f437de041dcaf75e98fa Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 17 Jul 2024 17:37:41 +0100 Subject: [PATCH 3/6] update titles --- docs/api/await_complete.md | 9 ++ docs/api/await_remove.md | 10 ++ docs/guide/workers.md | 2 +- src/textual/map_geometry.py | 34 +++++++ src/textual/system_commands.py | 75 ++++++++++++++ src/textual/worker_manager.py | 181 +++++++++++++++++++++++++++++++++ 6 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 src/textual/map_geometry.py create mode 100644 src/textual/system_commands.py create mode 100644 src/textual/worker_manager.py diff --git a/docs/api/await_complete.md b/docs/api/await_complete.md index 8a1eb58ac6..cefcedd959 100644 --- a/docs/api/await_complete.md +++ b/docs/api/await_complete.md @@ -2,4 +2,13 @@ title: "textual.await_complete" --- +This object is returned by methods that do work in the *background*. +You can await the return value if you need to know when that work has completed. +If you ignore it, Textual will wait for the work to be done before handling the next message. + +!!! note + + You are unlikely to need to explicitly create these objects yourself. + + ::: textual.await_complete diff --git a/docs/api/await_remove.md b/docs/api/await_remove.md index 68b8d92ac5..64e8a4ab5f 100644 --- a/docs/api/await_remove.md +++ b/docs/api/await_remove.md @@ -2,4 +2,14 @@ title: "textual.await_remove" --- + +This object is returned by [`Widget.remove()`][textual.widget.Widget.remove], and other methods which remove widgets. +You can await the return value if you need to know exactly when the widgets have been removed. +If you ignore it, Textual will wait for the widgets to be removed before handling the next message. + +!!! note + + You are unlikely to need to explicitly create these objects yourself. + + ::: textual.await_remove diff --git a/docs/guide/workers.md b/docs/guide/workers.md index ecc3f541b0..6200ec7f4a 100644 --- a/docs/guide/workers.md +++ b/docs/guide/workers.md @@ -100,7 +100,7 @@ You can also create workers which will *not* immediately exit on exception, by s ### Worker lifetime -Workers are managed by a single [WorkerManager][textual._worker_manager.WorkerManager] instance, which you can access via `app.workers`. +Workers are managed by a single [WorkerManager][textual.worker_manager.WorkerManager] instance, which you can access via `app.workers`. This is a container-like object which you iterate over to see your active workers. Workers are tied to the DOM node (widget, screen, or app) where they are created. diff --git a/src/textual/map_geometry.py b/src/textual/map_geometry.py new file mode 100644 index 0000000000..b0a24e634a --- /dev/null +++ b/src/textual/map_geometry.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from typing import NamedTuple + +from textual.geometry import Region, Size, Spacing + + +class MapGeometry(NamedTuple): + """Defines the absolute location of a Widget.""" + + region: Region + """The (screen) [region][textual.geometry.Region] occupied by the widget.""" + order: tuple[tuple[int, int, int], ...] + """Tuple of tuples defining the painting order of the widget. + + Each successive triple represents painting order information with regards to + ancestors in the DOM hierarchy and the last triple provides painting order + information for this specific widget. + """ + clip: Region + """A [region][textual.geometry.Region] to clip the widget by (if a Widget is within a container).""" + virtual_size: Size + """The virtual [size][textual.geometry.Size] (scrollable area) of a widget if it is a container.""" + container_size: Size + """The container [size][textual.geometry.Size] (area not occupied by scrollbars).""" + virtual_region: Region + """The [region][textual.geometry.Region] relative to the container (but not necessarily visible).""" + dock_gutter: Spacing + """Space from the container reserved by docked widgets.""" + + @property + def visible_region(self) -> Region: + """The Widget region after clipping.""" + return self.clip.intersection(self.region) diff --git a/src/textual/system_commands.py b/src/textual/system_commands.py new file mode 100644 index 0000000000..ffa73b263a --- /dev/null +++ b/src/textual/system_commands.py @@ -0,0 +1,75 @@ +"""A command palette command provider for Textual system commands. + +This is a simple command provider that makes the most obvious application +actions available via the [command palette][textual.command.CommandPalette]. +""" + +from __future__ import annotations + +from .command import DiscoveryHit, Hit, Hits, Provider +from .types import IgnoreReturnCallbackType + + +class SystemCommands(Provider): + """A [source][textual.command.Provider] of command palette commands that run app-wide tasks. + + Used by default in [`App.COMMANDS`][textual.app.App.COMMANDS]. + """ + + @property + def _system_commands(self) -> tuple[tuple[str, IgnoreReturnCallbackType, str], ...]: + """The system commands to reveal to the command palette.""" + return ( + ( + "Toggle light/dark mode", + self.app.action_toggle_dark, + "Toggle the application between light and dark mode", + ), + ( + "Quit the application", + self.app.action_quit, + "Quit the application as soon as possible", + ), + ( + "Ring the bell", + self.app.action_bell, + "Ring the terminal's 'bell'", + ), + ) + + async def discover(self) -> Hits: + """Handle a request for the discovery commands for this provider. + + Yields: + Commands that can be discovered. + """ + for name, runnable, help_text in self._system_commands: + yield DiscoveryHit( + name, + runnable, + help=help_text, + ) + + async def search(self, query: str) -> Hits: + """Handle a request to search for system commands that match the query. + + Args: + query: The user input to be matched. + + Yields: + Command hits for use in the command palette. + """ + # We're going to use Textual's builtin fuzzy matcher to find + # matching commands. + matcher = self.matcher(query) + + # Loop over all applicable commands, find those that match and offer + # them up to the command palette. + for name, runnable, help_text in self._system_commands: + if (match := matcher.match(name)) > 0: + yield Hit( + match, + matcher.highlight(name), + runnable, + help=help_text, + ) diff --git a/src/textual/worker_manager.py b/src/textual/worker_manager.py new file mode 100644 index 0000000000..6c90b9641a --- /dev/null +++ b/src/textual/worker_manager.py @@ -0,0 +1,181 @@ +""" +A class to manage [workers](/guide/workers) for an app. + +You access this object via [App.workers][textual.app.App.workers] or [Widget.workers][textual.dom.DOMNode.workers]. +""" + +from __future__ import annotations + +import asyncio +from collections import Counter +from operator import attrgetter +from typing import TYPE_CHECKING, Any, Iterable, Iterator + +import rich.repr + +from .worker import Worker, WorkerState, WorkType + +if TYPE_CHECKING: + from .app import App + from .dom import DOMNode + + +@rich.repr.auto(angular=True) +class WorkerManager: + """An object to manager a number of workers. + + You will not have to construct this class manually, as widgets, screens, and apps + have a worker manager accessibly via a `workers` attribute. + """ + + def __init__(self, app: App) -> None: + """Initialize a worker manager. + + Args: + app: An App instance. + """ + self._app = app + """A reference to the app.""" + self._workers: set[Worker] = set() + """The workers being managed.""" + + def __rich_repr__(self) -> rich.repr.Result: + counter: Counter[WorkerState] = Counter() + counter.update(worker.state for worker in self._workers) + for state, count in sorted(counter.items()): + yield state.name, count + + def __iter__(self) -> Iterator[Worker[Any]]: + return iter(sorted(self._workers, key=attrgetter("_created_time"))) + + def __reversed__(self) -> Iterator[Worker[Any]]: + return iter( + sorted(self._workers, key=attrgetter("_created_time"), reverse=True) + ) + + def __bool__(self) -> bool: + return bool(self._workers) + + def __len__(self) -> int: + return len(self._workers) + + def __contains__(self, worker: object) -> bool: + return worker in self._workers + + def add_worker( + self, worker: Worker, start: bool = True, exclusive: bool = True + ) -> None: + """Add a new worker. + + Args: + worker: A Worker instance. + start: Start the worker if True, otherwise the worker must be started manually. + exclusive: Cancel all workers in the same group as `worker`. + """ + if exclusive and worker.group: + self.cancel_group(worker.node, worker.group) + self._workers.add(worker) + if start: + worker._start(self._app, self._remove_worker) + + def _new_worker( + self, + work: WorkType, + node: DOMNode, + *, + name: str | None = "", + group: str = "default", + description: str = "", + exit_on_error: bool = True, + start: bool = True, + exclusive: bool = False, + thread: bool = False, + ) -> Worker: + """Create a worker from a function, coroutine, or awaitable. + + Args: + work: A callable, a coroutine, or other awaitable. + name: A name to identify the worker. + group: The worker group. + description: A description of the worker. + exit_on_error: Exit the app if the worker raises an error. Set to `False` to suppress exceptions. + start: Automatically start the worker. + exclusive: Cancel all workers in the same group. + thread: Mark the worker as a thread worker. + + Returns: + A Worker instance. + """ + worker: Worker[Any] = Worker( + node, + work, + name=name or getattr(work, "__name__", "") or "", + group=group, + description=description or repr(work), + exit_on_error=exit_on_error, + thread=thread, + ) + self.add_worker(worker, start=start, exclusive=exclusive) + return worker + + def _remove_worker(self, worker: Worker) -> None: + """Remove a worker from the manager. + + Args: + worker: A Worker instance. + """ + self._workers.discard(worker) + + def start_all(self) -> None: + """Start all the workers.""" + for worker in self._workers: + worker._start(self._app, self._remove_worker) + + def cancel_all(self) -> None: + """Cancel all workers.""" + for worker in self._workers: + worker.cancel() + + def cancel_group(self, node: DOMNode, group: str) -> list[Worker]: + """Cancel a single group. + + Args: + node: Worker DOM node. + group: A group name. + + Returns: + A list of workers that were cancelled. + """ + workers = [ + worker + for worker in self._workers + if (worker.group == group and worker.node == node) + ] + for worker in workers: + worker.cancel() + return workers + + def cancel_node(self, node: DOMNode) -> list[Worker]: + """Cancel all workers associated with a given node + + Args: + node: A DOM node (widget, screen, or App). + + Returns: + List of cancelled workers. + """ + workers = [worker for worker in self._workers if worker.node == node] + for worker in workers: + worker.cancel() + return workers + + async def wait_for_complete(self, workers: Iterable[Worker] | None = None) -> None: + """Wait for workers to complete. + + Args: + workers: An iterable of workers or None to wait for all workers in the manager. + """ + try: + await asyncio.gather(*[worker.wait() for worker in (workers or self)]) + except asyncio.CancelledError: + pass From 5b32e19d40d141a2abbd63ea4dfc756a8e8f9320 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 17 Jul 2024 21:27:36 +0100 Subject: [PATCH 4/6] better API docs --- .../python/material/_base/class.html | 114 ------------------ .../material/_base/docstring/admonition.html | 5 - .../material/_base/docstring/attributes.html | 78 ------------ .../material/_base/docstring/parameters.html | 96 --------------- .../material/_base/docstring/raises.html | 72 ----------- .../material/_base/docstring/returns.html | 91 -------------- .../python/material/_base/function.html | 73 ----------- .../python/material/_base/signature.html | 48 -------- docs/api/await_complete.md | 4 +- docs/api/await_remove.md | 6 +- mkdocs-common.yml | 2 + src/textual/binding.py | 2 +- src/textual/cache.py | 4 +- src/textual/constants.py | 2 +- 14 files changed, 11 insertions(+), 586 deletions(-) delete mode 100644 docs/_templates/python/material/_base/class.html delete mode 100644 docs/_templates/python/material/_base/docstring/admonition.html delete mode 100644 docs/_templates/python/material/_base/docstring/attributes.html delete mode 100644 docs/_templates/python/material/_base/docstring/parameters.html delete mode 100644 docs/_templates/python/material/_base/docstring/raises.html delete mode 100644 docs/_templates/python/material/_base/docstring/returns.html delete mode 100644 docs/_templates/python/material/_base/function.html delete mode 100644 docs/_templates/python/material/_base/signature.html diff --git a/docs/_templates/python/material/_base/class.html b/docs/_templates/python/material/_base/class.html deleted file mode 100644 index 120fe338f1..0000000000 --- a/docs/_templates/python/material/_base/class.html +++ /dev/null @@ -1,114 +0,0 @@ -{{ log.debug("Rendering " + class.path) }} - -
-{% with html_id = class.path %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - - - {% if 1 %} - - {% filter heading(heading_level, - role="class", - id=html_id, - class="doc doc-heading", - toc_label=class.name) %} - - {% if config.separate_signature %} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% elif config.merge_init_into_class and "__init__" in class.members -%} - {%- with function = class.members["__init__"] -%} - {%- filter highlight(language="python", inline=True) -%} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {%- include "signature.html" with context -%} - {%- endfilter -%} - {%- endwith -%} - {% else %} - {% if show_full_path %}{{ class.path }}{% else %}{{ class.name }}{% endif %} - {% endif %} - - {% with labels = ['class'] %} - {% include "labels.html" with context %} - {% endwith %} - - {% endfilter %} - - {% if config.separate_signature and config.merge_init_into_class %} - {% if "__init__" in class.members %} - {% with function = class.members["__init__"] %} - {% filter highlight(language="python", inline=False) -%} - def {% filter format_signature(config.line_length) %} -__init__{% include "signature.html" with context %} - {% endfilter %}: - {% endfilter %} - {% endwith %} - {% endif %} - {% endif %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="class", - id=html_id, - toc_label=class.path if config.show_root_full_path else class.name, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% if config.show_bases and class.bases %} -

- Bases: {% for expression in class.bases -%} - {% include "expression.html" with context %}{% if not loop.last %}, {% endif %} - {% endfor -%} -

- {% endif %} - - {% with docstring_sections = class.docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} - - {% if config.merge_init_into_class %} - {% if "__init__" in class.members and class.members["__init__"].has_docstring %} - {% with docstring_sections = class.members["__init__"].docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} - {% endif %} - {% endif %} - - {% if config.show_source %} - {% if config.merge_init_into_class %} - {% if "__init__" in class.members and class.members["__init__"].source %} -
- Source code in {{ class.relative_filepath }} - {{ class.members["__init__"].source|highlight(language="python", linestart=class.members["__init__"].lineno, linenums=True) }} -
- {% endif %} - {% elif class.source %} -
- Source code in {{ class.relative_filepath }} - {{ class.source|highlight(language="python", linestart=class.lineno, linenums=True) }} -
- {% endif %} - {% endif %} - - {% with obj = class %} - {% set root = False %} - {% set heading_level = heading_level + 1 %} - {% include "children.html" with context %} - {% endwith %} -
- -{% endwith %} -
diff --git a/docs/_templates/python/material/_base/docstring/admonition.html b/docs/_templates/python/material/_base/docstring/admonition.html deleted file mode 100644 index 2105eab710..0000000000 --- a/docs/_templates/python/material/_base/docstring/admonition.html +++ /dev/null @@ -1,5 +0,0 @@ -{{ log.debug("Rendering admonition") }} -
- {{ section.title|convert_markdown(heading_level, html_id, strip_paragraph=True) }} - {{ section.value.contents|convert_markdown(heading_level, html_id) }} -
diff --git a/docs/_templates/python/material/_base/docstring/attributes.html b/docs/_templates/python/material/_base/docstring/attributes.html deleted file mode 100644 index 99465b874b..0000000000 --- a/docs/_templates/python/material/_base/docstring/attributes.html +++ /dev/null @@ -1,78 +0,0 @@ -{{ log.debug("Rendering attributes section") }} -{% if config.docstring_section_style == "table" %} - {% block table_style %} -
{{ section.title or "Attributes" }}
- - - - - - - - - - {% for attribute in section.value %} - - - - - - {% endfor %} - -
NameTypeDescription
{{ attribute.name }} - {% if attribute.annotation %} - {% with expression = attribute.annotation %} - {% include "expression.html" with context %} - {% endwith %} - {% endif %} - {{ attribute.description|convert_markdown(heading_level, html_id) }}
- {% endblock table_style %} -{% elif config.docstring_section_style == "list" %} - {% block list_style %} -

{{ section.title or "Attributes:" }}

-
    - {% for attribute in section.value %} -
  • - {{ attribute.name }} - {% if attribute.annotation %} - {% with expression = attribute.annotation %} - ({% include "expression.html" with context %}) - {% endwith %} - {% endif %} - – {{ attribute.description|convert_markdown(heading_level, html_id) }} -
  • - {% endfor %} -
- {% endblock list_style %} -{% elif config.docstring_section_style == "spacy" %} - {% block spacy_style %} - - - - - - - - - {% for attribute in section.value %} - - - - - {% endfor %} - -
{{ (section.title or "ATTRIBUTE").rstrip(":").upper() }}DESCRIPTION
{{ attribute.name }} - {{ attribute.description|convert_markdown(heading_level, html_id) }} -

- {% if attribute.annotation %} - - TYPE: - {% with expression = attribute.annotation %} - {% include "expression.html" with context %} - {% endwith %} - - {% endif %} -

-
- {% endblock spacy_style %} -{% endif %} diff --git a/docs/_templates/python/material/_base/docstring/parameters.html b/docs/_templates/python/material/_base/docstring/parameters.html deleted file mode 100644 index 10176a23ae..0000000000 --- a/docs/_templates/python/material/_base/docstring/parameters.html +++ /dev/null @@ -1,96 +0,0 @@ -{{ log.debug("Rendering parameters section") }} -{% if config.docstring_section_style == "table" %} - {% block table_style %} -
{{ section.title or "Parameters" }}
- - - - - - - - - - {% for parameter in section.value %} - - - - - - {% endfor %} - -
ParameterDefaultDescription
- {{ parameter.name }} -
- {% if parameter.annotation %} - {% with expression = parameter.annotation %} - {% include "expression.html" with context %} - {% endwith %} - {% endif %} -
- {% if parameter.default %} - {% with expression = parameter.default %} - {% include "expression.html" with context %} - {% endwith %} - {% else %} - required - {% endif %} - {{ parameter.description|convert_markdown(heading_level, html_id) }}
- {% endblock table_style %} -{% elif config.docstring_section_style == "list" %} - {% block list_style %} -

{{ section.title or "Parameters:" }}

-
    - {% for parameter in section.value %} -
  • - {{ parameter.name }} - {% if parameter.annotation %} - {% with expression = parameter.annotation %} - ({% include "expression.html" with context %}) - {% endwith %} - {% endif %} - – {{ parameter.description|convert_markdown(heading_level, html_id) }} -
  • - {% endfor %} -
- {% endblock list_style %} -{% elif config.docstring_section_style == "spacy" %} - {% block spacy_style %} - - - - - - - - - {% for parameter in section.value %} - - - - - {% endfor %} - -
{{ (section.title or "PARAMETER").rstrip(":").upper() }}DESCRIPTION
{{ parameter.name }} - {{ parameter.description|convert_markdown(heading_level, html_id) }} -

- {% if parameter.annotation %} - - TYPE: - {% with expression = parameter.annotation %} - {% include "expression.html" with context %} - {% endwith %} - - {% endif %} - {% if parameter.default %} - - DEFAULT: - {% with expression = parameter.default %} - {% include "expression.html" with context %} - {% endwith %} - - {% endif %} -

-
- {% endblock spacy_style %} -{% endif %} diff --git a/docs/_templates/python/material/_base/docstring/raises.html b/docs/_templates/python/material/_base/docstring/raises.html deleted file mode 100644 index 6e59775f46..0000000000 --- a/docs/_templates/python/material/_base/docstring/raises.html +++ /dev/null @@ -1,72 +0,0 @@ -{{ log.debug("Rendering raises section") }} -{% if config.docstring_section_style == "table" %} - {% block table_style %} -
{{ section.title or "Raises" }}
- - - - - - - - - {% for raises in section.value %} - - - - - {% endfor %} - -
TypeDescription
- {% if raises.annotation %} - {% with expression = raises.annotation %} - {% include "expression.html" with context %} - {% endwith %} - {% endif %} - {{ raises.description|convert_markdown(heading_level, html_id) }}
- {% endblock table_style %} -{% elif config.docstring_section_style == "list" %} - {% block list_style %} -

{{ section.title or "Raises:" }}

-
    - {% for raises in section.value %} -
  • - {% if raises.annotation %} - {% with expression = raises.annotation %} - {% include "expression.html" with context %} - {% endwith %} - – - {% endif %} - {{ raises.description|convert_markdown(heading_level, html_id) }} -
  • - {% endfor %} -
- {% endblock list_style %} -{% elif config.docstring_section_style == "spacy" %} - {% block spacy_style %} - - - - - - - - - {% for raises in section.value %} - - - - - {% endfor %} - -
{{ (section.title or "RAISES").rstrip(":").upper() }}DESCRIPTION
- - {% with expression = raises.annotation %} - {% include "expression.html" with context %} - {% endwith %} - - - {{ raises.description|convert_markdown(heading_level, html_id) }} -
- {% endblock spacy_style %} -{% endif %} diff --git a/docs/_templates/python/material/_base/docstring/returns.html b/docs/_templates/python/material/_base/docstring/returns.html deleted file mode 100644 index a1f78b32c0..0000000000 --- a/docs/_templates/python/material/_base/docstring/returns.html +++ /dev/null @@ -1,91 +0,0 @@ -{{ log.debug("Rendering returns section") }} -{% if config.docstring_section_style == "table" %} - {% block table_style %} - {% set name_column = section.value|selectattr("name")|any %} -
{{ section.title or "Returns" }}
- - - - {% if name_column %}{% endif %} - - - - - - {% for returns in section.value %} - - {% if name_column %}{% endif %} - - - - {% endfor %} - -
NameTypeDescription
{% if returns.name %}{{ returns.name }}{% endif %} - {% if returns.annotation %} - {% with expression = returns.annotation %} - {% include "expression.html" with context %} - {% endwith %} - {% endif %} - {{ returns.description|convert_markdown(heading_level, html_id) }}
- {% endblock table_style %} -{% elif config.docstring_section_style == "list" %} - {% block list_style %} -

{{ section.title or "Returns:" }}

-
    - {% for returns in section.value %} -
  • - {% if returns.name %}{{ returns.name }}{% endif %} - {% if returns.annotation %} - {% with expression = returns.annotation %} - {% if returns.name %}({% endif %} - {% include "expression.html" with context %} - {% if returns.name %}){% endif %} - {% endwith %} - {% endif %} - – {{ returns.description|convert_markdown(heading_level, html_id) }} -
  • - {% endfor %} -
- {% endblock list_style %} -{% elif config.docstring_section_style == "spacy" %} - {% block spacy_style %} - - - - - - - - - {% for returns in section.value %} - - - - - {% endfor %} - -
{{ (section.title or "RETURNS").rstrip(":").upper() }}DESCRIPTION
- {% if returns.name %} - {{ returns.name }} - {% elif returns.annotation %} - - {% with expression = returns.annotation %} - {% include "expression.html" with context %} - {% endwith %} - - {% endif %} - - {{ returns.description|convert_markdown(heading_level, html_id) }} - {% if returns.name and returns.annotation %} -

- - TYPE: - {% with expression = returns.annotation %} - {% include "expression.html" with context %} - {% endwith %} - -

- {% endif %} -
- {% endblock spacy_style %} -{% endif %} diff --git a/docs/_templates/python/material/_base/function.html b/docs/_templates/python/material/_base/function.html deleted file mode 100644 index 58a4c37ad4..0000000000 --- a/docs/_templates/python/material/_base/function.html +++ /dev/null @@ -1,73 +0,0 @@ -{{ log.debug("Rendering " + function.path) }} - -
-{% with html_id = function.path %} - - {% if root %} - {% set show_full_path = config.show_root_full_path %} - {% set root_members = True %} - {% elif root_members %} - {% set show_full_path = config.show_root_members_full_path or config.show_object_full_path %} - {% set root_members = False %} - {% else %} - {% set show_full_path = config.show_object_full_path %} - {% endif %} - - {% if not root or config.show_root_heading %} - - {% filter heading(heading_level, - role="function", - id=html_id, - class="doc doc-heading", - toc_label=function.name ~ "()") %} - - {% if config.separate_signature %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% else %} - {% filter highlight(language="python", inline=True) %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% include "signature.html" with context %} - {% endfilter %} - {% endif %} - {% with labels = function.labels or [(function.parameters._parameters_list and function.parameters._parameters_list[0].name == 'self') and 'method' or 'function'] %} - {% include "labels.html" with context %} - {% endwith %} - - {% endfilter %} - - {% if config.separate_signature %} - {% filter highlight(language="python", inline=False) %} -def {% filter format_signature(config.line_length) %} - {% if show_full_path %}{{ function.path }}{% else %}{{ function.name }}{% endif %} - {% include "signature.html" with context %} - {% endfilter %}: - {% endfilter %} - {% endif %} - - {% else %} - {% if config.show_root_toc_entry %} - {% filter heading(heading_level, - role="function", - id=html_id, - toc_label=function.path if config.show_root_full_path else function.name, - hidden=True) %} - {% endfilter %} - {% endif %} - {% set heading_level = heading_level - 1 %} - {% endif %} - -
- {% with docstring_sections = function.docstring.parsed %} - {% include "docstring.html" with context %} - {% endwith %} - - {% if config.show_source and function.source %} -
- Source code in {{ function.relative_filepath }} - {{ function.source|highlight(language="python", linestart=function.lineno, linenums=True) }} -
- {% endif %} -
- -{% endwith %} -
diff --git a/docs/_templates/python/material/_base/signature.html b/docs/_templates/python/material/_base/signature.html deleted file mode 100644 index 2e1ecd6357..0000000000 --- a/docs/_templates/python/material/_base/signature.html +++ /dev/null @@ -1,48 +0,0 @@ -{%- if config.show_signature -%} - {{ log.debug("Rendering signature") }} - {%- with -%} - - {%- set ns = namespace(has_pos_only=False, render_pos_only_separator=True, render_kw_only_separator=True, equal="=") -%} - - {%- if config.show_signature_annotations -%} - {%- set ns.equal = " = " -%} - {%- endif -%} - - ( - {%- for parameter in function.parameters -%} - {%- if 1 -%} - - {%- if parameter.kind.value == "positional-only" -%} - {%- set ns.has_pos_only = True -%} - {%- else -%} - {%- if ns.has_pos_only and ns.render_pos_only_separator -%} - {%- set ns.render_pos_only_separator = False %}/, {% endif -%} - {%- if parameter.kind.value == "keyword-only" -%} - {%- if ns.render_kw_only_separator -%} - {%- set ns.render_kw_only_separator = False %}*, {% endif -%} - {%- endif -%} - {%- endif -%} - - {%- if config.show_signature_annotations and parameter.annotation is not none -%} - {%- set annotation = ": " + parameter.annotation|safe -%} - {%- endif -%} - - {%- if parameter.default is not none and parameter.kind.value != "variadic positional" and parameter.kind.value != "variadic keyword" -%} - {%- set default = ns.equal + parameter.default|safe -%} - {%- endif -%} - - {%- if parameter.kind.value == "variadic positional" -%} - {%- set ns.render_kw_only_separator = False -%} - {%- endif -%} - - {% if parameter.kind.value == "variadic positional" %}*{% elif parameter.kind.value == "variadic keyword" %}**{% endif -%} - {{ parameter.name }}{{ annotation }}{{ default }} - {%- if not loop.last %}, {% endif -%} - - {%- endif -%} - {%- endfor -%} - ) - {%- if config.show_signature_annotations and function.annotation %} -> {{ function.annotation|safe }}{%- endif -%} - - {%- endwith -%} -{%- endif -%} diff --git a/docs/api/await_complete.md b/docs/api/await_complete.md index cefcedd959..1782553bb4 100644 --- a/docs/api/await_complete.md +++ b/docs/api/await_complete.md @@ -3,8 +3,8 @@ title: "textual.await_complete" --- This object is returned by methods that do work in the *background*. -You can await the return value if you need to know when that work has completed. -If you ignore it, Textual will wait for the work to be done before handling the next message. +You can await this object if you need to know when that work has completed. +Or you can ignore it, and Textual will automatically await the work before handling the next message. !!! note diff --git a/docs/api/await_remove.md b/docs/api/await_remove.md index 64e8a4ab5f..d13479a3f5 100644 --- a/docs/api/await_remove.md +++ b/docs/api/await_remove.md @@ -3,9 +3,9 @@ title: "textual.await_remove" --- -This object is returned by [`Widget.remove()`][textual.widget.Widget.remove], and other methods which remove widgets. -You can await the return value if you need to know exactly when the widgets have been removed. -If you ignore it, Textual will wait for the widgets to be removed before handling the next message. +This object is returned by [`Widget.remove()`][textual.widget.Widget.remove] and other methods which remove widgets. +You can await the return value if you need to know exactly when the widget(s) have been removed. +Or you can ignore it and Textual will wait for the widgets to be removed before handling the next message. !!! note diff --git a/mkdocs-common.yml b/mkdocs-common.yml index 883653f6a1..6bae8ebdbb 100644 --- a/mkdocs-common.yml +++ b/mkdocs-common.yml @@ -71,10 +71,12 @@ plugins: handlers: python: options: + modernize_annotations: false show_symbol_type_heading: true show_symbol_type_toc: true show_signature_annotations: false separate_signature: true + signature_crossrefs: true merge_init_into_class: true docstring_options: ignore_init_summary: true diff --git a/src/textual/binding.py b/src/textual/binding.py index 3cc2828df3..5ff189bb15 100644 --- a/src/textual/binding.py +++ b/src/textual/binding.py @@ -53,7 +53,7 @@ class Binding: class ActiveBinding(NamedTuple): - """Information about an active binding (returned from [active_bindings][textual.screen.active_bindings]).""" + """Information about an active binding (returned from [active_bindings][textual.screen.Screen.active_bindings]).""" node: DOMNode """The node where the binding is defined.""" diff --git a/src/textual/cache.py b/src/textual/cache.py index 9186df7cda..85937b3449 100644 --- a/src/textual/cache.py +++ b/src/textual/cache.py @@ -1,8 +1,8 @@ """ -Containers to implement caching. +Cache classes are dict-like containers used to avoid recalculating expensive operations such as rendering. -These are used in Textual to avoid recalculating expensive operations, such as rendering. +You can also use them in your own apps if you need them. """ diff --git a/src/textual/constants.py b/src/textual/constants.py index 6d0ebfe323..9c420a7dc7 100644 --- a/src/textual/constants.py +++ b/src/textual/constants.py @@ -1,5 +1,5 @@ """ -Constants that we might want to expose via the public API. +This module contains constants, which may be set in environment variables. """ from __future__ import annotations From f242d477b934b757366d586b584e79ea193ed582 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 17 Jul 2024 21:35:02 +0100 Subject: [PATCH 5/6] add api --- mkdocs-nav.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index 5544e28ad6..f52cb4df78 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -208,6 +208,7 @@ nav: - "api/screen.md" - "api/scrollbar.md" - "api/scroll_view.md" + - "api/signal.md" - "api/strip.md" - "api/suggester.md" - "api/system_commands_source.md" From 27b1b06f369dd3c9647a4f12a1ed8b8b585eff45 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 18 Jul 2024 09:50:36 +0100 Subject: [PATCH 6/6] tweaks --- docs/api/constants.md | 1 - docs/api/signal.md | 1 - mkdocs-common.yml | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/constants.md b/docs/api/constants.md index ed6b671d9d..52894fea25 100644 --- a/docs/api/constants.md +++ b/docs/api/constants.md @@ -2,5 +2,4 @@ title: "textual.constants" --- - ::: textual.constants diff --git a/docs/api/signal.md b/docs/api/signal.md index b3d242615b..bd47ac3fb1 100644 --- a/docs/api/signal.md +++ b/docs/api/signal.md @@ -2,5 +2,4 @@ title: "textual.signal" --- - ::: textual.signal diff --git a/mkdocs-common.yml b/mkdocs-common.yml index 6bae8ebdbb..0d842060d1 100644 --- a/mkdocs-common.yml +++ b/mkdocs-common.yml @@ -78,6 +78,8 @@ plugins: separate_signature: true signature_crossrefs: true merge_init_into_class: true + parameter_headings: true + show_root_heading: false docstring_options: ignore_init_summary: true show_source: false