mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Export types & doc improvements (#2329)
* Export types used in app.py * Export more linked types/errors/classes. * Remove custom template. * Address review comments. We need to have explicit 'Returns:' sections in properties if we want to link to the return type while https://github.com/mkdocstrings/python/issues/65 is open. * Improve docs.
This commit is contained in:
committed by
GitHub
parent
911ffdb144
commit
914e50a70f
67
docs/_templates/python/material/attribute.html
vendored
67
docs/_templates/python/material/attribute.html
vendored
@@ -1,67 +0,0 @@
|
||||
{{ log.debug("Rendering " + attribute.path) }}
|
||||
|
||||
<div class="doc doc-object doc-attribute">
|
||||
{% with html_id = attribute.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="data" if attribute.parent.kind.value == "module" else "attr",
|
||||
id=html_id,
|
||||
class="doc doc-heading",
|
||||
toc_label=attribute.name) %}
|
||||
|
||||
{% if config.separate_signature %}
|
||||
<span class="doc doc-object-name doc-attribute-name">{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}</span>
|
||||
{% else %}
|
||||
{% filter highlight(language="python", inline=True) %}
|
||||
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
|
||||
{% if attribute.annotation %}: {{ attribute.annotation }}{% endif %}
|
||||
{% endfilter %}
|
||||
{% endif %}
|
||||
|
||||
{% with labels = attribute.labels %}
|
||||
{% include "labels.html" with context %}
|
||||
{% endwith %}
|
||||
|
||||
{% endfilter %}
|
||||
|
||||
{% if config.separate_signature %}
|
||||
{% filter highlight(language="python", inline=False) %}
|
||||
{% filter format_code(config.line_length) %}
|
||||
{% if show_full_path %}{{ attribute.path }}{% else %}{{ attribute.name }}{% endif %}
|
||||
{% if attribute.annotation %}: {{ attribute.annotation|safe }}{% endif %}
|
||||
{% endfilter %}
|
||||
{% endfilter %}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if config.show_root_toc_entry %}
|
||||
{% filter heading(heading_level,
|
||||
role="data" if attribute.parent.kind.value == "module" else "attr",
|
||||
id=html_id,
|
||||
toc_label=attribute.path if config.show_root_full_path else attribute.name,
|
||||
hidden=True) %}
|
||||
{% endfilter %}
|
||||
{% endif %}
|
||||
{% set heading_level = heading_level - 1 %}
|
||||
{% endif %}
|
||||
|
||||
<div class="doc doc-contents {% if root %}first{% endif %}">
|
||||
{% with docstring_sections = attribute.docstring.parsed %}
|
||||
{% include "docstring.html" with context %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
{% endwith %}
|
||||
</div>
|
||||
1
docs/api/errors.md
Normal file
1
docs/api/errors.md
Normal file
@@ -0,0 +1 @@
|
||||
::: textual.errors
|
||||
1
docs/api/filter.md
Normal file
1
docs/api/filter.md
Normal file
@@ -0,0 +1 @@
|
||||
::: textual.filter
|
||||
1
docs/api/scrollbar.md
Normal file
1
docs/api/scrollbar.md
Normal file
@@ -0,0 +1 @@
|
||||
::: textual.scrollbar
|
||||
1
docs/api/types.md
Normal file
1
docs/api/types.md
Normal file
@@ -0,0 +1 @@
|
||||
::: textual.types
|
||||
@@ -164,6 +164,8 @@ nav:
|
||||
- "api/coordinate.md"
|
||||
- "api/dom_node.md"
|
||||
- "api/events.md"
|
||||
- "api/errors.md"
|
||||
- "api/filter.md"
|
||||
- "api/geometry.md"
|
||||
- "api/index.md"
|
||||
- "api/logger.md"
|
||||
@@ -175,9 +177,11 @@ nav:
|
||||
- "api/query.md"
|
||||
- "api/reactive.md"
|
||||
- "api/screen.md"
|
||||
- "api/scrollbar.md"
|
||||
- "api/scroll_view.md"
|
||||
- "api/strip.md"
|
||||
- "api/timer.md"
|
||||
- "api/types.md"
|
||||
- "api/walk.md"
|
||||
- "api/widget.md"
|
||||
- "api/work.md"
|
||||
|
||||
@@ -21,6 +21,10 @@ if TYPE_CHECKING:
|
||||
"""Animation keys are the id of the object and the attribute being animated."""
|
||||
|
||||
EasingFunction = Callable[[float], float]
|
||||
"""Signature for a function that parametrises animation speed.
|
||||
|
||||
An easing function must map the interval [0, 1] into the interval [0, 1].
|
||||
"""
|
||||
|
||||
|
||||
class AnimationError(Exception):
|
||||
@@ -32,6 +36,12 @@ ReturnType = TypeVar("ReturnType")
|
||||
|
||||
@runtime_checkable
|
||||
class Animatable(Protocol):
|
||||
"""Protocol for objects that can have their intrinsic values animated.
|
||||
|
||||
For example, the transition between two colors can be animated
|
||||
because the class [`Color`][textual.color.Color.blend] satisfies this protocol.
|
||||
"""
|
||||
|
||||
def blend(
|
||||
self: ReturnType, destination: ReturnType, factor: float
|
||||
) -> ReturnType: # pragma: no cover
|
||||
|
||||
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class NoActiveAppError(RuntimeError):
|
||||
pass
|
||||
"""Runtime error raised if we try to retrieve the active app when there is none."""
|
||||
|
||||
|
||||
active_app: ContextVar["App"] = ContextVar("active_app")
|
||||
|
||||
@@ -8,6 +8,8 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class MessageTarget(Protocol):
|
||||
"""Protocol that must be followed by objects that can receive messages."""
|
||||
|
||||
async def _post_message(self, message: "Message") -> bool:
|
||||
...
|
||||
|
||||
@@ -25,6 +27,7 @@ class EventTarget(Protocol):
|
||||
|
||||
SegmentLines = List[List["Segment"]]
|
||||
CallbackType = Union[Callable[[], Awaitable[None]], Callable[[], None]]
|
||||
"""Type used for arbitrary callables used in callbacks."""
|
||||
WatchCallbackType = Union[
|
||||
Callable[[], Awaitable[None]],
|
||||
Callable[[Any], Awaitable[None]],
|
||||
@@ -33,3 +36,4 @@ WatchCallbackType = Union[
|
||||
Callable[[Any], None],
|
||||
Callable[[Any, Any], None],
|
||||
]
|
||||
"""Type used for callbacks passed to the `watch` method of widgets."""
|
||||
|
||||
@@ -97,8 +97,11 @@ from .widget import AwaitMount, Widget
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Coroutine, TypeAlias
|
||||
|
||||
# Unused & ignored imports are needed for the docs to link to these objects:
|
||||
from .css.query import WrongType # type: ignore # noqa: F401
|
||||
from .devtools.client import DevtoolsClient
|
||||
from .pilot import Pilot
|
||||
from .widget import MountError # type: ignore # noqa: F401
|
||||
|
||||
PLATFORM = platform.system()
|
||||
WINDOWS = PLATFORM == "Windows"
|
||||
@@ -137,6 +140,7 @@ ComposeResult = Iterable[Widget]
|
||||
RenderResult = RenderableType
|
||||
|
||||
AutopilotCallbackType: TypeAlias = "Callable[[Pilot], Coroutine[Any, Any, None]]"
|
||||
"""Signature for valid callbacks that can be used to control apps."""
|
||||
|
||||
|
||||
class AppError(Exception):
|
||||
@@ -167,6 +171,7 @@ CSSPathType = Union[
|
||||
PurePath,
|
||||
List[Union[str, PurePath]],
|
||||
]
|
||||
"""Valid ways of specifying paths to CSS files."""
|
||||
|
||||
CallThreadReturnType = TypeVar("CallThreadReturnType")
|
||||
|
||||
@@ -186,17 +191,7 @@ class _NullFile:
|
||||
|
||||
@rich.repr.auto
|
||||
class App(Generic[ReturnType], DOMNode):
|
||||
"""The base class for Textual Applications.
|
||||
|
||||
Args:
|
||||
driver_class: Driver class or `None` to auto-detect. This will be used by some Textual tools.
|
||||
css_path: Path to CSS or `None` to use the `CSS_PATH` class variable.
|
||||
To load multiple CSS files, pass a list of strings or paths which will be loaded in order.
|
||||
watch_css: Reload CSS if the files changed. This is set automatically if you are using `textual run` with the `dev` switch.
|
||||
|
||||
Raises:
|
||||
CssPathError: When the supplied CSS path(s) are an unexpected type.
|
||||
"""
|
||||
"""The base class for Textual Applications."""
|
||||
|
||||
CSS: ClassVar[str] = ""
|
||||
"""Inline CSS, useful for quick scripts. This is loaded after CSS_PATH,
|
||||
@@ -218,8 +213,10 @@ class App(Generic[ReturnType], DOMNode):
|
||||
"""
|
||||
|
||||
SCREENS: ClassVar[dict[str, Screen | Callable[[], Screen]]] = {}
|
||||
"""Screens associated with the app for the lifetime of the app."""
|
||||
_BASE_PATH: str | None = None
|
||||
CSS_PATH: ClassVar[CSSPathType | None] = None
|
||||
"""File paths to load CSS from."""
|
||||
|
||||
TITLE: str | None = None
|
||||
"""A class variable to set the *default* title for the application.
|
||||
@@ -255,6 +252,20 @@ class App(Generic[ReturnType], DOMNode):
|
||||
css_path: CSSPathType | None = None,
|
||||
watch_css: bool = False,
|
||||
):
|
||||
"""Create an instance of an app.
|
||||
|
||||
Args:
|
||||
driver_class: Driver class or `None` to auto-detect.
|
||||
This will be used by some Textual tools.
|
||||
css_path: Path to CSS or `None` to use the `CSS_PATH` class variable.
|
||||
To load multiple CSS files, pass a list of strings or paths which
|
||||
will be loaded in order.
|
||||
watch_css: Reload CSS if the files changed. This is set automatically if
|
||||
you are using `textual run` with the `dev` switch.
|
||||
|
||||
Raises:
|
||||
CssPathError: When the supplied CSS path(s) are an unexpected type.
|
||||
"""
|
||||
super().__init__()
|
||||
self.features: frozenset[FeatureFlag] = parse_features(os.getenv("TEXTUAL", ""))
|
||||
|
||||
@@ -406,7 +417,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def return_value(self) -> ReturnType | None:
|
||||
"""The return value of the app, or `None` if it as not yet been set.
|
||||
"""The return value of the app, or `None` if it has not yet been set.
|
||||
|
||||
The return value is set when calling [exit][textual.app.App.exit].
|
||||
"""
|
||||
@@ -414,10 +425,11 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def children(self) -> Sequence["Widget"]:
|
||||
"""A view on to the App's children.
|
||||
"""A view onto the app's immediate children.
|
||||
|
||||
This attribute exists on all widgets.
|
||||
In the case of the App, it will only every contain a single child, which will be the currently active screen.
|
||||
In the case of the App, it will only ever contain a single child, which will
|
||||
be the currently active screen.
|
||||
|
||||
Returns:
|
||||
A sequence of widgets.
|
||||
@@ -499,7 +511,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def screen_stack(self) -> Sequence[Screen]:
|
||||
"""The current screen stack.
|
||||
"""A snapshot of the current screen stack.
|
||||
|
||||
Returns:
|
||||
A snapshot of the current state of the screen stack.
|
||||
@@ -523,7 +535,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def focused(self) -> Widget | None:
|
||||
"""The widget that is focused on the currently active screen.
|
||||
"""The widget that is focused on the currently active screen, or `None`.
|
||||
|
||||
Focused widgets receive keyboard input.
|
||||
|
||||
@@ -534,7 +546,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def namespace_bindings(self) -> dict[str, tuple[DOMNode, Binding]]:
|
||||
"""Get current bindings.
|
||||
"""Get currently active bindings.
|
||||
|
||||
If no widget is focused, then app-level bindings are returned.
|
||||
If a widget is focused, then any bindings present in the active screen and app are merged and returned.
|
||||
@@ -542,8 +554,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
This property may be used to inspect current bindings.
|
||||
|
||||
Returns:
|
||||
|
||||
A mapping of keys on to node + binding.
|
||||
A mapping of keys onto pairs of nodes and bindings.
|
||||
"""
|
||||
|
||||
namespace_binding_map: dict[str, tuple[DOMNode, Binding]] = {}
|
||||
@@ -650,7 +661,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def screen(self) -> Screen:
|
||||
"""Screen: The current screen.
|
||||
"""The current active screen.
|
||||
|
||||
Returns:
|
||||
The currently active (visible) screen.
|
||||
@@ -689,7 +700,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
@property
|
||||
def log(self) -> Logger:
|
||||
"""Textual log interface.
|
||||
"""The textual logger.
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1162,14 +1173,15 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
Args:
|
||||
id: The ID of the node to search for.
|
||||
expect_type: Require the object be of the supplied type, or None for any type.
|
||||
expect_type: Require the object be of the supplied type,
|
||||
or use `None` to apply no type restriction.
|
||||
|
||||
Returns:
|
||||
The first child of this node with the specified ID.
|
||||
|
||||
Raises:
|
||||
NoMatches: if no children could be found for this ID
|
||||
WrongType: if the wrong type was found.
|
||||
NoMatches: If no children could be found for this ID.
|
||||
WrongType: If the wrong type was found.
|
||||
"""
|
||||
return (
|
||||
self.screen.get_child_by_id(id)
|
||||
@@ -1463,7 +1475,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
with [install_screen][textual.app.App.install_screen].
|
||||
Textual will also uninstall screens automatically on exit.
|
||||
|
||||
|
||||
Args:
|
||||
screen: The screen to uninstall or the name of a installed screen.
|
||||
|
||||
@@ -1836,6 +1847,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
*widgets: The widget(s) to register.
|
||||
before: A location to mount before.
|
||||
after: A location to mount after.
|
||||
|
||||
Returns:
|
||||
List of modified widgets.
|
||||
"""
|
||||
@@ -2140,7 +2152,7 @@ class App(Generic[ReturnType], DOMNode):
|
||||
or None to use app.
|
||||
|
||||
Returns:
|
||||
True if the event has handled.
|
||||
True if the event has been handled.
|
||||
"""
|
||||
if isinstance(action, str):
|
||||
target, params = actions.parse(action)
|
||||
|
||||
@@ -536,7 +536,7 @@ class Color(NamedTuple):
|
||||
return self.darken(-amount, alpha)
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def get_contrast_text(self, alpha=0.95) -> Color:
|
||||
def get_contrast_text(self, alpha: float = 0.95) -> Color:
|
||||
"""Get a light or dark color that best contrasts this color, for use with text.
|
||||
|
||||
Args:
|
||||
@@ -576,9 +576,8 @@ class Gradient:
|
||||
|
||||
Positions that are between stops will return a blended color.
|
||||
|
||||
|
||||
Args:
|
||||
factor: A number between 0 and 1, where 0 is the first stop, and 1 is the last.
|
||||
position: A number between 0 and 1, where 0 is the first stop, and 1 is the last.
|
||||
|
||||
Returns:
|
||||
A color.
|
||||
|
||||
@@ -54,12 +54,16 @@ if TYPE_CHECKING:
|
||||
from .worker import Worker, WorkType, ResultType
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
# Unused & ignored imports are needed for the docs to link to these objects:
|
||||
from .css.query import NoMatches, TooManyMatches, WrongType # type: ignore # noqa: F401
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
_re_identifier = re.compile(IDENTIFIER)
|
||||
|
||||
|
||||
WalkMethod: TypeAlias = Literal["depth", "breadth"]
|
||||
"""Valid walking methods for the [`DOMNode.walk_children` method][textual.dom.DOMNode.walk_children]."""
|
||||
|
||||
|
||||
class BadIdentifier(Exception):
|
||||
|
||||
@@ -14,4 +14,8 @@ class RenderError(TextualError):
|
||||
|
||||
|
||||
class DuplicateKeyHandlers(TextualError):
|
||||
"""More than one handler for a single key press. E.g. key_ctrl_i and key_tab handlers both found on one object."""
|
||||
"""More than one handler for a single key press.
|
||||
|
||||
For example, if the handlers `key_ctrl_i` and `key_tab` were defined on the same
|
||||
widget, then this error would be raised.
|
||||
"""
|
||||
|
||||
@@ -27,6 +27,7 @@ if TYPE_CHECKING:
|
||||
SpacingDimensions: TypeAlias = Union[
|
||||
int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]
|
||||
]
|
||||
"""The valid ways in which you can specify spacing."""
|
||||
|
||||
T = TypeVar("T", int, float)
|
||||
|
||||
@@ -1043,4 +1044,4 @@ class Spacing(NamedTuple):
|
||||
|
||||
|
||||
NULL_OFFSET: Final = Offset(0, 0)
|
||||
"""An Offset constant for (0, 0)."""
|
||||
"""An [offset][textual.geometry.Offset] constant for (0, 0)."""
|
||||
|
||||
@@ -337,7 +337,7 @@ class MessagePump(metaclass=_MessagePumpMeta):
|
||||
self._timers.add(timer)
|
||||
return timer
|
||||
|
||||
def call_after_refresh(self, callback: Callable, *args, **kwargs) -> None:
|
||||
def call_after_refresh(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
|
||||
"""Schedule a callback to run after all messages are processed and the screen
|
||||
has been refreshed. Positional and keyword arguments are passed to the callable.
|
||||
|
||||
@@ -350,7 +350,7 @@ class MessagePump(metaclass=_MessagePumpMeta):
|
||||
message = messages.InvokeLater(partial(callback, *args, **kwargs))
|
||||
self.post_message(message)
|
||||
|
||||
def call_later(self, callback: Callable, *args, **kwargs) -> None:
|
||||
def call_later(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
|
||||
"""Schedule a callback to run after all messages are processed in this object.
|
||||
Positional and keywords arguments are passed to the callable.
|
||||
|
||||
@@ -362,7 +362,7 @@ class MessagePump(metaclass=_MessagePumpMeta):
|
||||
message = events.Callback(callback=partial(callback, *args, **kwargs))
|
||||
self.post_message(message)
|
||||
|
||||
def call_next(self, callback: Callable, *args, **kwargs) -> None:
|
||||
def call_next(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
|
||||
"""Schedule a callback to run immediately after processing the current message.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -41,6 +41,9 @@ from .widget import Widget
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Final
|
||||
|
||||
# Unused & ignored imports are needed for the docs to link to these objects:
|
||||
from .errors import NoWidget # type: ignore # noqa: F401
|
||||
|
||||
# Screen updates will be batched so that they don't happen more often than 60 times per second:
|
||||
UPDATE_PERIOD: Final[float] = 1 / 60
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
"""
|
||||
Implements the scrollbar-related widgets for internal use.
|
||||
|
||||
You will not need to use the widgets defined in this module.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from math import ceil
|
||||
@@ -18,7 +23,7 @@ from .widget import Widget
|
||||
|
||||
|
||||
class ScrollMessage(Message, bubble=False):
|
||||
pass
|
||||
"""Base class for all scrollbar messages."""
|
||||
|
||||
|
||||
@rich.repr.auto
|
||||
|
||||
@@ -20,6 +20,7 @@ from ._time import sleep
|
||||
from ._types import MessageTarget
|
||||
|
||||
TimerCallback = Union[Callable[[], Awaitable[None]], Callable[[], None]]
|
||||
"""Type of valid callbacks to be used with timers."""
|
||||
|
||||
|
||||
class EventTargetGone(Exception):
|
||||
|
||||
20
src/textual/types.py
Normal file
20
src/textual/types.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
Export some objects that are used by Textual and that help document other features.
|
||||
"""
|
||||
|
||||
from ._animator import Animatable, EasingFunction
|
||||
from ._context import NoActiveAppError
|
||||
from ._types import CallbackType, MessageTarget, WatchCallbackType
|
||||
from .actions import ActionParseResult
|
||||
from .css.styles import RenderStyles
|
||||
|
||||
__all__ = [
|
||||
"ActionParseResult",
|
||||
"Animatable",
|
||||
"CallbackType",
|
||||
"EasingFunction",
|
||||
"MessageTarget",
|
||||
"NoActiveAppError",
|
||||
"RenderStyles",
|
||||
"WatchCallbackType",
|
||||
]
|
||||
@@ -99,6 +99,7 @@ WorkType: TypeAlias = Union[
|
||||
Callable[[], ResultType],
|
||||
Awaitable[ResultType],
|
||||
]
|
||||
"""Type used for [workers](/guide/workers/)."""
|
||||
|
||||
|
||||
class _ReprText:
|
||||
|
||||
Reference in New Issue
Block a user