From 73d0958db918d0db756dfd018bfebbeb1146c6dd Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Thu, 26 Sep 2024 23:03:26 +0200 Subject: [PATCH 01/56] more work on input box styles --- frontend/code/components/textInput.ts | 13 +++---------- frontend/code/inputBox.ts | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/frontend/code/components/textInput.ts b/frontend/code/components/textInput.ts index ff92b9da..edeb487b 100644 --- a/frontend/code/components/textInput.ts +++ b/frontend/code/components/textInput.ts @@ -1,6 +1,6 @@ import { ComponentBase, ComponentState } from "./componentBase"; import { Debouncer } from "../debouncer"; -import { InputBox } from "../inputBox"; +import { InputBox, InputBoxStyle } from "../inputBox"; import { markEventAsHandled } from "../eventHandling"; export type TextInputState = ComponentState & { @@ -8,7 +8,7 @@ export type TextInputState = ComponentState & { text?: string; label?: string; accessibility_label?: string; - style?: "rectangular" | "pill"; + style?: InputBoxStyle; prefix_text?: string; suffix_text?: string; is_secret?: boolean; @@ -159,14 +159,7 @@ export class TextInputComponent extends ComponentBase { deltaState.style = "rectangular"; if (deltaState.style !== undefined) { - this.element.classList.remove( - "rio-input-box-style-rectangle", - "rio-input-box-style-pill" - ); - - this.element.classList.add( - `rio-input-box-style-${this.state.style}` - ); + this.inputBox.style = deltaState.style; } } diff --git a/frontend/code/inputBox.ts b/frontend/code/inputBox.ts index 05118346..222c0001 100644 --- a/frontend/code/inputBox.ts +++ b/frontend/code/inputBox.ts @@ -1,5 +1,6 @@ import { markEventAsHandled, stopPropagation } from "./eventHandling"; -import { createUniqueId } from "./utils"; + +export type InputBoxStyle = "rectangular" | "pill"; /// A text input field providing the following features and more: /// @@ -33,7 +34,10 @@ export class InputBox { connectClickHandlers?: boolean; } = {}) { this.outerElement = document.createElement("div"); - this.outerElement.classList.add("rio-input-box"); + this.outerElement.classList.add( + "rio-input-box", + "rio-input-box-style-rectangular" + ); this.outerElement.innerHTML = `
@@ -229,6 +233,15 @@ export class InputBox { } } + set style(style: InputBoxStyle) { + this.outerElement.classList.remove( + "rio-input-box-style-rectangular", + "rio-input-box-style-pill" + ); + + this.outerElement.classList.add(`rio-input-box-style-${style}`); + } + get isSensitive(): boolean { return !this._inputElement.disabled; } From f5c88021cf40b22dc86d7da727188b52f5dee9d0 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Thu, 26 Sep 2024 23:04:04 +0200 Subject: [PATCH 02/56] added strikethrough to text styles Implements #136 --- frontend/code/cssUtils.ts | 17 +++++++++++++---- frontend/code/dataModels.ts | 1 + frontend/css/style.scss | 8 ++++---- rio/session.py | 15 ++++++++++++--- rio/text_style.py | 8 +++++++- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/frontend/code/cssUtils.ts b/frontend/code/cssUtils.ts index 558e8def..81a0d434 100644 --- a/frontend/code/cssUtils.ts +++ b/frontend/code/cssUtils.ts @@ -101,7 +101,7 @@ export function textStyleToCss( let fontSize: string; let fontWeight: string; let fontStyle: string; - let textDecoration: string; + let textDecorations: string[] = []; let textTransform: string; let color: string; let background: string; @@ -136,7 +136,7 @@ export function textStyleToCss( fontFamily = globalPrefix + "font-name)"; fontSize = globalPrefix + "font-size)"; fontStyle = globalPrefix + "font-italic)"; - textDecoration = globalPrefix + "underlined)"; + textDecorations.push(globalPrefix + "text-decoration)"); textTransform = globalPrefix + "all-caps)"; } @@ -145,7 +145,15 @@ export function textStyleToCss( fontSize = style.fontSize + "em"; fontStyle = style.italic ? "italic" : "normal"; fontWeight = style.fontWeight; - textDecoration = style.underlined ? "underline" : "none"; + + if (style.underlined) { + textDecorations.push("underline"); + } + + if (style.strikethrough) { + textDecorations.push("line-through"); + } + textTransform = style.allCaps ? "uppercase" : "none"; // If no font family is provided, stick to the theme's. @@ -197,7 +205,8 @@ export function textStyleToCss( "font-size": fontSize, "font-weight": fontWeight, "font-style": fontStyle, - "text-decoration": textDecoration, + "text-decoration": + textDecorations.length > 0 ? textDecorations.join(" ") : "none", "text-transform": textTransform, color: color, background: background, diff --git a/frontend/code/dataModels.ts b/frontend/code/dataModels.ts index 20520edc..97526fed 100644 --- a/frontend/code/dataModels.ts +++ b/frontend/code/dataModels.ts @@ -60,6 +60,7 @@ export type TextStyle = { italic: boolean; fontWeight: "normal" | "bold"; underlined: boolean; + strikethrough: boolean; allCaps: boolean; }; diff --git a/frontend/css/style.scss b/frontend/css/style.scss index 26f4653c..4cdcc1f8 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -2002,7 +2002,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; font-size: var(--rio-global-heading1-font-size); font-style: var(--rio-global-heading1-italic); font-weight: var(--rio-global-heading1-font-weight); - text-decoration: var(--rio-global-heading1-underlined); + text-decoration: var(--rio-global-heading1-text-decoration); text-transform: var(--rio-global-heading1-all-caps); &:not(:first-child) { @@ -2020,7 +2020,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; font-size: var(--rio-global-heading2-font-size); font-style: var(--rio-global-heading2-italic); font-weight: var(--rio-global-heading2-font-weight); - text-decoration: var(--rio-global-heading2-underlined); + text-decoration: var(--rio-global-heading2-text-decoration); text-transform: var(--rio-global-heading2-all-caps); margin-top: 0; @@ -2040,7 +2040,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; font-size: var(--rio-global-heading3-font-size); font-style: var(--rio-global-heading3-italic); font-weight: var(--rio-global-heading3-font-weight); - text-decoration: var(--rio-global-heading3-underlined); + text-decoration: var(--rio-global-heading3-text-decoration); text-transform: var(--rio-global-heading3-all-caps); margin-top: 0; @@ -2061,7 +2061,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; line-height: 1.35em; // Purposely uses em font-style: var(--rio-global-text-italic); font-weight: var(--rio-global-text-font-weight); - text-decoration: var(--rio-global-text-underlined); + text-decoration: var(--rio-global-text-text-decoration); text-transform: var(--rio-global-text-all-caps); } diff --git a/rio/session.py b/rio/session.py index dd7ef10e..bc44519a 100644 --- a/rio/session.py +++ b/rio/session.py @@ -2366,13 +2366,22 @@ a.remove(); "italic" if style.italic else "normal" ) variables[f"{css_prefix}-font-weight"] = style.font_weight - variables[f"{css_prefix}-underlined"] = ( - "underline" if style.underlined else "unset" - ) variables[f"{css_prefix}-all-caps"] = ( "uppercase" if style.all_caps else "unset" ) + text_decorations: list[str] = [] + + if style.underlined: + text_decorations.append("underline") + + if style.strikethrough: + text_decorations.append("line-through") + + variables[f"{css_prefix}-text-decoration"] = ( + " ".join(text_decorations) if text_decorations else "none" + ) + # CSS variables for the fill assert ( style.fill is not None diff --git a/rio/text_style.py b/rio/text_style.py index 7445d301..5bb21ee5 100644 --- a/rio/text_style.py +++ b/rio/text_style.py @@ -107,7 +107,9 @@ class TextStyle(SelfSerializing): `font_weight`: Whether the text is normal or **bold**. - `underlined`: Whether the text is u̲n̲d̲e̲r̲l̲i̲n̲e̲d or not. + `underlined`: Whether the text is underlined or not. + + `strikethrough`: Whether the text should have ~~a line through it~~. `all_caps`: Whether the text is transformed to ALL CAPS or not. """ @@ -119,6 +121,7 @@ class TextStyle(SelfSerializing): italic: bool = False font_weight: Literal["normal", "bold"] = "normal" underlined: bool = False + strikethrough: bool = False all_caps: bool = False def replace( @@ -130,6 +133,7 @@ class TextStyle(SelfSerializing): italic: bool | None = None, font_weight: Literal["normal", "bold"] | None = None, underlined: bool | None = None, + strikethrough: bool | None = None, all_caps: bool | None = None, ) -> TextStyle: return type(self)( @@ -141,6 +145,7 @@ class TextStyle(SelfSerializing): if font_weight is None else font_weight, underlined=self.underlined if underlined is None else underlined, + strikethrough=self.strikethrough, all_caps=self.all_caps if all_caps is None else all_caps, ) @@ -154,5 +159,6 @@ class TextStyle(SelfSerializing): "italic": self.italic, "fontWeight": self.font_weight, "underlined": self.underlined, + "strikethrough": self.strikethrough, "allCaps": self.all_caps, } From 4d9b37562503322b1c9b1552c1ca045664f88213 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 27 Sep 2024 09:42:37 +0200 Subject: [PATCH 03/56] added new style to `rio.TextInput` --- changelog.md | 1 + frontend/code/components/textInput.ts | 12 ++++-------- rio/components/text_input.py | 13 +++++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index 3f2da470..17964cc9 100644 --- a/changelog.md +++ b/changelog.md @@ -18,6 +18,7 @@ - Page rework - Add `rio.Redirect` - TODO: Automatic page scan +- New experimental `rio.FilePickerArea` component ## 0.9.2 diff --git a/frontend/code/components/textInput.ts b/frontend/code/components/textInput.ts index edeb487b..d0e76828 100644 --- a/frontend/code/components/textInput.ts +++ b/frontend/code/components/textInput.ts @@ -132,6 +132,10 @@ export class TextInputComponent extends ComponentBase { this.inputBox.accessibilityLabel = deltaState.accessibility_label; } + if (deltaState.style !== undefined) { + this.inputBox.style = deltaState.style; + } + if (deltaState.prefix_text !== undefined) { this.inputBox.prefixText = deltaState.prefix_text; } @@ -153,14 +157,6 @@ export class TextInputComponent extends ComponentBase { if (deltaState.is_valid !== undefined) { this.inputBox.isValid = deltaState.is_valid; } - - // TODO: This isn't exposed to Python yet, so pretend the attribute - // exists by setting it here. - deltaState.style = "rectangular"; - - if (deltaState.style !== undefined) { - this.inputBox.style = deltaState.style; - } } grabKeyboardFocus(): void { diff --git a/rio/components/text_input.py b/rio/components/text_input.py index 2506eda0..77253892 100644 --- a/rio/components/text_input.py +++ b/rio/components/text_input.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Any, final import rio.docs @@ -15,7 +15,7 @@ __all__ = [ ] -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class TextInputChangeEvent: @@ -34,7 +34,7 @@ class TextInputChangeEvent: text: str -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class TextInputConfirmEvent: @@ -53,7 +53,7 @@ class TextInputConfirmEvent: text: str -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class TextInputFocusEvent: @@ -72,7 +72,7 @@ class TextInputFocusEvent: text: str -@final +@t.final class TextInput(KeyboardFocusableFundamentalComponent): """ A user-editable text field. @@ -160,6 +160,7 @@ class TextInput(KeyboardFocusableFundamentalComponent): _: KW_ONLY label: str = "" accessibility_label: str = "" + style: t.Literal["rectangular", "pill"] = "rectangular" prefix_text: str = "" suffix_text: str = "" is_secret: bool = False @@ -172,7 +173,7 @@ class TextInput(KeyboardFocusableFundamentalComponent): on_gain_focus: rio.EventHandler[TextInputFocusEvent] = None on_lose_focus: rio.EventHandler[TextInputFocusEvent] = None - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Listen for messages indicating the user has confirmed their input # # In addition to notifying the backend, these also include the input's From c4249bbe74c936ce5b081b1eda9fc860618e4c6b Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 27 Sep 2024 11:10:39 +0200 Subject: [PATCH 04/56] changed all typing imports to `import typing as t` --- rio/app.py | 25 ++-- rio/app_server/abstract_app_server.py | 1 - rio/app_server/fastapi_server.py | 10 +- rio/app_server/testing_server.py | 2 - rio/assets.py | 8 +- rio/byte_serving.py | 6 +- rio/cli/__init__.py | 10 +- rio/cli/cli_instance.py | 1 - rio/cli/nice_traceback.py | 16 +-- rio/cli/project_setup.py | 20 ++-- rio/cli/rio_api.py | 14 +-- rio/cli/run_project/app_loading.py | 10 +- rio/cli/run_project/arbiter.py | 12 +- rio/cli/run_project/file_watcher_worker.py | 4 +- rio/cli/run_project/run_utils.py | 8 +- rio/cli/run_project/uvicorn_worker.py | 6 +- rio/cli/run_project/webview_worker.py | 6 +- rio/color.py | 38 +++---- rio/component_meta.py | 10 +- rio/components/app_root.py | 4 +- rio/components/auto_form.py | 18 +-- rio/components/banner.py | 6 +- rio/components/button.py | 22 ++-- rio/components/calendar.py | 6 +- rio/components/card.py | 6 +- rio/components/checkbox.py | 6 +- rio/components/class_container.py | 6 +- rio/components/code_block.py | 4 +- rio/components/code_explorer.py | 6 +- rio/components/color_picker.py | 8 +- rio/components/component.py | 35 +++--- rio/components/container.py | 4 +- rio/components/date_input.py | 4 +- rio/components/default_root_component.py | 6 +- rio/components/devel_component.py | 11 +- rio/components/dialog_container.py | 8 +- rio/components/drawer.py | 8 +- rio/components/dropdown.py | 25 ++-- rio/components/file_picker_area.py | 6 +- rio/components/flow_container.py | 14 ++- rio/components/fundamental_component.py | 6 +- rio/components/grid.py | 15 ++- rio/components/html.py | 4 +- rio/components/icon.py | 12 +- rio/components/icon_button.py | 20 ++-- rio/components/image.py | 12 +- rio/components/key_event_listener.py | 20 ++-- rio/components/labeled_column.py | 15 ++- rio/components/linear_containers.py | 21 ++-- rio/components/link.py | 8 +- rio/components/list_items.py | 20 ++-- rio/components/list_view.py | 8 +- rio/components/markdown.py | 10 +- rio/components/media_player.py | 8 +- rio/components/mouse_event_listener.py | 26 ++--- rio/components/multi_line_text_input.py | 10 +- rio/components/node_input.py | 8 +- rio/components/node_output.py | 8 +- rio/components/number_input.py | 13 +-- rio/components/overlay.py | 6 +- rio/components/page_view.py | 7 +- rio/components/plot.py | 14 +-- rio/components/popup.py | 8 +- rio/components/progress_bar.py | 8 +- rio/components/progress_circle.py | 8 +- rio/components/rectangle.py | 4 +- rio/components/revealer.py | 10 +- rio/components/root_components.py | 7 +- rio/components/scroll_container.py | 8 +- rio/components/scroll_target.py | 4 +- rio/components/separator.py | 4 +- rio/components/slider.py | 10 +- rio/components/slideshow.py | 8 +- rio/components/spacer.py | 6 +- rio/components/stack.py | 8 +- rio/components/switch.py | 6 +- rio/components/switcher.py | 4 +- rio/components/switcher_bar.py | 35 +++--- rio/components/table.py | 41 +++---- rio/components/text.py | 12 +- rio/components/theme_context_switcher.py | 4 +- rio/components/tooltip.py | 14 +-- rio/components/website.py | 4 +- rio/data_models.py | 6 +- rio/dataclass.py | 25 ++-- rio/debug/dev_tools/component_attributes.py | 8 +- rio/debug/dev_tools/dev_tools_sidebar.py | 4 +- rio/debug/dev_tools/icons_page.py | 10 +- rio/debug/dev_tools/layout_display.py | 6 +- rio/debug/dev_tools/layout_explainer.py | 16 +-- rio/debug/dev_tools/layout_subpage.py | 6 +- rio/debug/dev_tools/rio_developer_page.py | 4 +- rio/debug/dev_tools/sample_icons_grid.py | 4 +- rio/debug/dev_tools/theme_picker_page.py | 8 +- rio/debug/dev_tools/tree_page.py | 4 +- rio/debug/layouter.py | 31 +++-- rio/debug/monkeypatches.py | 4 +- rio/debug/typing_utils.py | 1 - rio/debug/validator.py | 14 +-- rio/deprecations.py | 30 ++--- rio/docs.py | 24 ++-- rio/event.py | 22 ++-- rio/fills.py | 4 +- rio/global_state.py | 2 - rio/icon_registry.py | 10 +- rio/inspection.py | 15 ++- rio/maybes.py | 8 +- rio/path_match.py | 4 +- rio/project_config.py | 26 ++--- rio/routing.py | 17 ++- rio/serialization.py | 26 +++-- rio/session.py | 107 +++++++++--------- rio/session_attachments.py | 11 +- rio/snippets/__init__.py | 23 ++-- .../howto-get-value-from-child-component.md | 6 +- .../components/chat_message.py | 2 - .../components/chat_suggestion_card.py | 2 - .../components/empty_chat_placeholder.py | 2 - .../generating_response_placeholder.py | 2 - .../conversation.py | 8 +- .../pages/chat_page.py | 1 - .../components/footer.py | 2 - .../components/navbar.py | 1 - .../components/news_article.py | 2 - .../components/root_component.py | 2 - .../components/testimonial.py | 2 - .../pages/app_page.py | 2 - .../pages/app_page/about_page.py | 2 - .../pages/app_page/home_page.py | 2 - .../pages/app_page/news_page.py | 2 - .../pages/login_page.py | 2 - .../root_init.py | 1 - .../components/balance_card.py | 2 - .../components/crypto_card.py | 2 - .../components/crypto_chart.py | 2 - .../data_models.py | 4 +- .../pages/dashboard_page.py | 1 - .../components/footer.py | 2 - .../components/navbar.py | 2 - .../components/news_article.py | 2 - .../components/root_component.py | 2 - .../components/testimonial.py | 2 - .../pages/about_page.py | 2 - .../pages/home_page.py | 2 - .../pages/news_page.py | 2 - .../data_models.py | 1 - .../pages/crud_page.py | 4 +- .../components/field.py | 4 +- .../pages/tic_tac_toe_page.py | 8 +- .../components/new_todo_item_input.py | 1 - .../components/todo_item_component.py | 2 - .../pages/todo_list_page.py | 1 - .../pages/tic_tac_toe_page.py | 4 +- .../components/field.py | 4 +- .../pages/tic_tac_toe_page.py | 4 +- .../components/field.py | 4 +- .../pages/tic_tac_toe_page.py | 6 +- .../components/field.py | 4 +- .../pages/tic_tac_toe_page.py | 8 +- rio/state_properties.py | 7 +- rio/testing.py | 28 ++--- rio/text_style.py | 10 +- rio/theme.py | 14 +-- rio/tools/create_code_for_icon_set.py | 4 +- rio/transports/message_recorder_transport.py | 4 +- rio/user_settings_module.py | 10 +- rio/utils.py | 30 ++--- scripts/benchmark.py | 6 +- scripts/build_material_icon_set.py | 1 - tests/test_attribute_bindings.py | 6 +- tests/test_docstring_code_blocks.py | 8 +- tests/test_documentation.py | 6 +- tests/test_layouting.py | 10 +- tests/test_project_templates.py | 4 +- tests/test_table_indexing.py | 10 +- tests/utils/layouting.py | 6 +- 176 files changed, 800 insertions(+), 873 deletions(-) diff --git a/rio/app.py b/rio/app.py index 588e6b1b..9401a439 100644 --- a/rio/app.py +++ b/rio/app.py @@ -3,11 +3,10 @@ from __future__ import annotations import os import sys import threading +import typing as t import webbrowser -from collections.abc import Callable, Iterable from datetime import timedelta from pathlib import Path -from typing import * # type: ignore import fastapi import uvicorn @@ -60,7 +59,7 @@ def make_default_connection_lost_component() -> rio.Component: return DefaultConnectionLostComponent() -@final +@t.final class App: """ Contains all the information needed to run a Rio app. @@ -137,11 +136,11 @@ class App: def __init__( self, *, - build: Callable[[], rio.Component] | None = None, + build: t.Callable[[], rio.Component] | None = None, name: str | None = None, description: str | None = None, icon: ImageLike | None = None, - pages: Iterable[rio.ComponentPage | rio.Redirect] + pages: t.Iterable[rio.ComponentPage | rio.Redirect] | os.PathLike | str | None = None, @@ -149,11 +148,11 @@ class App: on_app_close: rio.EventHandler[App] = None, on_session_start: rio.EventHandler[rio.Session] = None, on_session_close: rio.EventHandler[rio.Session] = None, - default_attachments: Iterable[Any] = (), + default_attachments: t.Iterable[t.Any] = (), ping_pong_interval: int | float | timedelta = timedelta(seconds=50), assets_dir: str | os.PathLike = "assets", theme: rio.Theme | tuple[rio.Theme, rio.Theme] | None = None, - build_connection_lost_message: Callable[ + build_connection_lost_message: t.Callable[ [], rio.Component ] = make_default_connection_lost_component, meta_tags: dict[str, str] = {}, @@ -297,7 +296,7 @@ class App: self._on_app_close = on_app_close self._on_session_start = on_session_start self._on_session_close = on_session_close - self.default_attachments: MutableSequence[Any] = list( + self.default_attachments: t.MutableSequence[t.Any] = list( default_attachments ) self._theme = theme @@ -320,7 +319,7 @@ class App: self.assets_dir = self._module_path / self._assets_dir def _load_pages(self) -> None: - pages: Iterable[rio.ComponentPage | rio.Redirect] + pages: t.Iterable[rio.ComponentPage | rio.Redirect] if self._raw_pages is None: pages = routing.auto_detect_pages( @@ -339,7 +338,7 @@ class App: *, debug_mode: bool, running_in_window: bool, - internal_on_app_start: Callable[[], Any] | None, + internal_on_app_start: t.Callable[[], t.Any] | None, base_url: rio.URL | str | None, ) -> fastapi.FastAPI: """ @@ -418,8 +417,8 @@ class App: port: int, quiet: bool, running_in_window: bool, - internal_on_app_start: Callable[[], None] | None = None, - internal_on_server_created: Callable[[uvicorn.Server], None] + internal_on_app_start: t.Callable[[], None] | None = None, + internal_on_server_created: t.Callable[[uvicorn.Server], None] | None = None, base_url: rio.URL | str | None = None, ) -> None: @@ -693,7 +692,7 @@ pixels_per_rem ) finally: - server = cast( + server = t.cast( uvicorn.Server, server ) # Prevents "unreachable code" warning assert isinstance(server, uvicorn.Server) diff --git a/rio/app_server/abstract_app_server.py b/rio/app_server/abstract_app_server.py index f09862d3..135ed862 100644 --- a/rio/app_server/abstract_app_server.py +++ b/rio/app_server/abstract_app_server.py @@ -11,7 +11,6 @@ import warnings import weakref from datetime import date from pathlib import Path -from typing import * import langcodes import pytz diff --git a/rio/app_server/fastapi_server.py b/rio/app_server/fastapi_server.py index 04b22d67..25958fcb 100644 --- a/rio/app_server/fastapi_server.py +++ b/rio/app_server/fastapi_server.py @@ -8,10 +8,10 @@ import io import json import logging import secrets +import typing as t import weakref from datetime import timedelta from pathlib import Path -from typing import * # type: ignore from xml.etree import ElementTree as ET import crawlerdetect @@ -48,7 +48,7 @@ __all__ = [ ] -P = ParamSpec("P") +P = t.ParamSpec("P") # Used to identify search engine crawlers (like googlebot) and serve them @@ -110,8 +110,8 @@ def read_frontend_template(template_name: str) -> str: def add_cache_headers( - func: Callable[P, Awaitable[fastapi.Response]], -) -> Callable[P, Coroutine[None, None, fastapi.Response]]: + func: t.Callable[P, t.Awaitable[fastapi.Response]], +) -> t.Callable[P, Coroutine[None, None, fastapi.Response]]: """ Decorator for routes that serve static files. Ensures that the response has the `Cache-Control` header set appropriately. @@ -181,7 +181,7 @@ class FastapiServer(fastapi.FastAPI, AbstractAppServer): app_: app.App, debug_mode: bool, running_in_window: bool, - internal_on_app_start: Callable[[], None] | None, + internal_on_app_start: t.Callable[[], None] | None, base_url: rio.URL | None, ) -> None: super().__init__( diff --git a/rio/app_server/testing_server.py b/rio/app_server/testing_server.py index a5f2b423..5d6a40bc 100644 --- a/rio/app_server/testing_server.py +++ b/rio/app_server/testing_server.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * - import rio from .. import assets, utils diff --git a/rio/assets.py b/rio/assets.py index 4c279ab0..0c90ce78 100644 --- a/rio/assets.py +++ b/rio/assets.py @@ -4,8 +4,8 @@ import abc import hashlib import io import os +import typing as t from pathlib import Path -from typing import * # type: ignore import httpx from PIL.Image import Image @@ -65,15 +65,15 @@ class Asset(SelfSerializing): # The MIME type of the asset self.media_type = media_type - @overload + @t.overload @classmethod def new(cls, data: bytes, media_type: str | None = None) -> BytesAsset: ... - @overload + @t.overload @classmethod def new(cls, data: Path, media_type: str | None = None) -> PathAsset: ... - @overload + @t.overload @classmethod def new(cls, data: URL, media_type: str | None = None) -> UrlAsset: ... diff --git a/rio/byte_serving.py b/rio/byte_serving.py index ebf12268..85840f9c 100644 --- a/rio/byte_serving.py +++ b/rio/byte_serving.py @@ -6,8 +6,8 @@ https://github.com/tiangolo/fastapi/issues/1240#issuecomment-1055396884 """ import mimetypes +import typing as t from pathlib import Path -from typing import * # type: ignore import fastapi from fastapi import HTTPException @@ -18,11 +18,11 @@ __all__ = [ def send_bytes_range_requests( - file_obj: BinaryIO, + file_obj: t.BinaryIO, start: int, end: int, chunk_size: int = 16 * 1024 * 1024, -) -> Iterator[bytes]: +) -> t.Iterator[bytes]: """ Send a file in chunks using Range Requests specification RFC7233. `start` and `end` are inclusive as per the spec. diff --git a/rio/cli/__init__.py b/rio/cli/__init__.py index da308035..bad6c155 100644 --- a/rio/cli/__init__.py +++ b/rio/cli/__init__.py @@ -5,8 +5,8 @@ from .. import project_config _logger = logging.getLogger(__name__) +import typing as t from pathlib import Path -from typing import Literal import introspection import revel @@ -67,7 +67,7 @@ def new( nicename: str, *, # Website is listed first to make it the default - type: Literal["website", "app"], + type: t.Literal["website", "app"], template: rio.snippets.AvailableTemplatesLiteral, ) -> None: project_setup.create_project( @@ -213,7 +213,7 @@ containing some template code will be created in the `pages` or `components` folder of your project. """, ) -def add(what: Literal["page", "component"], /, name: str) -> None: +def add(what: t.Literal["page", "component"], /, name: str) -> None: with project_config.RioProjectConfig.try_locate_and_load() as proj: module_path = proj.app_main_module_path if not module_path.is_dir(): @@ -241,7 +241,7 @@ def add(what: Literal["page", "component"], /, name: str) -> None: file_path.write_text( f"""from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -269,7 +269,7 @@ culpa qui officia deserunt mollit anim id est laborum. f"""from __future__ import annotations from dataclasses import KW_ONLY, field -from typing import * # type: ignore +import typing as t import rio diff --git a/rio/cli/cli_instance.py b/rio/cli/cli_instance.py index bea73f32..095b5961 100644 --- a/rio/cli/cli_instance.py +++ b/rio/cli/cli_instance.py @@ -1,7 +1,6 @@ from __future__ import annotations from pathlib import Path -from typing import * # type: ignore import keyring import platformdirs diff --git a/rio/cli/nice_traceback.py b/rio/cli/nice_traceback.py index 64c5641f..9a810349 100644 --- a/rio/cli/nice_traceback.py +++ b/rio/cli/nice_traceback.py @@ -8,18 +8,18 @@ import io import linecache import sys import traceback +import typing as t from pathlib import Path -from typing import * # type: ignore import revel def _format_single_exception_raw( - out: IO[str], + out: t.IO[str], err: BaseException, *, include_header: bool, - escape: Callable[[str], str], + escape: t.Callable[[str], str], bold: str, nobold: str, dim: str, @@ -29,7 +29,7 @@ def _format_single_exception_raw( red: str, nored: str, relpath: Path | None = None, - frame_filter: Callable[[traceback.FrameSummary], bool], + frame_filter: t.Callable[[traceback.FrameSummary], bool], ) -> None: # Get the traceback as a list of frames tb_list = traceback.extract_tb(err.__traceback__) @@ -127,7 +127,7 @@ def _format_single_exception_raw( def format_exception_raw( err: BaseException, *, - escape: Callable[[str], str], + escape: t.Callable[[str], str], bold: str, nobold: str, dim: str, @@ -137,7 +137,7 @@ def format_exception_raw( red: str, nored: str, relpath: Path | None = None, - frame_filter: Callable[[traceback.FrameSummary], bool] = lambda _: True, + frame_filter: t.Callable[[traceback.FrameSummary], bool] = lambda _: True, ) -> str: def format_inner(err: BaseException) -> None: # Chain to the cause or context if there is one @@ -189,7 +189,7 @@ def format_exception_revel( err: BaseException, *, relpath: Path | None = None, - frame_filter: Callable[[traceback.FrameSummary], bool] = lambda _: True, + frame_filter: t.Callable[[traceback.FrameSummary], bool] = lambda _: True, ) -> str: return format_exception_raw( err, @@ -211,7 +211,7 @@ def format_exception_html( err: BaseException, *, relpath: Path | None = None, - frame_filter: Callable[[traceback.FrameSummary], bool] = lambda _: True, + frame_filter: t.Callable[[traceback.FrameSummary], bool] = lambda _: True, ) -> str: result = format_exception_raw( err, diff --git a/rio/cli/project_setup.py b/rio/cli/project_setup.py index 55cf021e..7be4d048 100644 --- a/rio/cli/project_setup.py +++ b/rio/cli/project_setup.py @@ -2,8 +2,8 @@ import io import re import shutil import string +import typing as t from pathlib import Path -from typing import * # type: ignore import introspection import isort @@ -32,7 +32,9 @@ def class_name_from_snippet(snip: rio.snippets.Snippet) -> str: return "".join(part.capitalize() for part in parts) -def write_init_file(fil: IO, snippets: Iterable[rio.snippets.Snippet]) -> None: +def write_init_file( + fil: t.IO, snippets: t.Iterable[rio.snippets.Snippet] +) -> None: """ Write an `__init__.py` file that imports all of the snippets. @@ -51,10 +53,10 @@ def write_init_file(fil: IO, snippets: Iterable[rio.snippets.Snippet]) -> None: def generate_root_init( - out: TextIO, + out: t.TextIO, *, raw_name: str, - project_type: Literal["app", "website"], + project_type: t.Literal["app", "website"], template: rio.snippets.ProjectTemplate, ) -> None: """ @@ -77,7 +79,7 @@ def generate_root_init( from __future__ import annotations from pathlib import Path -from typing import * # type: ignore +import typing as t import rio @@ -218,7 +220,7 @@ def derive_module_name(raw_name: str) -> str: def generate_readme( - out: TextIO, + out: t.TextIO, raw_name: str, template: rio.snippets.ProjectTemplate, ) -> None: @@ -247,7 +249,7 @@ This project is based on the `{template.name}` template. def write_component_file( - out: TextIO, + out: t.TextIO, snip: rio.snippets.Snippet, import_depth: int, ) -> None: @@ -263,7 +265,7 @@ def write_component_file( f"""from __future__ import annotations from dataclasses import KW_ONLY, field -from typing import * # type: ignore +import typing as t import rio @@ -314,7 +316,7 @@ def generate_dependencies_file( def create_project( *, raw_name: str, - type: Literal["app", "website"], + type: t.Literal["app", "website"], template_name: rio.snippets.AvailableTemplatesLiteral, target_parent_directory: Path, ) -> None: diff --git a/rio/cli/rio_api.py b/rio/cli/rio_api.py index ceb7da58..70ae7e87 100644 --- a/rio/cli/rio_api.py +++ b/rio/cli/rio_api.py @@ -1,5 +1,5 @@ +import typing as t from datetime import timedelta -from typing import * # type: ignore import httpx @@ -46,7 +46,7 @@ class RioApi: async def __aenter__(self) -> "RioApi": return self - async def __aexit__(self, *args: Any) -> None: + async def __aexit__(self, *args: t.Any) -> None: await self.close() async def close(self) -> None: @@ -65,10 +65,10 @@ class RioApi: self, endpoint: str, *, - method: Literal["get", "post", "delete"] = "get", - json: dict[str, Any] | None = None, + method: t.Literal["get", "post", "delete"] = "get", + json: dict[str, t.Any] | None = None, file: BinaryIO | None = None, - ) -> Any: + ) -> t.Any: """ Make a request to the Rio API. """ @@ -137,7 +137,7 @@ class RioApi: await self.request("/auth/expireToken", method="post") - async def get_user(self) -> dict[str, Any]: + async def get_user(self) -> dict[str, t.Any]: """ Return the user's information, if logged in. """ @@ -149,7 +149,7 @@ class RioApi: *, name: str, packed_app: BinaryIO, - realm: Literal["pro", "free", "test"], + realm: t.Literal["pro", "free", "test"], start: bool, ) -> None: assert self.is_logged_in, "Must be logged in to create/update an app" diff --git a/rio/cli/run_project/app_loading.py b/rio/cli/run_project/app_loading.py index 9f96c523..e9e82a39 100644 --- a/rio/cli/run_project/app_loading.py +++ b/rio/cli/run_project/app_loading.py @@ -5,8 +5,8 @@ import os import sys import traceback import types +import typing as t from pathlib import Path -from typing import * # type: ignore import revel @@ -32,7 +32,7 @@ def traceback_frame_filter(frame: traceback.FrameSummary) -> bool: def make_traceback_html( *, - err: Union[str, BaseException], + err: t.Union[str, BaseException], project_directory: Path, ) -> str: error_icon_svg = icon_registry.get_icon_svg("material/error") @@ -70,7 +70,7 @@ def make_traceback_html( def make_error_message_component( - err: Union[str, BaseException], + err: t.Union[str, BaseException], project_directory: Path, ) -> rio.Component: html = make_traceback_html( @@ -86,9 +86,9 @@ def make_error_message_component( def make_error_message_app( - err: Union[str, BaseException], + err: t.Union[str, BaseException], project_directory: Path, - theme: rio.Theme | Tuple[rio.Theme, rio.Theme], + theme: rio.Theme | tuple[rio.Theme, rio.Theme], ) -> rio.App: """ Creates an app that displays the given error message. diff --git a/rio/cli/run_project/arbiter.py b/rio/cli/run_project/arbiter.py index a533269a..05f5888b 100644 --- a/rio/cli/run_project/arbiter.py +++ b/rio/cli/run_project/arbiter.py @@ -6,9 +6,9 @@ import socket import sys import threading import time +import typing as t from datetime import datetime, timedelta, timezone from pathlib import Path -from typing import * # type: ignore import httpx import revel @@ -32,7 +32,7 @@ from . import ( try: import webview # type: ignore except ImportError: - if TYPE_CHECKING: + if t.TYPE_CHECKING: import webview # type: ignore else: webview = None @@ -100,7 +100,7 @@ class Arbiter: # The app to use for creating apps. This keeps the theme consistent if # for-example the user's app crashes and then a mock-app is injected. - self._app_theme: Union[rio.Theme, tuple[rio.Theme, rio.Theme]] = ( + self._app_theme: t.Union[rio.Theme, tuple[rio.Theme, rio.Theme]] = ( rio.Theme.pair_from_colors() ) @@ -166,7 +166,7 @@ class Arbiter: return f"http://{local_ip}:{self.port}" @property - def running_tasks(self) -> Iterator[asyncio.Task[None]]: + def running_tasks(self) -> t.Iterator[asyncio.Task[None]]: for task in ( self._uvicorn_task, self._file_watcher_task, @@ -302,7 +302,7 @@ class Arbiter: except app_loading.AppLoadError as err: if err.__cause__ is not None: - err = cast(Exception, err.__cause__) + err = t.cast(Exception, err.__cause__) # Announce the problem in the terminal rio.cli._logger.critical(f"The app could not be loaded: {err}") @@ -668,7 +668,7 @@ class Arbiter: else: raise NotImplementedError(f'Unknown event "{event}"') - def _spawn_traceback_popups(self, err: Union[str, BaseException]) -> None: + def _spawn_traceback_popups(self, err: t.Union[str, BaseException]) -> None: """ Displays a popup with the traceback in the rio UI. """ diff --git a/rio/cli/run_project/file_watcher_worker.py b/rio/cli/run_project/file_watcher_worker.py index c8612c7f..c43c5e70 100644 --- a/rio/cli/run_project/file_watcher_worker.py +++ b/rio/cli/run_project/file_watcher_worker.py @@ -1,6 +1,6 @@ import time +import typing as t from pathlib import Path -from typing import * # type: ignore import watchfiles @@ -12,7 +12,7 @@ class FileWatcherWorker: def __init__( self, *, - push_event: Callable[[run_models.Event], None], + push_event: t.Callable[[run_models.Event], None], proj: project_config.RioProjectConfig, ) -> None: self.push_event = push_event diff --git a/rio/cli/run_project/run_utils.py b/rio/cli/run_project/run_utils.py index f98c0f45..4a34f5fe 100644 --- a/rio/cli/run_project/run_utils.py +++ b/rio/cli/run_project/run_utils.py @@ -1,12 +1,12 @@ import threading -from typing import * # type: ignore +import typing as t -T = TypeVar("T") +T = t.TypeVar("T") -class ThreadsafeFuture(Generic[T]): +class ThreadsafeFuture(t.Generic[T]): def __init__(self) -> None: - self._result_value: Any + self._result_value: t.Any self._event = threading.Event() def set_result(self, result: T) -> None: diff --git a/rio/cli/run_project/uvicorn_worker.py b/rio/cli/run_project/uvicorn_worker.py index ff2a15f4..3b5a683d 100644 --- a/rio/cli/run_project/uvicorn_worker.py +++ b/rio/cli/run_project/uvicorn_worker.py @@ -1,6 +1,6 @@ import asyncio import socket -from typing import * # type: ignore +import typing as t import revel import uvicorn @@ -18,7 +18,7 @@ class UvicornWorker: def __init__( self, *, - push_event: Callable[[run_models.Event], None], + push_event: t.Callable[[run_models.Event], None], app: rio.App, socket: socket.socket, quiet: bool, @@ -84,7 +84,7 @@ class UvicornWorker: # output in the console. This monkeypatch suppresses that. original_receive = uvicorn.lifespan.on.LifespanOn.receive - async def patched_receive(self) -> Any: + async def patched_receive(self) -> t.Any: try: return await original_receive(self) except asyncio.CancelledError: diff --git a/rio/cli/run_project/webview_worker.py b/rio/cli/run_project/webview_worker.py index 32fd063f..f506f8f4 100644 --- a/rio/cli/run_project/webview_worker.py +++ b/rio/cli/run_project/webview_worker.py @@ -1,13 +1,13 @@ import threading import time -from typing import * # type: ignore +import typing as t from . import run_models try: import webview # type: ignore except ImportError: - if TYPE_CHECKING: + if t.TYPE_CHECKING: import webview # type: ignore else: webview = None @@ -17,7 +17,7 @@ class WebViewWorker: def __init__( self, *, - push_event: Callable[[run_models.Event], None], + push_event: t.Callable[[run_models.Event], None], debug_mode: bool, url: str, ) -> None: diff --git a/rio/color.py b/rio/color.py index 118658a6..16313076 100644 --- a/rio/color.py +++ b/rio/color.py @@ -2,7 +2,7 @@ from __future__ import annotations import colorsys import math -from typing import * # type: ignore +import typing as t from typing_extensions import TypeAlias from uniserde import Jsonable @@ -17,7 +17,7 @@ __all__ = [ ] -@final +@t.final class Color(SelfSerializing): """ A color, optionally with an opacity. @@ -478,7 +478,7 @@ class Color(SelfSerializing): opacity=self.opacity if opacity is None else opacity, ) - def _map_rgb(self, func: Callable[[float], float]) -> "Color": + def _map_rgb(self, func: t.Callable[[float], float]) -> "Color": """ Apply a function to each of the RGB values of this color, and return a new `Color` instance with the result. The opacity value is copied @@ -631,28 +631,28 @@ class Color(SelfSerializing): return hash(self.rgba) # Greys - BLACK: ClassVar["Color"] - GREY: ClassVar["Color"] - WHITE: ClassVar["Color"] + BLACK: t.ClassVar["Color"] + GREY: t.ClassVar["Color"] + WHITE: t.ClassVar["Color"] # Pure colors - RED: ClassVar["Color"] - GREEN: ClassVar["Color"] - BLUE: ClassVar["Color"] + RED: t.ClassVar["Color"] + GREEN: t.ClassVar["Color"] + BLUE: t.ClassVar["Color"] # CMY - CYAN: ClassVar["Color"] - MAGENTA: ClassVar["Color"] - YELLOW: ClassVar["Color"] + CYAN: t.ClassVar["Color"] + MAGENTA: t.ClassVar["Color"] + YELLOW: t.ClassVar["Color"] # Others - PINK: ClassVar["Color"] - PURPLE: ClassVar["Color"] - ORANGE: ClassVar["Color"] - BROWN: ClassVar["Color"] + PINK: t.ClassVar["Color"] + PURPLE: t.ClassVar["Color"] + ORANGE: t.ClassVar["Color"] + BROWN: t.ClassVar["Color"] # Special - TRANSPARENT: ClassVar["Color"] + TRANSPARENT: t.ClassVar["Color"] Color.BLACK = Color.from_rgb(0.0, 0.0, 0.0) @@ -677,7 +677,7 @@ Color.TRANSPARENT = Color.from_rgb(0.0, 0.0, 0.0, 0.0) # Like color, but also allows referencing theme colors ColorSet: TypeAlias = ( Color - | Literal[ + | t.Literal[ "background", "neutral", "hud", @@ -693,4 +693,4 @@ ColorSet: TypeAlias = ( # Cache so the session can quickly determine whether a type annotation is # `ColorSet` -_color_set_args = set(get_args(ColorSet)) +_color_set_args = set(t.get_args(ColorSet)) diff --git a/rio/component_meta.py b/rio/component_meta.py index bffff9c2..18acb786 100644 --- a/rio/component_meta.py +++ b/rio/component_meta.py @@ -2,11 +2,11 @@ from __future__ import annotations import asyncio import sys +import typing as t import warnings import weakref from collections import defaultdict from dataclasses import field -from typing import * import introspection from typing_extensions import dataclass_transform @@ -22,7 +22,7 @@ from .warnings import RioPotentialMistakeWarning __all__ = ["ComponentMeta"] -C = TypeVar("C", bound="rio.Component") +C = t.TypeVar("C", bound="rio.Component") # For some reason vscode doesn't understand that this class is a @@ -41,7 +41,7 @@ class ComponentMeta(RioDataclassMeta): # The assigned value is needed so that the `Component` class itself has a # valid value. All subclasses override this value in `__init_subclass__`. _rio_event_handlers_: defaultdict[ - event.EventTag, list[tuple[Callable, Any]] + event.EventTag, list[tuple[t.Callable, t.Any]] ] # Whether this component class is built into Rio, rather than user defined, @@ -238,7 +238,7 @@ class ComponentMeta(RioDataclassMeta): async def _periodic_event_worker( weak_component: weakref.ReferenceType[rio.Component], - handler: Callable, + handler: t.Callable, period: float, ) -> None: # Get a handle on the session @@ -264,7 +264,7 @@ async def _periodic_event_worker( async def call_component_handler_once( weak_component: weakref.ReferenceType[rio.Component], - handler: Callable, + handler: t.Callable, ) -> bool: # Does the component still exist? component = weak_component() diff --git a/rio/components/app_root.py b/rio/components/app_root.py index 3b4adc1e..de81e10a 100644 --- a/rio/components/app_root.py +++ b/rio/components/app_root.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore import rio @@ -116,7 +116,7 @@ class Sidebar(component.Component): class AppRoot(component.Component): _: KW_ONLY - fallback_build: Callable[[], rio.Component] | None = None + fallback_build: t.Callable[[], rio.Component] | None = None _sidebar_is_open: bool = False diff --git a/rio/components/auto_form.py b/rio/components/auto_form.py index b6e85e1b..7fc03e04 100644 --- a/rio/components/auto_form.py +++ b/rio/components/auto_form.py @@ -2,8 +2,8 @@ from __future__ import annotations import dataclasses import enum +import typing as t from dataclasses import KW_ONLY, dataclass, is_dataclass -from typing import * # type: ignore import rio @@ -20,7 +20,7 @@ def prettify_name(name: str) -> str: @dataclass class AutoFormChangeEvent: field_name: str - value: Any + value: t.Any class AutoForm(component.Component): @@ -32,7 +32,7 @@ class AutoForm(component.Component): `public`: False """ - value: Any + value: t.Any _: KW_ONLY on_change: rio.EventHandler[[AutoFormChangeEvent]] = None @@ -43,7 +43,7 @@ class AutoForm(component.Component): f"The value to `AutoForm` must be a dataclass, not `{type(self.value)}`" ) - async def _update_value(self, field_name: str, value: Any) -> None: + async def _update_value(self, field_name: str, value: t.Any) -> None: # Update the value setattr(self, field_name, value) @@ -62,8 +62,8 @@ class AutoForm(component.Component): field_type: type, ) -> rio.Component: # Get sensible type information - origin = get_origin(field_type) - field_args = get_args(field_type) + origin = t.get_origin(field_type) + field_args = t.get_args(field_type) field_type = field_type if origin is None else origin del origin @@ -100,11 +100,11 @@ class AutoForm(component.Component): ) # `Literal` or `Enum` -> `Dropdown` - if field_type is Literal or issubclass(field_type, enum.Enum): - if field_type is Literal: + if field_type is t.Literal or issubclass(field_type, enum.Enum): + if field_type is t.Literal: mapping = {str(a): a for a in field_args} else: - field_type = cast(Type[enum.Enum], field_type) + field_type = t.cast(t.Type[enum.Enum], field_type) mapping = {prettify_name(f.name): f.value for f in field_type} return rio.Dropdown( diff --git a/rio/components/banner.py b/rio/components/banner.py index 68652bb2..a9725e7b 100644 --- a/rio/components/banner.py +++ b/rio/components/banner.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore import rio @@ -19,7 +19,7 @@ ICONS_AND_COLORS: dict[str, tuple[str, rio.ColorSet]] = { } -@final +@t.final class Banner(component.Component): r""" Displays a short message to the user. @@ -84,7 +84,7 @@ class Banner(component.Component): """ text: str | None - style: Literal["info", "success", "warning", "danger"] + style: t.Literal["info", "success", "warning", "danger"] _: KW_ONLY markdown: bool = False diff --git a/rio/components/button.py b/rio/components/button.py index f3350633..67aa9bb1 100644 --- a/rio/components/button.py +++ b/rio/components/button.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore from uniserde import JsonDoc @@ -19,7 +19,7 @@ CHILD_MARGIN_X = 1.0 CHILD_MARGIN_Y = 0.3 -@final +@t.final class Button(Component): """ A clickable button. @@ -112,10 +112,10 @@ class Button(Component): content: str | rio.Component = "" _: KW_ONLY icon: str | None = None - shape: Literal["pill", "rounded", "rectangle"] = "pill" - style: Literal["major", "minor", "colored-text", "plain-text", "plain"] = ( - "major" - ) + shape: t.Literal["pill", "rounded", "rectangle"] = "pill" + style: t.Literal[ + "major", "minor", "colored-text", "plain-text", "plain" + ] = "major" color: rio.ColorSet = "keep" is_sensitive: bool = True is_loading: bool = False @@ -207,10 +207,10 @@ class _ButtonInternal(FundamentalComponent): _: KW_ONLY on_press: rio.EventHandler[[]] content: rio.Component - shape: Literal["pill", "rounded", "rectangle", "circle"] - style: Literal["major", "minor", "colored-text", "plain-text", "plain"] = ( - "major" - ) + shape: t.Literal["pill", "rounded", "rectangle", "circle"] + style: t.Literal[ + "major", "minor", "colored-text", "plain-text", "plain" + ] = "major" color: rio.ColorSet is_sensitive: bool is_loading: bool @@ -230,7 +230,7 @@ class _ButtonInternal(FundamentalComponent): return {} - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg assert msg["type"] == "press", msg diff --git a/rio/components/calendar.py b/rio/components/calendar.py index e5878323..fe86d71d 100644 --- a/rio/components/calendar.py +++ b/rio/components/calendar.py @@ -1,8 +1,8 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass from datetime import date -from typing import * # type: ignore from uniserde import JsonDoc @@ -16,7 +16,7 @@ __all__ = [ ] -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class DateChangeEvent: @@ -122,7 +122,7 @@ class Calendar(FundamentalComponent): "firstDayOfWeek": self.session._first_day_of_week, } - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg diff --git a/rio/components/card.py b/rio/components/card.py index d8f71bd8..7d393b49 100644 --- a/rio/components/card.py +++ b/rio/components/card.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore from uniserde import JsonDoc @@ -14,7 +14,7 @@ __all__ = [ ] -@final +@t.final class Card(FundamentalComponent): """ A container that visually encompasses its content. @@ -111,7 +111,7 @@ class Card(FundamentalComponent): colorize_on_hover: bool | None = None color: rio.ColorSet = "neutral" - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Trigger the press event await self.call_event_handler(self.on_press) diff --git a/rio/components/checkbox.py b/rio/components/checkbox.py index 98e463f0..96259dd4 100644 --- a/rio/components/checkbox.py +++ b/rio/components/checkbox.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore from uniserde import JsonDoc @@ -15,13 +15,13 @@ __all__ = [ ] -@final +@t.final @dataclass class CheckboxChangeEvent: is_on: bool -@final +@t.final class Checkbox(FundamentalComponent): """ An input for `True` / `False` values. diff --git a/rio/components/class_container.py b/rio/components/class_container.py index f4d362a1..5b5a9aa1 100644 --- a/rio/components/class_container.py +++ b/rio/components/class_container.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Sequence +import typing as t import rio @@ -36,9 +36,9 @@ class ClassContainer(FundamentalComponent): """ content: rio.Component | None - classes: Sequence[str] + classes: t.Sequence[str] - def _get_debug_details_(self) -> dict[str, Any]: + def _get_debug_details_(self) -> dict[str, t.Any]: result = super()._get_debug_details_() result.pop("classes") return result diff --git a/rio/components/code_block.py b/rio/components/code_block.py index 3e124f58..d3da3c33 100644 --- a/rio/components/code_block.py +++ b/rio/components/code_block.py @@ -1,5 +1,5 @@ +import typing as t from dataclasses import KW_ONLY -from typing import final from .. import deprecations from .fundamental_component import FundamentalComponent @@ -9,7 +9,7 @@ __all__ = [ ] -@final +@t.final @deprecations.component_kwarg_renamed( since="0.9.2", old_name="display_controls", diff --git a/rio/components/code_explorer.py b/rio/components/code_explorer.py index 1ad76282..bc492ca1 100644 --- a/rio/components/code_explorer.py +++ b/rio/components/code_explorer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Literal, Sequence +import typing as t from uniserde import JsonDoc @@ -22,9 +22,9 @@ class CodeExplorer(FundamentalComponent): source_code: str build_result: rio.Component - line_indices_to_component_keys: Sequence[str | int | None] + line_indices_to_component_keys: t.Sequence[str | int | None] - style: Literal["horizontal", "vertical"] = "horizontal" + style: t.Literal["horizontal", "vertical"] = "horizontal" def _custom_serialize_(self) -> JsonDoc: return { diff --git a/rio/components/color_picker.py b/rio/components/color_picker.py index 37825936..b120a862 100644 --- a/rio/components/color_picker.py +++ b/rio/components/color_picker.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Any, final from uniserde import JsonDoc @@ -15,7 +15,7 @@ __all__ = [ ] -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class ColorChangeEvent: @@ -34,7 +34,7 @@ class ColorChangeEvent: color: rio.Color -@final +@t.final class ColorPicker(FundamentalComponent): """ Allows the user to pick a RGB(A) color. @@ -101,7 +101,7 @@ class ColorPicker(FundamentalComponent): pick_opacity: bool = False on_change: rio.EventHandler[ColorChangeEvent] = None - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg diff --git a/rio/components/component.py b/rio/components/component.py index ae59e86e..a78fc057 100644 --- a/rio/components/component.py +++ b/rio/components/component.py @@ -2,11 +2,10 @@ from __future__ import annotations import abc import io +import typing as t from abc import abstractmethod -from collections.abc import Callable, Iterable from dataclasses import KW_ONLY from pathlib import Path -from typing import * # type: ignore from typing_extensions import Self from uniserde import Jsonable, JsonDoc @@ -22,7 +21,7 @@ from ..state_properties import AttributeBindingMaker __all__ = ["Component"] -T = TypeVar("T") +T = t.TypeVar("T") # Using `metaclass=ComponentMeta` makes this an abstract class, but since @@ -223,8 +222,8 @@ class Component(abc.ABC, metaclass=ComponentMeta): align_x: float | None = None align_y: float | None = None - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never" - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never" + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never" + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never" margin_left: float | None = None margin_top: float | None = None @@ -242,13 +241,13 @@ class Component(abc.ABC, metaclass=ComponentMeta): # # Dataclasses like to turn this function into a method. Make sure it works # both with and without `self`. - _weak_builder_: Callable[[], Component | None] = internal_field( + _weak_builder_: t.Callable[[], Component | None] = internal_field( default=lambda *args: None, init=False, ) # Weak reference to the component's creator - _weak_creator_: Callable[[], Component | None] = internal_field( + _weak_creator_: t.Callable[[], Component | None] = internal_field( init=False, ) @@ -310,7 +309,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): # Hide this function from type checkers so they don't think that we accept # arbitrary args - if not TYPE_CHECKING: + if not t.TYPE_CHECKING: # Make sure users don't inherit from rio components. Inheriting from # their own components is fine, though. def __init_subclass__(cls, *args, **kwargs): @@ -332,7 +331,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): @staticmethod def _remap_constructor_arguments_(args: tuple, kwargs: dict): - width: float | Literal["grow", "natural"] | None = kwargs.pop( + width: float | t.Literal["grow", "natural"] | None = kwargs.pop( "width", None ) @@ -351,7 +350,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): else: kwargs["min_width"] = width - height: float | Literal["grow", "natural"] | None = kwargs.pop( + height: float | t.Literal["grow", "natural"] | None = kwargs.pop( "height", None ) @@ -406,7 +405,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): """ raise NotImplementedError() # pragma: no cover - def _iter_direct_children_(self) -> Iterable[Component]: + def _iter_direct_children_(self) -> t.Iterable[Component]: for name in inspection.get_child_component_containing_attribute_names( type(self) ): @@ -419,7 +418,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): yield value if isinstance(value, list): - value = cast(list[object], value) + value = t.cast(list[object], value) for item in value: if isinstance(item, Component): @@ -430,7 +429,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): *, include_self: bool, recurse_into_high_level_components: bool, - ) -> Iterable[Component]: + ) -> t.Iterable[Component]: from . import fundamental_component # Avoid circular import problem # Special case the component itself to handle `include_self` @@ -455,7 +454,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): def _iter_component_tree_( self, *, include_root: bool = True - ) -> Iterable[Component]: + ) -> t.Iterable[Component]: """ Iterate over all components in the component tree, with this component as the root. """ @@ -543,13 +542,13 @@ class Component(abc.ABC, metaclass=ComponentMeta): cache[self] = result return result - @overload + @t.overload async def call_event_handler( self, handler: rio.EventHandler[[]], ) -> None: ... # pragma: no cover - @overload + @t.overload async def call_event_handler( self, handler: rio.EventHandler[[T]], @@ -607,7 +606,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): await self.session._refresh() - def _get_debug_details_(self) -> dict[str, Any]: + def _get_debug_details_(self) -> dict[str, t.Any]: """ Used by Rio's dev tools to decide which properties to display to a user, when they select a component. @@ -636,7 +635,7 @@ class Component(abc.ABC, metaclass=ComponentMeta): return result + ">" - def _repr_tree_worker_(self, file: IO[str], indent: str) -> None: + def _repr_tree_worker_(self, file: t.IO[str], indent: str) -> None: file.write(indent) file.write(repr(self)) diff --git a/rio/components/container.py b/rio/components/container.py index b1ae0988..70e1c746 100644 --- a/rio/components/container.py +++ b/rio/components/container.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class Container(Component): """ An invisible component holding a single child. diff --git a/rio/components/date_input.py b/rio/components/date_input.py index e8d17f3e..8c066720 100644 --- a/rio/components/date_input.py +++ b/rio/components/date_input.py @@ -1,9 +1,9 @@ from __future__ import annotations import random +import typing as t from dataclasses import KW_ONLY from datetime import date -from typing import final import rio @@ -82,7 +82,7 @@ def make_fake_input_box( ) -@final +@t.final class DateInput(Component): """ Allows the user to pick a date from a calendar. diff --git a/rio/components/default_root_component.py b/rio/components/default_root_component.py index c6f7ea73..a854b908 100644 --- a/rio/components/default_root_component.py +++ b/rio/components/default_root_component.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -33,7 +33,7 @@ def with_debug_tooltip( return comp -@final +@t.final class NavButton(component.Component): page: rio.ComponentPage is_current: bool @@ -89,7 +89,7 @@ class NavButton(component.Component): return main_row -@final +@t.final class DefaultRootComponent(component.Component): """ ## Metadata diff --git a/rio/components/devel_component.py b/rio/components/devel_component.py index ef157bc3..1641e5b2 100644 --- a/rio/components/devel_component.py +++ b/rio/components/devel_component.py @@ -3,10 +3,9 @@ from __future__ import annotations import concurrent.futures import subprocess import tempfile -from collections.abc import Iterable +import typing as t from dataclasses import field from pathlib import Path -from typing import Sequence import rio @@ -31,12 +30,12 @@ class DevelComponent(FundamentalComponent): `public`: False """ - children: Sequence[rio.Component] = field(default_factory=list) + children: t.Sequence[rio.Component] = field(default_factory=list) def __init__( self, *, - children: Iterable[rio.Component], + children: t.Iterable[rio.Component], key: str | int | None = None, margin: float | None = None, margin_x: float | None = None, @@ -53,8 +52,8 @@ class DevelComponent(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, diff --git a/rio/components/dialog_container.py b/rio/components/dialog_container.py index eccd529d..830f5f8c 100644 --- a/rio/components/dialog_container.py +++ b/rio/components/dialog_container.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -12,9 +12,9 @@ __all__ = [ ] -@final +@t.final class DialogContainer(Component): - build_content: Callable[[], Component] + build_content: t.Callable[[], Component] owning_component_id: int is_modal: bool is_user_closeable: bool @@ -27,7 +27,7 @@ class DialogContainer(Component): # high-level on the Python side, but sent to the client as though they were # fundamental. To prevent a whole bunch of custom code in the serializer, # this function handles the serialization of dialog containers. - def serialize(self) -> dict[str, Any]: + def serialize(self) -> dict[str, t.Any]: return { "owning_component_id": self.owning_component_id, "is_modal": self.is_modal, diff --git a/rio/components/drawer.py b/rio/components/drawer.py index 2e726b20..ecab42fc 100644 --- a/rio/components/drawer.py +++ b/rio/components/drawer.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Literal, final from uniserde import JsonDoc @@ -15,7 +15,7 @@ __all__ = [ ] -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class DrawerOpenOrCloseEvent: @@ -34,7 +34,7 @@ class DrawerOpenOrCloseEvent: is_open: bool -@final +@t.final class Drawer(FundamentalComponent): """ A container which slides in from the edge of the screen. @@ -114,7 +114,7 @@ class Drawer(FundamentalComponent): content: rio.Component _: KW_ONLY on_open_or_close: rio.EventHandler[DrawerOpenOrCloseEvent] = None - side: Literal["left", "right", "top", "bottom"] = "left" + side: t.Literal["left", "right", "top", "bottom"] = "left" is_modal: bool = True is_open: bool = False is_user_openable: bool = True diff --git a/rio/components/dropdown.py b/rio/components/dropdown.py index 305159b1..e74dd49a 100644 --- a/rio/components/dropdown.py +++ b/rio/components/dropdown.py @@ -1,8 +1,7 @@ from __future__ import annotations -from collections.abc import Mapping, Sequence +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Any, Generic, TypeVar, final from uniserde import JsonDoc @@ -15,13 +14,13 @@ __all__ = [ "DropdownChangeEvent", ] -T = TypeVar("T") +T = t.TypeVar("T") -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass -class DropdownChangeEvent(Generic[T]): +class DropdownChangeEvent(t.Generic[T]): """ Holds information regarding a dropdown change event. @@ -37,8 +36,8 @@ class DropdownChangeEvent(Generic[T]): value: T -@final -class Dropdown(FundamentalComponent, Generic[T]): +@t.final +class Dropdown(FundamentalComponent, t.Generic[T]): """ A dropdown menu for selecting from one of several options. @@ -122,7 +121,7 @@ class Dropdown(FundamentalComponent, Generic[T]): ``` """ - options: Mapping[str, T] + options: t.Mapping[str, T] _: KW_ONLY label: str selected_value: T @@ -132,7 +131,7 @@ class Dropdown(FundamentalComponent, Generic[T]): def __init__( self, - options: Mapping[str, T] | Sequence[T], + options: t.Mapping[str, T] | t.Sequence[T], *, label: str = "", selected_value: T | None = None, @@ -155,8 +154,8 @@ class Dropdown(FundamentalComponent, Generic[T]): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): if not options: raise ValueError("`Dropdown` must have at least one option.") @@ -182,7 +181,7 @@ class Dropdown(FundamentalComponent, Generic[T]): # SCROLLING-REWORK scroll_y=scroll_y, ) - if isinstance(options, Sequence): + if isinstance(options, t.Sequence): options = {str(value): value for value in options} self.options = options @@ -224,7 +223,7 @@ class Dropdown(FundamentalComponent, Generic[T]): return result - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg diff --git a/rio/components/file_picker_area.py b/rio/components/file_picker_area.py index 4acde56f..b9fbe657 100644 --- a/rio/components/file_picker_area.py +++ b/rio/components/file_picker_area.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore from uniserde import JsonDoc @@ -17,7 +17,7 @@ __all__ = [ ] -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class FilePickEvent: @@ -36,7 +36,7 @@ class FilePickEvent: file: rio.FileInfo -@final +@t.final class FilePickerArea(FundamentalComponent): """ Drag & Drop are for files diff --git a/rio/components/flow_container.py b/rio/components/flow_container.py index 6d58691a..8ab81c2d 100644 --- a/rio/components/flow_container.py +++ b/rio/components/flow_container.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import Literal, final from typing_extensions import Self from uniserde import JsonDoc @@ -14,7 +14,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["FlowContainer"] -@final +@t.final class FlowContainer(FundamentalComponent): """ A container whose children will be rearranged to fill the available space @@ -54,7 +54,7 @@ class FlowContainer(FundamentalComponent): spacing: float | None row_spacing: float | None column_spacing: float | None - justify: Literal["left", "center", "right", "justify", "grow"] + justify: t.Literal["left", "center", "right", "justify", "grow"] def __init__( self, @@ -62,7 +62,9 @@ class FlowContainer(FundamentalComponent): spacing: float | None = None, row_spacing: float | None = None, column_spacing: float | None = None, - justify: Literal["left", "center", "right", "justify", "grow"] = "left", + justify: t.Literal[ + "left", "center", "right", "justify", "grow" + ] = "left", key: str | int | None = None, margin: float | None = None, margin_x: float | None = None, @@ -79,8 +81,8 @@ class FlowContainer(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: assert isinstance(children, tuple), children diff --git a/rio/components/fundamental_component.py b/rio/components/fundamental_component.py index a59be04c..042458f2 100644 --- a/rio/components/fundamental_component.py +++ b/rio/components/fundamental_component.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import * # type: ignore +import typing as t from uniserde import Jsonable, JsonDoc @@ -70,7 +70,7 @@ document.head.appendChild(style); class FundamentalComponent(Component): # Unique id for identifying this class in the frontend. This is initialized # in `Component.__init_subclass__`. - _unique_id_: ClassVar[str] + _unique_id_: t.ClassVar[str] def build(self) -> rio.Component: raise RuntimeError( @@ -162,7 +162,7 @@ class FundamentalComponent(Component): pass def _apply_delta_state_from_frontend( - self, delta_state: dict[str, Any] + self, delta_state: dict[str, t.Any] ) -> None: """ Applies the delta state received from the frontend without marking the diff --git a/rio/components/grid.py b/rio/components/grid.py index 476b1ebc..1670f371 100644 --- a/rio/components/grid.py +++ b/rio/components/grid.py @@ -1,9 +1,8 @@ from __future__ import annotations import math -from collections.abc import Iterable +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import final from typing_extensions import Self from uniserde import JsonDoc @@ -15,7 +14,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["Grid"] -@final +@t.final @dataclass class GridChildPosition: row: int @@ -24,7 +23,7 @@ class GridChildPosition: height: int = 1 -@final +@t.final class Grid(FundamentalComponent): """ A container which arranges its children in a table-like grid. @@ -102,7 +101,7 @@ class Grid(FundamentalComponent): def __init__( self, - *rows: rio.Component | Iterable[rio.Component], + *rows: rio.Component | t.Iterable[rio.Component], row_spacing: float = 0.0, column_spacing: float = 0.0, key: str | int | None = None, @@ -121,8 +120,8 @@ class Grid(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, @@ -158,7 +157,7 @@ class Grid(FundamentalComponent): def _add_initial_children( self, - children: Iterable[rio.Component | Iterable[rio.Component]], + children: t.Iterable[rio.Component | t.Iterable[rio.Component]], ) -> tuple[list[rio.Component], list[GridChildPosition]]: """ Adds the children added in the constructor to the component. This is diff --git a/rio/components/html.py b/rio/components/html.py index d8125f10..8a0231b5 100644 --- a/rio/components/html.py +++ b/rio/components/html.py @@ -1,11 +1,11 @@ -from typing import final +import typing as t from .fundamental_component import FundamentalComponent __all__ = ["Html"] -@final +@t.final class Html(FundamentalComponent): """ Displays raw HTML. diff --git a/rio/components/icon.py b/rio/components/icon.py index 8412c6e8..aaa28b0b 100644 --- a/rio/components/icon.py +++ b/rio/components/icon.py @@ -1,8 +1,8 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY from pathlib import Path -from typing import Literal, Union, final from uniserde import JsonDoc @@ -14,16 +14,16 @@ __all__ = [ ] -_IconFill = Union[ +_IconFill = t.Union[ "fills.SolidFill", "fills.LinearGradientFill", "fills.ImageFill", "color.ColorSet", - Literal["dim"], + t.Literal["dim"], ] -@final +@t.final class Icon(FundamentalComponent): """ Displays one of many pre-bundled icons. @@ -184,8 +184,8 @@ class Icon(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, diff --git a/rio/components/icon_button.py b/rio/components/icon_button.py index 66ff078d..5726a8b8 100644 --- a/rio/components/icon_button.py +++ b/rio/components/icon_button.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore from uniserde import JsonDoc @@ -14,7 +14,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["IconButton"] -@final +@t.final @deprecations.component_kwarg_renamed( since="0.10", old_name="size", @@ -109,7 +109,7 @@ class IconButton(Component): icon: str _: KW_ONLY - style: Literal["major", "minor", "colored-text", "plain-text", "plain"] + style: t.Literal["major", "minor", "colored-text", "plain-text", "plain"] color: rio.ColorSet is_sensitive: bool min_size: float @@ -119,7 +119,7 @@ class IconButton(Component): self, icon: str, *, - style: Literal[ + style: t.Literal[ "major", "minor", "colored-text", "plain-text", "plain" ] = "major", color: rio.ColorSet = "keep", @@ -138,8 +138,8 @@ class IconButton(Component): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( key=key, @@ -176,7 +176,7 @@ class IconButton(Component): min_height=self.min_size, ) - def _get_debug_details_(self) -> dict[str, Any]: + def _get_debug_details_(self) -> dict[str, t.Any]: result = super()._get_debug_details_() # `min_width` & `min_height` are replaced with `size` @@ -188,11 +188,11 @@ class IconButton(Component): class _IconButtonInternal(FundamentalComponent): content: rio.Component - style: Literal["major", "minor", "colored-text", "plain-text", "plain"] + style: t.Literal["major", "minor", "colored-text", "plain-text", "plain"] color: rio.ColorSet is_sensitive: bool on_press: rio.EventHandler[[]] - shape: Literal["circle"] = "circle" + shape: t.Literal["circle"] = "circle" def _custom_serialize_(self) -> JsonDoc: if self.style == "plain": @@ -209,7 +209,7 @@ class _IconButtonInternal(FundamentalComponent): return {} - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg assert msg["type"] == "press", msg diff --git a/rio/components/image.py b/rio/components/image.py index dfeb43fc..8b776e9f 100644 --- a/rio/components/image.py +++ b/rio/components/image.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import Literal, final from uniserde import Jsonable, JsonDoc @@ -12,7 +12,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["Image"] -@final +@t.final class Image(FundamentalComponent): """ Displays a raster image or SVG. @@ -111,7 +111,7 @@ class Image(FundamentalComponent): image: ImageLike _: KW_ONLY - fill_mode: Literal["fit", "stretch", "zoom"] = "fit" + fill_mode: t.Literal["fit", "stretch", "zoom"] = "fit" on_error: EventHandler[[]] = None corner_radius: float | tuple[float, float, float, float] = 0 accessibility_description: str = "" @@ -120,7 +120,7 @@ class Image(FundamentalComponent): self, image: ImageLike, *, - fill_mode: Literal["fit", "stretch", "zoom"] = "fit", + fill_mode: t.Literal["fit", "stretch", "zoom"] = "fit", on_error: EventHandler[[]] | None = None, corner_radius: float | tuple[float, float, float, float] = 0, accessibility_description: str = "", @@ -140,8 +140,8 @@ class Image(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( key=key, diff --git a/rio/components/key_event_listener.py b/rio/components/key_event_listener.py index 70c3fe2c..5b731983 100644 --- a/rio/components/key_event_listener.py +++ b/rio/components/key_event_listener.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Any, Literal, final from uniserde import Jsonable @@ -19,7 +19,7 @@ __all__ = [ ] -HardwareKey = Literal[ +HardwareKey = t.Literal[ "unknown", # Function keys "f1", @@ -216,7 +216,7 @@ HardwareKey = Literal[ "brightness-down", ] -SoftwareKey = Literal[ +SoftwareKey = t.Literal[ "unknown", # Modifiers "alt", @@ -562,7 +562,7 @@ SoftwareKey = Literal[ "separator", ] -ModifierKey = Literal["alt", "control", "meta", "shift"] +ModifierKey = t.Literal["alt", "control", "meta", "shift"] _MODIFIERS = ("control", "shift", "alt", "meta") @@ -598,7 +598,7 @@ class _KeyUpDownEvent: modifiers: frozenset[ModifierKey] @classmethod - def _from_json(cls, json_data: dict[str, Any]): + def _from_json(cls, json_data: dict[str, t.Any]): return cls( hardware_key=json_data["hardwareKey"], software_key=json_data["softwareKey"], @@ -613,25 +613,25 @@ class _KeyUpDownEvent: return " + ".join(keys) -@final +@t.final @rio.docs.mark_constructor_as_private class KeyDownEvent(_KeyUpDownEvent): pass -@final +@t.final @rio.docs.mark_constructor_as_private class KeyUpEvent(_KeyUpDownEvent): pass -@final +@t.final @rio.docs.mark_constructor_as_private class KeyPressEvent(_KeyUpDownEvent): pass -@final +@t.final class KeyEventListener(KeyboardFocusableFundamentalComponent): """ Calls an event handler when a key is pressed or released. @@ -669,7 +669,7 @@ class KeyEventListener(KeyboardFocusableFundamentalComponent): "reportKeyPress": self.on_key_press is not None, } - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg diff --git a/rio/components/labeled_column.py b/rio/components/labeled_column.py index 07551299..d3989d1e 100644 --- a/rio/components/labeled_column.py +++ b/rio/components/labeled_column.py @@ -1,8 +1,7 @@ from __future__ import annotations -from collections.abc import Mapping +import typing as t from dataclasses import field -from typing import final from typing_extensions import Self @@ -13,7 +12,7 @@ from .component import Component __all__ = ["LabeledColumn"] -@final +@t.final class LabeledColumn(Component): """ A container that lays out its children in a column, with labels for each @@ -47,7 +46,7 @@ class LabeledColumn(Component): def __init__( self, - content: Mapping[str, rio.Component], + content: t.Mapping[str, rio.Component], *, key: str | int | None = None, margin: float | None = None, @@ -65,8 +64,8 @@ class LabeledColumn(Component): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, @@ -92,11 +91,11 @@ class LabeledColumn(Component): self.content = content @property - def content(self) -> Mapping[str, Component]: + def content(self) -> t.Mapping[str, Component]: return self._content @content.setter - def content(self, children: Mapping[str, Component]) -> None: + def content(self, children: t.Mapping[str, Component]) -> None: self._content = dict(children) self._child_list = list(children.values()) diff --git a/rio/components/linear_containers.py b/rio/components/linear_containers.py index 2cdc856c..f046df3b 100644 --- a/rio/components/linear_containers.py +++ b/rio/components/linear_containers.py @@ -1,7 +1,6 @@ from __future__ import annotations -from collections.abc import Sequence -from typing import Literal, final +import typing as t from typing_extensions import Self from uniserde import JsonDoc @@ -19,7 +18,7 @@ __all__ = [ class _LinearContainer(FundamentalComponent): children: list[rio.Component] spacing: float = 0.0 - proportions: Literal["homogeneous"] | Sequence[float] | None = None + proportions: t.Literal["homogeneous"] | t.Sequence[float] | None = None # Don't let @dataclass generate a constructor def __init__(self, *args, **kwargs): @@ -33,7 +32,7 @@ class _LinearContainer(FundamentalComponent): return {"proportions": self.proportions} # type: ignore (variance) -@final +@t.final class Row(_LinearContainer): """ A container that lays out its children horizontally. @@ -125,7 +124,7 @@ class Row(_LinearContainer): self, *children: rio.Component, spacing: float = 0.0, - proportions: Literal["homogeneous"] | Sequence[float] | None = None, + proportions: t.Literal["homogeneous"] | t.Sequence[float] | None = None, key: str | int | None = None, margin: float | None = None, margin_x: float | None = None, @@ -142,8 +141,8 @@ class Row(_LinearContainer): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( key=key, @@ -191,7 +190,7 @@ class Row(_LinearContainer): Row._unique_id_ = "Row-builtin" -@final +@t.final class Column(_LinearContainer): """ A container that lays out its children vertically. @@ -281,7 +280,7 @@ class Column(_LinearContainer): self, *children: rio.Component, spacing: float = 0.0, - proportions: Literal["homogeneous"] | Sequence[float] | None = None, + proportions: t.Literal["homogeneous"] | t.Sequence[float] | None = None, key: str | int | None = None, margin: float | None = None, margin_x: float | None = None, @@ -298,8 +297,8 @@ class Column(_LinearContainer): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, diff --git a/rio/components/link.py b/rio/components/link.py index 8677462b..5f391825 100644 --- a/rio/components/link.py +++ b/rio/components/link.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import final +import typing as t from uniserde import JsonDoc @@ -14,7 +14,7 @@ __all__ = [ ] -@final +@t.final class Link(FundamentalComponent): """ Navigates to a page or URL when pressed. @@ -89,8 +89,8 @@ class Link(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: """ ## Parameters diff --git a/rio/components/list_items.py b/rio/components/list_items.py index e7087af7..8e7ae8b8 100644 --- a/rio/components/list_items.py +++ b/rio/components/list_items.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t from uniserde import JsonDoc @@ -17,7 +17,7 @@ __all__ = [ ] -@final +@t.final class HeadingListItem(FundamentalComponent): """ A list item acting as heading. @@ -83,7 +83,7 @@ class HeadingListItem(FundamentalComponent): HeadingListItem._unique_id_ = "HeadingListItem-builtin" -@final +@t.final class SeparatorListItem(FundamentalComponent): """ A visual separator between list items. @@ -134,7 +134,7 @@ class SeparatorListItem(FundamentalComponent): SeparatorListItem._unique_id_ = "SeparatorListItem-builtin" -@final +@t.final class SimpleListItem(Component): """ A simple list item with a header and optional secondary text and children. @@ -228,8 +228,8 @@ class SimpleListItem(Component): # MAX-SIZE-BRANCH max_height: float | None = None, grow_x: bool = False, grow_y: bool = False, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( min_width=min_width, @@ -301,7 +301,7 @@ class SimpleListItem(Component): ) -@final +@t.final class CustomListItem(FundamentalComponent): """ A list item with custom content. @@ -394,8 +394,8 @@ class CustomListItem(FundamentalComponent): # MAX-SIZE-BRANCH max_height: float | None = None, grow_x: bool = False, grow_y: bool = False, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( min_width=min_width, @@ -417,7 +417,7 @@ class CustomListItem(FundamentalComponent): "pressable": self.on_press is not None, } - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg assert msg["type"] == "press", msg diff --git a/rio/components/list_view.py b/rio/components/list_view.py index 2f044a71..fa9c2f4f 100644 --- a/rio/components/list_view.py +++ b/rio/components/list_view.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import final +import typing as t from typing_extensions import Self @@ -11,7 +11,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["ListView"] -@final +@t.final class ListView(FundamentalComponent): """ Vertically arranges and styles its children. @@ -114,8 +114,8 @@ class ListView(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( key=key, diff --git a/rio/components/markdown.py b/rio/components/markdown.py index 64c6aa75..41297977 100644 --- a/rio/components/markdown.py +++ b/rio/components/markdown.py @@ -1,5 +1,5 @@ +import typing as t from dataclasses import KW_ONLY -from typing import Literal, final from uniserde import JsonDoc @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class Markdown(FundamentalComponent): ''' Displays Markdown-formatted text. @@ -67,9 +67,9 @@ class Markdown(FundamentalComponent): _: KW_ONLY default_language: str | None = None selectable: bool = True - justify: Literal["left", "right", "center", "justify"] = "left" - wrap: bool | Literal["ellipsize"] = True - overflow: Literal["nowrap", "wrap", "ellipsize"] = "wrap" + justify: t.Literal["left", "right", "center", "justify"] = "left" + wrap: bool | t.Literal["ellipsize"] = True + overflow: t.Literal["nowrap", "wrap", "ellipsize"] = "wrap" def _custom_serialize_(self) -> JsonDoc: # The old `wrap` attribute has been replaced with `overflow`. Remap the diff --git a/rio/components/media_player.py b/rio/components/media_player.py index 97037a09..adb35745 100644 --- a/rio/components/media_player.py +++ b/rio/components/media_player.py @@ -1,7 +1,7 @@ from __future__ import annotations import pathlib -from typing import final +import typing as t from uniserde import JsonDoc @@ -14,7 +14,7 @@ from .fundamental_component import KeyboardFocusableFundamentalComponent __all__ = ["MediaPlayer"] -@final +@t.final class MediaPlayer(KeyboardFocusableFundamentalComponent): """ Plays audio and video files. @@ -123,8 +123,8 @@ class MediaPlayer(KeyboardFocusableFundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, diff --git a/rio/components/mouse_event_listener.py b/rio/components/mouse_event_listener.py index 8533494c..69d2ba7f 100644 --- a/rio/components/mouse_event_listener.py +++ b/rio/components/mouse_event_listener.py @@ -1,8 +1,8 @@ from __future__ import annotations import enum +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore from uniserde import JsonDoc @@ -25,7 +25,7 @@ __all__ = [ ] -@final +@t.final class MouseButton(enum.Enum): LEFT = "left" MIDDLE = "middle" @@ -53,7 +53,7 @@ class _PositionedEvent: y: float -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class PressEvent(_ButtonEvent, _PositionedEvent): @@ -70,7 +70,7 @@ class PressEvent(_ButtonEvent, _PositionedEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class MouseDownEvent(_ButtonEvent, _PositionedEvent): @@ -87,7 +87,7 @@ class MouseDownEvent(_ButtonEvent, _PositionedEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class MouseUpEvent(_ButtonEvent, _PositionedEvent): @@ -104,7 +104,7 @@ class MouseUpEvent(_ButtonEvent, _PositionedEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private class MouseMoveEvent(_PositionedEvent): """ @@ -116,7 +116,7 @@ class MouseMoveEvent(_PositionedEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private class MouseEnterEvent(_PositionedEvent): """ @@ -128,7 +128,7 @@ class MouseEnterEvent(_PositionedEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private class MouseLeaveEvent(_PositionedEvent): """ @@ -158,7 +158,7 @@ class _DragEvent(_ButtonEvent, _PositionedEvent): component: rio.Component -@final +@t.final @rio.docs.mark_constructor_as_private class DragStartEvent(_DragEvent): """ @@ -170,7 +170,7 @@ class DragStartEvent(_DragEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private class DragMoveEvent(_DragEvent): """ @@ -182,7 +182,7 @@ class DragMoveEvent(_DragEvent): """ -@final +@t.final @rio.docs.mark_constructor_as_private class DragEndEvent(_DragEvent): """ @@ -194,7 +194,7 @@ class DragEndEvent(_DragEvent): """ -@final +@t.final class MouseEventListener(FundamentalComponent): """ Allows you to listen for mouse events on a component. @@ -260,7 +260,7 @@ class MouseEventListener(FundamentalComponent): "reportDragEnd": self.on_drag_end is not None, } - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg diff --git a/rio/components/multi_line_text_input.py b/rio/components/multi_line_text_input.py index da76a7e7..84b8854b 100644 --- a/rio/components/multi_line_text_input.py +++ b/rio/components/multi_line_text_input.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Any, final from uniserde import JsonDoc @@ -16,19 +16,19 @@ __all__ = [ ] -@final +@t.final @dataclass class MultiLineTextInputChangeEvent: text: str -@final +@t.final @dataclass class MultiLineTextInputConfirmEvent: text: str -@final +@t.final class MultiLineTextInput(KeyboardFocusableFundamentalComponent): """ A user-editable text field. @@ -143,7 +143,7 @@ class MultiLineTextInput(KeyboardFocusableFundamentalComponent): self._apply_delta_state_from_frontend(delta_state) - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Listen for messages indicating the user has confirmed their input # # In addition to notifying the backend, these also include the input's diff --git a/rio/components/node_input.py b/rio/components/node_input.py index 80d37725..8f2b3599 100644 --- a/rio/components/node_input.py +++ b/rio/components/node_input.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class NodeInput(FundamentalComponent): """ ## Metadata @@ -43,8 +43,8 @@ class NodeInput(FundamentalComponent): # MAX-SIZE-BRANCH max_height: float | None = None, grow_x: bool = False, grow_y: bool = False, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): # Make sure the building component is a Node # TODO diff --git a/rio/components/node_output.py b/rio/components/node_output.py index 79b1665a..3bf413a7 100644 --- a/rio/components/node_output.py +++ b/rio/components/node_output.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class NodeOutput(FundamentalComponent): """ ## Metadata @@ -43,8 +43,8 @@ class NodeOutput(FundamentalComponent): # MAX-SIZE-BRANCH max_height: float | None = None, grow_x: bool = False, grow_y: bool = False, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): # Make sure the building component is a Node # TODO diff --git a/rio/components/number_input.py b/rio/components/number_input.py index 0f8aee41..a4af5b75 100644 --- a/rio/components/number_input.py +++ b/rio/components/number_input.py @@ -1,8 +1,7 @@ from __future__ import annotations -from collections.abc import Mapping +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore import rio.docs @@ -18,13 +17,13 @@ __all__ = [ # These must be ints so that `integer * multiplier` returns an int and not a # float -_multiplier_suffixes: Mapping[str, int] = { +_multiplier_suffixes: t.Mapping[str, int] = { "k": 1_000, "m": 1_000_000, } -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class NumberInputChangeEvent: @@ -43,7 +42,7 @@ class NumberInputChangeEvent: value: float -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class NumberInputConfirmEvent: @@ -62,7 +61,7 @@ class NumberInputConfirmEvent: value: float -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class NumberInputFocusEvent: @@ -81,7 +80,7 @@ class NumberInputFocusEvent: value: float -@final +@t.final class NumberInput(Component): """ Like `TextInput`, but specifically for inputting numbers. diff --git a/rio/components/overlay.py b/rio/components/overlay.py index 0a85404c..9945effd 100644 --- a/rio/components/overlay.py +++ b/rio/components/overlay.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class Overlay(FundamentalComponent): """ Displays its content above all other components. @@ -59,7 +59,7 @@ class Overlay(FundamentalComponent): self.content = content - def _get_debug_details_(self) -> dict[str, Any]: + def _get_debug_details_(self) -> dict[str, t.Any]: result = super()._get_debug_details_() # Overlays intentionally remove a lot of common properties, because they diff --git a/rio/components/page_view.py b/rio/components/page_view.py index 213963fa..d52c3869 100644 --- a/rio/components/page_view.py +++ b/rio/components/page_view.py @@ -1,8 +1,7 @@ from __future__ import annotations -from collections.abc import Callable +import typing as t from dataclasses import KW_ONLY, field -from typing import * # type: ignore import rio @@ -53,7 +52,7 @@ def default_fallback_build(sess: rio.Session) -> rio.Component: ) -@final +@t.final class PageView(Component): """ Placeholders for pages. @@ -111,7 +110,7 @@ class PageView(Component): _: KW_ONLY - fallback_build: Callable[[], rio.Component] | None = None + fallback_build: t.Callable[[], rio.Component] | None = None # How many other PageViews are above this one in the component tree. Zero # for top-level PageViews, 1 for the next level, and so on. diff --git a/rio/components/plot.py b/rio/components/plot.py index 5501a510..744b600f 100644 --- a/rio/components/plot.py +++ b/rio/components/plot.py @@ -1,14 +1,14 @@ from __future__ import annotations import io -from typing import TYPE_CHECKING, cast, final +import typing as t from uniserde import JsonDoc from .. import fills, maybes from .fundamental_component import FundamentalComponent -if TYPE_CHECKING: +if t.TYPE_CHECKING: import matplotlib.axes # type: ignore import matplotlib.figure # type: ignore import plotly.graph_objects # type: ignore @@ -17,7 +17,7 @@ if TYPE_CHECKING: __all__ = ["Plot"] -@final +@t.final class Plot(FundamentalComponent): """ Displays a `matplotlib`, `seaborn` or `plotly` plot. @@ -98,8 +98,8 @@ class Plot(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, @@ -138,7 +138,7 @@ class Plot(FundamentalComponent): # Plotly if isinstance(figure, maybes.PLOTLY_GRAPH_TYPES): # Make the plot transparent, so `self.background` shines through. - figure = cast("plotly.graph_objects.Figure", figure) + figure = t.cast("plotly.graph_objects.Figure", figure) plot = { "type": "plotly", @@ -150,7 +150,7 @@ class Plot(FundamentalComponent): if isinstance(figure, maybes.MATPLOTLIB_AXES_TYPES): figure = figure.figure - figure = cast("matplotlib.figure.Figure", figure) + figure = t.cast("matplotlib.figure.Figure", figure) file = io.BytesIO() figure.savefig( diff --git a/rio/components/popup.py b/rio/components/popup.py index 992c5c66..9371db2c 100644 --- a/rio/components/popup.py +++ b/rio/components/popup.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import Literal, final from uniserde import JsonDoc @@ -14,7 +14,7 @@ __all__ = [ ] -@final +@t.final # @deprecations.component_kwarg_renamed( # since="0.9.2", # old_name="direction", @@ -103,9 +103,9 @@ class Popup(FundamentalComponent): anchor: rio.Component content: rio.Component _: KW_ONLY - color: rio.ColorSet | Literal["none"] = "hud" + color: rio.ColorSet | t.Literal["none"] = "hud" corner_radius: float | tuple[float, float, float, float] | None = None - position: Literal[ + position: t.Literal[ "auto", "left", "top", "right", "bottom", "center", "fullscreen" ] = "center" alignment: float = 0.5 diff --git a/rio/components/progress_bar.py b/rio/components/progress_bar.py index 1f842931..d2e0d018 100644 --- a/rio/components/progress_bar.py +++ b/rio/components/progress_bar.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class ProgressBar(FundamentalComponent): """ A progress indicator in the shape of a horizontal bar. @@ -78,8 +78,8 @@ class ProgressBar(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): """ ## Parameters diff --git a/rio/components/progress_circle.py b/rio/components/progress_circle.py index 97ea5a7f..a7d1d0b5 100644 --- a/rio/components/progress_circle.py +++ b/rio/components/progress_circle.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -12,7 +12,7 @@ __all__ = [ ] -@final +@t.final @deprecations.component_kwarg_renamed( since="0.10", old_name="size", @@ -82,8 +82,8 @@ class ProgressCircle(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: """ ## Parameters diff --git a/rio/components/rectangle.py b/rio/components/rectangle.py index 2c77365b..a2db1dd6 100644 --- a/rio/components/rectangle.py +++ b/rio/components/rectangle.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import final from uniserde import JsonDoc @@ -16,7 +16,7 @@ __all__ = [ ] -@final +@t.final class Rectangle(FundamentalComponent): """ A customizable rectangle shape. diff --git a/rio/components/revealer.py b/rio/components/revealer.py index b2e02dd7..63e1bd1f 100644 --- a/rio/components/revealer.py +++ b/rio/components/revealer.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import Literal, TypeVar, final from uniserde import JsonDoc @@ -14,10 +14,10 @@ __all__ = [ "RevealerChangeEvent", ] -T = TypeVar("T") +T = t.TypeVar("T") -@final +@t.final @rio.docs.mark_constructor_as_private @dataclass class RevealerChangeEvent: @@ -36,7 +36,7 @@ class RevealerChangeEvent: is_open: bool -@final +@t.final class Revealer(FundamentalComponent): """ A component that can be used to hide and reveal content. @@ -98,7 +98,7 @@ class Revealer(FundamentalComponent): content: rio.Component _: KW_ONLY header_style: ( - Literal["heading1", "heading2", "heading3", "text"] | rio.TextStyle + t.Literal["heading1", "heading2", "heading3", "text"] | rio.TextStyle ) = "text" is_open: bool = False on_change: rio.EventHandler[RevealerChangeEvent] = None diff --git a/rio/components/root_components.py b/rio/components/root_components.py index 51aea177..1310ec80 100644 --- a/rio/components/root_components.py +++ b/rio/components/root_components.py @@ -1,7 +1,6 @@ from __future__ import annotations -from collections.abc import Callable -from typing import * # type: ignore +import typing as t from .. import utils from .component import Component @@ -24,8 +23,8 @@ class HighLevelRootComponent(Component): `public`: False """ - build_function: Callable[[], Component] - build_connection_lost_message_function: Callable[[], Component] + build_function: t.Callable[[], Component] + build_connection_lost_message_function: t.Callable[[], Component] def build(self) -> Component: # Spawn the dev tools if running in debug mode. diff --git a/rio/components/scroll_container.py b/rio/components/scroll_container.py index 50ba3ef0..59a25ee5 100644 --- a/rio/components/scroll_container.py +++ b/rio/components/scroll_container.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import Literal, final import rio @@ -10,7 +10,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["ScrollContainer"] -@final +@t.final class ScrollContainer(FundamentalComponent): """ Displays a scroll bar if its content grows too large. @@ -52,8 +52,8 @@ class ScrollContainer(FundamentalComponent): content: rio.Component _: KW_ONLY - scroll_x: Literal["never", "auto", "always"] = "auto" - scroll_y: Literal["never", "auto", "always"] = "auto" + scroll_x: t.Literal["never", "auto", "always"] = "auto" + scroll_y: t.Literal["never", "auto", "always"] = "auto" initial_x: float = 0 initial_y: float = 0 sticky_bottom: bool = False diff --git a/rio/components/scroll_target.py b/rio/components/scroll_target.py index 4ca7ef04..6d6c945a 100644 --- a/rio/components/scroll_target.py +++ b/rio/components/scroll_target.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import final from uniserde import JsonDoc @@ -12,7 +12,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["ScrollTarget"] -@final +@t.final class ScrollTarget(FundamentalComponent): """ Allows browsers to scroll to a specific component via URL fragment. diff --git a/rio/components/separator.py b/rio/components/separator.py index 79853484..52a06a45 100644 --- a/rio/components/separator.py +++ b/rio/components/separator.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore import rio @@ -12,7 +12,7 @@ __all__ = [ ] -@final +@t.final class Separator(FundamentalComponent): """ A line to separate content. diff --git a/rio/components/slider.py b/rio/components/slider.py index 7aac8cc5..5b79682e 100644 --- a/rio/components/slider.py +++ b/rio/components/slider.py @@ -1,8 +1,8 @@ from __future__ import annotations import math +import typing as t from dataclasses import dataclass -from typing import final from uniserde import JsonDoc @@ -16,13 +16,13 @@ __all__ = [ ] -@final +@t.final @dataclass class SliderChangeEvent: value: float -@final +@t.final class Slider(FundamentalComponent): """ A component for selecting a single number from a range. @@ -129,8 +129,8 @@ class Slider(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( key=key, diff --git a/rio/components/slideshow.py b/rio/components/slideshow.py index f3cc7a19..7f950c97 100644 --- a/rio/components/slideshow.py +++ b/rio/components/slideshow.py @@ -1,8 +1,8 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY from datetime import timedelta -from typing import final from uniserde import JsonDoc @@ -15,7 +15,7 @@ __all__ = [ ] -@final +@t.final class Slideshow(FundamentalComponent): """ Repeatedly switches between multiple components based on a timer. @@ -90,8 +90,8 @@ class Slideshow(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): if isinstance(linger_time, timedelta): linger_time = linger_time.total_seconds() diff --git a/rio/components/spacer.py b/rio/components/spacer.py index 645b45f0..a092f52f 100644 --- a/rio/components/spacer.py +++ b/rio/components/spacer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t from . import class_container @@ -9,7 +9,7 @@ __all__ = [ ] -@final +@t.final class Spacer(class_container.ClassContainer): """ An invisible component which grows by default. @@ -60,7 +60,7 @@ class Spacer(class_container.ClassContainer): grow_y=grow_y, ) - def _get_debug_details_(self) -> dict[str, Any]: + def _get_debug_details_(self) -> dict[str, t.Any]: result = super()._get_debug_details_() # Don't inherit the content from `rio.ClassContainer`. diff --git a/rio/components/stack.py b/rio/components/stack.py index 66aa4745..79bb482c 100644 --- a/rio/components/stack.py +++ b/rio/components/stack.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import final +import typing as t from typing_extensions import Self @@ -11,7 +11,7 @@ from .fundamental_component import FundamentalComponent __all__ = ["Stack"] -@final +@t.final class Stack(FundamentalComponent): """ A container that stacks its children in the Z direction. @@ -87,8 +87,8 @@ class Stack(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): super().__init__( key=key, diff --git a/rio/components/switch.py b/rio/components/switch.py index 87e5482c..6bf9b9f9 100644 --- a/rio/components/switch.py +++ b/rio/components/switch.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore from uniserde import JsonDoc @@ -15,13 +15,13 @@ __all__ = [ ] -@final +@t.final @dataclass class SwitchChangeEvent: is_on: bool -@final +@t.final class Switch(FundamentalComponent): """ An input for `True` / `False` values. diff --git a/rio/components/switcher.py b/rio/components/switcher.py index 4a1dce52..7a427466 100644 --- a/rio/components/switcher.py +++ b/rio/components/switcher.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import final import rio @@ -12,7 +12,7 @@ __all__ = [ ] -@final +@t.final class Switcher(FundamentalComponent): """ Smoothly transitions between components. diff --git a/rio/components/switcher_bar.py b/rio/components/switcher_bar.py index e8b740d9..49c71ab2 100644 --- a/rio/components/switcher_bar.py +++ b/rio/components/switcher_bar.py @@ -1,8 +1,7 @@ from __future__ import annotations -from collections.abc import Sequence +import typing as t from dataclasses import dataclass -from typing import Any, Generic, Literal, TypeVar, final from uniserde import JsonDoc @@ -16,17 +15,17 @@ __all__ = [ "SwitcherBar", ] -T = TypeVar("T") +T = t.TypeVar("T") -@final +@t.final @dataclass -class SwitcherBarChangeEvent(Generic[T]): +class SwitcherBarChangeEvent(t.Generic[T]): value: T | None -@final -class SwitcherBar(FundamentalComponent, Generic[T]): +@t.final +class SwitcherBar(FundamentalComponent, t.Generic[T]): """ Displays a series of options and allows the user to switch between them. @@ -120,11 +119,11 @@ class SwitcherBar(FundamentalComponent, Generic[T]): `experimental`: True """ - names: Sequence[str] - values: Sequence[T] - icons: Sequence[str | None] | None + names: t.Sequence[str] + values: t.Sequence[T] + icons: t.Sequence[str | None] | None color: rio.ColorSet - orientation: Literal["horizontal", "vertical"] + orientation: t.Literal["horizontal", "vertical"] spacing: float selected_value: T | None allow_none: bool @@ -132,12 +131,12 @@ class SwitcherBar(FundamentalComponent, Generic[T]): def __init__( self, - values: Sequence[T], + values: t.Sequence[T], *, - names: Sequence[str] | None = None, - icons: Sequence[str | None] | None = None, + names: t.Sequence[str] | None = None, + icons: t.Sequence[str | None] | None = None, color: rio.ColorSet = "keep", - orientation: Literal["horizontal", "vertical"] = "horizontal", + orientation: t.Literal["horizontal", "vertical"] = "horizontal", spacing: float = 1.0, allow_none: bool = False, selected_value: T | None = None, @@ -158,8 +157,8 @@ class SwitcherBar(FundamentalComponent, Generic[T]): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ): """ ## Parameters @@ -250,7 +249,7 @@ class SwitcherBar(FundamentalComponent, Generic[T]): "selectedName": self._fetch_selected_name(), } - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg diff --git a/rio/components/table.py b/rio/components/table.py index b36a06c0..72ecc4be 100644 --- a/rio/components/table.py +++ b/rio/components/table.py @@ -1,16 +1,15 @@ from __future__ import annotations import typing -from collections.abc import Iterable, Mapping +import typing as t from dataclasses import dataclass -from typing import * # type: ignore from uniserde import JsonDoc from .. import maybes from .fundamental_component import FundamentalComponent -if TYPE_CHECKING: +if t.TYPE_CHECKING: import numpy # type: ignore import pandas # type: ignore import polars # type: ignore @@ -22,20 +21,20 @@ __all__ = ["Table"] TableValue = int | float | str -@final +@t.final @dataclass class TableSelection: _left: int - _top: int | Literal["header"] + _top: int | t.Literal["header"] _width: int _height: int - _font_weight: Literal["normal", "bold"] | None = None + _font_weight: t.Literal["normal", "bold"] | None = None def style( self, *, - font_weight: Literal["normal", "bold"] | None = None, + font_weight: t.Literal["normal", "bold"] | None = None, ) -> None: if font_weight is not None: self._font_weight = font_weight @@ -60,9 +59,9 @@ class TableSelection: def _index_to_start_and_extent( index: int | slice | str, size_in_axis: int, - axis: Literal["x", "y"], -) -> Tuple[ - int | Literal["header"], + axis: t.Literal["x", "y"], +) -> tuple[ + int | t.Literal["header"], int, ]: """ @@ -139,9 +138,9 @@ def _string_index_to_start_and_extent( index: str | int | slice, column_names: list[str] | None, size_in_axis: int, - axis: Literal["x", "y"], -) -> Tuple[ - int | Literal["header"], + axis: t.Literal["x", "y"], +) -> tuple[ + int | t.Literal["header"], int, ]: """ @@ -176,7 +175,7 @@ def _indices_to_rectangle( data_height: int, ) -> tuple[ int, - int | Literal["header"], + int | t.Literal["header"], int, int, ]: @@ -215,7 +214,7 @@ def _indices_to_rectangle( return left, top, width, height -@final +@t.final class Table(FundamentalComponent): """ Display & input for tabular data. @@ -280,8 +279,8 @@ class Table(FundamentalComponent): data: ( pandas.DataFrame | polars.DataFrame - | Mapping[str, Iterable[TableValue]] - | Iterable[Iterable[TableValue]] + | t.Mapping[str, t.Iterable[TableValue]] + | t.Iterable[Iterable[TableValue]] | numpy.ndarray ) show_row_numbers: bool = True @@ -316,8 +315,10 @@ class Table(FundamentalComponent): self._data = self.data.tolist() # Mapping - elif isinstance(self.data, Mapping): - data = typing.cast(Mapping[str, Iterable[TableValue]], self.data) + elif isinstance(self.data, t.Mapping): + data = typing.cast( + t.Mapping[str, t.Iterable[TableValue]], self.data + ) self._headers = list(data.keys()) # Verify all columns have the same length @@ -384,7 +385,7 @@ class Table(FundamentalComponent): def __getitem__( self, index: str - | Tuple[ + | tuple[ int | slice | str, int | slice | str, ], diff --git a/rio/components/text.py b/rio/components/text.py index 2c4690bd..3a1a8b65 100644 --- a/rio/components/text.py +++ b/rio/components/text.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore from uniserde import JsonDoc @@ -15,7 +15,7 @@ __all__ = [ ] -@final +@t.final class Text(FundamentalComponent): """ Displays unformatted text. @@ -75,12 +75,12 @@ class Text(FundamentalComponent): _: KW_ONLY selectable: bool = True style: ( - Literal["heading1", "heading2", "heading3", "text", "dim"] + t.Literal["heading1", "heading2", "heading3", "text", "dim"] | rio.TextStyle ) = "text" - justify: Literal["left", "right", "center", "justify"] = "left" - wrap: bool | Literal["ellipsize"] = False - overflow: Literal["nowrap", "wrap", "ellipsize"] = "nowrap" + justify: t.Literal["left", "right", "center", "justify"] = "left" + wrap: bool | t.Literal["ellipsize"] = False + overflow: t.Literal["nowrap", "wrap", "ellipsize"] = "nowrap" def _custom_serialize_(self) -> JsonDoc: # Serialization doesn't handle unions. Hence the custom serialization diff --git a/rio/components/theme_context_switcher.py b/rio/components/theme_context_switcher.py index b737adf0..918ccd7c 100644 --- a/rio/components/theme_context_switcher.py +++ b/rio/components/theme_context_switcher.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t from uniserde import JsonDoc @@ -13,7 +13,7 @@ __all__ = [ ] -@final +@t.final class ThemeContextSwitcher(FundamentalComponent): """ A container which can switch between theme contexts ("neutral", "warning", diff --git a/rio/components/tooltip.py b/rio/components/tooltip.py index 7599735e..a28e9a28 100644 --- a/rio/components/tooltip.py +++ b/rio/components/tooltip.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal, final +import typing as t import rio @@ -11,7 +11,7 @@ __all__ = [ ] -@final +@t.final class Tooltip(FundamentalComponent): """ A small pop-up window that appears when the user hovers over an element. @@ -50,11 +50,11 @@ class Tooltip(FundamentalComponent): anchor: rio.Component tip: str | rio.Component - position: Literal["auto", "left", "top", "right", "bottom"] + position: t.Literal["auto", "left", "top", "right", "bottom"] gap: float # Hide internal attributes from the IDE - if not TYPE_CHECKING: + if not t.TYPE_CHECKING: _tip_component: rio.Component | None # Impute a Text instance if a string is passed in as the tip @@ -62,7 +62,7 @@ class Tooltip(FundamentalComponent): self, anchor: rio.Component, tip: str | rio.Component, - position: Literal["auto", "left", "top", "right", "bottom"] = "auto", + position: t.Literal["auto", "left", "top", "right", "bottom"] = "auto", *, gap: float = 0.5, key: str | int | None = None, @@ -81,8 +81,8 @@ class Tooltip(FundamentalComponent): grow_y: bool = False, align_x: float | None = None, align_y: float | None = None, - # SCROLLING-REWORK scroll_x: Literal["never", "auto", "always"] = "never", - # SCROLLING-REWORK scroll_y: Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_x: t.Literal["never", "auto", "always"] = "never", + # SCROLLING-REWORK scroll_y: t.Literal["never", "auto", "always"] = "never", ) -> None: super().__init__( key=key, diff --git a/rio/components/website.py b/rio/components/website.py index a235f068..3a488e1f 100644 --- a/rio/components/website.py +++ b/rio/components/website.py @@ -1,4 +1,4 @@ -from typing import final +import typing as t from ..utils import URL from .fundamental_component import FundamentalComponent @@ -8,7 +8,7 @@ __all__ = [ ] -@final +@t.final class Website(FundamentalComponent): """ Displays a website. diff --git a/rio/data_models.py b/rio/data_models.py index 1695a3fb..0d0e0ff0 100644 --- a/rio/data_models.py +++ b/rio/data_models.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import dataclass -from typing import * # type: ignore import uniserde @@ -35,7 +35,7 @@ class InitialClientMessage(uniserde.Serde): url: str # Don't annotate this as JsonDoc because uniserde doesn't support unions - user_settings: dict[str, Any] + user_settings: dict[str, t.Any] prefers_light_theme: bool @@ -183,7 +183,7 @@ class ComponentLayout(uniserde.Serde): @dataclass class UnittestComponentLayout(ComponentLayout): # Additional, component-specific information - aux: dict[str, Any] + aux: dict[str, t.Any] @dataclass diff --git a/rio/dataclass.py b/rio/dataclass.py index c9a14b1c..fb6c9d2a 100644 --- a/rio/dataclass.py +++ b/rio/dataclass.py @@ -5,16 +5,11 @@ import copy import dataclasses import functools import inspect -from collections.abc import Callable -from typing import * # type: ignore +import typing as t from typing_extensions import ( - Any, - ClassVar, Self, - TypeVar, dataclass_transform, - get_origin, ) from . import inspection @@ -28,18 +23,18 @@ __all__ = [ ] -T = TypeVar("T") +T = t.TypeVar("T") _FIELDS_BY_CLASS: dict[type, dict[str, RioField]] = {} -def class_local_fields(cls: type) -> Mapping[str, RioField]: +def class_local_fields(cls: type) -> t.Mapping[str, RioField]: return _FIELDS_BY_CLASS.get(cls, {}) @functools.cache -def all_class_fields(cls: type) -> Mapping[str, RioField]: +def all_class_fields(cls: type) -> t.Mapping[str, RioField]: result = dict[str, RioField]() for cls in reversed(cls.__mro__): @@ -65,11 +60,11 @@ class RioField(dataclasses.Field): repr: bool = True, hash: bool = False, compare: bool = False, - metadata: Any = None, + metadata: t.Any = None, kw_only: bool | dataclasses._MISSING_TYPE = dataclasses.MISSING, default: object = dataclasses.MISSING, default_factory: ( - Callable[[], object] | dataclasses._MISSING_TYPE + t.Callable[[], object] | dataclasses._MISSING_TYPE ) = dataclasses.MISSING, real_default_value: object = dataclasses.MISSING, state_property: bool = True, @@ -112,7 +107,7 @@ def internal_field( *, default: object = dataclasses.MISSING, default_factory: ( - Callable[[], object] | dataclasses._MISSING_TYPE + t.Callable[[], object] | dataclasses._MISSING_TYPE ) = dataclasses.MISSING, # vscode doesn't understand default values, so the parameter that affect # static type checking (like `init`) must be explicitly passed in. @@ -120,7 +115,7 @@ def internal_field( repr: bool = False, state_property: bool = False, serialize: bool = False, -) -> Any: +) -> t.Any: return RioField( default=default, default_factory=default_factory, @@ -131,7 +126,7 @@ def internal_field( ) -def _make_default_factory_for_value(value: T) -> Callable[[], T]: +def _make_default_factory_for_value(value: T) -> t.Callable[[], T]: return functools.partial(copy.deepcopy, value) @@ -182,7 +177,7 @@ class RioDataclassMeta(abc.ABCMeta): continue # Skip `ClassVar` annotations - if get_origin(annotation) is ClassVar: + if t.get_origin(annotation) is t.ClassVar: continue try: diff --git a/rio/debug/dev_tools/component_attributes.py b/rio/debug/dev_tools/component_attributes.py index 1d3a9868..da897806 100644 --- a/rio/debug/dev_tools/component_attributes.py +++ b/rio/debug/dev_tools/component_attributes.py @@ -1,8 +1,8 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY from pathlib import Path -from typing import * # type: ignore import rio import rio.docs @@ -140,7 +140,7 @@ class ComponentAttributes(rio.Component): self, result: DetailsGrid, target: rio.Component, - debug_details: dict[str, Any], + debug_details: dict[str, t.Any], ) -> None: # Add the component's attributes result.add_heading3("Custom Attributes", margin_top=0) @@ -337,7 +337,7 @@ class DetailsGrid: *, row: int | None = None, column: int, - justify: Literal["left", "center", "right"] = "right", + justify: t.Literal["left", "center", "right"] = "right", ellipsize: bool = False, component_width: float = 0, column_width: int = 1, @@ -362,7 +362,7 @@ class DetailsGrid: row: int | None = None, column: int, width: int = 1, - justify: Literal["left", "center", "right"] = "left", + justify: t.Literal["left", "center", "right"] = "left", ellipsize: bool = False, ) -> None: self.add( diff --git a/rio/debug/dev_tools/dev_tools_sidebar.py b/rio/debug/dev_tools/dev_tools_sidebar.py index d201d820..4baabc6e 100644 --- a/rio/debug/dev_tools/dev_tools_sidebar.py +++ b/rio/debug/dev_tools/dev_tools_sidebar.py @@ -1,6 +1,6 @@ import os +import typing as t from pathlib import Path -from typing import * # type: ignore import rio.components.class_container import rio.debug.dev_tools.dev_tools_connector @@ -20,7 +20,7 @@ class DevToolsSidebar(rio.Component): show_rio_developer_page: bool = False selected_page: ( - Literal[ + t.Literal[ "project", "tree", "docs", diff --git a/rio/debug/dev_tools/icons_page.py b/rio/debug/dev_tools/icons_page.py index b3d54e8b..76006674 100644 --- a/rio/debug/dev_tools/icons_page.py +++ b/rio/debug/dev_tools/icons_page.py @@ -1,8 +1,8 @@ import dataclasses import functools import re +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore import fuzzywuzzy.fuzz @@ -11,7 +11,7 @@ from rio import icon_registry from . import sample_icons_grid -T = TypeVar("T") +T = t.TypeVar("T") # Replace all sequences non-alphanumeric characters with a single dot @@ -41,7 +41,7 @@ def search( *, min_results: int = 5, threshold: float = 0.85, - key: Callable[[T], str] = lambda x: x, # type: ignore + key: t.Callable[[T], str] = lambda x: x, # type: ignore ) -> list[tuple[T, float]]: """ Given a set of candidate strings, return the best matches for the provided @@ -106,7 +106,7 @@ def get_available_icons() -> list[tuple[str, str, tuple[str | None, ...]]]: variants.add(variant_name) # Drop the dict - as_list: list[Any] = list(result_dict.values()) + as_list: list[t.Any] = list(result_dict.values()) # Sort the result as_list.sort(key=lambda x: x[1]) @@ -134,7 +134,7 @@ class IconsPage(rio.Component): dataclasses.field(default_factory=tuple) ) selected_variant: str | None = None - selected_fill: Literal[ + selected_fill: t.Literal[ "primary", "secondary", "success", diff --git a/rio/debug/dev_tools/layout_display.py b/rio/debug/dev_tools/layout_display.py index 82ae9cad..e9ec25ba 100644 --- a/rio/debug/dev_tools/layout_display.py +++ b/rio/debug/dev_tools/layout_display.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore from uniserde import JsonDoc @@ -14,7 +14,7 @@ __all__ = [ ] -@final +@t.final class LayoutDisplay(FundamentalComponent): component_id: int # This can be invalid. The component must deal with it. @@ -24,7 +24,7 @@ class LayoutDisplay(FundamentalComponent): on_component_change: rio.EventHandler[int] = None on_layout_change: rio.EventHandler[[]] = None - async def _on_message_(self, msg: Any) -> None: + async def _on_message_(self, msg: t.Any) -> None: # Parse the message assert isinstance(msg, dict), msg assert msg["type"] == "layoutChange", msg diff --git a/rio/debug/dev_tools/layout_explainer.py b/rio/debug/dev_tools/layout_explainer.py index 5a5eee98..b8f34043 100644 --- a/rio/debug/dev_tools/layout_explainer.py +++ b/rio/debug/dev_tools/layout_explainer.py @@ -1,7 +1,7 @@ from __future__ import annotations import io -from typing import * # type: ignore +import typing as t import rio.data_models @@ -40,7 +40,7 @@ FULL_SIZE_SINGLE_CONTAINERS: set[type[rio.Component]] = { # These components make use of the `grow_...` attributes in at least one axis. -CONTAINERS_SUPPORTING_GROW: Iterable[type[rio.Component]] = { +CONTAINERS_SUPPORTING_GROW: t.Iterable[type[rio.Component]] = { rio.Column, rio.Grid, rio.Row, @@ -133,9 +133,9 @@ class LayoutExplainer: def _explain_allocated_space_before_alignment( self, - axis_name: Literal["width", "height"], - suggest_shrink: Callable[[str], None], - suggest_grow: Callable[[str], None], + axis_name: t.Literal["width", "height"], + suggest_shrink: t.Callable[[str], None], + suggest_grow: t.Callable[[str], None], ) -> str: """ Given a component and its layout, return a human readable explanation @@ -269,9 +269,9 @@ class LayoutExplainer: async def _explain_layout_in_axis( self, - axis_name: Literal["width", "height"], - suggest_shrink: Callable[[str], None], - suggest_grow: Callable[[str], None], + axis_name: t.Literal["width", "height"], + suggest_shrink: t.Callable[[str], None], + suggest_grow: t.Callable[[str], None], ) -> str: """ Given a component, come up with a human readable explanation for why it was diff --git a/rio/debug/dev_tools/layout_subpage.py b/rio/debug/dev_tools/layout_subpage.py index 9b2fe1ea..6c73d337 100644 --- a/rio/debug/dev_tools/layout_subpage.py +++ b/rio/debug/dev_tools/layout_subpage.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY -from typing import * # type: ignore import rio import rio.components.fundamental_component @@ -11,7 +11,7 @@ from . import layout_explainer class SizeControls(rio.Component): - label: Literal["width", "height"] + label: t.Literal["width", "height"] grow: bool min_value: float @@ -216,7 +216,7 @@ class LayoutSubpage(rio.Component): except KeyError: self._layout_explainer = None - async def _update_target_attribute(self, name: str, value: Any) -> None: + async def _update_target_attribute(self, name: str, value: t.Any) -> None: """ Updates an attribute of the target component. diff --git a/rio/debug/dev_tools/rio_developer_page.py b/rio/debug/dev_tools/rio_developer_page.py index ca2643a3..95e62279 100644 --- a/rio/debug/dev_tools/rio_developer_page.py +++ b/rio/debug/dev_tools/rio_developer_page.py @@ -1,8 +1,8 @@ import cProfile import io import marshal +import typing as t from pathlib import Path -from typing import * # type: ignore import rio.debug.dev_tools.component_tree import rio.debug.layouter @@ -124,7 +124,7 @@ for identifying performance bottlenecks in your code. out_dir = Path.cwd() / "rio-layout-dump" out_dir.mkdir(parents=True, exist_ok=True) - def dump(which: Literal["should", "are"]) -> None: + def dump(which: t.Literal["should", "are"]) -> None: # Export the layouts to a JSON file ly.debug_dump_json( which=which, diff --git a/rio/debug/dev_tools/sample_icons_grid.py b/rio/debug/dev_tools/sample_icons_grid.py index 78368bad..9265b754 100644 --- a/rio/debug/dev_tools/sample_icons_grid.py +++ b/rio/debug/dev_tools/sample_icons_grid.py @@ -1,6 +1,6 @@ import functools import random -from typing import * # type: ignore +import typing as t import rio @@ -12,7 +12,7 @@ GRID_N_COLUMNS = 6 # Choose icons to display -def find_icons_to_display() -> Iterable[str]: +def find_icons_to_display() -> t.Iterable[str]: # Get a list of all available icons names_and_variants: list[tuple[str, str | None]] = list( icon_registry.all_icons_in_set(ICON_SET) diff --git a/rio/debug/dev_tools/theme_picker_page.py b/rio/debug/dev_tools/theme_picker_page.py index 16b28112..484276ca 100644 --- a/rio/debug/dev_tools/theme_picker_page.py +++ b/rio/debug/dev_tools/theme_picker_page.py @@ -1,6 +1,6 @@ import functools import io -from typing import * # type: ignore +import typing as t import rio @@ -21,7 +21,7 @@ def colors_equal(color1: rio.Color, color2: rio.Color) -> bool: ) -def get_minimum_theme_kwargs(theme: rio.Theme) -> dict[str, Any]: +def get_minimum_theme_kwargs(theme: rio.Theme) -> dict[str, t.Any]: """ Given a theme, returns a dictionary with the minimum set of keyword arguments required to recreate it. @@ -29,7 +29,7 @@ def get_minimum_theme_kwargs(theme: rio.Theme) -> dict[str, Any]: # This is more complex than it might seem at first, because many colors are # derived from other colors. For example, the neutral color is derived from # the primary one. - result: dict[str, Any] = {} + result: dict[str, t.Any] = {} # Light / dark mode can impact some colors. Make sure to get that value # first. @@ -103,7 +103,7 @@ def get_minimum_theme_kwargs(theme: rio.Theme) -> dict[str, Any]: async def update_and_apply_theme( session: rio.Session, - theme_replacements: dict[str, Any], + theme_replacements: dict[str, t.Any], ) -> None: """ Overrides the session's theme with the given one, and makes sure to update diff --git a/rio/debug/dev_tools/tree_page.py b/rio/debug/dev_tools/tree_page.py index 9ec352ed..91ba876e 100644 --- a/rio/debug/dev_tools/tree_page.py +++ b/rio/debug/dev_tools/tree_page.py @@ -1,4 +1,4 @@ -from typing import * # type: ignore +import typing as t import rio.debug.dev_tools.component_picker import rio.debug.dev_tools.component_tree @@ -16,7 +16,7 @@ class TreePage(rio.Component): views. """ - current_view: Literal["tree", "attributes", "layout"] = "tree" + current_view: t.Literal["tree", "attributes", "layout"] = "tree" # This can be invalid. The component must deal with it. selected_component_id: int = -1 diff --git a/rio/debug/layouter.py b/rio/debug/layouter.py index 7b802c71..122b5a03 100644 --- a/rio/debug/layouter.py +++ b/rio/debug/layouter.py @@ -2,8 +2,7 @@ from __future__ import annotations import json import sys -from typing import * # type: ignore -from typing import Iterable +import typing as t import PIL.Image import PIL.ImageDraw @@ -15,8 +14,8 @@ import rio.components.fundamental_component import rio.components.root_components from rio.data_models import UnittestComponentLayout -R = TypeVar("R") -P = ParamSpec("P") +R = t.TypeVar("R") +P = t.ParamSpec("P") # These components pass on the entirety of the available space to their @@ -34,7 +33,7 @@ FULL_SIZE_SINGLE_CONTAINERS: set[type[rio.Component]] = { } -def specialized(func: Callable[P, R]) -> Callable[P, R]: +def specialized(func: t.Callable[P, R]) -> t.Callable[P, R]: """ Decorator that defers to a specialized method if one exists. If none is found, calls the original method. @@ -44,7 +43,7 @@ def specialized(func: Callable[P, R]) -> Callable[P, R]: `foo` otherwise. """ - def result(self, component, *args, **kwargs) -> Any: + def result(self, component, *args, **kwargs) -> t.Any: # Special case: A lot of containers behave in the same way - they pass # on all space. Avoid having to implement them all separately. if type(component) in FULL_SIZE_SINGLE_CONTAINERS or not isinstance( @@ -68,7 +67,7 @@ def specialized(func: Callable[P, R]) -> Callable[P, R]: def _linear_container_get_major_axis_natural_size( child_requested_sizes: list[float], spacing: float, - proportions: None | Literal["homogeneous"] | Sequence[float], + proportions: None | t.Literal["homogeneous"] | t.Sequence[float], ) -> float: # Spacing result = spacing * (len(child_requested_sizes) - 1) @@ -107,7 +106,7 @@ def _linear_container_get_major_axis_allocated_sizes( child_requested_sizes: list[float], child_growers: list[bool], spacing: float, - proportions: None | Literal["homogeneous"] | Sequence[float], + proportions: None | t.Literal["homogeneous"] | t.Sequence[float], ) -> list[tuple[float, float]]: starts_and_sizes: list[tuple[float, float]] = [] @@ -197,7 +196,7 @@ def calculate_alignment( def iter_direct_tree_children( component: rio.Component, -) -> Iterable[rio.Component]: +) -> t.Iterable[rio.Component]: """ Iterates over the children of a component. In particular, the children which are part of the component tree, rather than those stored in the components @@ -228,7 +227,7 @@ class Layouter: # Function to filter uninteresting components. If this returns `False` for a # given component, that component and all of its children are ignored. - _filter: Callable[[rio.Component], bool] + _filter: t.Callable[[rio.Component], bool] # All components in the session, ordered such that each parent appears # before its children. @@ -244,7 +243,7 @@ class Layouter: async def create( session: rio.Session, *, - filter: Callable[[rio.Component], bool] = lambda _: True, + filter: t.Callable[[rio.Component], bool] = lambda _: True, ) -> Layouter: # Create a new instance self = Layouter.__new__(Layouter) @@ -326,7 +325,7 @@ class Layouter: def _get_toposorted( self, root: rio.Component, - ) -> Iterable[rio.Component]: + ) -> t.Iterable[rio.Component]: """ Returns the component, as well as all direct and indirect children. The results are ordered such that each parent appears before its children. @@ -786,8 +785,8 @@ class Layouter: def debug_dump_json( self, - which: Literal["should", "are"], - out: IO[str], + which: t.Literal["should", "are"], + out: t.IO[str], ) -> None: """ Dumps the layouts to a JSON file. @@ -798,7 +797,7 @@ class Layouter: ) # Convert the class instances to JSON - result: list[dict[str, Any]] = [] + result: list[dict[str, t.Any]] = [] def dump_recursive(component: rio.Component) -> None: # Honor the filter function @@ -836,7 +835,7 @@ class Layouter: def debug_draw( self, - which: Literal["should", "are"], + which: t.Literal["should", "are"], *, pixels_per_unit: float = 10, ) -> PIL.Image.Image: diff --git a/rio/debug/monkeypatches.py b/rio/debug/monkeypatches.py index 259a70ee..4b6d86a3 100644 --- a/rio/debug/monkeypatches.py +++ b/rio/debug/monkeypatches.py @@ -1,5 +1,5 @@ +import typing as t from pathlib import Path -from typing import * # type: ignore import introspection.typing @@ -157,7 +157,7 @@ def LinearContainer_init( wrapped_method, self: components.Row, *children, - proportions: Literal["homogeneous"] | Sequence[float] | None = None, + proportions: t.Literal["homogeneous"] | t.Sequence[float] | None = None, **kwargs, ) -> None: # Proportions related checks diff --git a/rio/debug/typing_utils.py b/rio/debug/typing_utils.py index 39967ba0..f7b2884c 100644 --- a/rio/debug/typing_utils.py +++ b/rio/debug/typing_utils.py @@ -1,6 +1,5 @@ import types from dataclasses import dataclass -from typing import * # type: ignore import introspection.typing diff --git a/rio/debug/validator.py b/rio/debug/validator.py index e45734e2..a552ca42 100644 --- a/rio/debug/validator.py +++ b/rio/debug/validator.py @@ -4,9 +4,9 @@ import collections import copy import json import re +import typing as t from dataclasses import dataclass from pathlib import Path -from typing import * # type: ignore from uniserde import Jsonable, JsonDoc @@ -66,7 +66,7 @@ class ClientComponent: state=delta_state, ) - def _get_child_attribute_names(self) -> Iterable[str]: + def _get_child_attribute_names(self) -> t.Iterable[str]: child_attr_names = inspection.get_child_component_containing_attribute_names_for_builtin_components() try: return child_attr_names[self.type] @@ -104,7 +104,7 @@ class ClientComponent: return result @property - def referenced_child_ids(self) -> Iterable[int]: + def referenced_child_ids(self) -> t.Iterable[int]: for property_value in self.child_containing_properties.values(): if property_value is None: continue @@ -281,7 +281,7 @@ class Validator: return result - def handle_incoming_message(self, msg: Any) -> None: + def handle_incoming_message(self, msg: t.Any) -> None: """ Process a message passed from Client -> Server. @@ -304,7 +304,7 @@ class Validator: handler(msg["params"]) - def handle_outgoing_message(self, msg: Any) -> None: + def handle_outgoing_message(self, msg: t.Any) -> None: """ Process a message passed from Server -> Client. @@ -327,7 +327,7 @@ class Validator: handler(msg["params"]) - def _handle_outgoing_updateComponentStates(self, msg: Any) -> None: + def _handle_outgoing_updateComponentStates(self, msg: t.Any) -> None: # Dump the message, if requested self.dump_message(msg, incoming=False) @@ -438,7 +438,7 @@ class Validator: # Dump the client state if requested self.dump_client_state() - def _handle_outgoing_evaluateJavascript(self, msg: Any): + def _handle_outgoing_evaluateJavascript(self, msg: t.Any): # Is this message registering a new component class? match = re.search( r"window.componentClasses\['(.*)'\]", msg["javaScriptSource"] diff --git a/rio/deprecations.py b/rio/deprecations.py index 25cc633d..45ad8ad4 100644 --- a/rio/deprecations.py +++ b/rio/deprecations.py @@ -1,6 +1,6 @@ import functools +import typing as t import warnings -from typing import * # type: ignore import introspection @@ -9,7 +9,7 @@ from .warnings import * # The alias here is necessary to avoid ruff stupidly replacing the import with # a `pass`. -if TYPE_CHECKING: +if t.TYPE_CHECKING: import rio as rio __all__ = [ @@ -20,9 +20,9 @@ __all__ = [ ] -CO = TypeVar("CO", bound="rio.Component") -C = TypeVar("C", bound=Union[Callable, ComponentMeta]) -F = TypeVar("F", bound=Callable) +CO = t.TypeVar("CO", bound="rio.Component") +C = t.TypeVar("C", bound=t.Union[t.Callable, ComponentMeta]) +F = t.TypeVar("F", bound=t.Callable) def warn( @@ -68,11 +68,11 @@ def warn_parameter_renamed( ) -@overload -def deprecated(*, since: str, replacement: Callable | str): ... +@t.overload +def deprecated(*, since: str, replacement: t.Callable | str): ... -@overload +@t.overload def deprecated(*, since: str, description: str): ... @@ -80,7 +80,7 @@ def deprecated( *, since: str, description: str | None = None, - replacement: Callable | str | None = None, + replacement: t.Callable | str | None = None, ): if replacement is not None: if not isinstance(replacement, str): @@ -115,7 +115,7 @@ def component_kwarg_renamed( the contained `_remap_constructor_arguments` method) """ - def decorator(component_class: Type[CO]) -> Type[CO]: + def decorator(component_class: t.Type[CO]) -> t.Type[CO]: old_remap = component_class._remap_constructor_arguments_ @staticmethod @@ -149,7 +149,7 @@ def component_kwarg_renamed( def parameters_remapped( *, since: str, - **params: Callable[[Any], dict[str, Any]], + **params: t.Callable[[t.Any], dict[str, t.Any]], ): """ This is a function decorator that's quite similar to `parameters_renamed`, @@ -160,14 +160,14 @@ def parameters_remapped( parameter as input and return a dict `{'new_parameter_name': value}`. Example: `Theme.from_colors` used to have a `light: bool = True` parameter - which was changed to `mode: Literal['light', 'dark'] = 'light'`. + which was changed to `mode: t.Literal['light', 'dark'] = 'light'`. class Theme: @parameters_remapped( '0.9', light=lambda light: {"mode": "light" if light else "dark"}, ) - def from_colors(..., mode: Literal['light', 'dark'] = 'light'): + def from_colors(..., mode: t.Literal['light', 'dark'] = 'light'): ... Theme.from_colors(light=False) # Equivalent to `mode='dark'` @@ -203,7 +203,7 @@ def _remap_kwargs( since: str, func_name: str, kwargs: dict[str, object], - old_names_to_new_names: Mapping[str, str], + old_names_to_new_names: t.Mapping[str, str], ) -> None: for old_name, new_name in old_names_to_new_names.items(): try: @@ -223,7 +223,7 @@ def function_kwarg_renamed( since: str, old_name: str, new_name: str, -) -> Callable[[F], F]: +) -> t.Callable[[F], F]: """ This decorator helps with renaming a keyword argument of a function, NOT a component. diff --git a/rio/docs.py b/rio/docs.py index a7ffb633..9a2a3272 100644 --- a/rio/docs.py +++ b/rio/docs.py @@ -6,7 +6,7 @@ import dataclasses import functools import re import types -from typing import * # type: ignore +import typing as t import imy.docstrings import introspection @@ -25,8 +25,8 @@ __all__ = [ ] -Class = TypeVar("Class", bound=type) -ClassOrFunction = TypeVar("ClassOrFunction", bound=type | types.FunctionType) +Class = t.TypeVar("Class", bound=type) +ClassOrFunction = t.TypeVar("ClassOrFunction", bound=type | types.FunctionType) _NAME_TO_URL: dict[str, str] | None = None @@ -118,16 +118,16 @@ def mark_constructor_as_private(cls: Class) -> Class: return cls -@overload +@t.overload def get_docs_for(obj: type) -> imy.docstrings.ClassDocs: ... -@overload +@t.overload def get_docs_for(obj: types.FunctionType) -> imy.docstrings.FunctionDocs: ... def get_docs_for( - obj: Callable | Type, + obj: t.Callable | t.Type, ) -> imy.docstrings.ClassDocs | imy.docstrings.FunctionDocs: """ Parse the docs for a component and return them. The results are cached, so @@ -181,7 +181,7 @@ def build_documentation_url( return rio.URL(result_string) -def _find_possibly_public_objects() -> Iterable[Type | Callable]: +def _find_possibly_public_objects() -> t.Iterable[t.Type | t.Callable]: """ Finds all objects in rio that might be public. This uses heuristics to filter out many internal objects, but it's not perfect. @@ -260,8 +260,9 @@ def _find_possibly_public_objects() -> Iterable[Type | Callable]: @functools.cache def _get_unprocessed_docs() -> ( - Mapping[ - type | Callable, imy.docstrings.ClassDocs | imy.docstrings.FunctionDocs + t.Mapping[ + type | t.Callable, + imy.docstrings.ClassDocs | imy.docstrings.FunctionDocs, ] ): """ @@ -296,8 +297,9 @@ def _get_unprocessed_docs() -> ( @functools.cache def find_documented_objects() -> ( - Mapping[ - type | Callable, imy.docstrings.ClassDocs | imy.docstrings.FunctionDocs + t.Mapping[ + type | t.Callable, + imy.docstrings.ClassDocs | imy.docstrings.FunctionDocs, ] ): """ diff --git a/rio/event.py b/rio/event.py index bbbccc69..61e76f46 100644 --- a/rio/event.py +++ b/rio/event.py @@ -1,8 +1,8 @@ from __future__ import annotations import enum +import typing as t from datetime import timedelta -from typing import * # type: ignore __all__ = [ "on_mount", @@ -14,15 +14,15 @@ __all__ = [ ] -R = TypeVar("R") -SyncOrAsync = R | Awaitable[R] -SyncOrAsyncNone = TypeVar("SyncOrAsyncNone", bound=SyncOrAsync[None]) +R = t.TypeVar("R") +SyncOrAsync = R | t.Awaitable[R] +SyncOrAsyncNone = t.TypeVar("SyncOrAsyncNone", bound=SyncOrAsync[None]) -Func = TypeVar("Func", bound=Callable) -Decorator = Callable[[Func], Func] +Func = t.TypeVar("Func", bound=t.Callable) +Decorator = t.Callable[[Func], Func] -MethodWithNoParametersVar = TypeVar( - "MethodWithNoParametersVar", bound=Callable[[Any], Any] +MethodWithNoParametersVar = t.TypeVar( + "MethodWithNoParametersVar", bound=t.Callable[[t.Any], t.Any] ) @@ -45,16 +45,16 @@ class EventTag(enum.Enum): def _tag_as_event_handler( - function: Callable, + function: t.Callable, tag: EventTag, - args: Any, + args: t.Any, ) -> None: """ Registers the function as an event handler for the given tag. This simply adds a marker to the function's `__dict__` so that it can later be recognized as an event handler. """ - all_events: dict[EventTag, list[Any]] = vars(function).setdefault( + all_events: dict[EventTag, list[t.Any]] = vars(function).setdefault( "_rio_events_", {} ) events_like_this = all_events.setdefault(tag, []) diff --git a/rio/fills.py b/rio/fills.py index f481bd59..591214e2 100644 --- a/rio/fills.py +++ b/rio/fills.py @@ -1,8 +1,8 @@ from __future__ import annotations +import typing as t from abc import ABC from dataclasses import dataclass -from typing import Literal from typing_extensions import TypeAlias from uniserde import Jsonable @@ -157,7 +157,7 @@ class ImageFill(Fill): self, image: ImageLike, *, - fill_mode: Literal["fit", "stretch", "zoom"] = "fit", + fill_mode: t.Literal["fit", "stretch", "zoom"] = "fit", ) -> None: """ ## Parameters diff --git a/rio/global_state.py b/rio/global_state.py index 50818fc7..a49663f9 100644 --- a/rio/global_state.py +++ b/rio/global_state.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio __all__ = [ diff --git a/rio/icon_registry.py b/rio/icon_registry.py index 50ee243d..1202c84b 100644 --- a/rio/icon_registry.py +++ b/rio/icon_registry.py @@ -2,8 +2,8 @@ from __future__ import annotations import logging import tarfile +import typing as t from pathlib import Path -from typing import * # type: ignore from . import utils from .errors import AssetError @@ -189,7 +189,7 @@ def get_icon_svg(icon_name: str) -> str: def _get_variant_directories( icon_set: str, -) -> Iterable[tuple[str | None, Path]]: +) -> t.Iterable[tuple[str | None, Path]]: """ Given the name of an icon set, list the names of all variants in that set along with the directory they are stored in. @@ -214,14 +214,14 @@ def _get_variant_directories( yield (None, icon_set_dir) -def all_icon_sets() -> Iterable[str]: +def all_icon_sets() -> t.Iterable[str]: """ Return the names of all icon set names known to rio. """ return icon_set_archives.keys() -def all_variants_in_set(icon_set: str) -> Iterable[str | None]: +def all_variants_in_set(icon_set: str) -> t.Iterable[str | None]: """ Given the name of an icon set, list the names of all variants in that set. @@ -234,7 +234,7 @@ def all_icons_in_set( icon_set: str, *, variant: str | None = None, -) -> Iterable[tuple[str, str | None]]: +) -> t.Iterable[tuple[str, str | None]]: """ Given the name of an icon set, list all icon names and variants in that set. If `variant` is given, only return icons with that variant. diff --git a/rio/inspection.py b/rio/inspection.py index 3ab256b8..7d192a88 100644 --- a/rio/inspection.py +++ b/rio/inspection.py @@ -4,8 +4,7 @@ import collections import functools import inspect import sys -from collections.abc import Collection, Iterator, Mapping -from typing import Type +import typing as t import introspection.typing @@ -20,7 +19,7 @@ __all__ = [ _EXPLICITLY_SET_STATE_PROPERTY_NAMES_CACHE: dict[ - tuple[Type[rio.Component], int, frozenset[str]], frozenset[str] + tuple[t.Type[rio.Component], int, frozenset[str]], frozenset[str] ] = {} @@ -28,7 +27,7 @@ _EXPLICITLY_SET_STATE_PROPERTY_NAMES_CACHE: dict[ # times can give different outputs. For example, if called immediately after the # input class has been created, some forward references may not be evaluatable # yet. -class get_local_annotations(Mapping[str, introspection.types.TypeAnnotation]): +class get_local_annotations(t.Mapping[str, introspection.types.TypeAnnotation]): def __init__(self, cls: type, *, strict: bool = False) -> None: # Note: Don't use `typing.get_type_hints` because it has a stupid bug in # python 3.10 where it dies if something is annotated as @@ -46,7 +45,7 @@ class get_local_annotations(Mapping[str, introspection.types.TypeAnnotation]): treat_name_errors_as_imports=True, ) - def __iter__(self) -> Iterator[str]: + def __iter__(self) -> t.Iterator[str]: return iter(self._annotations) def __len__(self) -> int: @@ -55,7 +54,7 @@ class get_local_annotations(Mapping[str, introspection.types.TypeAnnotation]): def get_resolved_type_annotations( cls: type, -) -> Mapping[str, type]: +) -> t.Mapping[str, type]: maps = [get_local_annotations(c, strict=True) for c in cls.__mro__] return collections.ChainMap(*maps) # type: ignore @@ -63,7 +62,7 @@ def get_resolved_type_annotations( @functools.lru_cache(maxsize=None) def get_child_component_containing_attribute_names( cls: type[rio.Component], -) -> Collection[str]: +) -> t.Collection[str]: from . import serialization attr_names: list[str] = [] @@ -95,7 +94,7 @@ def get_child_component_containing_attribute_names( @functools.lru_cache(maxsize=None) def get_child_component_containing_attribute_names_for_builtin_components() -> ( - Mapping[str, Collection[str]] + t.Mapping[str, t.Collection[str]] ): from .components.fundamental_component import FundamentalComponent diff --git a/rio/maybes.py b/rio/maybes.py index 2643f61d..ce5b1096 100644 --- a/rio/maybes.py +++ b/rio/maybes.py @@ -10,11 +10,11 @@ used. from __future__ import annotations import sys -from typing import * # type: ignore +import typing as t import introspection -if TYPE_CHECKING: +if t.TYPE_CHECKING: import matplotlib.axes # type: ignore import matplotlib.figure # type: ignore import numpy # type: ignore @@ -24,7 +24,7 @@ if TYPE_CHECKING: _IS_INITIALIZED = False -T = TypeVar("T") +T = t.TypeVar("T") FLOAT_TYPES = () @@ -42,7 +42,7 @@ MATPLOTLIB_GRAPH_TYPES: tuple[type, ...] = () MATPLOTLIB_AXES_TYPES: tuple[type[matplotlib.axes.Axes], ...] = () # This is a mapping of "weird" types to the "canonical" type, like `{np.int8: int}` -TYPE_NORMALIZERS: dict[type[T], Callable[[T], T]] = {} # type: ignore +TYPE_NORMALIZERS: dict[type[T], t.Callable[[T], T]] = {} # type: ignore def initialize(force: bool = False) -> None: diff --git a/rio/path_match.py b/rio/path_match.py index 09d85ba1..f92b7980 100644 --- a/rio/path_match.py +++ b/rio/path_match.py @@ -1,5 +1,5 @@ +import typing as t from pathlib import Path -from typing import * # type: ignore import gitignore_parser @@ -22,7 +22,7 @@ class PathMatch: self, base_dir: Path, *, - rules: Iterable[str] = tuple(), + rules: t.Iterable[str] = tuple(), ) -> None: self._base_dir = base_dir.resolve() self._rules: list[gitignore_parser.IgnoreRule] = [] diff --git a/rio/project_config.py b/rio/project_config.py index 3fa66872..6b96729a 100644 --- a/rio/project_config.py +++ b/rio/project_config.py @@ -1,8 +1,8 @@ from __future__ import annotations import functools +import typing as t from pathlib import Path -from typing import * # type: ignore import revel import tomlkit @@ -16,14 +16,14 @@ from . import path_match __all__ = ["RioProjectConfig"] -T = TypeVar("T") +T = t.TypeVar("T") DEFAULT_FATAL = object() DEFAULT_KEYERROR = object() -DEFAULT_PROJECT_FILES_GLOB_PATTERNS: Iterable[str] = ( +DEFAULT_PROJECT_FILES_GLOB_PATTERNS: t.Iterable[str] = ( "*.py", "/assets/", "/rio.toml", @@ -55,7 +55,7 @@ class RioProjectConfig: # { # (section, key): value # } - self._toml_dict: dict[tuple[str, str], Any] = {} + self._toml_dict: dict[tuple[str, str], t.Any] = {} self._replace_from_dictionary(toml_dict) def _replace_from_dictionary(self, raw: uniserde.JsonDoc) -> None: @@ -100,8 +100,8 @@ class RioProjectConfig: self, section_name: str, key_name: str, - key_type: Type[T], - default_value: Any, + key_type: t.Type[T], + default_value: t.Any, ) -> T: """ Fetches the value of a key from the `rio.toml` file. If the key is @@ -142,7 +142,7 @@ class RioProjectConfig: f" {key_type}, got {type(value).__name__}", ) - def set_key(self, section_name: str, key_name: str, value: Any) -> None: + def set_key(self, section_name: str, key_name: str, value: t.Any) -> None: """ Sets the value of a key in the `rio.toml` file. The value is not written to disk until `write()` is called. @@ -162,7 +162,7 @@ class RioProjectConfig: return self.rio_toml_path.parent @property - def app_type(self) -> Literal["app", "website"]: + def app_type(self) -> t.Literal["app", "website"]: """ Whether this project is a website or local app. """ @@ -176,7 +176,7 @@ class RioProjectConfig: return result @app_type.setter - def app_type(self, value: Literal["app", "website"]) -> None: + def app_type(self, value: t.Literal["app", "website"]) -> None: self.set_key("app", "app-type", value) @property @@ -205,7 +205,7 @@ class RioProjectConfig: return module_path.with_name(self.app_main_module + ".py") @property - def project_files_glob_patterns(self) -> Iterable[str]: + def project_files_glob_patterns(self) -> t.Iterable[str]: """ Each project includes a list of files which are considered to be part of the project. These files are specified using glob patterns. This @@ -219,7 +219,7 @@ class RioProjectConfig: ) @project_files_glob_patterns.setter - def project_files_glob_patterns(self, value: Iterable[str]) -> None: + def project_files_glob_patterns(self, value: t.Iterable[str]) -> None: self.set_key("app", "project-files", list(value)) def file_is_path_of_project(self, file_path: Path) -> bool: @@ -326,7 +326,7 @@ class RioProjectConfig: path: Path, *, main_module: str, - project_type: Literal["app", "website"], + project_type: t.Literal["app", "website"], ) -> RioProjectConfig: """ Write a new `rio.toml` file at the given file path. This file will @@ -527,7 +527,7 @@ def find_or_guess_project_directory() -> Path: return Path.cwd() -def iter_directories_upward(path: Path | None = None) -> Iterable[Path]: +def iter_directories_upward(path: Path | None = None) -> t.Iterable[Path]: if path is None: path = Path.cwd().absolute() diff --git a/rio/routing.py b/rio/routing.py index 469d2ca7..8fd3da04 100644 --- a/rio/routing.py +++ b/rio/routing.py @@ -3,7 +3,6 @@ from __future__ import annotations import logging import typing as t import warnings -from collections.abc import Callable, Iterable, Sequence from dataclasses import KW_ONLY, dataclass, field from pathlib import Path @@ -165,11 +164,11 @@ class ComponentPage: name: str url_segment: str - build: Callable[[], rio.Component] + build: t.Callable[[], rio.Component] _: KW_ONLY icon: str = DEFAULT_ICON - children: Sequence[ComponentPage | Redirect] = field(default_factory=list) - guard: Callable[[rio.GuardEvent], None | rio.URL | str] | None = None + children: t.Sequence[ComponentPage | Redirect] = field(default_factory=list) + guard: t.Callable[[rio.GuardEvent], None | rio.URL | str] | None = None meta_tags: dict[str, str] = field(default_factory=dict) # This is used to allow users to order pages when using the `rio.page` @@ -221,7 +220,7 @@ def Page(*args, **kwargs): def _get_active_page_instances( - available_pages: Iterable[rio.ComponentPage | rio.Redirect], + available_pages: t.Iterable[rio.ComponentPage | rio.Redirect], remaining_segments: tuple[str, ...], ) -> list[rio.ComponentPage | rio.Redirect]: """ @@ -278,7 +277,7 @@ class GuardEvent: # This is an `Sequence` rather than `list`, because the same event instance # is reused for multiple event handlers. This allows to assign a tuple, thus # preventing modifications. - active_pages: Sequence[ComponentPage | Redirect] + active_pages: t.Sequence[ComponentPage | Redirect] def check_page_guards( @@ -388,7 +387,7 @@ def check_page_guards( target_url_absolute = redirect -BuildFunction = Callable[[], "rio.Component"] +BuildFunction = t.Callable[[], "rio.Component"] C = t.TypeVar("C", bound=BuildFunction) @@ -400,7 +399,7 @@ def page( url_segment: str | None = None, name: str | None = None, icon: str = DEFAULT_ICON, - guard: Callable[[GuardEvent], None | rio.URL | str] | None = None, + guard: t.Callable[[GuardEvent], None | rio.URL | str] | None = None, meta_tags: dict[str, str] | None = None, order: int | None = None, ): @@ -534,7 +533,7 @@ def _auto_detect_pages_iter( directory: Path, *, package: str | None = None, -) -> Iterable[rio.ComponentPage]: +) -> t.Iterable[rio.ComponentPage]: try: contents = list(directory.iterdir()) except FileNotFoundError: diff --git a/rio/serialization.py b/rio/serialization.py index 6420c440..e25d4b05 100644 --- a/rio/serialization.py +++ b/rio/serialization.py @@ -7,7 +7,7 @@ import inspect import json import types import typing -from typing import * # type: ignore +import typing as t import introspection.types import uniserde @@ -23,11 +23,11 @@ from .self_serializing import SelfSerializing __all__ = ["serialize_json", "serialize_and_host_component"] -T = TypeVar("T") -Serializer = Callable[["session.Session", T], Jsonable] +T = t.TypeVar("T") +Serializer = t.Callable[["session.Session", T], Jsonable] -FILL_LIKES = {*get_args(fills._FillLike), None, type(None)} +FILL_LIKES = {*t.get_args(fills._FillLike), None, type(None)} def _float_or_zero(obj: object) -> float: @@ -171,8 +171,8 @@ def serialize_and_host_component(component: rio.Component) -> JsonDoc: @functools.lru_cache(maxsize=None) def get_attribute_serializers( - cls: Type[rio.Component], -) -> Mapping[str, Serializer]: + cls: t.Type[rio.Component], +) -> t.Mapping[str, Serializer]: """ Returns a dictionary of attribute names to their types that should be serialized for the given component class. @@ -223,13 +223,15 @@ def _serialize_child_component( def _serialize_sequence( - sess: session.Session, sequence: Sequence[T], item_serializer: Serializer[T] + sess: session.Session, + sequence: t.Sequence[T], + item_serializer: Serializer[T], ) -> Jsonable: return [item_serializer(sess, item) for item in sequence] def _serialize_enum( - sess: session.Session, value: object, as_type: Type[enum.Enum] + sess: session.Session, value: object, as_type: t.Type[enum.Enum] ) -> Jsonable: return uniserde.as_json(value, as_type=as_type) @@ -270,8 +272,8 @@ def _get_serializer_for_annotation( if annotation in (int, float, str, bool, None): return _serialize_basic_json_value - origin = get_origin(annotation) - args = get_args(annotation) + origin = t.get_origin(annotation) + args = t.get_args(annotation) # Python 3.10 crashes if you try `issubclass(list[str], SelfSerializing)`, # so we must make absolutely sure the annotation isn't a generic type @@ -299,10 +301,10 @@ def _get_serializer_for_annotation( ) # Literal - if origin is Literal: + if origin is t.Literal: return _serialize_basic_json_value - if origin in (Union, types.UnionType): + if origin in (t.Union, types.UnionType): # ColorSet if set(args) == color._color_set_args: return _serialize_colorset diff --git a/rio/session.py b/rio/session.py index bc44519a..d126c909 100644 --- a/rio/session.py +++ b/rio/session.py @@ -13,10 +13,9 @@ import string import time import traceback import typing +import typing as t import weakref -from collections.abc import Callable, Coroutine, Iterable from datetime import tzinfo -from typing import * # type: ignore import starlette.datastructures import unicall @@ -112,14 +111,14 @@ class Session(unicall.Unicall): # Type hints so the documentation generator knows which fields exist timezone: tzinfo - preferred_languages: Sequence[str] + preferred_languages: t.Sequence[str] window_width: float window_height: float theme: rio.Theme - http_headers: Mapping[str, str] + http_headers: t.Mapping[str, str] def __init__( self, @@ -129,7 +128,7 @@ class Session(unicall.Unicall): client_port: int, http_headers: starlette.datastructures.Headers, timezone: tzinfo, - preferred_languages: Iterable[str], + preferred_languages: t.Iterable[str], month_names_long: tuple[ str, str, str, str, str, str, str, str, str, str, str, str ], @@ -220,13 +219,13 @@ class Session(unicall.Unicall): # The methods don't have the component bound yet, so they don't unduly # prevent the component from being garbage collected. self._page_change_callbacks: weakref.WeakKeyDictionary[ - rio.Component, tuple[Callable[[rio.Component], None], ...] + rio.Component, tuple[t.Callable[[rio.Component], None], ...] ] = weakref.WeakKeyDictionary() # All components / methods which should be called when the session's # window size has changed. self._on_window_size_change_callbacks: weakref.WeakKeyDictionary[ - rio.Component, tuple[Callable[[rio.Component], None], ...] + rio.Component, tuple[t.Callable[[rio.Component], None], ...] ] = weakref.WeakKeyDictionary() # All fonts which have been registered with the session. This maps the @@ -268,7 +267,7 @@ class Session(unicall.Unicall): # A dict of {build_function: error_message}. This is cleared at # the start of every refresh, and tracks which build functions failed. # Used for unit testing. - self._crashed_build_functions = dict[Callable, str]() + self._crashed_build_functions = dict[t.Callable, str]() # Weak dictionaries to hold additional information about components. # These are split in two to avoid the dictionaries keeping the @@ -317,7 +316,7 @@ class Session(unicall.Unicall): # Information about the visitor self._client_ip: str = client_ip self._client_port: int = client_port - self.http_headers: Mapping[str, str] = http_headers + self.http_headers: t.Mapping[str, str] = http_headers # Instantiate the root component global_state.currently_building_component = None @@ -563,7 +562,7 @@ window.resizeTo(screen.availWidth, screen.availHeight); """ return self._transport is not None - def attach(self, value: Any) -> None: + def attach(self, value: t.Any) -> None: """ Attaches the given value to the `Session`. It can be retrieved later using `session[...]`. @@ -685,12 +684,12 @@ window.resizeTo(screen.availWidth, screen.availHeight); await asyncio.sleep(0.2) - @overload + @t.overload async def _call_event_handler( self, handler: utils.EventHandler[[]], *, refresh: bool ) -> None: ... - @overload + @t.overload async def _call_event_handler( self, handler: utils.EventHandler[[T]], @@ -732,13 +731,13 @@ window.resizeTo(screen.availWidth, screen.availHeight); if refresh: await self._refresh() - @overload + @t.overload def _call_event_handler_sync( self, handler: utils.EventHandler[[]], ) -> None: ... - @overload + @t.overload def _call_event_handler_sync( self, handler: utils.EventHandler[[T]], @@ -816,7 +815,7 @@ window.resizeTo(screen.availWidth, screen.availHeight); def create_task( self, - coro: Coroutine[Any, None, T], + coro: t.Coroutine[t.Any, None, T], *, name: str | None = None, ) -> asyncio.Task[T]: @@ -986,7 +985,7 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) def _refresh_sync( self, ) -> tuple[ - set[rio.Component], Iterable[rio.Component], Iterable[rio.Component] + set[rio.Component], t.Iterable[rio.Component], t.Iterable[rio.Component] ]: """ See `refresh` for details on what this function does. @@ -1405,7 +1404,7 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) # List / Collection elif isinstance(attr_value, list): - attr_value = cast(list[object], attr_value) + attr_value = t.cast(list[object], attr_value) for ii, item in enumerate(attr_value): if isinstance(item, rio.Component): @@ -1530,8 +1529,8 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) if not isinstance(old, list): return False - old = cast(list[object], old) - new = cast(list[object], new) + old = t.cast(list[object], old) + new = t.cast(list[object], new) if len(old) != len(new): return False @@ -1580,7 +1579,7 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) self, old_build: rio.Component, new_build: rio.Component, - ) -> Iterable[tuple[rio.Component, rio.Component]]: + ) -> t.Iterable[tuple[rio.Component, rio.Component]]: """ Given two component trees, find pairs of components which can be reconciled, i.e. which represent the "same" component. When exactly @@ -1638,7 +1637,7 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) return [attr] if isinstance(attr, list): - attr = cast(list[object], attr) + attr = t.cast(list[object], attr) return [ item for item in attr if isinstance(item, rio.Component) @@ -1851,7 +1850,7 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) section_name, _, key = key.rpartition(":") if section_name: - section = cast( + section = t.cast( JsonDoc, settings_json.setdefault("section:" + section_name, {}), ) @@ -1912,15 +1911,15 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) async def _save_settings_now_in_window( self, - settings_to_save: Iterable[ - tuple[user_settings_module.UserSettings, Iterable[str]] + settings_to_save: t.Iterable[ + tuple[user_settings_module.UserSettings, t.Iterable[str]] ], ) -> None: import aiofiles for settings, dirty_attributes in settings_to_save: if settings.section_name: - section = cast( + section = t.cast( JsonDoc, self._settings_json.setdefault( "section:" + settings.section_name, {} @@ -1953,11 +1952,11 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) async def _save_settings_now_in_browser( self, - settings_to_save: Iterable[ - tuple[user_settings_module.UserSettings, Iterable[str]] + settings_to_save: t.Iterable[ + tuple[user_settings_module.UserSettings, t.Iterable[str]] ], ) -> None: - delta_settings: dict[str, Any] = {} + delta_settings: dict[str, t.Any] = {} for settings, dirty_attributes in settings_to_save: prefix = ( @@ -2025,20 +2024,20 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) else: await self._remote_set_title(title) - @overload + @t.overload async def pick_file( self, *, - file_types: Iterable[str] | None = None, - multiple: Literal[False] = False, + file_types: t.Iterable[str] | None = None, + multiple: t.Literal[False] = False, ) -> utils.FileInfo: ... - @overload + @t.overload async def pick_file( self, *, - file_types: Iterable[str] | None = None, - multiple: Literal[True], + file_types: t.Iterable[str] | None = None, + multiple: t.Literal[True], ) -> list[utils.FileInfo]: ... @deprecations.function_kwarg_renamed( @@ -2049,7 +2048,7 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) async def pick_file( self, *, - file_types: Iterable[str] | None = None, + file_types: t.Iterable[str] | None = None, multiple: bool = False, ) -> utils.FileInfo | list[utils.FileInfo]: """ @@ -2096,20 +2095,20 @@ window.history.{method}(null, "", {json.dumps(active_page_url.path)}) multiple=multiple, ) - @overload + @t.overload async def file_chooser( self, *, - file_types: Iterable[str] | None = None, - multiple: Literal[False] = False, + file_types: t.Iterable[str] | None = None, + multiple: t.Literal[False] = False, ) -> utils.FileInfo: ... - @overload + @t.overload async def file_chooser( self, *, - file_types: Iterable[str] | None = None, - multiple: Literal[True], + file_types: t.Iterable[str] | None = None, + multiple: t.Literal[True], ) -> list[utils.FileInfo]: ... @deprecations.function_kwarg_renamed( @@ -2405,7 +2404,7 @@ a.remove(); async def show_custom_dialog( self, - build: Callable[[], rio.Component], + build: t.Callable[[], rio.Component], *, modal: bool = True, user_closeable: bool = True, @@ -2614,7 +2613,7 @@ a.remove(); *, title: str, content: rio.Component | str, - options: Mapping[str, T] | Sequence[T], + options: t.Mapping[str, T] | t.Sequence[T], # default_option: T | None = None, owning_component: rio.Component | None = None, ) -> T: @@ -2691,7 +2690,7 @@ a.remove(); """ # Standardize the options - if isinstance(options, Sequence): + if isinstance(options, t.Sequence): options = {str(value): value for value in options} # Prepare a build function @@ -3010,7 +3009,7 @@ a.remove(); async def _remote_apply_theme( self, css_variables: dict[str, str], - theme_variant: Literal["light", "dark"], + theme_variant: t.Literal["light", "dark"], ) -> None: raise NotImplementedError # pragma: no cover @@ -3039,7 +3038,7 @@ a.remove(); self, # Maps component ids to serialized components. The components may be partial, # i.e. any property may be missing. - delta_states: dict[int, Any], + delta_states: dict[int, t.Any], # Tells the client to make the given component the new root component. root_component_id: int | None, ) -> None: @@ -3053,7 +3052,7 @@ a.remove(); parameter_format="dict", await_response=False, ) - async def _evaluate_javascript(self, java_script_source: str) -> Any: + async def _evaluate_javascript(self, java_script_source: str) -> t.Any: """ Evaluate the given JavaScript code on the client. @@ -3074,7 +3073,7 @@ a.remove(); async def _evaluate_javascript_and_get_result( self, java_script_source: str, - ) -> Any: + ) -> t.Any: """ Evaluate the given JavaScript code in the client and return the result. @@ -3108,7 +3107,9 @@ a.remove(); raise NotImplementedError # pragma: no cover @unicall.remote(name="setUserSettings", await_response=False) - async def _set_user_settings(self, delta_settings: dict[str, Any]) -> None: + async def _set_user_settings( + self, delta_settings: dict[str, t.Any] + ) -> None: """ Persistently store the given key-value pairs at the user. The values have to be jsonable. @@ -3153,7 +3154,7 @@ a.remove(); async def _component_state_update( self, component_id: int, - delta_state: Any, + delta_state: t.Any, ) -> None: # Get the component component = self._try_get_component_for_message(component_id) @@ -3178,7 +3179,7 @@ a.remove(); async def _component_message( self, component_id: int, - payload: Any, + payload: t.Any, ) -> None: # Get the component component = self._try_get_component_for_message(component_id) @@ -3409,7 +3410,7 @@ a.remove(); ) async def _remote_get_component_layouts( self, component_ids: list[int] - ) -> list[dict[str, Any] | None]: + ) -> list[dict[str, t.Any] | None]: raise NotImplementedError() # pragma: no cover async def _get_unittest_client_layout_info( @@ -3463,7 +3464,7 @@ a.remove(); ) async def __get_unittest_client_layout_info( self, - ) -> Any: + ) -> t.Any: raise NotImplementedError() # pragma: no cover @unicall.remote( diff --git a/rio/session_attachments.py b/rio/session_attachments.py index 2c17afcd..5f6c2d23 100644 --- a/rio/session_attachments.py +++ b/rio/session_attachments.py @@ -1,14 +1,13 @@ from __future__ import annotations -from collections.abc import Iterator -from typing import Any, TypeVar, cast +import typing as t from . import dataclass, session, user_settings_module __all__ = ["SessionAttachments"] -T = TypeVar("T") +T = t.TypeVar("T") class SessionAttachments: @@ -16,7 +15,7 @@ class SessionAttachments: self._session = sess self._attachments: dict[type, object] = {} - def __iter__(self) -> Iterator[object]: + def __iter__(self) -> t.Iterator[object]: return iter(self._attachments.values()) def __contains__(self, typ: type) -> bool: @@ -40,7 +39,7 @@ class SessionAttachments: # If a UserSettings object is already attached, unlink it from the # session try: - old_value = cast( + old_value = t.cast( user_settings_module.UserSettings, self._attachments[cls] ) except KeyError: @@ -66,7 +65,7 @@ class SessionAttachments: self._session._save_settings_soon() - def add(self, value: Any) -> None: + def add(self, value: t.Any) -> None: self._add(value, synchronize=True) def remove(self, typ: type) -> None: diff --git a/rio/snippets/__init__.py b/rio/snippets/__init__.py index 453bc276..083ece0c 100644 --- a/rio/snippets/__init__.py +++ b/rio/snippets/__init__.py @@ -4,12 +4,13 @@ import copy import functools import json import re +import typing as t import urllib.parse from dataclasses import dataclass from pathlib import Path -from typing import * # type: ignore import uniserde +from typing_extensions import TypeAlias from .. import utils @@ -30,7 +31,7 @@ DEFAULT_META_DICT = { # # THE ORDER MATTERS. `revel` will display the options in the same order as they # appear in the literal -AvailableTemplatesLiteral: TypeAlias = Literal[ +AvailableTemplatesLiteral: TypeAlias = t.Literal[ # Keep the empty template first "Empty", # Sort the remainder alphabetically @@ -52,7 +53,7 @@ class _TemplateConfig(uniserde.Serde): """ # Allows displaying the templates in a structured way - level: Literal["beginner", "intermediate", "advanced"] + level: t.Literal["beginner", "intermediate", "advanced"] # Very short, one or two line description of the template summary: str @@ -221,7 +222,7 @@ def get_snippet_groups() -> set[str]: @functools.lru_cache(maxsize=None) -def all_snippets_in_group(group: str) -> Iterable[Snippet]: +def all_snippets_in_group(group: str) -> t.Iterable[Snippet]: """ Returns all snippets in the given group. @@ -266,7 +267,7 @@ class ProjectTemplate: name: AvailableTemplatesLiteral # How difficult the project is - level: Literal["beginner", "intermediate", "advanced"] + level: t.Literal["beginner", "intermediate", "advanced"] # A short description of the project template summary: str @@ -304,13 +305,13 @@ class ProjectTemplate: @staticmethod def _from_snippet_group( snippet_name: str, - snippets: Iterable[Snippet], + snippets: t.Iterable[Snippet], ) -> ProjectTemplate: assert ( - snippet_name in get_args(AvailableTemplatesLiteral) + snippet_name in t.get_args(AvailableTemplatesLiteral) or snippet_name == "Empty" ), snippet_name - name = cast(AvailableTemplatesLiteral, snippet_name) + name = t.cast(AvailableTemplatesLiteral, snippet_name) # Find all snippets needed for the project template readme_snippet: Snippet | None = None @@ -337,7 +338,7 @@ class ProjectTemplate: # And the metadata if snippet.name == "meta.json": - meta_dict: dict[str, Any] = copy.deepcopy(DEFAULT_META_DICT) + meta_dict: dict[str, t.Any] = copy.deepcopy(DEFAULT_META_DICT) meta_dict.update(json.loads(snippet.stripped_code())) metadata = _TemplateConfig.from_json(meta_dict) continue @@ -431,7 +432,7 @@ class ProjectTemplate: @functools.lru_cache(maxsize=None) -def get_project_templates(include_empty: bool) -> Iterable[ProjectTemplate]: +def get_project_templates(include_empty: bool) -> t.Iterable[ProjectTemplate]: """ Iterates over all available project templates. @@ -524,7 +525,7 @@ class HowtoGuide: @functools.lru_cache(maxsize=None) -def get_howto_guides() -> Iterable[HowtoGuide]: +def get_howto_guides() -> t.Iterable[HowtoGuide]: """ Iterates over all available how-to guides. """ diff --git a/rio/snippets/snippet-files/howtos/howto-get-value-from-child-component.md b/rio/snippets/snippet-files/howtos/howto-get-value-from-child-component.md index c7e8a256..e71a94b2 100644 --- a/rio/snippets/snippet-files/howtos/howto-get-value-from-child-component.md +++ b/rio/snippets/snippet-files/howtos/howto-get-value-from-child-component.md @@ -164,14 +164,14 @@ class MyComponent(rio.Component): Common use cases for attachments are: -- **Database connections**: Attach a database connection to the session, so that +- **Database connections**: Attach a database connection to the session, so that all components can access it. -- **User authentication**: When the user logs in, attach the logged in user's +- **User authentication**: When the user logs in, attach the logged in user's name and id to the session. This way, every component always knows which user it's talking to. -- **Per-user Settings**: Any classes which inherit from `rio.UserSettings` will +- **Per-user Settings**: Any classes which inherit from `rio.UserSettings` will be stored persistentnly on the user's device. This means they'll still be present when the user visits your app again later. diff --git a/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_message.py b/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_message.py index f63123ed..874b8d1b 100644 --- a/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_message.py +++ b/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_message.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio # diff --git a/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_suggestion_card.py b/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_suggestion_card.py index 5be49cf2..d2f09451 100644 --- a/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_suggestion_card.py +++ b/rio/snippets/snippet-files/project-template-AI Chatbot/components/chat_suggestion_card.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-AI Chatbot/components/empty_chat_placeholder.py b/rio/snippets/snippet-files/project-template-AI Chatbot/components/empty_chat_placeholder.py index f05fe422..0abe281a 100644 --- a/rio/snippets/snippet-files/project-template-AI Chatbot/components/empty_chat_placeholder.py +++ b/rio/snippets/snippet-files/project-template-AI Chatbot/components/empty_chat_placeholder.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from .. import components as comps diff --git a/rio/snippets/snippet-files/project-template-AI Chatbot/components/generating_response_placeholder.py b/rio/snippets/snippet-files/project-template-AI Chatbot/components/generating_response_placeholder.py index f2f6f7e7..177109eb 100644 --- a/rio/snippets/snippet-files/project-template-AI Chatbot/components/generating_response_placeholder.py +++ b/rio/snippets/snippet-files/project-template-AI Chatbot/components/generating_response_placeholder.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-AI Chatbot/conversation.py b/rio/snippets/snippet-files/project-template-AI Chatbot/conversation.py index be5bcc70..378413ca 100644 --- a/rio/snippets/snippet-files/project-template-AI Chatbot/conversation.py +++ b/rio/snippets/snippet-files/project-template-AI Chatbot/conversation.py @@ -1,6 +1,6 @@ +import typing as t from dataclasses import dataclass, field from datetime import datetime, timezone -from typing import * # type: ignore import openai # type: ignore (hidden from user) @@ -12,7 +12,7 @@ class ChatMessage: chat message. """ - role: Literal["user", "assistant"] + role: t.Literal["user", "assistant"] timestamp: datetime text: str @@ -25,7 +25,7 @@ class Conversation: """ # The entire message history - messages: List[ChatMessage] = field(default_factory=list) + messages: list[ChatMessage] = field(default_factory=list) async def respond(self, client: openai.AsyncOpenAI) -> ChatMessage: """ @@ -42,7 +42,7 @@ class Conversation: raise ValueError("The most recent message must be by the user") # Convert all messages to the format needed by the API - api_messages: list[Any] = [ + api_messages: list[t.Any] = [ { "role": "system", "content": "You are a helpful assistant. Format your response in markdown, for example by using **bold**, and _italic_ amongst others.", diff --git a/rio/snippets/snippet-files/project-template-AI Chatbot/pages/chat_page.py b/rio/snippets/snippet-files/project-template-AI Chatbot/pages/chat_page.py index f14b7af1..56d0203c 100644 --- a/rio/snippets/snippet-files/project-template-AI Chatbot/pages/chat_page.py +++ b/rio/snippets/snippet-files/project-template-AI Chatbot/pages/chat_page.py @@ -4,7 +4,6 @@ from dataclasses import field # from datetime import datetime, timezone -from typing import * # type: ignore import openai # type: ignore (hidden from user) diff --git a/rio/snippets/snippet-files/project-template-Authentication/components/footer.py b/rio/snippets/snippet-files/project-template-Authentication/components/footer.py index c8c05f9b..016be3a4 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/components/footer.py +++ b/rio/snippets/snippet-files/project-template-Authentication/components/footer.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Authentication/components/navbar.py b/rio/snippets/snippet-files/project-template-Authentication/components/navbar.py index 55c4129c..063acd45 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/components/navbar.py +++ b/rio/snippets/snippet-files/project-template-Authentication/components/navbar.py @@ -2,7 +2,6 @@ from __future__ import annotations # from datetime import datetime, timezone -from typing import * # type: ignore import rio diff --git a/rio/snippets/snippet-files/project-template-Authentication/components/news_article.py b/rio/snippets/snippet-files/project-template-Authentication/components/news_article.py index 077b6dd9..eb13d7f4 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/components/news_article.py +++ b/rio/snippets/snippet-files/project-template-Authentication/components/news_article.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Authentication/components/root_component.py b/rio/snippets/snippet-files/project-template-Authentication/components/root_component.py index e7996f5a..df40a859 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/components/root_component.py +++ b/rio/snippets/snippet-files/project-template-Authentication/components/root_component.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from .. import components as comps diff --git a/rio/snippets/snippet-files/project-template-Authentication/components/testimonial.py b/rio/snippets/snippet-files/project-template-Authentication/components/testimonial.py index 08041375..ccb0452b 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/components/testimonial.py +++ b/rio/snippets/snippet-files/project-template-Authentication/components/testimonial.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page.py b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page.py index e7d936ea..ff721e61 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page.py +++ b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio # diff --git a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/about_page.py b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/about_page.py index 619f9ac4..56007f64 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/about_page.py +++ b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/about_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/home_page.py b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/home_page.py index 9d934a7d..65c0a2c3 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/home_page.py +++ b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/home_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from ... import components as comps diff --git a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/news_page.py b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/news_page.py index 84a7645e..3f5851f5 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/news_page.py +++ b/rio/snippets/snippet-files/project-template-Authentication/pages/app_page/news_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from ... import components as comps diff --git a/rio/snippets/snippet-files/project-template-Authentication/pages/login_page.py b/rio/snippets/snippet-files/project-template-Authentication/pages/login_page.py index 87fd4bd8..176aff72 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/pages/login_page.py +++ b/rio/snippets/snippet-files/project-template-Authentication/pages/login_page.py @@ -1,8 +1,6 @@ from __future__ import annotations # -from typing import * # type: ignore - import rio from .. import components as comps diff --git a/rio/snippets/snippet-files/project-template-Authentication/root_init.py b/rio/snippets/snippet-files/project-template-Authentication/root_init.py index 600a530b..1b80f999 100644 --- a/rio/snippets/snippet-files/project-template-Authentication/root_init.py +++ b/rio/snippets/snippet-files/project-template-Authentication/root_init.py @@ -2,7 +2,6 @@ from __future__ import annotations from datetime import datetime, timedelta, timezone -from typing import * # type: ignore import rio diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py index 4bdf54eb..f5817a32 100644 --- a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/balance_card.py @@ -1,5 +1,3 @@ -from typing import * # type:ignore - # import pandas as pd import plotly.express as px diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py index c05fc2d0..bff71d0b 100644 --- a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_card.py @@ -1,5 +1,3 @@ -from typing import * # type:ignore - # import pandas as pd import plotly.express as px diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py index 4cfcad06..688a916a 100644 --- a/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/components/crypto_chart.py @@ -1,5 +1,3 @@ -from typing import * # type:ignore - # import pandas as pd import plotly.express as px diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py index 19d0cefd..6563e82b 100644 --- a/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/data_models.py @@ -1,5 +1,3 @@ -from typing import * # type:ignore - import pandas as pd # list of cryptocurrencies we want to display in our dashboard @@ -11,7 +9,7 @@ BAR_CHART: pd.DataFrame = pd.DataFrame([4, 5, 6, 5, 4, 6, 7], columns=["data"]) # example data for our crypto portfolio. Our portfolio consists of three coins: # bitcoin, litecoin, and ethereum. Each coin has a value, a ticker, a color, and a logo. -MY_COINS: dict[str, Tuple[float, str, str, str]] = { +MY_COINS: dict[str, tuple[float, str, str, str]] = { "bitcoin": ( 13.344546, "BTC", diff --git a/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py index ea2a5c13..99e62c4f 100644 --- a/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py +++ b/rio/snippets/snippet-files/project-template-Crypto Dashboard/pages/dashboard_page.py @@ -2,7 +2,6 @@ from dataclasses import field # from pathlib import Path -from typing import * # type: ignore import numpy as np import pandas as pd diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/components/footer.py b/rio/snippets/snippet-files/project-template-Multipage Website/components/footer.py index c8c05f9b..016be3a4 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/components/footer.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/components/footer.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/components/navbar.py b/rio/snippets/snippet-files/project-template-Multipage Website/components/navbar.py index ae576ee4..0a887f3a 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/components/navbar.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/components/navbar.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/components/news_article.py b/rio/snippets/snippet-files/project-template-Multipage Website/components/news_article.py index 077b6dd9..eb13d7f4 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/components/news_article.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/components/news_article.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/components/root_component.py b/rio/snippets/snippet-files/project-template-Multipage Website/components/root_component.py index 68179cd5..a74f3d9c 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/components/root_component.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/components/root_component.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from .. import components as comps diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/components/testimonial.py b/rio/snippets/snippet-files/project-template-Multipage Website/components/testimonial.py index 7e0c951e..773f85e6 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/components/testimonial.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/components/testimonial.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/pages/about_page.py b/rio/snippets/snippet-files/project-template-Multipage Website/pages/about_page.py index 50bd1d8d..4100f3de 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/pages/about_page.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/pages/about_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/pages/home_page.py b/rio/snippets/snippet-files/project-template-Multipage Website/pages/home_page.py index a4ca7572..255cf068 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/pages/home_page.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/pages/home_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from .. import components as comps diff --git a/rio/snippets/snippet-files/project-template-Multipage Website/pages/news_page.py b/rio/snippets/snippet-files/project-template-Multipage Website/pages/news_page.py index 072f8bf5..f440a45f 100644 --- a/rio/snippets/snippet-files/project-template-Multipage Website/pages/news_page.py +++ b/rio/snippets/snippet-files/project-template-Multipage Website/pages/news_page.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio from .. import components as comps diff --git a/rio/snippets/snippet-files/project-template-Simple CRUD/data_models.py b/rio/snippets/snippet-files/project-template-Simple CRUD/data_models.py index fb4a0c85..3b60eefb 100644 --- a/rio/snippets/snippet-files/project-template-Simple CRUD/data_models.py +++ b/rio/snippets/snippet-files/project-template-Simple CRUD/data_models.py @@ -2,7 +2,6 @@ from __future__ import annotations import copy from dataclasses import dataclass -from typing import * # type:ignore @dataclass diff --git a/rio/snippets/snippet-files/project-template-Simple CRUD/pages/crud_page.py b/rio/snippets/snippet-files/project-template-Simple CRUD/pages/crud_page.py index b57a7d3e..50a1c3b1 100644 --- a/rio/snippets/snippet-files/project-template-Simple CRUD/pages/crud_page.py +++ b/rio/snippets/snippet-files/project-template-Simple CRUD/pages/crud_page.py @@ -1,6 +1,6 @@ # import functools -from typing import * # type:ignore +import typing as t import rio @@ -38,7 +38,7 @@ class CrudPage(rio.Component): menu_items: list[data_models.MenuItem] = [] currently_selected_menu_item: data_models.MenuItem | None = None banner_text: str = "" - banner_style: Literal["success", "danger", "info"] = "success" + banner_style: t.Literal["success", "danger", "info"] = "success" @rio.event.on_populate def on_populate(self) -> None: diff --git a/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/components/field.py b/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/components/field.py index 59dfaaad..90a159fa 100644 --- a/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/components/field.py +++ b/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/components/field.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -14,7 +14,7 @@ class Field(rio.Component): """ # The current value of the field. - value: Literal["X", "O", ""] + value: t.Literal["X", "O", ""] # If this is `True`, the field will dim its content. This allows the game to # dim any fields that are not part of the winning combination when a player diff --git a/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/pages/tic_tac_toe_page.py index 7fc4a027..dc129aa3 100644 --- a/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/project-template-Tic-Tac-Toe/pages/tic_tac_toe_page.py @@ -2,7 +2,7 @@ from __future__ import annotations # import functools -from typing import * # type: ignore +import typing as t import rio @@ -31,13 +31,13 @@ class TicTacToePage(rio.Component): # # The first value is the top-left field. The remaining fields follow from # left to right, top to bottom. - fields: list[Literal["X", "O", ""]] = [""] * 9 + fields: list[t.Literal["X", "O", ""]] = [""] * 9 # The player who is currently on turn - player: Literal["X", "O"] = "X" + player: t.Literal["X", "O"] = "X" # The winner of the game, if any - winner: Literal["X", "O", "draw"] | None = None + winner: t.Literal["X", "O", "draw"] | None = None # If there is a winner, these are the indices of the fields which made them # win diff --git a/rio/snippets/snippet-files/project-template-Todo App/components/new_todo_item_input.py b/rio/snippets/snippet-files/project-template-Todo App/components/new_todo_item_input.py index 75aa872a..8eae094a 100644 --- a/rio/snippets/snippet-files/project-template-Todo App/components/new_todo_item_input.py +++ b/rio/snippets/snippet-files/project-template-Todo App/components/new_todo_item_input.py @@ -3,7 +3,6 @@ from __future__ import annotations # import datetime from dataclasses import field -from typing import * # type: ignore import rio diff --git a/rio/snippets/snippet-files/project-template-Todo App/components/todo_item_component.py b/rio/snippets/snippet-files/project-template-Todo App/components/todo_item_component.py index a85d8f39..59030a9f 100644 --- a/rio/snippets/snippet-files/project-template-Todo App/components/todo_item_component.py +++ b/rio/snippets/snippet-files/project-template-Todo App/components/todo_item_component.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import * # type: ignore - import rio # diff --git a/rio/snippets/snippet-files/project-template-Todo App/pages/todo_list_page.py b/rio/snippets/snippet-files/project-template-Todo App/pages/todo_list_page.py index 78e507e3..e4ddb2fc 100644 --- a/rio/snippets/snippet-files/project-template-Todo App/pages/todo_list_page.py +++ b/rio/snippets/snippet-files/project-template-Todo App/pages/todo_list_page.py @@ -2,7 +2,6 @@ from __future__ import annotations # import functools -from typing import * # type: ignore import rio diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py index 3af465f1..2085756c 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -10,7 +10,7 @@ import rio class TicTacToePage(rio.Component): # # - fields: list[Literal["X", "O", ""]] = [""] * 9 + fields: list[t.Literal["X", "O", ""]] = [""] * 9 # # diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/components/field.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/components/field.py index ce3bde58..268c25e8 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/components/field.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/components/field.py @@ -1,13 +1,13 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio # class Field(rio.Component): - value: Literal["X", "O", ""] + value: t.Literal["X", "O", ""] on_press: rio.EventHandler[[]] = None diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py index 4ec569ef..9730a236 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio @@ -11,7 +11,7 @@ from .. import components as comps class TicTacToePage(rio.Component): # The contents of all fields. Each field can contain an X, an O, or be # empty. The initial state is an empty board. - fields: list[Literal["X", "O", ""]] = [""] * 9 + fields: list[t.Literal["X", "O", ""]] = [""] * 9 def build(self) -> rio.Component: # Spawn components for the fields diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/components/field.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/components/field.py index ce3bde58..268c25e8 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/components/field.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/components/field.py @@ -1,13 +1,13 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio # class Field(rio.Component): - value: Literal["X", "O", ""] + value: t.Literal["X", "O", ""] on_press: rio.EventHandler[[]] = None diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py index dc87479a..f1db265f 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py @@ -1,7 +1,7 @@ from __future__ import annotations import functools -from typing import * # type: ignore +import typing as t import rio @@ -12,11 +12,11 @@ from .. import components as comps class TicTacToePage(rio.Component): # The contents of all fields. Each field can contain an X, an O, or be # empty. The initial state is an empty board. - fields: list[Literal["X", "O", ""]] = [""] * 9 + fields: list[t.Literal["X", "O", ""]] = [""] * 9 # # The player who is currently on turn - player: Literal["X", "O"] = "X" + player: t.Literal["X", "O"] = "X" # # diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/components/field.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/components/field.py index 2290d592..0c2c704c 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/components/field.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/components/field.py @@ -1,13 +1,13 @@ from __future__ import annotations -from typing import * # type: ignore +import typing as t import rio # class Field(rio.Component): - value: Literal["X", "O", ""] + value: t.Literal["X", "O", ""] dim: bool on_press: rio.EventHandler[[]] = None diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py index b6ad3376..61daab36 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py @@ -1,7 +1,7 @@ from __future__ import annotations import functools -from typing import * # type: ignore +import typing as t import rio @@ -12,14 +12,14 @@ from .. import components as comps class TicTacToePage(rio.Component): # The contents of all fields. Each field can contain an X, an O, or be # empty. The initial state is an empty board. - fields: list[Literal["X", "O", ""]] = [""] * 9 + fields: list[t.Literal["X", "O", ""]] = [""] * 9 # The player who is currently on turn - player: Literal["X", "O"] = "X" + player: t.Literal["X", "O"] = "X" # # The winner of the game, if any - winner: Literal["X", "O", "draw"] | None = None + winner: t.Literal["X", "O", "draw"] | None = None # If there is a winner, these are the indices of the fields which made them # win diff --git a/rio/state_properties.py b/rio/state_properties.py index 9ac14554..773185a9 100644 --- a/rio/state_properties.py +++ b/rio/state_properties.py @@ -2,15 +2,14 @@ from __future__ import annotations import dataclasses import types +import typing as t import weakref -from collections.abc import Callable -from typing import TYPE_CHECKING import introspection.typing from . import global_state -if TYPE_CHECKING: +if t.TYPE_CHECKING: from .components import Component @@ -175,7 +174,7 @@ class StateProperty: @dataclasses.dataclass(eq=False) class AttributeBinding: # Weak reference to the component containing this binding - owning_component_weak: Callable[[], Component | None] + owning_component_weak: t.Callable[[], Component | None] # The state property whose value this binding is owning_property: StateProperty diff --git a/rio/testing.py b/rio/testing.py index a5862a1e..53f77438 100644 --- a/rio/testing.py +++ b/rio/testing.py @@ -1,9 +1,9 @@ import asyncio -from collections.abc import Callable, Iterable, Iterator, Mapping +import typing as t import ordered_set import starlette.datastructures -from typing_extensions import Self, TypeVar, overload +from typing_extensions import Self from uniserde import JsonDoc import rio @@ -15,12 +15,12 @@ from .transports import MessageRecorderTransport, TransportInterrupted __all__ = ["TestClient"] -T = TypeVar("T") -C = TypeVar("C", bound=rio.Component) +T = t.TypeVar("T") +C = t.TypeVar("C", bound=rio.Component) class TestClient: - @overload + @t.overload def __init__( self, app: rio.App, @@ -31,13 +31,13 @@ class TestClient: use_ordered_dirty_set: bool = False, ): ... - @overload + @t.overload def __init__( self, - build: Callable[[], rio.Component] = rio.Spacer, + build: t.Callable[[], rio.Component] = rio.Spacer, *, app_name: str = "mock-app", - default_attachments: Iterable[object] = (), + default_attachments: t.Iterable[object] = (), running_in_window: bool = False, user_settings: JsonDoc = {}, active_url: str = "/", @@ -46,12 +46,12 @@ class TestClient: def __init__( # type: ignore self, - app_or_build: rio.App | Callable[[], rio.Component] | None = None, + app_or_build: rio.App | t.Callable[[], rio.Component] | None = None, *, app: rio.App | None = None, - build: Callable[[], rio.Component] | None = None, + build: t.Callable[[], rio.Component] | None = None, app_name: str = "test-app", - default_attachments: Iterable[object] = (), + default_attachments: t.Iterable[object] = (), running_in_window: bool = False, user_settings: JsonDoc = {}, active_url: str = "/", @@ -164,7 +164,7 @@ class TestClient: @property def _last_component_state_changes( self, - ) -> Mapping[rio.Component, Mapping[str, object]]: + ) -> t.Mapping[rio.Component, t.Mapping[str, object]]: for message in reversed(self._transport.sent_messages): if message["method"] == "updateComponentStates": delta_states: dict = message["params"]["deltaStates"] # type: ignore @@ -199,14 +199,14 @@ class TestClient: return self._session @property - def crashed_build_functions(self) -> Mapping[Callable, str]: + def crashed_build_functions(self) -> t.Mapping[t.Callable, str]: return self.session._crashed_build_functions @property def root_component(self) -> rio.Component: return self.session._get_user_root_component() - def get_components(self, component_type: type[C]) -> Iterator[C]: + def get_components(self, component_type: type[C]) -> t.Iterator[C]: root_component = self.root_component for component in root_component._iter_component_tree_(): diff --git a/rio/text_style.py b/rio/text_style.py index 5bb21ee5..b51679fd 100644 --- a/rio/text_style.py +++ b/rio/text_style.py @@ -1,8 +1,8 @@ from __future__ import annotations import pathlib +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore from uniserde import JsonDoc @@ -66,8 +66,8 @@ class Font(SelfSerializing): return sess._register_font(self) # Predefined fonts - ROBOTO: ClassVar[Font] - ROBOTO_MONO: ClassVar[Font] + ROBOTO: t.ClassVar[Font] + ROBOTO_MONO: t.ClassVar[Font] Font.ROBOTO = Font( @@ -119,7 +119,7 @@ class TextStyle(SelfSerializing): fill: _TextFill | None = None font_size: float = 1.0 italic: bool = False - font_weight: Literal["normal", "bold"] = "normal" + font_weight: t.Literal["normal", "bold"] = "normal" underlined: bool = False strikethrough: bool = False all_caps: bool = False @@ -131,7 +131,7 @@ class TextStyle(SelfSerializing): fill: _TextFill | None | UnsetType = UNSET, font_size: float | None = None, italic: bool | None = None, - font_weight: Literal["normal", "bold"] | None = None, + font_weight: t.Literal["normal", "bold"] | None = None, underlined: bool | None = None, strikethrough: bool | None = None, all_caps: bool | None = None, diff --git a/rio/theme.py b/rio/theme.py index b30892e7..d46537b0 100644 --- a/rio/theme.py +++ b/rio/theme.py @@ -1,7 +1,7 @@ from __future__ import annotations +import typing as t from dataclasses import KW_ONLY, dataclass -from typing import * # type: ignore from uniserde import Jsonable @@ -16,7 +16,7 @@ __all__ = [ ] -T = TypeVar("T") +T = t.TypeVar("T") def _derive_color( @@ -89,7 +89,7 @@ def _make_semantic_palette(color: rio.Color) -> Palette: ) -@final +@t.final @dataclass() class Palette: background: rio.Color @@ -131,7 +131,7 @@ class Palette: ) -@final +@t.final @dataclass() class Theme: """ @@ -263,12 +263,12 @@ class Theme: corner_radius_small: float = 0.5, corner_radius_medium: float = 1.0, corner_radius_large: float = 2.0, - heading_fill: Literal["primary", "plain", "auto"] + heading_fill: t.Literal["primary", "plain", "auto"] | text_style_module._TextFill = "auto", text_color: rio.Color | None = None, font: text_style_module.Font = text_style_module.Font.ROBOTO, monospace_font: text_style_module.Font = text_style_module.Font.ROBOTO_MONO, - mode: Literal["light", "dark"] = "light", + mode: t.Literal["light", "dark"] = "light", ) -> Theme: """ Creates a new theme based on the provided colors. @@ -597,7 +597,7 @@ class Theme: | None = None, font: text_style_module.Font = text_style_module.Font.ROBOTO, monospace_font: text_style_module.Font = text_style_module.Font.ROBOTO_MONO, - heading_fill: Literal["primary", "plain", "auto"] + heading_fill: t.Literal["primary", "plain", "auto"] | text_style_module._TextFill = "auto", ) -> tuple[Theme, Theme]: """ diff --git a/rio/tools/create_code_for_icon_set.py b/rio/tools/create_code_for_icon_set.py index 76919471..d54c9a5a 100644 --- a/rio/tools/create_code_for_icon_set.py +++ b/rio/tools/create_code_for_icon_set.py @@ -27,12 +27,12 @@ def create(archive_file: str, output_file: str | None = None) -> None: icons = collect_icons(archive_file) with outfile_ctx as outfile: - outfile.write("from typing import Literal\n\n") + outfile.write("import typing as t\n\n") icon_set_name_pascal = introspection.convert_case( icon_set_name, "pascal" ) - outfile.write(f"{icon_set_name_pascal}Icon = Literal[\n") + outfile.write(f"{icon_set_name_pascal}Icon = t.Literal[\n") for icon_name in sorted(icons): variants = sorted( diff --git a/rio/transports/message_recorder_transport.py b/rio/transports/message_recorder_transport.py index c7d4ae74..ef00562b 100644 --- a/rio/transports/message_recorder_transport.py +++ b/rio/transports/message_recorder_transport.py @@ -1,6 +1,6 @@ import asyncio import json -from collections.abc import Callable +import typing as t from uniserde import JsonDoc @@ -11,7 +11,7 @@ __all__ = ["MessageRecorderTransport"] class MessageRecorderTransport(AbstractTransport): def __init__( - self, *, process_sent_message: Callable[[JsonDoc], None] | None = None + self, *, process_sent_message: t.Callable[[JsonDoc], None] | None = None ): super().__init__() diff --git a/rio/user_settings_module.py b/rio/user_settings_module.py index ff7a1047..5453e68c 100644 --- a/rio/user_settings_module.py +++ b/rio/user_settings_module.py @@ -1,8 +1,8 @@ from __future__ import annotations import copy +import typing as t from dataclasses import field -from typing import * # type: ignore import uniserde from typing_extensions import Self @@ -81,7 +81,7 @@ class UserSettings(metaclass=RioDataclassMeta): # Any values from this class will be stored in the configuration file under # this section. This has to be set to a string. If empty, the values will be # set outside of any sections. - section_name: ClassVar[str] = "" + section_name: t.ClassVar[str] = "" _rio_session_: session.Session | None = field( default=None, init=False, repr=False, compare=False @@ -110,7 +110,7 @@ class UserSettings(metaclass=RioDataclassMeta): settings_vars = vars(self) if cls.section_name: - section = cast( + section = t.cast( dict[str, object], settings_json.get("section:" + cls.section_name, {}), ) @@ -139,7 +139,7 @@ class UserSettings(metaclass=RioDataclassMeta): return self # This function kinda ruins linting, so we'll hide it from the IDE - def __setattr(self, name: str, value: Any) -> None: + def __setattr(self, name: str, value: t.Any) -> None: # These attributes doesn't exist yet during the constructor dct = vars(self) dirty_attribute_names = dct.setdefault( @@ -160,7 +160,7 @@ class UserSettings(metaclass=RioDataclassMeta): # if self._rio_session_ is not None: # self._rio_session_._save_settings_soon() - if not TYPE_CHECKING: + if not t.TYPE_CHECKING: __setattr__ = __setattr def _equals(self, other: Self) -> bool: diff --git a/rio/utils.py b/rio/utils.py index 8562aa58..7faaf4c9 100644 --- a/rio/utils.py +++ b/rio/utils.py @@ -8,10 +8,10 @@ import re import secrets import socket import sys +import typing as t from dataclasses import dataclass from io import BytesIO, StringIO from pathlib import Path -from typing import * # type: ignore import imy.assets from PIL.Image import Image @@ -56,7 +56,7 @@ else: # Constants & types _READONLY = object() -T = TypeVar("T") +T = t.TypeVar("T") Readonly = Annotated[T, _READONLY] ImageLike = Path | Image | URL | bytes @@ -77,7 +77,7 @@ MARKDOWN_CODE_ESCAPE = re.compile(r"([\\`])") I_KNOW_WHAT_IM_DOING = set[object]() -def i_know_what_im_doing(thing: Callable): +def i_know_what_im_doing(thing: t.Callable): I_KNOW_WHAT_IM_DOING.add(thing) return thing @@ -158,13 +158,15 @@ class FileInfo: """ return self._contents.decode(encoding) - @overload - async def open(self, type: Literal["r"]) -> StringIO: ... + @t.overload + async def open(self, type: t.Literal["r"]) -> StringIO: ... - @overload - async def open(self, type: Literal["rb"]) -> BytesIO: ... + @t.overload + async def open(self, type: t.Literal["rb"]) -> BytesIO: ... - async def open(self, type: Literal["r", "rb"] = "r") -> StringIO | BytesIO: + async def open( + self, type: t.Literal["r", "rb"] = "r" + ) -> StringIO | BytesIO: """ Asynchronously opens the file, as though it were a regular file on this device. @@ -191,10 +193,10 @@ class FileInfo: raise ValueError("Invalid type. Expected 'r' or 'rb'.") -T = TypeVar("T") -P = ParamSpec("P") +T = t.TypeVar("T") +P = t.ParamSpec("P") -EventHandler = Callable[P, Any | Awaitable[Any]] | None +EventHandler = t.Callable[P, t.Any | t.Awaitable[t.Any]] | None def make_url_relative(base: URL, other: URL) -> URL: @@ -309,7 +311,7 @@ def first_non_null(*values: T | None) -> T: raise ValueError("At least one value must be non-`None`") -def _repr_build_function(build_function: Callable[[], rio.Component]) -> str: +def _repr_build_function(build_function: t.Callable[[], rio.Component]) -> str: """ Return a recognizable name for the provided function such as `Component.build`. @@ -326,7 +328,7 @@ def _repr_build_function(build_function: Callable[[], rio.Component]) -> str: return f"{type(self).__name__}.{build_function.__name__}" -def safe_build(build_function: Callable[[], rio.Component]) -> rio.Component: +def safe_build(build_function: t.Callable[[], rio.Component]) -> rio.Component: """ Calls a build function and returns its result. This differs from just calling the function directly, because it catches any exceptions and returns @@ -483,7 +485,7 @@ def normalize_file_type(file_type: str) -> str: return file_type -def soft_sort(elements: list[T], key: Callable[[T], int | None]) -> None: +def soft_sort(elements: list[T], key: t.Callable[[T], int | None]) -> None: """ Sorts the given list in-place, allowing for `None` values in the key. diff --git a/scripts/benchmark.py b/scripts/benchmark.py index cc3ec716..e417e718 100644 --- a/scripts/benchmark.py +++ b/scripts/benchmark.py @@ -3,7 +3,7 @@ import cProfile import itertools import pstats import time -from typing import Callable +import typing as t import rio @@ -30,7 +30,9 @@ class ComplexComponent(rio.Component): class BenchmarkComponent(rio.Component): - child_factory: Callable[[], rio.Component] = lambda: rio.Text("Starting...") + child_factory: t.Callable[[], rio.Component] = lambda: rio.Text( + "Starting..." + ) def __post_init__(self): self.benchmark_start_time = time.monotonic() diff --git a/scripts/build_material_icon_set.py b/scripts/build_material_icon_set.py index 0ccd859b..5d7a73a9 100644 --- a/scripts/build_material_icon_set.py +++ b/scripts/build_material_icon_set.py @@ -10,7 +10,6 @@ import re import tarfile import tempfile from pathlib import Path -from typing import * # type: ignore from xml.etree import ElementTree as ET import revel diff --git a/tests/test_attribute_bindings.py b/tests/test_attribute_bindings.py index d954c2de..ae3561d4 100644 --- a/tests/test_attribute_bindings.py +++ b/tests/test_attribute_bindings.py @@ -1,4 +1,4 @@ -from typing import cast +import typing as t import rio.testing from rio.state_properties import PleaseTurnThisIntoAnAttributeBinding @@ -128,7 +128,7 @@ async def test_binding_assignment_on_sibling(): async with rio.testing.TestClient(Root) as test_client: root_component = test_client.get_component(Root) - text1, text2 = cast( + text1, text2 = t.cast( list[rio.Text], test_client._get_build_output(root_component, rio.Column).children, ) @@ -150,7 +150,7 @@ async def test_binding_assignment_on_sibling(): async def test_binding_assignment_on_grandchild(): async with rio.testing.TestClient(Grandparent) as test_client: root_component = test_client.get_component(Grandparent) - parent = cast(Parent, test_client._get_build_output(root_component)) + parent = t.cast(Parent, test_client._get_build_output(root_component)) text_component: rio.Text = test_client._get_build_output(parent) assert not test_client._dirty_components diff --git a/tests/test_docstring_code_blocks.py b/tests/test_docstring_code_blocks.py index 93ccba53..2142ef90 100644 --- a/tests/test_docstring_code_blocks.py +++ b/tests/test_docstring_code_blocks.py @@ -4,8 +4,8 @@ import subprocess import sys import tempfile import textwrap +import typing as t from pathlib import Path -from typing import * # type: ignore import pytest @@ -27,7 +27,7 @@ def ruff(*args: str | Path) -> subprocess.CompletedProcess: ) -def get_code_blocks(obj: type | Callable) -> list[str]: +def get_code_blocks(obj: type | t.Callable) -> list[str]: """ Returns a list of all code blocks in the docstring of a component. """ @@ -113,7 +113,7 @@ self = rio.Spacer() @pytest.mark.parametrize("obj", all_documented_objects) -def test_code_block_is_formatted(obj: type | Callable) -> None: +def test_code_block_is_formatted(obj: type | t.Callable) -> None: # Make sure all code blocks are formatted according to ruff for source in get_code_blocks(obj): formatted_source = ruff_format(source) @@ -128,7 +128,7 @@ def test_code_block_is_formatted(obj: type | Callable) -> None: @pytest.mark.parametrize("obj", all_documented_objects) -def test_analyze_code_block(obj: type | Callable) -> None: +def test_analyze_code_block(obj: type | t.Callable) -> None: # A lot of snippets are missing context, so it's only natural that ruff will # find issues with the code. There isn't really anything we can do about it, # so we'll just skip those object. diff --git a/tests/test_documentation.py b/tests/test_documentation.py index 34e9c80c..5e43a913 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -1,4 +1,4 @@ -from collections.abc import Iterable +import typing as t import imy.docstrings import pytest @@ -6,7 +6,7 @@ import pytest import rio.docs -def _create_tests(): +def _create_tests() -> None: for obj, docs in rio.docs.find_documented_objects().items(): if isinstance(docs, imy.docstrings.FunctionDocs): test_cls = _create_function_tests(docs) @@ -114,7 +114,7 @@ def _create_class_tests(cls: type, docs: imy.docstrings.ClassDocs) -> type: def parametrize_with_name( param_name: str, - docs: Iterable[ + docs: t.Iterable[ imy.docstrings.FunctionDocs | imy.docstrings.ClassDocs | imy.docstrings.ClassField diff --git a/tests/test_layouting.py b/tests/test_layouting.py index 113a97f7..3d476fed 100644 --- a/tests/test_layouting.py +++ b/tests/test_layouting.py @@ -1,5 +1,5 @@ import math -from typing import * # type: ignore +import typing as t import pytest @@ -37,7 +37,7 @@ async def test_single_component(text: str) -> None: [rio.Row, rio.Column], ) async def test_linear_container_with_no_extra_width( - container_type: Type, + container_type: t.Type, ) -> None: await verify_layout( lambda: container_type( @@ -72,7 +72,7 @@ async def test_linear_container_with_extra_width( horizontal: bool, first_child_grows: bool, second_child_grows: bool, - proportions: None | Literal["homogeneous"] | List[int], + proportions: None | t.Literal["homogeneous"] | list[int], ) -> None: """ A battery of scenarios to test the most common containers - Rows & Columns. @@ -297,8 +297,8 @@ async def test_aspect_ratio_container_large_child( ], ) async def test_scrolling( - scroll_x: Literal["never", "always", "auto"], - scroll_y: Literal["never", "always", "auto"], + scroll_x: t.Literal["never", "always", "auto"], + scroll_y: t.Literal["never", "always", "auto"], ) -> None: await verify_layout( lambda: rio.ScrollContainer( diff --git a/tests/test_project_templates.py b/tests/test_project_templates.py index 2e4e4ab0..5f8a6498 100644 --- a/tests/test_project_templates.py +++ b/tests/test_project_templates.py @@ -7,8 +7,8 @@ This file ensures that the snippets for project templates match expectations. from __future__ import annotations import tempfile +import typing as t from pathlib import Path -from typing import * # type: ignore import pytest @@ -47,7 +47,7 @@ def test_available_template_literal_matches_templates() -> None: """ # Find all templates according to the literal templates_according_to_literal = set( - get_args(rio.snippets.AvailableTemplatesLiteral) + t.get_args(rio.snippets.AvailableTemplatesLiteral) ) | {"Empty"} # Find all defined templates diff --git a/tests/test_table_indexing.py b/tests/test_table_indexing.py index f2dc06ba..e01a8afc 100644 --- a/tests/test_table_indexing.py +++ b/tests/test_table_indexing.py @@ -3,7 +3,7 @@ Tables support numpy-style 2D indexing. This is rather complex, hence the tests here. """ -from typing import * +import typing as t import pytest @@ -12,7 +12,7 @@ import rio # Helper class for easily creating indices class MakeIndex: - def __getitem__(self, index) -> Any: + def __getitem__(self, index) -> t.Any: return index @@ -260,15 +260,15 @@ make_index = MakeIndex() ], ) def test_indices( - index: Any, + index: t.Any, enable_column_names: bool, result_should: tuple[ int, - int | Literal["header"], + int | t.Literal["header"], int, int, ] - | Type[Exception], + | t.Type[Exception], ) -> None: if enable_column_names: column_names = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] diff --git a/tests/utils/layouting.py b/tests/utils/layouting.py index bc92d32f..ef08f1e0 100644 --- a/tests/utils/layouting.py +++ b/tests/utils/layouting.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio import typing -from collections.abc import Callable +import typing as t import playwright.async_api import playwright.sync_api @@ -22,7 +22,7 @@ __all__ = ["verify_layout", "cleanup"] layouter_factory: LayouterFactory | None = None -async def verify_layout(build: Callable[[], rio.Component]) -> Layouter: +async def verify_layout(build: t.Callable[[], rio.Component]) -> Layouter: """ Rio contains two layout implementations: One on the client side, which determines the real layout of components, and a second one on the server @@ -112,7 +112,7 @@ class LayouterFactory: await self._uvicorn_serve_task async def create_layouter( - self, build: Callable[[], rio.Component] + self, build: t.Callable[[], rio.Component] ) -> Layouter: self._app._build = build session, page = await self._create_session() From ceffb12874a1c22f7a1875e71da67f8e1932ae7e Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 27 Sep 2024 12:35:44 +0200 Subject: [PATCH 05/56] changed all typing imports to `import typing_extensions as te` --- rio/app_server/fastapi_server.py | 2 +- rio/assets.py | 8 ++++---- rio/color.py | 4 ++-- rio/component_meta.py | 6 +++--- rio/components/component.py | 6 +++--- rio/components/flow_container.py | 4 ++-- rio/components/grid.py | 4 ++-- rio/components/labeled_column.py | 4 ++-- rio/components/linear_containers.py | 8 ++++---- rio/components/list_view.py | 4 ++-- rio/components/stack.py | 4 ++-- rio/data_models.py | 7 +++---- rio/dataclass.py | 11 ++++------- rio/fills.py | 4 ++-- rio/snippets/__init__.py | 4 ++-- rio/testing.py | 4 ++-- rio/user_settings_module.py | 8 ++++---- rio/utils.py | 4 ++-- 18 files changed, 46 insertions(+), 50 deletions(-) diff --git a/rio/app_server/fastapi_server.py b/rio/app_server/fastapi_server.py index 25958fcb..41a192c5 100644 --- a/rio/app_server/fastapi_server.py +++ b/rio/app_server/fastapi_server.py @@ -111,7 +111,7 @@ def read_frontend_template(template_name: str) -> str: def add_cache_headers( func: t.Callable[P, t.Awaitable[fastapi.Response]], -) -> t.Callable[P, Coroutine[None, None, fastapi.Response]]: +) -> t.Callable[P, t.Coroutine[None, None, fastapi.Response]]: """ Decorator for routes that serve static files. Ensures that the response has the `Cache-Control` header set appropriately. diff --git a/rio/assets.py b/rio/assets.py index 0c90ce78..b3641f80 100644 --- a/rio/assets.py +++ b/rio/assets.py @@ -8,8 +8,8 @@ import typing as t from pathlib import Path import httpx +import typing_extensions as te from PIL.Image import Image -from typing_extensions import Self from yarl import URL import rio @@ -150,7 +150,7 @@ class Asset(SelfSerializing): return self._eq(other) @abc.abstractmethod - def _eq(self, other: Self) -> bool: + def _eq(self, other: te.Self) -> bool: raise NotImplementedError @abc.abstractmethod @@ -191,7 +191,7 @@ class HostedAsset(Asset): def __hash__(self) -> int: return hash(self.secret_id) - def _eq(self, other: Self) -> bool: + def _eq(self, other: te.Self) -> bool: return self.secret_id == other.secret_id def _serialize(self, sess: rio.Session) -> str: @@ -277,7 +277,7 @@ class UrlAsset(Asset): def __hash__(self) -> int: return hash(self._url) - def _eq(self, other: Self) -> bool: + def _eq(self, other: te.Self) -> bool: return self._url == other._url def _serialize(self, sess: rio.Session) -> str: diff --git a/rio/color.py b/rio/color.py index 16313076..d2f644de 100644 --- a/rio/color.py +++ b/rio/color.py @@ -4,7 +4,7 @@ import colorsys import math import typing as t -from typing_extensions import TypeAlias +import typing_extensions as te from uniserde import Jsonable import rio @@ -675,7 +675,7 @@ Color.TRANSPARENT = Color.from_rgb(0.0, 0.0, 0.0, 0.0) # Like color, but also allows referencing theme colors -ColorSet: TypeAlias = ( +ColorSet: te.TypeAlias = ( Color | t.Literal[ "background", diff --git a/rio/component_meta.py b/rio/component_meta.py index 18acb786..60c74a13 100644 --- a/rio/component_meta.py +++ b/rio/component_meta.py @@ -9,7 +9,7 @@ from collections import defaultdict from dataclasses import field import introspection -from typing_extensions import dataclass_transform +import typing_extensions as te import rio @@ -26,8 +26,8 @@ C = t.TypeVar("C", bound="rio.Component") # For some reason vscode doesn't understand that this class is a -# `@dataclass_transform`, so we'll annotate it again... -@dataclass_transform( +# `@te.dataclass_transform`, so we'll annotate it again... +@te.dataclass_transform( eq_default=False, field_specifiers=(internal_field, field), ) diff --git a/rio/components/component.py b/rio/components/component.py index a78fc057..e60b5ef6 100644 --- a/rio/components/component.py +++ b/rio/components/component.py @@ -7,7 +7,7 @@ from abc import abstractmethod from dataclasses import KW_ONLY from pathlib import Path -from typing_extensions import Self +import typing_extensions as te from uniserde import Jsonable, JsonDoc import rio @@ -378,9 +378,9 @@ class Component(abc.ABC, metaclass=ComponentMeta): """ return self._session_ - # There isn't really a good type annotation for this... Self is the closest + # There isn't really a good type annotation for this... `te.Self` is the closest # thing - def bind(self) -> Self: + def bind(self) -> te.Self: return AttributeBindingMaker(self) # type: ignore def _custom_serialize_(self) -> JsonDoc: diff --git a/rio/components/flow_container.py b/rio/components/flow_container.py index 8ab81c2d..d32b10b8 100644 --- a/rio/components/flow_container.py +++ b/rio/components/flow_container.py @@ -3,7 +3,7 @@ from __future__ import annotations import typing as t from dataclasses import KW_ONLY -from typing_extensions import Self +import typing_extensions as te from uniserde import JsonDoc import rio @@ -113,7 +113,7 @@ class FlowContainer(FundamentalComponent): self.column_spacing = column_spacing self.justify = justify - def add(self, child: rio.Component) -> Self: + def add(self, child: rio.Component) -> te.Self: """ Appends a child component. diff --git a/rio/components/grid.py b/rio/components/grid.py index 1670f371..82e06160 100644 --- a/rio/components/grid.py +++ b/rio/components/grid.py @@ -4,7 +4,7 @@ import math import typing as t from dataclasses import KW_ONLY, dataclass -from typing_extensions import Self +import typing_extensions as te from uniserde import JsonDoc import rio @@ -210,7 +210,7 @@ class Grid(FundamentalComponent): *, width: int = 1, height: int = 1, - ) -> Self: + ) -> te.Self: """ Add a child to the grid at a specified position. diff --git a/rio/components/labeled_column.py b/rio/components/labeled_column.py index d3989d1e..adb96152 100644 --- a/rio/components/labeled_column.py +++ b/rio/components/labeled_column.py @@ -3,7 +3,7 @@ from __future__ import annotations import typing as t from dataclasses import field -from typing_extensions import Self +import typing_extensions as te import rio @@ -99,7 +99,7 @@ class LabeledColumn(Component): self._content = dict(children) self._child_list = list(children.values()) - def add(self, label: str, child: rio.Component) -> Self: + def add(self, label: str, child: rio.Component) -> te.Self: """ Appends a child component. diff --git a/rio/components/linear_containers.py b/rio/components/linear_containers.py index f046df3b..6d5365ec 100644 --- a/rio/components/linear_containers.py +++ b/rio/components/linear_containers.py @@ -2,7 +2,7 @@ from __future__ import annotations import typing as t -from typing_extensions import Self +import typing_extensions as te from uniserde import JsonDoc import rio @@ -24,7 +24,7 @@ class _LinearContainer(FundamentalComponent): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def add(self, child: rio.Component) -> Self: + def add(self, child: rio.Component) -> te.Self: self.children.append(child) return self @@ -169,7 +169,7 @@ class Row(_LinearContainer): self.spacing = spacing self.proportions = proportions - def add(self, child: rio.Component) -> Self: + def add(self, child: rio.Component) -> te.Self: """ Appends a child component. @@ -325,7 +325,7 @@ class Column(_LinearContainer): self.spacing = spacing self.proportions = proportions - def add(self, child: rio.Component) -> Self: + def add(self, child: rio.Component) -> te.Self: """ Appends a child component. diff --git a/rio/components/list_view.py b/rio/components/list_view.py index fa9c2f4f..f7d7ba76 100644 --- a/rio/components/list_view.py +++ b/rio/components/list_view.py @@ -2,7 +2,7 @@ from __future__ import annotations import typing as t -from typing_extensions import Self +import typing_extensions as te import rio @@ -140,7 +140,7 @@ class ListView(FundamentalComponent): self.children = list(children) - def add(self, child: rio.Component) -> Self: + def add(self, child: rio.Component) -> te.Self: """ Appends a child component. diff --git a/rio/components/stack.py b/rio/components/stack.py index 79bb482c..5330abbb 100644 --- a/rio/components/stack.py +++ b/rio/components/stack.py @@ -2,7 +2,7 @@ from __future__ import annotations import typing as t -from typing_extensions import Self +import typing_extensions as te import rio @@ -113,7 +113,7 @@ class Stack(FundamentalComponent): self.children = list(children) - def add(self, child: rio.Component) -> Self: + def add(self, child: rio.Component) -> te.Self: """ Appends a child component. diff --git a/rio/data_models.py b/rio/data_models.py index 0d0e0ff0..44767fb9 100644 --- a/rio/data_models.py +++ b/rio/data_models.py @@ -3,11 +3,10 @@ from __future__ import annotations import typing as t from dataclasses import dataclass -import uniserde - # Never import * from typing_extensions! It breaks `Any` on 3.10, preventing # users from connecting. Ask me how I know. -from typing_extensions import Self +import typing_extensions as te +import uniserde import rio @@ -89,7 +88,7 @@ class InitialClientMessage(uniserde.Serde): *, url: str, user_settings: uniserde.JsonDoc = {}, - ) -> Self: + ) -> te.Self: """ Convenience method for creating default settings when they don't really matter: unit tests, crawlers, etc. diff --git a/rio/dataclass.py b/rio/dataclass.py index fb6c9d2a..4f85f668 100644 --- a/rio/dataclass.py +++ b/rio/dataclass.py @@ -7,10 +7,7 @@ import functools import inspect import typing as t -from typing_extensions import ( - Self, - dataclass_transform, -) +import typing_extensions as te from . import inspection @@ -86,7 +83,7 @@ class RioField(dataclasses.Field): self.real_default_value = real_default_value @classmethod - def from_dataclass_field(cls, field: dataclasses.Field) -> Self: + def from_dataclass_field(cls, field: dataclasses.Field) -> te.Self: if field.default is dataclasses.MISSING: default = field.default default_factory = field.default_factory @@ -130,12 +127,12 @@ def _make_default_factory_for_value(value: T) -> t.Callable[[], T]: return functools.partial(copy.deepcopy, value) -@dataclass_transform( +@te.dataclass_transform( eq_default=False, field_specifiers=(internal_field, dataclasses.field), ) class RioDataclassMeta(abc.ABCMeta): - def __init__(cls, *args, **kwargs): + def __init__(cls, *args, **kwargs) -> None: super().__init__(*args, **kwargs) cls_vars = vars(cls) diff --git a/rio/fills.py b/rio/fills.py index 591214e2..4a569505 100644 --- a/rio/fills.py +++ b/rio/fills.py @@ -4,7 +4,7 @@ import typing as t from abc import ABC from dataclasses import dataclass -from typing_extensions import TypeAlias +import typing_extensions as te from uniserde import Jsonable import rio @@ -239,6 +239,6 @@ class FrostedGlassFill(Fill): } -_FillLike: TypeAlias = ( +_FillLike: te.TypeAlias = ( SolidFill | LinearGradientFill | ImageFill | FrostedGlassFill | Color ) diff --git a/rio/snippets/__init__.py b/rio/snippets/__init__.py index 083ece0c..d266528c 100644 --- a/rio/snippets/__init__.py +++ b/rio/snippets/__init__.py @@ -9,8 +9,8 @@ import urllib.parse from dataclasses import dataclass from pathlib import Path +import typing_extensions as te import uniserde -from typing_extensions import TypeAlias from .. import utils @@ -31,7 +31,7 @@ DEFAULT_META_DICT = { # # THE ORDER MATTERS. `revel` will display the options in the same order as they # appear in the literal -AvailableTemplatesLiteral: TypeAlias = t.Literal[ +AvailableTemplatesLiteral: te.TypeAlias = t.Literal[ # Keep the empty template first "Empty", # Sort the remainder alphabetically diff --git a/rio/testing.py b/rio/testing.py index 53f77438..191861ad 100644 --- a/rio/testing.py +++ b/rio/testing.py @@ -3,7 +3,7 @@ import typing as t import ordered_set import starlette.datastructures -from typing_extensions import Self +import typing_extensions as te from uniserde import JsonDoc import rio @@ -102,7 +102,7 @@ class TestClient: if message["method"] == "updateComponentStates": self._first_refresh_completed.set() - async def __aenter__(self) -> Self: + async def __aenter__(self) -> te.Self: url = str(rio.URL("http://unit.test") / self._active_url.lstrip("/")) self._session = await self._app_server.create_session( diff --git a/rio/user_settings_module.py b/rio/user_settings_module.py index 5453e68c..1ced12d2 100644 --- a/rio/user_settings_module.py +++ b/rio/user_settings_module.py @@ -4,8 +4,8 @@ import copy import typing as t from dataclasses import field +import typing_extensions as te import uniserde -from typing_extensions import Self from . import inspection, session from .dataclass import RioDataclassMeta, all_class_fields @@ -102,8 +102,8 @@ class UserSettings(metaclass=RioDataclassMeta): def _from_json( cls, settings_json: uniserde.JsonDoc, - defaults: Self, - ) -> Self: + defaults: te.Self, + ) -> te.Self: # Create the instance for this attachment. Bypass the constructor so the # instance doesn't immediately try to synchronize with the frontend. self = object.__new__(cls) @@ -163,7 +163,7 @@ class UserSettings(metaclass=RioDataclassMeta): if not t.TYPE_CHECKING: __setattr__ = __setattr - def _equals(self, other: Self) -> bool: + def _equals(self, other: te.Self) -> bool: if type(self) != type(other): return False diff --git a/rio/utils.py b/rio/utils.py index 7faaf4c9..06353efb 100644 --- a/rio/utils.py +++ b/rio/utils.py @@ -14,8 +14,8 @@ from io import BytesIO, StringIO from pathlib import Path import imy.assets +import typing_extensions as te from PIL.Image import Image -from typing_extensions import Annotated from yarl import URL import rio @@ -57,7 +57,7 @@ else: # Constants & types _READONLY = object() T = t.TypeVar("T") -Readonly = Annotated[T, _READONLY] +Readonly = te.Annotated[T, _READONLY] ImageLike = Path | Image | URL | bytes From e9007f93c1bc7217ae483a7ba75471e9c049c797 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 27 Sep 2024 20:57:51 +0200 Subject: [PATCH 06/56] many small fixes --- frontend/code/components/link.ts | 1 - frontend/code/components/switcherBar.ts | 1 - frontend/code/components/table.ts | 2 -- frontend/code/inputBox.ts | 6 +++--- frontend/code/rpc.ts | 3 --- frontend/css/style.scss | 11 +++++++++++ rio/app_server/fastapi_server.py | 2 ++ rio/cli/rio_api.py | 4 ++-- rio/cli/run_project/webview_worker.py | 2 +- rio/components/icon.py | 2 +- rio/components/table.py | 9 +++------ rio/components/text_input.py | 2 +- rio/dialog.py | 6 +++--- rio/icons.py | 22 ++++++++++------------ rio/serialization.py | 3 +-- rio/session.py | 9 +++++---- tests/utils/layouting.py | 3 +-- 17 files changed, 44 insertions(+), 44 deletions(-) diff --git a/frontend/code/components/link.ts b/frontend/code/components/link.ts index 3d32a9f7..df918249 100644 --- a/frontend/code/components/link.ts +++ b/frontend/code/components/link.ts @@ -66,7 +66,6 @@ export class LinkComponent extends ComponentBase { this.removeHtmlChild(latentComponents); // Add the icon, if any - console.debug(deltaState.icon, this.state.icon); let icon = deltaState.icon ?? this.state.icon; if (icon !== null) { diff --git a/frontend/code/components/switcherBar.ts b/frontend/code/components/switcherBar.ts index 9e204396..faeac520 100644 --- a/frontend/code/components/switcherBar.ts +++ b/frontend/code/components/switcherBar.ts @@ -356,7 +356,6 @@ export class SwitcherBarComponent extends ComponentBase { if (deltaState.selectedName !== this.state.selectedName) { this.state.selectedName = deltaState.selectedName; this.state.names = deltaState.names ?? this.state.names; - this.animateToCurrentTarget(); } } else if (deltaState.selectedName === null) { diff --git a/frontend/code/components/table.ts b/frontend/code/components/table.ts index 1999015b..8c6380cf 100644 --- a/frontend/code/components/table.ts +++ b/frontend/code/components/table.ts @@ -48,8 +48,6 @@ export class TableComponent extends ComponentBase { // Content if (deltaState.data !== undefined) { - console.log(`Headers ${deltaState.headers}`); - console.log(`Data ${deltaState.data}`); this.updateContent(); // Since the content was completely replaced, there is no need to diff --git a/frontend/code/inputBox.ts b/frontend/code/inputBox.ts index 222c0001..6172c027 100644 --- a/frontend/code/inputBox.ts +++ b/frontend/code/inputBox.ts @@ -1,6 +1,6 @@ import { markEventAsHandled, stopPropagation } from "./eventHandling"; -export type InputBoxStyle = "rectangular" | "pill"; +export type InputBoxStyle = "rounded" | "pill"; /// A text input field providing the following features and more: /// @@ -36,7 +36,7 @@ export class InputBox { this.outerElement = document.createElement("div"); this.outerElement.classList.add( "rio-input-box", - "rio-input-box-style-rectangular" + "rio-input-box-style-rounded" ); this.outerElement.innerHTML = ` @@ -235,7 +235,7 @@ export class InputBox { set style(style: InputBoxStyle) { this.outerElement.classList.remove( - "rio-input-box-style-rectangular", + "rio-input-box-style-rounded", "rio-input-box-style-pill" ); diff --git a/frontend/code/rpc.ts b/frontend/code/rpc.ts index fc54b06b..a571eaa4 100644 --- a/frontend/code/rpc.ts +++ b/frontend/code/rpc.ts @@ -417,9 +417,6 @@ export async function processMessageReturnResponse( message.params.themeVariant ); - // Remove the default anti-flashbang gray - document.documentElement.style.background = ""; - response = null; break; diff --git a/frontend/css/style.scss b/frontend/css/style.scss index 4cdcc1f8..57810cef 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -558,11 +558,22 @@ $rio-input-box-small-label-spacing-top: 0.5rem; background-color: var(--rio-local-bg-variant); transition: background-color 0.1s linear; +} +.rio-input-box-style-rounded { border-radius: var(--rio-global-corner-radius-small) var(--rio-global-corner-radius-small) 0 0; } +.rio-input-box-style-pill { + border-radius: $infinite-corner-radius; + + & > .rio-input-box-plain-bar, + & > .rio-input-box-color-bar { + display: none; + } +} + .rio-input-box:hover:not(.rio-insensitive) { background-color: var(--rio-local-bg-active); } diff --git a/rio/app_server/fastapi_server.py b/rio/app_server/fastapi_server.py index 41a192c5..8b17c394 100644 --- a/rio/app_server/fastapi_server.py +++ b/rio/app_server/fastapi_server.py @@ -483,6 +483,8 @@ class FastapiServer(fastapi.FastAPI, AbstractAppServer): # Load the template html_ = read_frontend_template("index.html") + self.app._theme.background_color + html_ = html_.replace( "{session_token}", session_token, diff --git a/rio/cli/rio_api.py b/rio/cli/rio_api.py index 70ae7e87..fbd25eb0 100644 --- a/rio/cli/rio_api.py +++ b/rio/cli/rio_api.py @@ -67,7 +67,7 @@ class RioApi: *, method: t.Literal["get", "post", "delete"] = "get", json: dict[str, t.Any] | None = None, - file: BinaryIO | None = None, + file: t.BinaryIO | None = None, ) -> t.Any: """ Make a request to the Rio API. @@ -148,7 +148,7 @@ class RioApi: self, *, name: str, - packed_app: BinaryIO, + packed_app: t.BinaryIO, realm: t.Literal["pro", "free", "test"], start: bool, ) -> None: diff --git a/rio/cli/run_project/webview_worker.py b/rio/cli/run_project/webview_worker.py index f506f8f4..4998d092 100644 --- a/rio/cli/run_project/webview_worker.py +++ b/rio/cli/run_project/webview_worker.py @@ -26,7 +26,7 @@ class WebViewWorker: self.url = url # If running, this is the webview window - self.window: Optional[webview.Window] = None + self.window: webview.Window | None = None def run(self) -> None: """ diff --git a/rio/components/icon.py b/rio/components/icon.py index aaa28b0b..21112d1f 100644 --- a/rio/components/icon.py +++ b/rio/components/icon.py @@ -117,7 +117,7 @@ class Icon(FundamentalComponent): `set_name`: The name of the new icon set. This will be used to access the icons. - `icon_set_archive_path`: The path to the `.tar.xz` archive containing the + `set_archive_path`: The path to the `.tar.xz` archive containing the icon set. """ diff --git a/rio/components/table.py b/rio/components/table.py index 72ecc4be..e04d59e0 100644 --- a/rio/components/table.py +++ b/rio/components/table.py @@ -1,6 +1,5 @@ from __future__ import annotations -import typing import typing as t from dataclasses import dataclass @@ -280,7 +279,7 @@ class Table(FundamentalComponent): pandas.DataFrame | polars.DataFrame | t.Mapping[str, t.Iterable[TableValue]] - | t.Iterable[Iterable[TableValue]] + | t.Iterable[t.Iterable[TableValue]] | numpy.ndarray ) show_row_numbers: bool = True @@ -316,9 +315,7 @@ class Table(FundamentalComponent): # Mapping elif isinstance(self.data, t.Mapping): - data = typing.cast( - t.Mapping[str, t.Iterable[TableValue]], self.data - ) + data = t.cast(t.Mapping[str, t.Iterable[TableValue]], self.data) self._headers = list(data.keys()) # Verify all columns have the same length @@ -338,7 +335,7 @@ class Table(FundamentalComponent): # Iterable of iterables else: - data = typing.cast(Iterable[Iterable[TableValue]], self.data) + data = t.cast(t.Iterable[t.Iterable[TableValue]], self.data) self._headers = None self._data = [] row_lengths = set() diff --git a/rio/components/text_input.py b/rio/components/text_input.py index 77253892..c155623a 100644 --- a/rio/components/text_input.py +++ b/rio/components/text_input.py @@ -160,7 +160,7 @@ class TextInput(KeyboardFocusableFundamentalComponent): _: KW_ONLY label: str = "" accessibility_label: str = "" - style: t.Literal["rectangular", "pill"] = "rectangular" + style: t.Literal["rounded", "pill"] = "rounded" prefix_text: str = "" suffix_text: str = "" is_secret: bool = False diff --git a/rio/dialog.py b/rio/dialog.py index b320f179..a90af1f8 100644 --- a/rio/dialog.py +++ b/rio/dialog.py @@ -1,14 +1,14 @@ from __future__ import annotations import asyncio -import typing +import typing as t from . import component, dialog_container -T = typing.TypeVar("T") +T = t.TypeVar("T") -class Dialog(typing.Generic[T]): +class Dialog(t.Generic[T]): # The component that has created this dialog _owning_component: component.Component diff --git a/rio/icons.py b/rio/icons.py index e8424aa8..f87dc58d 100644 --- a/rio/icons.py +++ b/rio/icons.py @@ -9,8 +9,8 @@ __all__ = ["register_icon_set", "register_icon"] since="0.9.2", replacement="rio.Icon.register_icon_set" ) def register_icon_set( - icon_set_name: str, - icon_set_archive_path: Path, + set_name: str, + set_archive_path: Path, ) -> None: """ Add an icon set to the global registry. This allows the icons to be accessed @@ -34,24 +34,22 @@ def register_icon_set( ## Parameters - `icon_set_name`: The name of the new icon set. This will be used to access + `set_name`: The name of the new icon set. This will be used to access the icons. - `icon_set_archive_path`: The path to the `.tar.xz` archive containing the + `set_archive_path`: The path to the `.tar.xz` archive containing the icon set. """ - if icon_set_name in icon_registry.icon_set_archives: - raise ValueError( - f"There is already an icon set named `{icon_set_name}`" - ) + if set_name in icon_registry.icon_set_archives: + raise ValueError(f"There is already an icon set named `{set_name}`") - icon_registry.icon_set_archives[icon_set_name] = icon_set_archive_path + icon_registry.icon_set_archives[set_name] = set_archive_path @deprecations.deprecated(since="0.9.2", replacement="rio.Icon.register_icon") def register_icon( icon_source: Path, - icon_set_name: str, + set_name: str, icon_name: str, variant_name: str | None = None, ) -> None: @@ -89,8 +87,8 @@ def register_icon( # Add it to the icon registry's cache if variant_name is None: - name = f"{icon_set_name}/{icon_name}" + name = f"{set_name}/{icon_name}" else: - name = f"{icon_set_name}/{icon_name}:{variant_name}" + name = f"{set_name}/{icon_name}:{variant_name}" icon_registry.cached_icons[name] = svg_source diff --git a/rio/serialization.py b/rio/serialization.py index e25d4b05..ff624a18 100644 --- a/rio/serialization.py +++ b/rio/serialization.py @@ -6,7 +6,6 @@ import functools import inspect import json import types -import typing import typing as t import introspection.types @@ -291,7 +290,7 @@ def _get_serializer_for_annotation( return functools.partial(_serialize_enum, as_type=annotation) # Sequences of serializable values - if origin in (list, typing.Sequence, collections.abc.Sequence): + if origin in (list, t.Sequence, collections.abc.Sequence): item_serializer = _get_serializer_for_annotation(args[0]) if item_serializer is None: return None diff --git a/rio/session.py b/rio/session.py index d126c909..c7fe51f0 100644 --- a/rio/session.py +++ b/rio/session.py @@ -12,7 +12,6 @@ import shutil import string import time import traceback -import typing import typing as t import weakref from datetime import tzinfo @@ -49,7 +48,7 @@ from .transports import AbstractTransport, TransportInterrupted __all__ = ["Session"] -T = typing.TypeVar("T") +T = t.TypeVar("T") class WontSerialize(Exception): @@ -520,7 +519,9 @@ class Session(unicall.Unicall): window = await self._get_webview_window() if is_maximized: - window.maximize() + # Pyright has trouble with the `maximize` method, though it most + # definitely exists. + window.maximize() # type: ignore else: raise NotImplementedError # FIXME else: @@ -2771,7 +2772,7 @@ a.remove(); # Wait for the user to select an option result = await dialog.wait_for_close() - result = typing.cast(T, result) + result = t.cast(T, result) # Done! return result diff --git a/tests/utils/layouting.py b/tests/utils/layouting.py index ef08f1e0..a51e3de5 100644 --- a/tests/utils/layouting.py +++ b/tests/utils/layouting.py @@ -1,7 +1,6 @@ from __future__ import annotations import asyncio -import typing import typing as t import playwright.async_api @@ -189,7 +188,7 @@ class LayouterFactory: **playwright_obj.devices["Desktop Chrome"] ) - async def _create_session(self) -> tuple[Session, typing.Any]: + async def _create_session(self) -> tuple[Session, t.Any]: assert ( self._app_server is not None ), "Uvicorn isn't running for some reason" From 45a330ae4625398bbda0fa173fe734bf618f48ff Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 27 Sep 2024 22:14:09 +0200 Subject: [PATCH 07/56] flashbang prevention prevention --- frontend/code/rpc.ts | 3 +++ frontend/index.html | 31 +++++++++++++++++++++++++------ rio/app_server/fastapi_server.py | 20 ++++++++++++++++++-- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/frontend/code/rpc.ts b/frontend/code/rpc.ts index a571eaa4..1efd7104 100644 --- a/frontend/code/rpc.ts +++ b/frontend/code/rpc.ts @@ -417,6 +417,9 @@ export async function processMessageReturnResponse( message.params.themeVariant ); + // Remove the default anti-flashbang color + document.documentElement.style.background = ""; + response = null; break; diff --git a/frontend/index.html b/frontend/index.html index 5d4b2fa7..ab40566b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,10 +1,5 @@ - - + {title} @@ -26,4 +21,28 @@ white screen + + diff --git a/rio/app_server/fastapi_server.py b/rio/app_server/fastapi_server.py index 8b17c394..61093da1 100644 --- a/rio/app_server/fastapi_server.py +++ b/rio/app_server/fastapi_server.py @@ -483,8 +483,6 @@ class FastapiServer(fastapi.FastAPI, AbstractAppServer): # Load the template html_ = read_frontend_template("index.html") - self.app._theme.background_color - html_ = html_.replace( "{session_token}", session_token, @@ -528,6 +526,24 @@ class FastapiServer(fastapi.FastAPI, AbstractAppServer): html_base_url, ) + theme = self.app._theme + if isinstance(theme, tuple): + light_theme_background_color = theme[0].background_color + dark_theme_background_color = theme[1].background_color + else: + light_theme_background_color = theme.background_color + dark_theme_background_color = theme.background_color + + html_ = html_.replace( + "{light_theme_background_color}", + f"#{light_theme_background_color.hex}", + ) + + html_ = html_.replace( + "{dark_theme_background_color}", + f"#{dark_theme_background_color.hex}", + ) + # Since the title is user-defined, it might contain placeholders like # `{debug_mode}`. So it's important that user-defined content is # inserted last. From 9f852059f594d0a0e0e5d1ed1ee7dfab2051bfc6 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Sat, 28 Sep 2024 12:26:34 +0200 Subject: [PATCH 08/56] fix navigate_to removing query and fragment --- rio/session.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rio/session.py b/rio/session.py index c7fe51f0..9d3cd7f7 100644 --- a/rio/session.py +++ b/rio/session.py @@ -931,7 +931,9 @@ let element = { }; element.scrollTo({{ top: 0, behavior: "smooth" }}); -window.history.{method}(null, "", {json.dumps(active_page_url.path)}) +// Sometimes the frontend and backend disagree about the domain or protocol, +// which can cause issues. So to be safe, we only send a relative URL. +window.history.{method}(null, "", {json.dumps(str(active_page_url.relative()))}) """, ) From e53adcec1017a9ad501399518c87333ddfc0f961 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Sat, 28 Sep 2024 12:29:45 +0200 Subject: [PATCH 09/56] remove stray line breaks in doc summaries --- rio/docs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rio/docs.py b/rio/docs.py index 9a2a3272..765b0abc 100644 --- a/rio/docs.py +++ b/rio/docs.py @@ -289,6 +289,12 @@ def _get_unprocessed_docs() -> ( if not docs.metadata.public: continue + # Make the summary into a single line. (This is because the summary is + # sometimes displayed inside a `rio.Text`, which honors newlines. We + # don't want that.) + if docs.summary: + docs.summary = docs.summary.replace("\n", " ") + # This object is public result[obj] = docs From 88794c0e71df0b2ecfff9a92a7e6e3f59bd63fe8 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Sat, 28 Sep 2024 19:54:14 +0200 Subject: [PATCH 10/56] bugfix: don't die on empty grid rows --- rio/components/grid.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rio/components/grid.py b/rio/components/grid.py index 82e06160..11073579 100644 --- a/rio/components/grid.py +++ b/rio/components/grid.py @@ -177,11 +177,19 @@ class Grid(FundamentalComponent): else: row = list(row) + # Don't die on empty rows + if not row: + row = t.cast( + list[rio.Component], + [rio.Spacer(grow_x=False, grow_y=False)], + ) + rows.append(row) row_widths.append(len(row)) # Find the target number of columns target_columns = math.lcm(*row_widths) + assert target_columns > 0, (target_columns, row_widths) # Add the children for yy, row_components in enumerate(rows): From 0fdda4a15c82d1dfde15ed321055487011c6e7c2 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Sat, 28 Sep 2024 21:41:15 +0200 Subject: [PATCH 11/56] improved how theme colores are derived --- rio/session.py | 44 +++++++++++++++++++++++++------------------- rio/theme.py | 10 +++++----- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/rio/session.py b/rio/session.py index 9d3cd7f7..b04c0ca3 100644 --- a/rio/session.py +++ b/rio/session.py @@ -2298,17 +2298,13 @@ a.remove(); "backdrop-filter": "none", } - async def _apply_theme(self, thm: theme.Theme) -> None: + def _calculate_theme_css_values(self, thm: theme.Theme) -> dict[str, str]: """ - Updates the client's theme to match the given one. + Determines and returns all CSS values that should be applied to a HTML + element to achieve the given theme. """ - # Store the theme - self.theme = thm - - # Build the set of all CSS variables that must be set - # Miscellaneous - variables: dict[str, str] = { + result: dict[str, str] = { "--rio-global-font": thm.font._serialize(self), "--rio-global-monospace-font": thm.monospace_font._serialize(self), "--rio-global-corner-radius-small": f"{thm.corner_radius_small}rem", @@ -2334,16 +2330,16 @@ a.remove(); palette = getattr(thm, f"{palette_name}_palette") assert isinstance(palette, theme.Palette), palette - variables[f"--rio-global-{palette_name}-bg"] = ( + result[f"--rio-global-{palette_name}-bg"] = ( f"#{palette.background.hex}" ) - variables[f"--rio-global-{palette_name}-bg-variant"] = ( + result[f"--rio-global-{palette_name}-bg-variant"] = ( f"#{palette.background_variant.hex}" ) - variables[f"--rio-global-{palette_name}-bg-active"] = ( + result[f"--rio-global-{palette_name}-bg-active"] = ( f"#{palette.background_active.hex}" ) - variables[f"--rio-global-{palette_name}-fg"] = ( + result[f"--rio-global-{palette_name}-fg"] = ( f"#{palette.foreground.hex}" ) @@ -2360,15 +2356,15 @@ a.remove(); assert isinstance(style, rio.TextStyle), style css_prefix = f"--rio-global-{style_name}" - variables[f"{css_prefix}-font-name"] = ( + result[f"{css_prefix}-font-name"] = ( "inherit" if style.font is None else style.font._serialize(self) ) - variables[f"{css_prefix}-font-size"] = f"{style.font_size}rem" - variables[f"{css_prefix}-italic"] = ( + result[f"{css_prefix}-font-size"] = f"{style.font_size}rem" + result[f"{css_prefix}-italic"] = ( "italic" if style.italic else "normal" ) - variables[f"{css_prefix}-font-weight"] = style.font_weight - variables[f"{css_prefix}-all-caps"] = ( + result[f"{css_prefix}-font-weight"] = style.font_weight + result[f"{css_prefix}-all-caps"] = ( "uppercase" if style.all_caps else "unset" ) @@ -2380,7 +2376,7 @@ a.remove(); if style.strikethrough: text_decorations.append("line-through") - variables[f"{css_prefix}-text-decoration"] = ( + result[f"{css_prefix}-text-decoration"] = ( " ".join(text_decorations) if text_decorations else "none" ) @@ -2393,7 +2389,17 @@ a.remove(); ) for var, value in fill_variables.items(): - variables[f"{css_prefix}-{var}"] = value + result[f"{css_prefix}-{var}"] = value + + # Done + return result + + async def _apply_theme(self, thm: theme.Theme) -> None: + # Store the theme in the session + self.theme = thm + + # Get all CSS values to apply + variables = self._calculate_theme_css_values(thm) # Update the variables client-side await self._remote_apply_theme( diff --git a/rio/theme.py b/rio/theme.py index d46537b0..6c5ffe5d 100644 --- a/rio/theme.py +++ b/rio/theme.py @@ -59,7 +59,7 @@ def _derive_color( # Desaturate the color slightly hue, saturation, value = result.hsv - saturation = max(saturation - offset * 0.6, 0) + saturation = max(saturation * 0.9, 0) return rio.Color.from_hsv( hue=hue, @@ -410,11 +410,11 @@ class Theme: if background_color is None: if mode == "light": background_color = rio.Color.from_grey(1.00).blend( - primary_color, 0.05 + primary_color, 0.08 ) else: background_color = rio.Color.from_grey(0.08).blend( - primary_color, 0.05 + primary_color, 0.02 ) if text_color is None: @@ -438,7 +438,7 @@ class Theme: ), background_active=_derive_color( background_color, - 0.4, + 0.45, bias_to_bright=0.15, target_color=primary_color, ), @@ -456,7 +456,7 @@ class Theme: background=neutral_color, background_variant=_derive_color( neutral_color, - 0.35, + 0.3, bias_to_bright=-0.15, target_color=primary_color, ), From 64bf2c4b5df5cb5906d6c4584ec6f647788767d4 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Sun, 29 Sep 2024 07:05:17 +0200 Subject: [PATCH 12/56] button backgrounds now animate smoothly --- frontend/css/style.scss | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/css/style.scss b/frontend/css/style.scss index 57810cef..f87605e5 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -1262,6 +1262,8 @@ $rio-input-box-small-label-spacing-top: 0.5rem; .rio-buttonstyle-minor { border: 0.1rem solid var(--rio-local-bg); --rio-local-text-color: var(--rio-local-bg); + + transition: background-color 0.15s ease-in-out; } .rio-buttonstyle-minor:hover { @@ -1285,8 +1287,8 @@ $rio-input-box-small-label-spacing-top: 0.5rem; --rio-local-text-color: var(--outer-text-color); } -.rio-buttonstyle-colored-text:hover, -.rio-buttonstyle-plain-text:hover { +.rio-buttonstyle-colored-text, +.rio-buttonstyle-plain-text { cursor: pointer; --rio-local-text-color: var(--rio-local-bg); @@ -1303,9 +1305,15 @@ $rio-input-box-small-label-spacing-top: 0.5rem; z-index: -1; background-color: var(--outer-text-color); - opacity: 0.1; + opacity: 0; border-radius: var(--border-radius); + + transition: opacity 0.1s ease-in-out; + } + + &:hover::after { + opacity: 0.15; } } From bea61ff0936ed5ec5ffc9740070a071d65d370cf Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Sun, 29 Sep 2024 08:36:37 +0200 Subject: [PATCH 13/56] fixed plain text button color --- frontend/css/style.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/css/style.scss b/frontend/css/style.scss index f87605e5..981c4e9e 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -1290,8 +1290,6 @@ $rio-input-box-small-label-spacing-top: 0.5rem; .rio-buttonstyle-colored-text, .rio-buttonstyle-plain-text { cursor: pointer; - --rio-local-text-color: var(--rio-local-bg); - position: relative; &::after { From 25e647ac9185b3ee4919dbac006816a3d45e041d Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Sun, 29 Sep 2024 16:49:01 +0200 Subject: [PATCH 14/56] more theming tweaks --- frontend/css/style.scss | 9 ++++++--- rio/theme.py | 12 ++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/frontend/css/style.scss b/frontend/css/style.scss index 981c4e9e..e8e606b7 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -1263,7 +1263,10 @@ $rio-input-box-small-label-spacing-top: 0.5rem; border: 0.1rem solid var(--rio-local-bg); --rio-local-text-color: var(--rio-local-bg); - transition: background-color 0.15s ease-in-out; + // Note the lack of transition here. While a transition would be nice, the + // text & icon wouldn't animate alongside the background, because they're + // independent high-level components. Having just the background transition + // but the foreground switch immediately is jarring. } .rio-buttonstyle-minor:hover { @@ -2468,7 +2471,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; // the drawer content z-index: 2; - box-shadow: 0 0 1rem var(--rio-global-shadow-color); + box-shadow: 0 0 1.3rem var(--rio-global-shadow-color); transition: transform 0.3s ease-out; } @@ -2611,7 +2614,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; } .rio-card-elevate-on-hover:hover { - box-shadow: 0 0.15rem 0.3rem var(--rio-global-shadow-color); + box-shadow: 0 0.15rem 0.4rem var(--rio-global-shadow-color); } .rio-card-colorize-on-hover:hover { diff --git a/rio/theme.py b/rio/theme.py index 6c5ffe5d..460186a6 100644 --- a/rio/theme.py +++ b/rio/theme.py @@ -41,7 +41,7 @@ def _derive_color( if difference < 0.01: offset_scale = 1 else: - offset_scale = min(1.0 / difference, 1) + offset_scale = min(1.5 / difference, 1) result = color.blend(target_color, offset * offset_scale) @@ -410,7 +410,7 @@ class Theme: if background_color is None: if mode == "light": background_color = rio.Color.from_grey(1.00).blend( - primary_color, 0.08 + primary_color, 0.05 ) else: background_color = rio.Color.from_grey(0.08).blend( @@ -419,9 +419,9 @@ class Theme: if text_color is None: neutral_and_background_text_color = ( - rio.Color.from_grey(0.2) + rio.Color.from_grey(0.3) if background_color.perceived_brightness > 0.5 - else rio.Color.from_grey(0.8) + else rio.Color.from_grey(0.7) ) else: neutral_and_background_text_color = text_color @@ -432,13 +432,13 @@ class Theme: background=background_color, background_variant=_derive_color( background_color, - 0.25, + 0.15, bias_to_bright=-0.15, target_color=primary_color, ), background_active=_derive_color( background_color, - 0.45, + 0.3, bias_to_bright=0.15, target_color=primary_color, ), From 168dc69bde91b24bd222bbebd01ae494d47d7d39 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Sun, 29 Sep 2024 18:31:37 +0200 Subject: [PATCH 15/56] tweaked corner radii --- frontend/css/style.scss | 13 ++++++------- rio/theme.py | 12 ++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/frontend/css/style.scss b/frontend/css/style.scss index e8e606b7..b0d84983 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -2012,7 +2012,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; code { font-family: $monospace-fonts; background: var(--rio-local-bg-variant); - border-radius: var(--rio-global-corner-radius-small); + border-radius: var(--rio-global-corner-radius-medium); padding: 0.1rem 0.3rem; } @@ -2145,10 +2145,10 @@ $rio-input-box-small-label-spacing-top: 0.5rem; flex-direction: column; align-items: stretch; gap: 0.5rem; - padding: 0.5rem; + padding: var(--rio-global-corner-radius-medium); background: var(--rio-local-bg-variant); - border-radius: var(--rio-global-corner-radius-small); + border-radius: var(--rio-global-corner-radius-medium); box-sizing: border-box; } @@ -2176,7 +2176,6 @@ $rio-input-box-small-label-spacing-top: 0.5rem; cursor: pointer; border: none; background: none; - border-radius: var(--rio-global-corner-radius-small); margin: 0; padding: 0; opacity: 0.4; @@ -2264,7 +2263,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; min-height: 6rem; cursor: crosshair; margin-bottom: 0.7rem; - border-radius: var(--rio-global-corner-radius-small); + border-radius: var(--rio-global-corner-radius-medium); flex-grow: 1; } @@ -2891,7 +2890,7 @@ $rio-input-box-small-label-spacing-top: 0.5rem; overflow-wrap: break-word; padding: 0.5rem 1rem; background-color: var(--rio-local-bg-variant); - border-radius: var(--rio-global-corner-radius-small); + border-radius: var(--rio-global-corner-radius-medium); } .rio-traceback-footer { @@ -3296,7 +3295,7 @@ html.picking-component * { align-items: stretch; background: var(--rio-global-danger-bg); - border-radius: var(--rio-global-corner-radius-small); + border-radius: var(--rio-global-corner-radius-medium); // `rio-error-placeholder-content` can't have a corner radius set, because that // would make the barber pole peek through the corners. Instead enforce diff --git a/rio/theme.py b/rio/theme.py index 460186a6..6bf4118d 100644 --- a/rio/theme.py +++ b/rio/theme.py @@ -260,9 +260,9 @@ class Theme: success_color: rio.Color | None = None, warning_color: rio.Color | None = None, danger_color: rio.Color | None = None, - corner_radius_small: float = 0.5, - corner_radius_medium: float = 1.0, - corner_radius_large: float = 2.0, + corner_radius_small: float = 0.3, + corner_radius_medium: float = 0.8, + corner_radius_large: float = 1.8, heading_fill: t.Literal["primary", "plain", "auto"] | text_style_module._TextFill = "auto", text_color: rio.Color | None = None, @@ -589,9 +589,9 @@ class Theme: success_color: rio.Color | None = None, warning_color: rio.Color | None = None, danger_color: rio.Color | None = None, - corner_radius_small: float = 0.6, - corner_radius_medium: float = 1.6, - corner_radius_large: float = 2.6, + corner_radius_small: float = 0.3, + corner_radius_medium: float = 0.8, + corner_radius_large: float = 1.8, text_color: rio.Color | tuple[rio.Color | None, rio.Color | None] | None = None, From 585f031e2190e09f5c6119465abdb921aa397f84 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Mon, 30 Sep 2024 14:42:17 +0200 Subject: [PATCH 16/56] minor improvements in multi-page howto --- rio/routing.py | 5 ++-- .../snippet-files/howtos/multiple-pages.md | 30 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/rio/routing.py b/rio/routing.py index 8fd3da04..2548018c 100644 --- a/rio/routing.py +++ b/rio/routing.py @@ -420,8 +420,9 @@ def page( style="heading1", ) - For additional details, please refer to the how-to guide: - `https://rio.dev/docs/howto/multiple-pages`. + For additional details, please refer to the how-to guide [Multiple + Pages](https://rio.dev/docs/howto/multiple-pages). + ## Parameters diff --git a/rio/snippets/snippet-files/howtos/multiple-pages.md b/rio/snippets/snippet-files/howtos/multiple-pages.md index 4211cab2..f8d18ae2 100644 --- a/rio/snippets/snippet-files/howtos/multiple-pages.md +++ b/rio/snippets/snippet-files/howtos/multiple-pages.md @@ -144,18 +144,18 @@ For simpler cases, you can use the `rio.Link` component to create a link to a page: ```python -rio.Link("Home", url="/") +rio.Link("Home", "/") ``` ### Combining Links and Buttons -OYou can also use the `rio.Link` component to wrap other components, such as +You can also use the `rio.Link` component to wrap other components, such as buttons, to create interactive navigation elements: ```python rio.Link( rio.Button("Home"), - url="/", + target_url="/", ) ``` @@ -166,7 +166,7 @@ of a link. Users can navigate directly to a specific page by entering the corresponding URL in their browser. For example, to visit the About Us page, they would go to -`https://MyDomain.com/about-page`. +`https://my-domain.com/about-page`. ## Nested Pages @@ -205,9 +205,13 @@ class AboutPage(rio.Component): """ def build(self) -> rio.Component: - return rio.Markdown("This is the main page of the app section.", - "Explore more about our features and functionalities here." - ) + return rio.Column( + rio.Markdown( + "This is the main page of the app section.\n\n" + "Explore more about our features and functionalities here." + ), + rio.PageView(), + ) ``` ### Example: Creating a Nested About Us Page @@ -229,7 +233,7 @@ class AboutPage(rio.Component): ### Accessing Nested Pages To navigate to these nested pages, users can use URLs like: -`https://MyDomain.com/app/about-page` +`https://my-domain.com/app/about-page` This URL structure reflects the nested hierarchy, making it easier for users to understand the organization of your application. @@ -243,7 +247,7 @@ the necessary permissions to access a page. ### How Guards Work A guard is a function that takes a `GuardEvent` object as an argument. Based on -the logic within the guard, it returns a str (the url_segment) to redirect +the logic within the guard, it returns a str (the `url_segment`) to redirect unauthorized users or `None` to grant access. In more details see our @@ -289,10 +293,6 @@ To protect a page with a guard, simply add the `guard` parameter to the guard=guard, # Apply the guard function ) class AppPage(rio.Component): - """ - A sample login page. - """ - def build(self) -> rio.Component: return rio.Markdown("This page is protected. Only authorized users can view this content.") ``` @@ -313,8 +313,8 @@ guards for access control in a Rio application. To dive deeper into these topics, refer to the following resources: - [Authentication Example](https://rio.dev/examples/authentication) -- [API Documentation for GuardEvent](https://rio.dev/docs/api/guardevent) -- [API Documentation for @rio.page decorator](https://rio.dev/docs/api/page) +- [API Documentation for `GuardEvent`](https://rio.dev/docs/api/guardevent) +- [API Documentation for `@rio.page` decorator](https://rio.dev/docs/api/page) By following these practices, you can build a well-structured, secure, and user-friendly application using the Rio framework. From bc830505b172401222cb58ad9fb850b8f1af17d4 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Tue, 1 Oct 2024 17:44:10 +0200 Subject: [PATCH 17/56] improved input box styling --- frontend/code/components/multiLineTextInput.ts | 7 ++++++- frontend/code/inputBox.ts | 3 ++- frontend/css/style.scss | 14 +++++++++----- rio/components/multi_line_text_input.py | 6 ++++++ rio/components/text_input.py | 4 +++- rio/theme.py | 4 ++-- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/frontend/code/components/multiLineTextInput.ts b/frontend/code/components/multiLineTextInput.ts index 25ce2641..98158073 100644 --- a/frontend/code/components/multiLineTextInput.ts +++ b/frontend/code/components/multiLineTextInput.ts @@ -1,5 +1,5 @@ import { markEventAsHandled } from "../eventHandling"; -import { InputBox } from "../inputBox"; +import { InputBox, InputBoxStyle } from "../inputBox"; import { ComponentBase, ComponentState } from "./componentBase"; export type MultiLineTextInputState = ComponentState & { @@ -7,6 +7,7 @@ export type MultiLineTextInputState = ComponentState & { text?: string; label?: string; accessibility_label?: string; + style?: InputBoxStyle; is_sensitive?: boolean; is_valid?: boolean; }; @@ -66,6 +67,10 @@ export class MultiLineTextInputComponent extends ComponentBase { this.inputBox.accessibilityLabel = deltaState.accessibility_label; } + if (deltaState.style !== undefined) { + this.inputBox.style = deltaState.style; + } + if (deltaState.is_sensitive !== undefined) { this.inputBox.isSensitive = deltaState.is_sensitive; } diff --git a/frontend/code/inputBox.ts b/frontend/code/inputBox.ts index 6172c027..fb886a94 100644 --- a/frontend/code/inputBox.ts +++ b/frontend/code/inputBox.ts @@ -1,6 +1,6 @@ import { markEventAsHandled, stopPropagation } from "./eventHandling"; -export type InputBoxStyle = "rounded" | "pill"; +export type InputBoxStyle = "underlined" | "rounded" | "pill"; /// A text input field providing the following features and more: /// @@ -235,6 +235,7 @@ export class InputBox { set style(style: InputBoxStyle) { this.outerElement.classList.remove( + "rio-input-box-style-underlined", "rio-input-box-style-rounded", "rio-input-box-style-pill" ); diff --git a/frontend/css/style.scss b/frontend/css/style.scss index b0d84983..d057347b 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -560,18 +560,22 @@ $rio-input-box-small-label-spacing-top: 0.5rem; transition: background-color 0.1s linear; } -.rio-input-box-style-rounded { +.rio-input-box-style-underlined { border-radius: var(--rio-global-corner-radius-small) var(--rio-global-corner-radius-small) 0 0; } +.rio-input-box-style-rounded { + border-radius: var(--rio-global-corner-radius-small); +} + .rio-input-box-style-pill { border-radius: $infinite-corner-radius; +} - & > .rio-input-box-plain-bar, - & > .rio-input-box-color-bar { - display: none; - } +*:not(.rio-input-box-style-underlined) > .rio-input-box-plain-bar, +*:not(.rio-input-box-style-underlined) > .rio-input-box-color-bar { + display: none; } .rio-input-box:hover:not(.rio-insensitive) { diff --git a/rio/components/multi_line_text_input.py b/rio/components/multi_line_text_input.py index 84b8854b..4022bb76 100644 --- a/rio/components/multi_line_text_input.py +++ b/rio/components/multi_line_text_input.py @@ -42,6 +42,8 @@ class MultiLineTextInput(KeyboardFocusableFundamentalComponent): `label`: A short text to display next to the text input. + `style`: Changes the visual appearance of the text input. + `is_sensitive`: Whether the text input should respond to user input. `is_valid`: Visually displays to the user whether the current text is @@ -115,6 +117,10 @@ class MultiLineTextInput(KeyboardFocusableFundamentalComponent): on_confirm: rio.EventHandler[MultiLineTextInputConfirmEvent] = None accessibility_label: str = "" + # Note the lack of the `"pill"` style. It looks silly with tall components + # so is intentionally omitted here. + style: t.Literal["underlined", "rounded"] = "underlined" + def _validate_delta_state_from_frontend(self, delta_state: JsonDoc) -> None: if not set(delta_state) <= {"text"}: raise AssertionError( diff --git a/rio/components/text_input.py b/rio/components/text_input.py index c155623a..b629ccc5 100644 --- a/rio/components/text_input.py +++ b/rio/components/text_input.py @@ -88,6 +88,8 @@ class TextInput(KeyboardFocusableFundamentalComponent): `label`: A short text to display next to the text input. + `style`: Changes the visual appearance of the text input. + `prefix_text`: A short text to display before the text input. Useful for displaying currency symbols or other prefixed units. @@ -160,7 +162,7 @@ class TextInput(KeyboardFocusableFundamentalComponent): _: KW_ONLY label: str = "" accessibility_label: str = "" - style: t.Literal["rounded", "pill"] = "rounded" + style: t.Literal["underlined", "rounded", "pill"] = "underlined" prefix_text: str = "" suffix_text: str = "" is_secret: bool = False diff --git a/rio/theme.py b/rio/theme.py index 6bf4118d..68706ef4 100644 --- a/rio/theme.py +++ b/rio/theme.py @@ -260,7 +260,7 @@ class Theme: success_color: rio.Color | None = None, warning_color: rio.Color | None = None, danger_color: rio.Color | None = None, - corner_radius_small: float = 0.3, + corner_radius_small: float = 0.4, corner_radius_medium: float = 0.8, corner_radius_large: float = 1.8, heading_fill: t.Literal["primary", "plain", "auto"] @@ -589,7 +589,7 @@ class Theme: success_color: rio.Color | None = None, warning_color: rio.Color | None = None, danger_color: rio.Color | None = None, - corner_radius_small: float = 0.3, + corner_radius_small: float = 0.4, corner_radius_medium: float = 0.8, corner_radius_large: float = 1.8, text_color: rio.Color From 793ef3da6509b4bc6c9d9b6ac939fcfb42531285 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Wed, 2 Oct 2024 10:53:53 +0200 Subject: [PATCH 18/56] fix unused parameter in TextStyle.replace --- rio/text_style.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rio/text_style.py b/rio/text_style.py index b51679fd..7e342e97 100644 --- a/rio/text_style.py +++ b/rio/text_style.py @@ -141,11 +141,13 @@ class TextStyle(SelfSerializing): fill=self.fill if isinstance(fill, UnsetType) else fill, font_size=self.font_size if font_size is None else font_size, italic=self.italic if italic is None else italic, - font_weight=self.font_weight - if font_weight is None - else font_weight, + font_weight=( + self.font_weight if font_weight is None else font_weight + ), underlined=self.underlined if underlined is None else underlined, - strikethrough=self.strikethrough, + strikethrough=( + self.strikethrough if strikethrough is None else strikethrough + ), all_caps=self.all_caps if all_caps is None else all_caps, ) From e1c2f8e9260e1d28c1c74eb4f32ba9c6a1b1af21 Mon Sep 17 00:00:00 2001 From: Sn3llius Date: Wed, 2 Oct 2024 18:15:38 +0200 Subject: [PATCH 19/56] removed TODO from table comp --- rio/components/table.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rio/components/table.py b/rio/components/table.py index e04d59e0..70abc2e9 100644 --- a/rio/components/table.py +++ b/rio/components/table.py @@ -213,6 +213,7 @@ def _indices_to_rectangle( return left, top, width, height +# TODO: add more content to docstring @t.final class Table(FundamentalComponent): """ @@ -223,8 +224,6 @@ class Table(FundamentalComponent): spreadsheets, databases, or CSV files. Tables can be sorted by clicking on the column headers. - TODO - ## Attributes @@ -381,11 +380,13 @@ class Table(FundamentalComponent): def __getitem__( self, - index: str - | tuple[ - int | slice | str, - int | slice | str, - ], + index: ( + str + | tuple[ + int | slice | str, + int | slice | str, + ] + ), ) -> TableSelection: # Get the index as a tuple (top, left, height, width) data_height, data_width = self._shape() From 6f830ac10998f8fedbd097904e6b5b95d88fd7e1 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Wed, 2 Oct 2024 19:19:14 +0200 Subject: [PATCH 20/56] dialogs no longer close themselves when clicking into an inactive child --- frontend/code/components/dialog_container.ts | 18 ++++++++++++++++++ frontend/css/style.scss | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/frontend/code/components/dialog_container.ts b/frontend/code/components/dialog_container.ts index b16cd4d0..e926844b 100644 --- a/frontend/code/components/dialog_container.ts +++ b/frontend/code/components/dialog_container.ts @@ -35,6 +35,24 @@ export class DialogContainerComponent extends ComponentBase { element.addEventListener("click", (event) => { markEventAsHandled(event); + // Don't close the dialog if the click was inside the dialog. This + // is a bit tricky, because of various cases: + // + // - The click was handled by a component inside of the dialog (e.g. + // a Button). This is simple, since the event will never reach the + // dialog container. + // - The click was onto a component in the dialog, but not handled. + // (Think a `rio.Card`). This must be detected and the dialog NOT + // closed. + // - The click was technically into a component, but that component + // doesn't accept clicks. (Think the spacing of a `rio.Row`.) + // Since no component was technically clicked, the dialog should + // close. + // + if (event.target !== element) { + return; + } + // Is the dialog user-closable? if (!this.state.is_user_closable) { return; diff --git a/frontend/css/style.scss b/frontend/css/style.scss index d057347b..ec2a1033 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -269,6 +269,11 @@ select { // All components .rio-component { + // This class isn't directly assigned to the element created by a component, + // but rather a `div` further out, that houses the margin & alignment. It + // thus shouldn't receive any pointer events. + pointer-events: none; + @include shared-component-style(); } From b17419ee2e41573a5ddb0bd1bc0f915589725406 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Wed, 2 Oct 2024 19:22:01 +0200 Subject: [PATCH 21/56] prevent multi line text input click-through --- frontend/code/components/multiLineTextInput.ts | 18 +++++++++++++++++- frontend/code/components/textInput.ts | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/frontend/code/components/multiLineTextInput.ts b/frontend/code/components/multiLineTextInput.ts index 98158073..341374cd 100644 --- a/frontend/code/components/multiLineTextInput.ts +++ b/frontend/code/components/multiLineTextInput.ts @@ -30,7 +30,7 @@ export class MultiLineTextInputComponent extends ComponentBase { }); }); - // Detect shift+enter key and send it to the backend + // Detect `shift+enter` and send it to the backend // // In addition to notifying the backend, also include the input's // current value. This ensures any event handlers actually use the up-to @@ -46,6 +46,22 @@ export class MultiLineTextInputComponent extends ComponentBase { } }); + // Eat click events so the element can't be clicked-through + element.addEventListener("click", (event) => { + event.stopPropagation(); + event.stopImmediatePropagation(); + }); + + element.addEventListener("mousedown", (event) => { + event.stopPropagation(); + event.stopImmediatePropagation(); + }); + + element.addEventListener("mouseup", (event) => { + event.stopPropagation(); + event.stopImmediatePropagation(); + }); + return element; } diff --git a/frontend/code/components/textInput.ts b/frontend/code/components/textInput.ts index d0e76828..ae5928ba 100644 --- a/frontend/code/components/textInput.ts +++ b/frontend/code/components/textInput.ts @@ -66,7 +66,7 @@ export class TextInputComponent extends ComponentBase { }); }); - // Detect the enter key and send them to the backend + // Detect `enter` and send them to the backend // // In addition to notifying the backend, also include the input's // current value. This ensures any event handlers actually use the up-to From bedcdb267804c28aa81f4fae4296f6aa903b9c14 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Wed, 2 Oct 2024 19:23:23 +0200 Subject: [PATCH 22/56] bump version --- rio/__init__.py | 100 ++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/rio/__init__.py b/rio/__init__.py index f77d8da8..42d5ee59 100644 --- a/rio/__init__.py +++ b/rio/__init__.py @@ -1,50 +1,50 @@ -__version__ = "0.10.2" - - -# There is an issue with `rye test`. rye passes a `--rootdir` argument to -# pytest, and webview parses command line arguments when it is imported. It -# crashes parsing the `--rootdir` argument, so we'll temporarily remove the -# command line arguments while importing webview. -import sys - -argv = sys.argv -sys.argv = argv[:1] - -try: - import webview # noqa: F401 -except ImportError: - pass -finally: - sys.argv = argv - - -import logging - -_logger = logging.getLogger(__name__) - -# Re-export dataclass stuff for easy use. -from dataclasses import KW_ONLY as KW_ONLY -from dataclasses import field as field - -# URLs are used as an important datatype within rio. Re-export them for easy -# use. -from yarl import URL as URL - -from . import event as event -from . import icons as icons # For backwards compat. Delete eventually -from . import patches_for_3rd_party_stuff -from .app import * -from .color import * -from .components import * -from .cursor_style import * -from .dialog import * -from .errors import * -from .fills import * -from .routing import * -from .session import * -from .text_style import * -from .theme import * -from .user_settings_module import * -from .utils import * - -del patches_for_3rd_party_stuff +__version__ = "0.10.3rc0" + + +# There is an issue with `rye test`. rye passes a `--rootdir` argument to +# pytest, and webview parses command line arguments when it is imported. It +# crashes parsing the `--rootdir` argument, so we'll temporarily remove the +# command line arguments while importing webview. +import sys + +argv = sys.argv +sys.argv = argv[:1] + +try: + import webview # noqa: F401 +except ImportError: + pass +finally: + sys.argv = argv + + +import logging + +_logger = logging.getLogger(__name__) + +# Re-export dataclass stuff for easy use. +from dataclasses import KW_ONLY as KW_ONLY +from dataclasses import field as field + +# URLs are used as an important datatype within rio. Re-export them for easy +# use. +from yarl import URL as URL + +from . import event as event +from . import icons as icons # For backwards compat. Delete eventually +from . import patches_for_3rd_party_stuff +from .app import * +from .color import * +from .components import * +from .cursor_style import * +from .dialog import * +from .errors import * +from .fills import * +from .routing import * +from .session import * +from .text_style import * +from .theme import * +from .user_settings_module import * +from .utils import * + +del patches_for_3rd_party_stuff From 859e6576e2f9a5fc41d472d5404da6ee3e89ae98 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Thu, 3 Oct 2024 05:41:55 +0200 Subject: [PATCH 23/56] fix crash when a page can't be imported --- rio/routing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rio/routing.py b/rio/routing.py index 2548018c..6cdee975 100644 --- a/rio/routing.py +++ b/rio/routing.py @@ -9,7 +9,7 @@ from pathlib import Path import introspection from introspection import convert_case -import rio +import rio.components.error_placeholder import rio.docs from . import deprecations, utils @@ -564,7 +564,7 @@ def _page_from_python_file( page = _error_page_from_file_name( file_path, error_summary=f"Failed to import '{file_path}'", - error_details=f"{type(error)}: {error}", + error_details=f"{type(error).__name__}: {error}", ) else: # Search the module for the callable decorated with `@rio.page` From a66d25f2b31c7ab1f1c6f550c4f9a99e85fc23e0 Mon Sep 17 00:00:00 2001 From: Sn3llius Date: Thu, 3 Oct 2024 15:05:06 +0200 Subject: [PATCH 24/56] updated tic tac toe example --- .../tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py | 4 ++++ .../tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py | 4 ++++ .../tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py | 4 ++++ .../tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py index 2085756c..97010ddf 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-2/pages/tic_tac_toe_page.py @@ -7,6 +7,10 @@ import rio # #
+@rio.page( + name="Tic Tac Toe", + url_segment="", +) class TicTacToePage(rio.Component): #
# diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py index 9730a236..26350990 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-3/pages/tic_tac_toe_page.py @@ -8,6 +8,10 @@ from .. import components as comps # +@rio.page( + name="Tic Tac Toe", + url_segment="", +) class TicTacToePage(rio.Component): # The contents of all fields. Each field can contain an X, an O, or be # empty. The initial state is an empty board. diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py index f1db265f..b973eedd 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-4/pages/tic_tac_toe_page.py @@ -9,6 +9,10 @@ from .. import components as comps # +@rio.page( + name="Tic Tac Toe", + url_segment="", +) class TicTacToePage(rio.Component): # The contents of all fields. Each field can contain an X, an O, or be # empty. The initial state is an empty board. diff --git a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py index 61daab36..daedd9e0 100644 --- a/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py +++ b/rio/snippets/snippet-files/tutorial-tic-tac-toe-part-5/pages/tic_tac_toe_page.py @@ -9,6 +9,10 @@ from .. import components as comps # +@rio.page( + name="Tic Tac Toe", + url_segment="", +) class TicTacToePage(rio.Component): # The contents of all fields. Each field can contain an X, an O, or be # empty. The initial state is an empty board. From 15b9d6d8d792e689be0529535620cf8a7ed62305 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Thu, 3 Oct 2024 21:56:48 +0200 Subject: [PATCH 25/56] fix placeholder link in default sidebar --- rio/components/default_root_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rio/components/default_root_component.py b/rio/components/default_root_component.py index a854b908..f09a468f 100644 --- a/rio/components/default_root_component.py +++ b/rio/components/default_root_component.py @@ -187,7 +187,7 @@ class DefaultRootComponent(component.Component): rio.Link( "What's this?", icon="material/library_books", - target_url="https://rio.dev/TODO/LINK/TO/HOWTO/REMOVE/NAVIGATION", + target_url="https://rio.dev/docs/howto/remove-default-navbar", open_in_new_tab=True, margin_x=OUTER_MARGIN, margin_y=1, From 5f761740f48217c9a07e336ed27456562e92a1a7 Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Thu, 3 Oct 2024 22:14:02 +0200 Subject: [PATCH 26/56] fix dialogs detecting clicks incorrectly --- frontend/code/components/dialog_container.ts | 1 - frontend/css/style.scss | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/code/components/dialog_container.ts b/frontend/code/components/dialog_container.ts index e926844b..167c24c2 100644 --- a/frontend/code/components/dialog_container.ts +++ b/frontend/code/components/dialog_container.ts @@ -48,7 +48,6 @@ export class DialogContainerComponent extends ComponentBase { // doesn't accept clicks. (Think the spacing of a `rio.Row`.) // Since no component was technically clicked, the dialog should // close. - // if (event.target !== element) { return; } diff --git a/frontend/css/style.scss b/frontend/css/style.scss index ec2a1033..77300198 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -269,16 +269,13 @@ select { // All components .rio-component { - // This class isn't directly assigned to the element created by a component, - // but rather a `div` further out, that houses the margin & alignment. It - // thus shouldn't receive any pointer events. - pointer-events: none; - @include shared-component-style(); } // User-defined components .rio-high-level-component { + pointer-events: none; + @include single-container(); } From acb0a80d72bb38496dd5be9ce0d54e41028e632b Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Thu, 3 Oct 2024 22:16:36 +0200 Subject: [PATCH 27/56] bump version --- rio/__init__.py | 100 ++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/rio/__init__.py b/rio/__init__.py index 42d5ee59..91e48798 100644 --- a/rio/__init__.py +++ b/rio/__init__.py @@ -1,50 +1,50 @@ -__version__ = "0.10.3rc0" - - -# There is an issue with `rye test`. rye passes a `--rootdir` argument to -# pytest, and webview parses command line arguments when it is imported. It -# crashes parsing the `--rootdir` argument, so we'll temporarily remove the -# command line arguments while importing webview. -import sys - -argv = sys.argv -sys.argv = argv[:1] - -try: - import webview # noqa: F401 -except ImportError: - pass -finally: - sys.argv = argv - - -import logging - -_logger = logging.getLogger(__name__) - -# Re-export dataclass stuff for easy use. -from dataclasses import KW_ONLY as KW_ONLY -from dataclasses import field as field - -# URLs are used as an important datatype within rio. Re-export them for easy -# use. -from yarl import URL as URL - -from . import event as event -from . import icons as icons # For backwards compat. Delete eventually -from . import patches_for_3rd_party_stuff -from .app import * -from .color import * -from .components import * -from .cursor_style import * -from .dialog import * -from .errors import * -from .fills import * -from .routing import * -from .session import * -from .text_style import * -from .theme import * -from .user_settings_module import * -from .utils import * - -del patches_for_3rd_party_stuff +__version__ = "0.10.4" + + +# There is an issue with `rye test`. rye passes a `--rootdir` argument to +# pytest, and webview parses command line arguments when it is imported. It +# crashes parsing the `--rootdir` argument, so we'll temporarily remove the +# command line arguments while importing webview. +import sys + +argv = sys.argv +sys.argv = argv[:1] + +try: + import webview # noqa: F401 +except ImportError: + pass +finally: + sys.argv = argv + + +import logging + +_logger = logging.getLogger(__name__) + +# Re-export dataclass stuff for easy use. +from dataclasses import KW_ONLY as KW_ONLY +from dataclasses import field as field + +# URLs are used as an important datatype within rio. Re-export them for easy +# use. +from yarl import URL as URL + +from . import event as event +from . import icons as icons # For backwards compat. Delete eventually +from . import patches_for_3rd_party_stuff +from .app import * +from .color import * +from .components import * +from .cursor_style import * +from .dialog import * +from .errors import * +from .fills import * +from .routing import * +from .session import * +from .text_style import * +from .theme import * +from .user_settings_module import * +from .utils import * + +del patches_for_3rd_party_stuff From 753132718d87cbe9f82f7916ae8229d7c1d41e7b Mon Sep 17 00:00:00 2001 From: Aran-Fey Date: Fri, 4 Oct 2024 15:04:57 +0200 Subject: [PATCH 28/56] fix typescript warning about override of `state` --- frontend/code/componentManagement.ts | 2 +- frontend/code/components/aspectRatioContainer.ts | 2 +- frontend/code/components/buttons.ts | 6 +++--- frontend/code/components/calendar.ts | 2 +- frontend/code/components/card.ts | 2 +- frontend/code/components/checkbox.ts | 2 +- frontend/code/components/classContainer.ts | 2 +- frontend/code/components/codeBlock.ts | 2 +- frontend/code/components/codeExplorer.ts | 2 +- frontend/code/components/colorPicker.ts | 2 +- frontend/code/components/componentBase.ts | 2 +- frontend/code/components/componentTree.ts | 2 +- frontend/code/components/customListItem.ts | 2 +- frontend/code/components/devToolsConnector.ts | 2 +- .../components/{dialog_container.ts => dialogContainer.ts} | 2 +- frontend/code/components/drawer.ts | 2 +- frontend/code/components/dropdown.ts | 2 +- frontend/code/components/errorPlaceholder.ts | 2 +- frontend/code/components/filePickerArea.ts | 2 +- frontend/code/components/flowContainer.ts | 2 +- frontend/code/components/fundamentalRootComponent.ts | 2 +- frontend/code/components/grid.ts | 2 +- frontend/code/components/headingListItem.ts | 2 +- frontend/code/components/highLevelComponent.ts | 2 +- frontend/code/components/html.ts | 2 +- frontend/code/components/icon.ts | 2 +- frontend/code/components/image.ts | 2 +- frontend/code/components/keyEventListener.ts | 2 +- frontend/code/components/layoutDisplay.ts | 2 +- frontend/code/components/linearContainers.ts | 2 +- frontend/code/components/link.ts | 2 +- frontend/code/components/listView.ts | 2 +- frontend/code/components/markdown.ts | 2 +- frontend/code/components/mediaPlayer.ts | 2 +- frontend/code/components/mouseEventListener.ts | 2 +- frontend/code/components/multiLineTextInput.ts | 2 +- frontend/code/components/nodeInput.ts | 2 +- frontend/code/components/nodeOutput.ts | 2 +- frontend/code/components/overlay.ts | 2 +- frontend/code/components/plot.ts | 2 +- frontend/code/components/popup.ts | 2 +- frontend/code/components/progressBar.ts | 2 +- frontend/code/components/progressCircle.ts | 2 +- frontend/code/components/rectangle.ts | 2 +- frontend/code/components/revealer.ts | 2 +- frontend/code/components/scrollContainer.ts | 2 +- frontend/code/components/scrollTarget.ts | 2 +- frontend/code/components/separator.ts | 2 +- frontend/code/components/separatorListItem.ts | 2 +- frontend/code/components/slider.ts | 2 +- frontend/code/components/slideshow.ts | 2 +- frontend/code/components/stack.ts | 2 +- frontend/code/components/switch.ts | 2 +- frontend/code/components/switcher.ts | 2 +- frontend/code/components/switcherBar.ts | 2 +- frontend/code/components/table.ts | 2 +- frontend/code/components/text.ts | 2 +- frontend/code/components/textInput.ts | 2 +- frontend/code/components/themeContextSwitcher.ts | 2 +- frontend/code/components/tooltip.ts | 2 +- frontend/code/components/website.ts | 2 +- 61 files changed, 63 insertions(+), 63 deletions(-) rename frontend/code/components/{dialog_container.ts => dialogContainer.ts} (98%) diff --git a/frontend/code/componentManagement.ts b/frontend/code/componentManagement.ts index 02ddd5f7..a91b742d 100644 --- a/frontend/code/componentManagement.ts +++ b/frontend/code/componentManagement.ts @@ -17,7 +17,7 @@ import { ComponentTreeComponent } from "./components/componentTree"; import { CustomListItemComponent } from "./components/customListItem"; import { devToolsConnector } from "./app"; import { DevToolsConnectorComponent } from "./components/devToolsConnector"; -import { DialogContainerComponent } from "./components/dialog_container"; +import { DialogContainerComponent } from "./components/dialogContainer"; import { DrawerComponent } from "./components/drawer"; import { DropdownComponent } from "./components/dropdown"; import { FilePickerAreaComponent } from "./components/filePickerArea"; diff --git a/frontend/code/components/aspectRatioContainer.ts b/frontend/code/components/aspectRatioContainer.ts index 7b4184d2..ba02da9f 100644 --- a/frontend/code/components/aspectRatioContainer.ts +++ b/frontend/code/components/aspectRatioContainer.ts @@ -9,7 +9,7 @@ export type AspectRatioContainerState = ComponentState & { }; export class AspectRatioContainerComponent extends ComponentBase { - state: Required; + declare state: Required; private innerElement: HTMLElement; private childContainer: HTMLElement; diff --git a/frontend/code/components/buttons.ts b/frontend/code/components/buttons.ts index 78a8ef21..14d837fb 100644 --- a/frontend/code/components/buttons.ts +++ b/frontend/code/components/buttons.ts @@ -13,7 +13,7 @@ type AbstractButtonState = ComponentState & { }; abstract class AbstractButtonComponent extends ComponentBase { - state: Required; + declare state: Required; // This is the element with the `rio-button` class. The subclass is // responsible for creating it (by calling `createButtonElement()`). @@ -132,7 +132,7 @@ export type ButtonState = AbstractButtonState & { }; export class ButtonComponent extends AbstractButtonComponent { - state: Required; + declare state: Required; createElement(): HTMLElement { this.buttonElement = this.createButtonElement(); @@ -147,7 +147,7 @@ export type IconButtonState = AbstractButtonState & { }; export class IconButtonComponent extends AbstractButtonComponent { - state: Required; + declare state: Required; private resizeObserver: ResizeObserver; diff --git a/frontend/code/components/calendar.ts b/frontend/code/components/calendar.ts index 822e3481..7517ccc2 100644 --- a/frontend/code/components/calendar.ts +++ b/frontend/code/components/calendar.ts @@ -16,7 +16,7 @@ export type CalendarState = ComponentState & { }; export class CalendarComponent extends ComponentBase { - state: Required; + declare state: Required; // Internal HTML Elements private prevYearButton: HTMLElement; diff --git a/frontend/code/components/card.ts b/frontend/code/components/card.ts index 36a590b2..6f650094 100644 --- a/frontend/code/components/card.ts +++ b/frontend/code/components/card.ts @@ -16,7 +16,7 @@ export type CardState = ComponentState & { }; export class CardComponent extends ComponentBase { - state: Required; + declare state: Required; // If this card has a ripple effect, this is the ripple instance. `null` // otherwise. diff --git a/frontend/code/components/checkbox.ts b/frontend/code/components/checkbox.ts index 10b70dc6..c51c32bc 100644 --- a/frontend/code/components/checkbox.ts +++ b/frontend/code/components/checkbox.ts @@ -8,7 +8,7 @@ export type CheckboxState = ComponentState & { }; export class CheckboxComponent extends ComponentBase { - state: Required; + declare state: Required; private checkboxElement: HTMLInputElement; private borderElement: HTMLElement; diff --git a/frontend/code/components/classContainer.ts b/frontend/code/components/classContainer.ts index e739bcc3..922d62f3 100644 --- a/frontend/code/components/classContainer.ts +++ b/frontend/code/components/classContainer.ts @@ -8,7 +8,7 @@ export type ClassContainerState = ComponentState & { }; export class ClassContainerComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { return document.createElement("div"); diff --git a/frontend/code/components/codeBlock.ts b/frontend/code/components/codeBlock.ts index 4afe420f..34d3b2d7 100644 --- a/frontend/code/components/codeBlock.ts +++ b/frontend/code/components/codeBlock.ts @@ -127,7 +127,7 @@ export function convertDivToCodeBlock( } export class CodeBlockComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { const element = document.createElement("div"); diff --git a/frontend/code/components/codeExplorer.ts b/frontend/code/components/codeExplorer.ts index f0e7fb27..ac867a4c 100644 --- a/frontend/code/components/codeExplorer.ts +++ b/frontend/code/components/codeExplorer.ts @@ -13,7 +13,7 @@ export type CodeExplorerState = ComponentState & { }; export class CodeExplorerComponent extends ComponentBase { - state: Required; + declare state: Required; private sourceCodeElement: HTMLElement; private arrowElement: HTMLElement; diff --git a/frontend/code/components/colorPicker.ts b/frontend/code/components/colorPicker.ts index 248beba6..e0c8933f 100644 --- a/frontend/code/components/colorPicker.ts +++ b/frontend/code/components/colorPicker.ts @@ -10,7 +10,7 @@ export type ColorPickerState = ComponentState & { }; export class ColorPickerComponent extends ComponentBase { - state: Required; + declare state: Required; private colorSquare: HTMLElement; private squareKnob: HTMLElement; diff --git a/frontend/code/components/componentBase.ts b/frontend/code/components/componentBase.ts index 43960a5e..bf75bfe7 100644 --- a/frontend/code/components/componentBase.ts +++ b/frontend/code/components/componentBase.ts @@ -10,7 +10,7 @@ import { import { ComponentId } from "../dataModels"; import { insertWrapperElement, replaceElement } from "../utils"; import { devToolsConnector } from "../app"; -import { DialogContainerComponent } from "./dialog_container"; +import { DialogContainerComponent } from "./dialogContainer"; /// Base for all component states. Updates received from the backend are /// partial, hence most properties may be undefined. diff --git a/frontend/code/components/componentTree.ts b/frontend/code/components/componentTree.ts index 5cd15890..6be5d41d 100644 --- a/frontend/code/components/componentTree.ts +++ b/frontend/code/components/componentTree.ts @@ -16,7 +16,7 @@ export type ComponentTreeState = ComponentState & { }; export class ComponentTreeComponent extends ComponentBase { - state: Required; + declare state: Required; private highlighter = new Highlighter(); diff --git a/frontend/code/components/customListItem.ts b/frontend/code/components/customListItem.ts index 873176cc..8dd258c1 100644 --- a/frontend/code/components/customListItem.ts +++ b/frontend/code/components/customListItem.ts @@ -9,7 +9,7 @@ export type CustomListItemState = ComponentState & { }; export class CustomListItemComponent extends ComponentBase { - state: Required; + declare state: Required; // If this item has a ripple effect, this is the ripple instance. `null` // otherwise. diff --git a/frontend/code/components/devToolsConnector.ts b/frontend/code/components/devToolsConnector.ts index b3fac984..589b8d5f 100644 --- a/frontend/code/components/devToolsConnector.ts +++ b/frontend/code/components/devToolsConnector.ts @@ -7,7 +7,7 @@ export type DevToolsConnectorState = ComponentState & { }; export class DevToolsConnectorComponent extends ComponentBase { - state: Required; + declare state: Required; // If component tree components exists, they register here public componentTreeComponent: ComponentTreeComponent | null = null; diff --git a/frontend/code/components/dialog_container.ts b/frontend/code/components/dialogContainer.ts similarity index 98% rename from frontend/code/components/dialog_container.ts rename to frontend/code/components/dialogContainer.ts index 167c24c2..0affb67a 100644 --- a/frontend/code/components/dialog_container.ts +++ b/frontend/code/components/dialogContainer.ts @@ -14,7 +14,7 @@ export type DialogContainerState = ComponentState & { }; export class DialogContainerComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { // Create the element diff --git a/frontend/code/components/drawer.ts b/frontend/code/components/drawer.ts index 09941f35..876c24a3 100644 --- a/frontend/code/components/drawer.ts +++ b/frontend/code/components/drawer.ts @@ -17,7 +17,7 @@ export type DrawerState = ComponentState & { }; export class DrawerComponent extends ComponentBase { - state: Required; + declare state: Required; private anchorContainer: HTMLElement; private contentOuterContainer: HTMLElement; diff --git a/frontend/code/components/dropdown.ts b/frontend/code/components/dropdown.ts index 4b94727e..5284d578 100644 --- a/frontend/code/components/dropdown.ts +++ b/frontend/code/components/dropdown.ts @@ -16,7 +16,7 @@ export type DropdownState = ComponentState & { }; export class DropdownComponent extends ComponentBase { - state: Required; + declare state: Required; private inputBox: InputBox; private hiddenOptionsElement: HTMLElement; diff --git a/frontend/code/components/errorPlaceholder.ts b/frontend/code/components/errorPlaceholder.ts index 000f61c8..6fcf3c90 100644 --- a/frontend/code/components/errorPlaceholder.ts +++ b/frontend/code/components/errorPlaceholder.ts @@ -8,7 +8,7 @@ export type BuildFailedState = ComponentState & { }; export class ErrorPlaceholderComponent extends ComponentBase { - state: Required; + declare state: Required; private iconElement: HTMLElement; private summaryElement: HTMLElement; diff --git a/frontend/code/components/filePickerArea.ts b/frontend/code/components/filePickerArea.ts index 857ad3d3..62654008 100644 --- a/frontend/code/components/filePickerArea.ts +++ b/frontend/code/components/filePickerArea.ts @@ -86,7 +86,7 @@ type FilePickerAreaState = ComponentState & { }; export class FilePickerAreaComponent extends ComponentBase { - state: Required; + declare state: Required; private fileInput: HTMLInputElement; private iconElement: HTMLElement; diff --git a/frontend/code/components/flowContainer.ts b/frontend/code/components/flowContainer.ts index 6dd114c7..3ccb4c40 100644 --- a/frontend/code/components/flowContainer.ts +++ b/frontend/code/components/flowContainer.ts @@ -11,7 +11,7 @@ export type FlowState = ComponentState & { }; export class FlowComponent extends ComponentBase { - state: Required; + declare state: Required; private innerElement: HTMLElement; diff --git a/frontend/code/components/fundamentalRootComponent.ts b/frontend/code/components/fundamentalRootComponent.ts index 9c872757..07b9e448 100644 --- a/frontend/code/components/fundamentalRootComponent.ts +++ b/frontend/code/components/fundamentalRootComponent.ts @@ -26,7 +26,7 @@ export type FundamentalRootComponentState = ComponentState & { }; export class FundamentalRootComponent extends ComponentBase { - state: Required; + declare state: Required; public overlaysContainer: HTMLElement; diff --git a/frontend/code/components/grid.ts b/frontend/code/components/grid.ts index c79b62f3..9f041056 100644 --- a/frontend/code/components/grid.ts +++ b/frontend/code/components/grid.ts @@ -19,7 +19,7 @@ export type GridState = ComponentState & { }; export class GridComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/headingListItem.ts b/frontend/code/components/headingListItem.ts index 2579f9b5..893c6033 100644 --- a/frontend/code/components/headingListItem.ts +++ b/frontend/code/components/headingListItem.ts @@ -7,7 +7,7 @@ export type HeadingListItemState = ComponentState & { }; export class HeadingListItemComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { // Create the element diff --git a/frontend/code/components/highLevelComponent.ts b/frontend/code/components/highLevelComponent.ts index 919dddee..3d56d735 100644 --- a/frontend/code/components/highLevelComponent.ts +++ b/frontend/code/components/highLevelComponent.ts @@ -7,7 +7,7 @@ export type HighLevelComponentState = ComponentState & { }; export class HighLevelComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/html.ts b/frontend/code/components/html.ts index 71143fb5..3a920b87 100644 --- a/frontend/code/components/html.ts +++ b/frontend/code/components/html.ts @@ -6,7 +6,7 @@ export type HtmlState = ComponentState & { }; export class HtmlComponent extends ComponentBase { - state: Required; + declare state: Required; private isInitialized = false; diff --git a/frontend/code/components/icon.ts b/frontend/code/components/icon.ts index 5fbec06b..6db6c966 100644 --- a/frontend/code/components/icon.ts +++ b/frontend/code/components/icon.ts @@ -15,7 +15,7 @@ export type IconState = ComponentState & { }; export class IconComponent extends ComponentBase { - state: Required; + declare state: Required; private svgElement: SVGSVGElement; diff --git a/frontend/code/components/image.ts b/frontend/code/components/image.ts index ce80584d..457aa65f 100644 --- a/frontend/code/components/image.ts +++ b/frontend/code/components/image.ts @@ -17,7 +17,7 @@ export type ImageState = ComponentState & { }; export class ImageComponent extends ComponentBase { - state: Required; + declare state: Required; private imageElement: HTMLImageElement; private resizeObserver: ResizeObserver; diff --git a/frontend/code/components/keyEventListener.ts b/frontend/code/components/keyEventListener.ts index 1a587516..8564e49a 100644 --- a/frontend/code/components/keyEventListener.ts +++ b/frontend/code/components/keyEventListener.ts @@ -692,7 +692,7 @@ export type KeyEventListenerState = ComponentState & { }; export class KeyEventListenerComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/layoutDisplay.ts b/frontend/code/components/layoutDisplay.ts index 04bb24d4..8068cdb7 100644 --- a/frontend/code/components/layoutDisplay.ts +++ b/frontend/code/components/layoutDisplay.ts @@ -13,7 +13,7 @@ export type LayoutDisplayState = ComponentState & { }; export class LayoutDisplayComponent extends ComponentBase { - state: Required; + declare state: Required; // Represents the target component's parent. It matches the aspect ratio of // the parent and is centered within this component. diff --git a/frontend/code/components/linearContainers.ts b/frontend/code/components/linearContainers.ts index 2885fabb..8b8e6d38 100644 --- a/frontend/code/components/linearContainers.ts +++ b/frontend/code/components/linearContainers.ts @@ -14,7 +14,7 @@ export type LinearContainerState = ComponentState & { const PROPORTIONS_SPACER_SIZE = 30; export abstract class LinearContainer extends ComponentBase { - state: Required; + declare state: Required; index = -1; // 0 for Rows, 1 for Columns sizeAttribute = ""; // 'width' for Rows, 'height' for Columns diff --git a/frontend/code/components/link.ts b/frontend/code/components/link.ts index df918249..1522ec21 100644 --- a/frontend/code/components/link.ts +++ b/frontend/code/components/link.ts @@ -13,7 +13,7 @@ export type LinkState = ComponentState & { }; export class LinkComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("a"); diff --git a/frontend/code/components/listView.ts b/frontend/code/components/listView.ts index a0e738c0..053c628f 100644 --- a/frontend/code/components/listView.ts +++ b/frontend/code/components/listView.ts @@ -11,7 +11,7 @@ export type ListViewState = ComponentState & { }; export class ListViewComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/markdown.ts b/frontend/code/components/markdown.ts index dd8b97d4..0aff530c 100644 --- a/frontend/code/components/markdown.ts +++ b/frontend/code/components/markdown.ts @@ -113,7 +113,7 @@ function hijackLocalLinks(div: HTMLElement): void { } export class MarkdownComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { const element = document.createElement("div"); diff --git a/frontend/code/components/mediaPlayer.ts b/frontend/code/components/mediaPlayer.ts index 31f4f270..efcd62c3 100644 --- a/frontend/code/components/mediaPlayer.ts +++ b/frontend/code/components/mediaPlayer.ts @@ -59,7 +59,7 @@ async function hasAudio(element: HTMLMediaElement): Promise { } export class MediaPlayerComponent extends ComponentBase { - state: Required; + declare state: Required; private mediaPlayer: HTMLVideoElement; private altDisplay: HTMLElement; diff --git a/frontend/code/components/mouseEventListener.ts b/frontend/code/components/mouseEventListener.ts index 0c1c87f6..41f0aa2d 100644 --- a/frontend/code/components/mouseEventListener.ts +++ b/frontend/code/components/mouseEventListener.ts @@ -33,7 +33,7 @@ export type MouseEventListenerState = ComponentState & { }; export class MouseEventListenerComponent extends ComponentBase { - state: Required; + declare state: Required; private _dragHandler: DragHandler | null = null; diff --git a/frontend/code/components/multiLineTextInput.ts b/frontend/code/components/multiLineTextInput.ts index 341374cd..20b95123 100644 --- a/frontend/code/components/multiLineTextInput.ts +++ b/frontend/code/components/multiLineTextInput.ts @@ -13,7 +13,7 @@ export type MultiLineTextInputState = ComponentState & { }; export class MultiLineTextInputComponent extends ComponentBase { - state: Required; + declare state: Required; private inputBox: InputBox; diff --git a/frontend/code/components/nodeInput.ts b/frontend/code/components/nodeInput.ts index e07570b6..45b2c666 100644 --- a/frontend/code/components/nodeInput.ts +++ b/frontend/code/components/nodeInput.ts @@ -10,7 +10,7 @@ export type NodeInputState = ComponentState & { }; export class NodeInputComponent extends ComponentBase { - state: Required; + declare state: Required; textElement: HTMLElement; circleElement: HTMLElement; diff --git a/frontend/code/components/nodeOutput.ts b/frontend/code/components/nodeOutput.ts index 042a119f..59855094 100644 --- a/frontend/code/components/nodeOutput.ts +++ b/frontend/code/components/nodeOutput.ts @@ -10,7 +10,7 @@ export type NodeOutputState = ComponentState & { }; export class NodeOutputComponent extends ComponentBase { - state: Required; + declare state: Required; textElement: HTMLElement; circleElement: HTMLElement; diff --git a/frontend/code/components/overlay.ts b/frontend/code/components/overlay.ts index c88ec9f9..1e1cb485 100644 --- a/frontend/code/components/overlay.ts +++ b/frontend/code/components/overlay.ts @@ -8,7 +8,7 @@ export type OverlayState = ComponentState & { }; export class OverlayComponent extends ComponentBase { - state: Required; + declare state: Required; private overlayElement: HTMLElement; diff --git a/frontend/code/components/plot.ts b/frontend/code/components/plot.ts index 24296a6d..6d5ff85e 100644 --- a/frontend/code/components/plot.ts +++ b/frontend/code/components/plot.ts @@ -22,7 +22,7 @@ type PlotState = ComponentState & { }; export class PlotComponent extends ComponentBase { - state: Required; + declare state: Required; // I know this abstraction looks like overkill, but plotly does so much // stuff with a time delay (loading plotly, setTimeout, resizeObserver, ...) diff --git a/frontend/code/components/popup.ts b/frontend/code/components/popup.ts index 15b9660f..40b1acaf 100644 --- a/frontend/code/components/popup.ts +++ b/frontend/code/components/popup.ts @@ -23,7 +23,7 @@ export type PopupState = ComponentState & { }; export class PopupComponent extends ComponentBase { - state: Required; + declare state: Required; private anchorContainer: HTMLElement; private contentContainer: HTMLElement; diff --git a/frontend/code/components/progressBar.ts b/frontend/code/components/progressBar.ts index 58efcb3c..385772fc 100644 --- a/frontend/code/components/progressBar.ts +++ b/frontend/code/components/progressBar.ts @@ -10,7 +10,7 @@ export type ProgressBarState = ComponentState & { }; export class ProgressBarComponent extends ComponentBase { - state: Required; + declare state: Required; fillElement: HTMLElement; diff --git a/frontend/code/components/progressCircle.ts b/frontend/code/components/progressCircle.ts index 396e3329..ec97431c 100644 --- a/frontend/code/components/progressCircle.ts +++ b/frontend/code/components/progressCircle.ts @@ -9,7 +9,7 @@ export type ProgressCircleState = ComponentState & { }; export class ProgressCircleComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/rectangle.ts b/frontend/code/components/rectangle.ts index 8110f4c1..3ccec6a8 100644 --- a/frontend/code/components/rectangle.ts +++ b/frontend/code/components/rectangle.ts @@ -72,7 +72,7 @@ function cursorToCSS(cursor: string): string { } export class RectangleComponent extends ComponentBase { - state: Required; + declare state: Required; // If this rectangle has a ripple effect, this is the ripple instance. // `null` otherwise. diff --git a/frontend/code/components/revealer.ts b/frontend/code/components/revealer.ts index 5fa09dcf..2c7ebd70 100644 --- a/frontend/code/components/revealer.ts +++ b/frontend/code/components/revealer.ts @@ -16,7 +16,7 @@ export type RevealerState = ComponentState & { }; export class RevealerComponent extends ComponentBase { - state: Required; + declare state: Required; private headerElement: HTMLElement; private labelElement: HTMLElement; diff --git a/frontend/code/components/scrollContainer.ts b/frontend/code/components/scrollContainer.ts index 24afd66a..07979639 100644 --- a/frontend/code/components/scrollContainer.ts +++ b/frontend/code/components/scrollContainer.ts @@ -12,7 +12,7 @@ export type ScrollContainerState = ComponentState & { }; export class ScrollContainerComponent extends ComponentBase { - state: Required; + declare state: Required; private scrollerElement: HTMLElement; private childContainer: HTMLElement; diff --git a/frontend/code/components/scrollTarget.ts b/frontend/code/components/scrollTarget.ts index 7e6d266d..832a51eb 100644 --- a/frontend/code/components/scrollTarget.ts +++ b/frontend/code/components/scrollTarget.ts @@ -13,7 +13,7 @@ export type ScrollTargetState = ComponentState & { }; export class ScrollTargetComponent extends ComponentBase { - state: Required; + declare state: Required; childContainerElement: HTMLElement; buttonContainerElement: HTMLElement; diff --git a/frontend/code/components/separator.ts b/frontend/code/components/separator.ts index 8c7804b1..538ed128 100644 --- a/frontend/code/components/separator.ts +++ b/frontend/code/components/separator.ts @@ -9,7 +9,7 @@ export type SeparatorState = ComponentState & { }; export class SeparatorComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/separatorListItem.ts b/frontend/code/components/separatorListItem.ts index 8ad059ca..fd549c7e 100644 --- a/frontend/code/components/separatorListItem.ts +++ b/frontend/code/components/separatorListItem.ts @@ -5,7 +5,7 @@ export type SeparatorListItemState = ComponentState & { }; export class SeparatorListItemComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/slider.ts b/frontend/code/components/slider.ts index ca03bdbf..f5456b1e 100644 --- a/frontend/code/components/slider.ts +++ b/frontend/code/components/slider.ts @@ -14,7 +14,7 @@ export type SliderState = ComponentState & { }; export class SliderComponent extends ComponentBase { - state: Required; + declare state: Required; private innerElement: HTMLElement; private minValueElement: HTMLElement; diff --git a/frontend/code/components/slideshow.ts b/frontend/code/components/slideshow.ts index ae87ccea..44e41e5e 100644 --- a/frontend/code/components/slideshow.ts +++ b/frontend/code/components/slideshow.ts @@ -13,7 +13,7 @@ export type SlideshowState = ComponentState & { }; export class SlideshowComponent extends ComponentBase { - state: Required; + declare state: Required; private childContainer: HTMLElement; private progressBar: HTMLElement; diff --git a/frontend/code/components/stack.ts b/frontend/code/components/stack.ts index ba64a94f..bef8773a 100644 --- a/frontend/code/components/stack.ts +++ b/frontend/code/components/stack.ts @@ -7,7 +7,7 @@ export type StackState = ComponentState & { }; export class StackComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/switch.ts b/frontend/code/components/switch.ts index e230852b..cc908bf7 100644 --- a/frontend/code/components/switch.ts +++ b/frontend/code/components/switch.ts @@ -8,7 +8,7 @@ export type SwitchState = ComponentState & { }; export class SwitchComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/switcher.ts b/frontend/code/components/switcher.ts index 8a7133fc..b388167f 100644 --- a/frontend/code/components/switcher.ts +++ b/frontend/code/components/switcher.ts @@ -10,7 +10,7 @@ export type SwitcherState = ComponentState & { }; export class SwitcherComponent extends ComponentBase { - state: Required; + declare state: Required; private activeChildContainer: HTMLElement | null = null; private resizerElement: HTMLElement | null = null; diff --git a/frontend/code/components/switcherBar.ts b/frontend/code/components/switcherBar.ts index faeac520..4b4828fd 100644 --- a/frontend/code/components/switcherBar.ts +++ b/frontend/code/components/switcherBar.ts @@ -19,7 +19,7 @@ export type SwitcherBarState = ComponentState & { }; export class SwitcherBarComponent extends ComponentBase { - state: Required; + declare state: Required; private innerElement: HTMLElement; // Used for alignment private markerElement: HTMLElement; // Highlights the selected item diff --git a/frontend/code/components/table.ts b/frontend/code/components/table.ts index 8c6380cf..eed3f478 100644 --- a/frontend/code/components/table.ts +++ b/frontend/code/components/table.ts @@ -21,7 +21,7 @@ type TableState = ComponentState & { }; export class TableComponent extends ComponentBase { - state: Required; + declare state: Required; private tableElement: HTMLElement; diff --git a/frontend/code/components/text.ts b/frontend/code/components/text.ts index 8b9b29f6..787a81b3 100644 --- a/frontend/code/components/text.ts +++ b/frontend/code/components/text.ts @@ -12,7 +12,7 @@ export type TextState = ComponentState & { }; export class TextComponent extends ComponentBase { - state: Required; + declare state: Required; private inner: HTMLElement; diff --git a/frontend/code/components/textInput.ts b/frontend/code/components/textInput.ts index ae5928ba..340b656a 100644 --- a/frontend/code/components/textInput.ts +++ b/frontend/code/components/textInput.ts @@ -17,7 +17,7 @@ export type TextInputState = ComponentState & { }; export class TextInputComponent extends ComponentBase { - state: Required; + declare state: Required; private inputBox: InputBox; private onChangeLimiter: Debouncer; diff --git a/frontend/code/components/themeContextSwitcher.ts b/frontend/code/components/themeContextSwitcher.ts index af5d7698..5a020b72 100644 --- a/frontend/code/components/themeContextSwitcher.ts +++ b/frontend/code/components/themeContextSwitcher.ts @@ -9,7 +9,7 @@ export type ThemeContextSwitcherState = ComponentState & { }; export class ThemeContextSwitcherComponent extends ComponentBase { - state: Required; + declare state: Required; createElement(): HTMLElement { let element = document.createElement("div"); diff --git a/frontend/code/components/tooltip.ts b/frontend/code/components/tooltip.ts index f1ba9bbb..9de65328 100644 --- a/frontend/code/components/tooltip.ts +++ b/frontend/code/components/tooltip.ts @@ -11,7 +11,7 @@ export type TooltipState = ComponentState & { }; export class TooltipComponent extends ComponentBase { - state: Required; + declare state: Required; private popupElement: HTMLElement; private popupManager: PopupManager; diff --git a/frontend/code/components/website.ts b/frontend/code/components/website.ts index 36d1db69..2fd80451 100644 --- a/frontend/code/components/website.ts +++ b/frontend/code/components/website.ts @@ -6,7 +6,7 @@ export type WebsiteState = ComponentState & { }; export class WebsiteComponent extends ComponentBase { - state: Required; + declare state: Required; element: HTMLIFrameElement; createElement(): HTMLElement { From 4798c2c37d216442c2e7ac8c804811de9ee2c6a5 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 4 Oct 2024 19:33:21 +0200 Subject: [PATCH 29/56] switch all JS mouse events to pointer events --- frontend/code/components/colorPicker.ts | 91 +++++++------------ frontend/code/components/componentTree.ts | 12 +-- frontend/code/components/dropdown.ts | 21 ++--- .../code/components/multiLineTextInput.ts | 4 +- frontend/code/components/textInput.ts | 4 +- frontend/code/inputBox.ts | 28 +++--- rio/debug/dev_tools/icons_page.py | 1 + 7 files changed, 64 insertions(+), 97 deletions(-) diff --git a/frontend/code/components/colorPicker.ts b/frontend/code/components/colorPicker.ts index e0c8933f..05ce6b68 100644 --- a/frontend/code/components/colorPicker.ts +++ b/frontend/code/components/colorPicker.ts @@ -25,8 +25,6 @@ export class ColorPickerComponent extends ComponentBase { private selectedHsv: [number, number, number] = [0, 0, 0]; - private latentEventHandlers: any[] = []; - private isInitialized = false; createElement(): HTMLElement { @@ -84,20 +82,28 @@ export class ColorPickerComponent extends ComponentBase { ".rio-color-picker-selected-color-label" )!; - // Subscribe to mouse down events. The other events will be subscribed - // to only once needed. - this.colorSquare.addEventListener( - "mousedown", - this.onSquareMouseDown.bind(this) - ); - this.hueBarOuter.addEventListener( - "mousedown", - this.onHueBarMouseDown.bind(this) - ); - this.opacityBarOuter.addEventListener( - "mousedown", - this.onOpacityBarMouseDown.bind(this) - ); + // Subscribe to pointer down events + this.addDragHandler({ + element: this.colorSquare, + onStart: this.onSquarePointerDown.bind(this), + onMove: this.onSquarePointerMove.bind(this), + onEnd: this.onSelectionFinished.bind(this), + }); + + this.addDragHandler({ + element: this.hueBarOuter, + onStart: this.onHueBarPointerDown.bind(this), + onMove: this.onHueBarPointerMove.bind(this), + onEnd: this.onSelectionFinished.bind(this), + }); + + this.addDragHandler({ + element: this.opacityBarOuter, + onStart: this.onOpacityBarPointerDown.bind(this), + onMove: this.onOpacityBarPointerMove.bind(this), + onEnd: this.onSelectionFinished.bind(this), + }); + this.selectedColorLabel.addEventListener( "change", this.setFromUserHex.bind(this) @@ -244,63 +250,36 @@ export class ColorPickerComponent extends ComponentBase { this.matchComponentToSelectedHsv(); } - bindHandler(eventName: string, handler: any) { - let boundHandler = handler.bind(this); - document.addEventListener(eventName, boundHandler); - this.latentEventHandlers.push([eventName, boundHandler]); - } - - onSquareMouseDown(event) { + onSquarePointerDown(event): boolean { this.updateSaturationBrightness(event.clientX, event.clientY); - - // Subscribe to other events and keep track of them - this.bindHandler("mousemove", this.onSquareMouseMove); - this.bindHandler("click", this.onSelectionFinished); - - // Eat the event markEventAsHandled(event); + return true; } - onSquareMouseMove(event) { + onSquarePointerMove(event) { this.updateSaturationBrightness(event.clientX, event.clientY); - - // Eat the event markEventAsHandled(event); } - onHueBarMouseDown(event) { + onHueBarPointerDown(event): boolean { this.updateHue(event.clientX); - - // Subscribe to other events and keep track of them - this.bindHandler("mousemove", this.onHueBarMouseMove); - this.bindHandler("click", this.onSelectionFinished); - - // Eat the event markEventAsHandled(event); + return true; } - onHueBarMouseMove(event) { + onHueBarPointerMove(event) { this.updateHue(event.clientX); - - // Eat the event markEventAsHandled(event); } - onOpacityBarMouseDown(event) { + onOpacityBarPointerDown(event): boolean { this.updateOpacity(event.clientX); - - // Subscribe to other events and keep track of them - this.bindHandler("mousemove", this.onOpacityBarMouseMove); - this.bindHandler("click", this.onSelectionFinished); - - // Eat the event markEventAsHandled(event); + return true; } - onOpacityBarMouseMove(event) { + onOpacityBarPointerMove(event) { this.updateOpacity(event.clientX); - - // Eat the event markEventAsHandled(event); } @@ -310,14 +289,6 @@ export class ColorPickerComponent extends ComponentBase { color: this.state.color, }); - // Unsubscribe from all events - for (let handler of this.latentEventHandlers) { - let [eventName, boundHandler] = handler; - document.removeEventListener(eventName, boundHandler); - } - - this.latentEventHandlers = []; - // Eat the event markEventAsHandled(event); } diff --git a/frontend/code/components/componentTree.ts b/frontend/code/components/componentTree.ts index 6be5d41d..4999e35c 100644 --- a/frontend/code/components/componentTree.ts +++ b/frontend/code/components/componentTree.ts @@ -467,19 +467,19 @@ export class ComponentTreeComponent extends ComponentBase { this.highlighter.moveTo(null); document.documentElement.classList.remove("picking-component"); - window.removeEventListener("mousemove", onMouseMove, true); + window.removeEventListener("pointermove", onMouseMove, true); window.removeEventListener("click", onClick, true); - window.removeEventListener("mousedown", markEventAsHandled, true); - window.removeEventListener("mouseup", markEventAsHandled, true); + window.removeEventListener("pointerdown", markEventAsHandled, true); + window.removeEventListener("pointerup", markEventAsHandled, true); resolvePromise(undefined); }; document.documentElement.classList.add("picking-component"); - window.addEventListener("mousemove", onMouseMove, true); + window.addEventListener("pointermove", onMouseMove, true); window.addEventListener("click", onClick, true); - window.addEventListener("mousedown", markEventAsHandled, true); - window.addEventListener("mouseup", markEventAsHandled, true); + window.addEventListener("pointerdown", markEventAsHandled, true); + window.addEventListener("pointerup", markEventAsHandled, true); await donePicking; } diff --git a/frontend/code/components/dropdown.ts b/frontend/code/components/dropdown.ts index 5284d578..1f1b64f6 100644 --- a/frontend/code/components/dropdown.ts +++ b/frontend/code/components/dropdown.ts @@ -70,8 +70,8 @@ export class DropdownComponent extends ComponentBase { // Connect events element.addEventListener( - "mousedown", - this._onMouseDown.bind(this), + "pointerdown", + this._onPointerDown.bind(this), true ); @@ -230,9 +230,8 @@ export class DropdownComponent extends ComponentBase { // entering input. Depending on whether they have put in a valid option, // either save it or reset. // - // Careful: Clicking on a dropdown option with the mouse also causes us - // to lose focus. If we close the popup too early, the click won't hit - // anything. + // Careful: Clicking on a dropdown option also causes us to lose focus. + // If we close the popup too early, the click won't hit anything. // // In Firefox the click is triggered before the focusout, so // that's no problem. But in Chrome, we have to check whether the focus @@ -281,7 +280,7 @@ export class DropdownComponent extends ComponentBase { } /// Open the dropdown and show all options - private _onMouseDown(event: MouseEvent): void { + private _onPointerDown(event: PointerEvent): void { // Do we care? if (!this.state.is_sensitive || event.button !== 0) { return; @@ -312,13 +311,13 @@ export class DropdownComponent extends ComponentBase { // Enter -> select the highlighted option else if (event.key === "Enter") { if (this.highlightedOptionElement !== null) { - let mouseDownEvent = new MouseEvent("mousedown", { + let pointerDownEvent = new PointerEvent("pointerdown", { bubbles: true, cancelable: true, view: window, }); - this.highlightedOptionElement.dispatchEvent(mouseDownEvent); + this.highlightedOptionElement.dispatchEvent(pointerDownEvent); } } @@ -485,15 +484,15 @@ export class DropdownComponent extends ComponentBase { match.classList.add("rio-dropdown-option"); element.appendChild(match); - match.addEventListener("mouseenter", () => { + match.addEventListener("pointerenter", () => { this._highlightOption(match); }); // With a `click` handler, the element loses focus for a // little while, which is noticeable because the floating label will // quickly move down and then back up. To avoid this, we use - // `mousedown` instead. - match.addEventListener("mousedown", (event) => { + // `pointerdown` instead. + match.addEventListener("pointerdown", (event) => { this.submitInput(optionName); markEventAsHandled(event); }); diff --git a/frontend/code/components/multiLineTextInput.ts b/frontend/code/components/multiLineTextInput.ts index 20b95123..9aaf5bbe 100644 --- a/frontend/code/components/multiLineTextInput.ts +++ b/frontend/code/components/multiLineTextInput.ts @@ -52,12 +52,12 @@ export class MultiLineTextInputComponent extends ComponentBase { event.stopImmediatePropagation(); }); - element.addEventListener("mousedown", (event) => { + element.addEventListener("pointerdown", (event) => { event.stopPropagation(); event.stopImmediatePropagation(); }); - element.addEventListener("mouseup", (event) => { + element.addEventListener("pointerup", (event) => { event.stopPropagation(); event.stopImmediatePropagation(); }); diff --git a/frontend/code/components/textInput.ts b/frontend/code/components/textInput.ts index 340b656a..5cbadf40 100644 --- a/frontend/code/components/textInput.ts +++ b/frontend/code/components/textInput.ts @@ -101,12 +101,12 @@ export class TextInputComponent extends ComponentBase { event.stopImmediatePropagation(); }); - element.addEventListener("mousedown", (event) => { + element.addEventListener("pointerdown", (event) => { event.stopPropagation(); event.stopImmediatePropagation(); }); - element.addEventListener("mouseup", (event) => { + element.addEventListener("pointerup", (event) => { event.stopPropagation(); event.stopImmediatePropagation(); }); diff --git a/frontend/code/inputBox.ts b/frontend/code/inputBox.ts index fb886a94..faaed435 100644 --- a/frontend/code/inputBox.ts +++ b/frontend/code/inputBox.ts @@ -36,7 +36,7 @@ export class InputBox { this.outerElement = document.createElement("div"); this.outerElement.classList.add( "rio-input-box", - "rio-input-box-style-rounded" + "rio-input-box-style-underlined" ); this.outerElement.innerHTML = ` @@ -110,27 +110,24 @@ export class InputBox { } private connectClickHandlers(): void { - // Detect clicks on any part of the component and focus the input - // - // The `mousedown` are needed to prevent any potential drag events from - // starting. + // Consider any clicks on the input box as handled. This prevents e.g. + // drag events when trying to select something. this.prefixTextElement.addEventListener( - "mousedown", + "pointerdown", markEventAsHandled ); - this.suffixTextElement.addEventListener( - "mousedown", + this.suffixElementContainer.addEventListener( + "pointerdown", markEventAsHandled ); - // The `click` events pass focus to the input and move the cursor. - // This has to be done in `mouseup`, rather than `mousedown`, because - // otherwise the browser removes the focus again on mouseup. + // When clicked, focus the text element and move the cursor accordingly. let selectStart = (event: Event) => { this._inputElement.focus(); this._inputElement.setSelectionRange(0, 0); markEventAsHandled(event); }; + this.suffixElementContainer; this.prefixTextElement.addEventListener("click", selectStart); let selectEnd = (event: Event) => { @@ -141,7 +138,6 @@ export class InputBox { ); markEventAsHandled(event); }; - this.suffixElementContainer.addEventListener("click", selectEnd); this.suffixTextElement.addEventListener("click", selectEnd); @@ -151,10 +147,10 @@ export class InputBox { paddingLeft.addEventListener("click", selectStart); paddingRight.addEventListener("click", selectEnd); - // Mousedown selects the input element and/or text in it (via dragging), - // so let it do its default behavior but then stop it from propagating - // to other elements - this._inputElement.addEventListener("mousedown", stopPropagation); + // Pointer down events select the input element and/or text in it (via + // dragging), so let them do their default behavior but then stop them + // from propagating to other elements + this._inputElement.addEventListener("pointerdown", stopPropagation); } get inputElement(): HTMLInputElement { diff --git a/rio/debug/dev_tools/icons_page.py b/rio/debug/dev_tools/icons_page.py index 76006674..49bd2d43 100644 --- a/rio/debug/dev_tools/icons_page.py +++ b/rio/debug/dev_tools/icons_page.py @@ -344,6 +344,7 @@ Use the `rio.Icon` component like this: children.append( rio.TextInput( label="Search for an icon", + style="pill", text=self.bind().search_text, on_change=self._on_search_text_change, key="search-text-input", From 71b19d08dc9633ba8b5608d5deb61077533cfb0b Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Fri, 4 Oct 2024 19:48:01 +0200 Subject: [PATCH 30/56] switched more mouse events to pointer events --- changelog.md | 117 +++++++++++----------- frontend/code/components/codeExplorer.ts | 14 +-- frontend/code/components/drawer.ts | 8 +- frontend/code/components/image.ts | 2 +- frontend/code/components/layoutDisplay.ts | 10 +- frontend/code/components/mediaPlayer.ts | 26 ++--- frontend/code/components/revealer.ts | 4 +- frontend/code/components/slider.ts | 14 +-- frontend/code/components/slideshow.ts | 4 +- frontend/code/components/table.ts | 4 +- frontend/code/components/tooltip.ts | 4 +- frontend/code/eventHandling.ts | 50 ++++----- 12 files changed, 131 insertions(+), 126 deletions(-) diff --git a/changelog.md b/changelog.md index 17964cc9..436a8829 100644 --- a/changelog.md +++ b/changelog.md @@ -1,95 +1,100 @@ # Changelog -- `rio.Dropdown` will now open a fullscreen popup on mobile devices -- `rio.MediaPlayer` now also triggers the `on_playback_end` event when the +- New styles for input boxes: "rounded" and "pill" +- Improved mobile support: Dragging is now much smoother + +## 0.10 + +- `rio.Dropdown` will now open a fullscreen popup on mobile devices +- `rio.MediaPlayer` now also triggers the `on_playback_end` event when the video loops -- experimental support for base-URL -- dialogs! -- dialogs can now store a result value similar to futures -- `rio.Text.wrap` is now `rio.Text.overflow`. Same for markdown. -- removed `rio.Popup.on_open_or_close`. This event never actually fired. -- `rio.Link` can now optionally display an icon -- Rio will automatically create basic navigation for you, if your app has more +- experimental support for base-URL +- dialogs! +- dialogs can now store a result value similar to futures +- `rio.Text.wrap` is now `rio.Text.overflow`. Same for markdown. +- removed `rio.Popup.on_open_or_close`. This event never actually fired. +- `rio.Link` can now optionally display an icon +- Rio will automatically create basic navigation for you, if your app has more than one page -- Updated button styles: Added `colored-text` and renamed `plain` -> +- Updated button styles: Added `colored-text` and renamed `plain` -> `plain-text` -- Methods for creating dialogs are now in `rio.Session` rather than +- Methods for creating dialogs are now in `rio.Session` rather than `rio.Component`. -- Page rework - - Add `rio.Redirect` - - TODO: Automatic page scan -- New experimental `rio.FilePickerArea` component +- Page rework + - Add `rio.Redirect` + - TODO: Automatic page scan +- New experimental `rio.FilePickerArea` component ## 0.9.2 -- restyled `rio.Switch` -- New ~~experimental~~ broken component `AspectRatioContainer` +- restyled `rio.Switch` +- New ~~experimental~~ broken component `AspectRatioContainer` ## 0.9.1 -- added gain_focus / lose_focus events to TextInput and NumberInput -- `.rioignore` has been superseeded by the new `project-files` setting in +- added gain_focus / lose_focus events to TextInput and NumberInput +- `.rioignore` has been superseeded by the new `project-files` setting in `rio.toml` -- values in `rio.toml` are now written in kebab-case instead of +- values in `rio.toml` are now written in kebab-case instead of all_lower_case. Rio will still recognize the old names and automatically fix them for you. -- deprecated `light` parameter of `Theme.from_color`, has been superseded by +- deprecated `light` parameter of `Theme.from_color`, has been superseded by `mode` -- Tooltips now default to `position="auto"` -- Icons now use `_` instead of `-` in their names. This brings them more in line +- Tooltips now default to `position="auto"` +- Icons now use `_` instead of `-` in their names. This brings them more in line with Python naming conventions -- Checkbox restyling +- Checkbox restyling ## 0.9 -- Buttons now have a smaller minimum size when using a `rio.Component` as +- Buttons now have a smaller minimum size when using a `rio.Component` as content -- `FrostedGlassFill` added (Contributed by MiniTT) -- added `@rio.event.on_window_size_change` -- popups now default to the "hud" color -- popups and tooltips are no longer cut off by other components -- Add HTML meta tags -- Add functions for reading and writing clipboard contents to the `Session` +- `FrostedGlassFill` added (Contributed by MiniTT) +- added `@rio.event.on_window_size_change` +- popups now default to the "hud" color +- popups and tooltips are no longer cut off by other components +- Add HTML meta tags +- Add functions for reading and writing clipboard contents to the `Session` (Contributed by MiniTT) -- The color of drawers is now configurable, and also sets the theme context -- added `Calendar` component -- added `DateInput` component -- massive dev-tools overhaul -- new (but experimental) `Switcher` component -- TextInputs now update their text in real-time -- `rio run` no longer opens a browser -- `rio.HTML` components now execute embedded `