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:
Rodrigo Girão Serrão
2023-05-02 15:12:53 +01:00
committed by GitHub
parent 911ffdb144
commit 914e50a70f
20 changed files with 108 additions and 103 deletions

View File

@@ -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
View File

@@ -0,0 +1 @@
::: textual.errors

1
docs/api/filter.md Normal file
View File

@@ -0,0 +1 @@
::: textual.filter

1
docs/api/scrollbar.md Normal file
View File

@@ -0,0 +1 @@
::: textual.scrollbar

1
docs/api/types.md Normal file
View File

@@ -0,0 +1 @@
::: textual.types

View File

@@ -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"

View File

@@ -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

View File

@@ -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")

View File

@@ -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."""

View File

@@ -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)

View File

@@ -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.

View File

@@ -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):

View File

@@ -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.
"""

View File

@@ -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)."""

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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",
]

View File

@@ -99,6 +99,7 @@ WorkType: TypeAlias = Union[
Callable[[], ResultType],
Awaitable[ResultType],
]
"""Type used for [workers](/guide/workers/)."""
class _ReprText: