add missing docstrings

This commit is contained in:
Aran-Fey
2025-09-23 21:56:17 +02:00
parent 4d168e2772
commit 8edf327c62
7 changed files with 118 additions and 76 deletions
+9 -3
View File
@@ -15,6 +15,7 @@ from pathlib import Path
import langcodes
import pytz
import revel
import starlette.datastructures
import rio
@@ -23,6 +24,7 @@ from .. import (
assets,
data_models,
language_info,
nice_traceback,
routing,
session,
text_style,
@@ -253,9 +255,13 @@ class AbstractAppServer(abc.ABC):
) -> t.Iterable[text_style.FontFace]:
font_faces = list[text_style.FontFace]()
async for font_face in font._get_faces():
self.weakly_host_asset(font_face.file)
font_faces.append(font_face)
try:
async for font_face in font._get_faces():
self.weakly_host_asset(font_face.file)
font_faces.append(font_face)
except Exception as error:
revel.error(f"Failed to load font faces of {font!r}:")
nice_traceback.print_exception(error)
return font_faces
+14
View File
@@ -97,6 +97,20 @@ AccessibilityRole = t.Literal[
@dataclasses.dataclass
class ComponentResizeEvent:
"""
Holds information regarding a component resize event.
This is a simple dataclass that stores useful information for when the size
of a component changes. You'll typically receive this as argument in
`@rio.event.on_resize` events.
## Attributes
`width`: The new width of the component.
`height`: The new height of the component.
"""
width: float
height: float
+2 -14
View File
@@ -20,7 +20,6 @@ import introspection
import ordered_set
import revel
import starlette.datastructures
import typing_extensions as te
import unicall
import unicall.json_rpc
from identity_containers import IdentityDefaultDict, IdentitySet
@@ -48,7 +47,7 @@ from . import (
)
from .components import dialog_container, fundamental_component, root_components
from .data_models import BuildData, UnittestComponentLayout
from .observables.dataclass import Dataclass
from .observables.dataclass import RioDataclassMeta
from .observables.observable_property import AttributeBinding
from .observables.session_attachments import SessionAttachments
from .observables.session_property import SessionProperty
@@ -69,7 +68,7 @@ class WontSerialize(Exception):
pass
class Session(unicall.Unicall, Dataclass):
class Session(unicall.Unicall, metaclass=RioDataclassMeta):
"""
Represents a single client connection to the app.
@@ -467,17 +466,6 @@ class Session(unicall.Unicall, Dataclass):
# *after* all the other Session initialization (like loading user
# settings) is done.
# This method is inherited from dataclasses but not meant to be public
@te.override
def bind(self, *args, **kwargs) -> t.NoReturn:
"""
## Metadata
`public`: False
"""
raise AttributeError()
async def _refresh_whenever_necessary(self) -> None:
while True:
await self._refresh_required_event.wait()
+34 -1
View File
@@ -97,7 +97,23 @@ class Font(SelfSerializing):
self._css_file: pathlib.Path | rio.URL | str | None = None
@staticmethod
def from_css_file(css_file: pathlib.Path | rio.URL | str) -> Font:
def from_css_file(css_file: pathlib.Path | rio.URL | str, /) -> Font:
"""
Loads a font from a CSS file. Any content other than `@font-face`
declarations is ignored.
The Rio server will download the font files and rehost them. This means
clients can use the font even if they don't have an internet connection.
Note that this method only creates a `Font` object, the CSS is only
loaded and parsed once your application uses the font. If an error
occurs during this process, it is printed to stderr.
## Parameters
`css_file`: The CSS file to load. Can be a path, a URL, or a string
containing the CSS text.
"""
font = Font(b"")
font._faces.clear()
font._css_file = css_file
@@ -105,6 +121,23 @@ class Font(SelfSerializing):
@staticmethod
def from_google_fonts(font_name: str) -> Font:
"""
Loads a font from Google Fonts.
The Rio server will download the font files and rehost them. This means
clients can use the font even if they don't have an internet connection,
since they don't need to access Google Fonts themselves.
Note that this method only creates a `Font` object; the font files are
only downloaded from Google Fonts once your application uses the font.
If an error occurs during this process (for example because the font
name is misspelled), it is printed to stderr.
## Parameters
`font_name`: The name of the font to load. Case-sensitive.
"""
css_url = rio.URL("https://fonts.googleapis.com/css2").with_query(
family=font_name
)
+20 -17
View File
@@ -217,25 +217,28 @@ def _create_class_tests(docs: imy.docstrings.ClassDocs) -> type:
f"Attribute {attr.name!r} has no details"
)
# Create tests for all members of this class
for member in docs.members.values():
if isinstance(member, imy.docstrings.FunctionDocs):
# Ignore the constructor of Enums
if member.name == "__init__" and issubclass(
docs.object, enum.Enum
):
continue
# List, Set, and Dict methods don't need to be documented, since they're
# just clones of well-known classes.
if docs.object not in (rio.List, rio.Set, rio.Dict):
# Create tests for all members of this class
for member in docs.members.values():
if isinstance(member, imy.docstrings.FunctionDocs):
# Ignore the constructor of Enums
if member.name == "__init__" and issubclass(
docs.object, enum.Enum
):
continue
test = _create_function_tests(member)
elif isinstance(member, imy.docstrings.PropertyDocs):
test = _create_property_tests(member)
else:
raise Exception(
f"Don't know how to create tests for a {type(member).__name__} object"
)
test = _create_function_tests(member)
elif isinstance(member, imy.docstrings.PropertyDocs):
test = _create_property_tests(member)
else:
raise Exception(
f"Don't know how to create tests for a {type(member).__name__} object"
)
test.__name__ = f"Test<{member.name}>"
vars()[test.__name__] = test
test.__name__ = f"Test<{member.name}>"
locals()[test.__name__] = test
return ClassTests
+39 -2
View File
@@ -2,7 +2,22 @@ import asyncio
import typing as t
import rio.testing
from rio.components import Component
from rio.debug.layouter import Layouter
class ResizeEventRecorder(rio.Component):
recorded_events: list[rio.ComponentResizeEvent] = []
@rio.event.on_resize
def on_resize(self, resize_event: rio.ComponentResizeEvent) -> None:
self.recorded_events.append(resize_event)
def build(self):
return rio.Rectangle(
fill=rio.Color.BLUE,
min_width=5.0,
min_height=10.0,
)
class ChildMounter(rio.Component):
@@ -40,7 +55,7 @@ class EventCounter(rio.Component):
def _on_unmount(self):
self.unmount_count += 1
def build(self) -> Component:
def build(self) -> rio.Component:
return self.child
@@ -259,3 +274,25 @@ async def test_populate_dead_child():
assert not test_client._last_updated_components, (
"Unmounted component was sent to the frontend"
)
async def test_size_observer_reports_content_dimensions():
async with rio.testing.BrowserClient(ResizeEventRecorder) as client:
resize_event_recorder = client.get_component(ResizeEventRecorder)
rectangle = client.get_component(rio.Rectangle)
layouter = await Layouter.create(client.session)
recorder_layout = layouter.get_layout_is(resize_event_recorder)
rectangle_layout = layouter.get_layout_is(rectangle)
assert (
recorder_layout.allocated_outer_width
== rectangle_layout.allocated_outer_width
)
assert (
recorder_layout.allocated_outer_height
== rectangle_layout.allocated_outer_height
)
event = resize_event_recorder.recorded_events[-1]
assert event.width == recorder_layout.allocated_outer_width
assert event.height == recorder_layout.allocated_outer_height
@@ -1,39 +0,0 @@
import rio.testing
from rio.debug.layouter import Layouter
class ResizeEventRecorder(rio.Component):
recorded_events: list[rio.ComponentResizeEvent] = []
@rio.event.on_resize
def on_resize(self, resize_event: rio.ComponentResizeEvent) -> None:
self.recorded_events.append(resize_event)
def build(self):
return rio.Rectangle(
fill=rio.Color.BLUE,
min_width=5.0,
min_height=10.0,
)
async def test_size_observer_reports_content_dimensions():
async with rio.testing.BrowserClient(ResizeEventRecorder) as client:
resize_event_recorder = client.get_component(ResizeEventRecorder)
rectangle = client.get_component(rio.Rectangle)
layouter = await Layouter.create(client.session)
recorder_layout = layouter.get_layout_is(resize_event_recorder)
rectangle_layout = layouter.get_layout_is(rectangle)
assert (
recorder_layout.allocated_outer_width
== rectangle_layout.allocated_outer_width
)
assert (
recorder_layout.allocated_outer_height
== rectangle_layout.allocated_outer_height
)
event = resize_event_recorder.recorded_events[-1]
assert event.width == recorder_layout.allocated_outer_width
assert event.height == recorder_layout.allocated_outer_height