diff --git a/rio/__init__.py b/rio/__init__.py index 37f1705a..e9b4afaf 100644 --- a/rio/__init__.py +++ b/rio/__init__.py @@ -16,7 +16,7 @@ from . import event as event from . import patches_for_3rd_party_stuff from .app import * from .color import * -from .common import * +from .utils import * from .components import * from .cursor_style import * from .errors import * diff --git a/rio/app.py b/rio/app.py index ddb290bb..badf2be1 100644 --- a/rio/app.py +++ b/rio/app.py @@ -15,8 +15,8 @@ import uvicorn import rio -from . import app_server, assets, common, debug, maybes -from .common import ImageLike +from . import app_server, assets, debug, maybes, utils +from .utils import ImageLike __all__ = [ @@ -224,7 +224,7 @@ class App: name = _get_default_app_name(main_file) if icon is None: - icon = common.HOSTED_ASSETS_DIR / "rio-logos/rio-logo-square.png" + icon = utils.HOSTED_ASSETS_DIR / "rio-logos/rio-logo-square.png" if build is None: build = rio.PageView @@ -328,7 +328,7 @@ class App: Internal equivalent of `run_as_web_server` that takes additional arguments. """ - port = common.ensure_valid_port(host, port) + port = utils.ensure_valid_port(host, port) # Suppress stdout messages if requested kwargs = {} @@ -443,7 +443,7 @@ class App: quiet: If `True` Rio won't send any routine messages to `stdout`. Error messages will be printed regardless of this setting. """ - port = common.ensure_valid_port(host, port) + port = utils.ensure_valid_port(host, port) def on_startup() -> None: webbrowser.open(f"http://{host}:{port}") @@ -504,7 +504,7 @@ class App: # it down when the window is closed. host = "localhost" - port = common.ensure_valid_port(host, None) + port = utils.ensure_valid_port(host, None) url = f"http://{host}:{port}" # This lock is released once the server is running diff --git a/rio/app_server.py b/rio/app_server.py index 3813aae9..14e9d053 100644 --- a/rio/app_server.py +++ b/rio/app_server.py @@ -28,7 +28,6 @@ from . import ( app, assets, byte_serving, - common, components, debug, global_state, @@ -36,8 +35,9 @@ from . import ( routing, session, user_settings_module, + utils, ) -from .common import URL +from .utils import URL from .components.root_components import HighLevelRootComponent from .errors import AssetError from .serialization import serialize_json @@ -95,7 +95,7 @@ def read_frontend_template(template_name: str) -> str: Read a text file from the frontend directory and return its content. The results are cached to avoid repeated disk access. """ - return (common.GENERATED_DIR / template_name).read_text(encoding="utf-8") + return (utils.GENERATED_DIR / template_name).read_text(encoding="utf-8") class InitialClientMessage(uniserde.Serde): @@ -181,7 +181,7 @@ class AppServer(fastapi.FastAPI): # All pending file uploads. These are stored in memory for a limited # time. When a file is uploaded the corresponding future is set. self._pending_file_uploads: timer_dict.TimerDict[ - str, asyncio.Future[list[common.FileInfo]] + str, asyncio.Future[list[utils.FileInfo]] ] = timer_dict.TimerDict(default_duration=timedelta(minutes=15)) # FastAPI @@ -519,14 +519,14 @@ Sitemap: {request_url.with_path("/rio/sitemap")} # Well known asset? if not asset_id.startswith("temp-"): # Construct the path to the target file - asset_file_path = common.HOSTED_ASSETS_DIR / asset_id + asset_file_path = utils.HOSTED_ASSETS_DIR / asset_id # Make sure the path is inside the hosted assets directory # # TODO: Is this safe? Would this allow the client to break out from # the directory using tricks such as "../", links, etc? asset_file_path = asset_file_path.resolve() - if common.HOSTED_ASSETS_DIR not in asset_file_path.parents: + if utils.HOSTED_ASSETS_DIR not in asset_file_path.parents: logging.warning( f'Client requested asset "{asset_id}" which is not located inside the hosted assets directory. Somebody might be trying to break out of the assets directory!' ) @@ -635,7 +635,7 @@ Sitemap: {request_url.with_path("/rio/sitemap")} # Complete the future future.set_result( [ - common.FileInfo( + utils.FileInfo( name=file_names[ii], size_in_bytes=parsed_file_sizes[ii], media_type=file_types[ii], @@ -920,7 +920,7 @@ Sitemap: {request_url.with_path("/rio/sitemap")} # Is this a page, or a full URL to another site? try: - common.make_url_relative( + utils.make_url_relative( sess._base_url, active_page_url_absolute, ) diff --git a/rio/assets.py b/rio/assets.py index 4a42724e..386eabc1 100644 --- a/rio/assets.py +++ b/rio/assets.py @@ -14,7 +14,7 @@ from yarl import URL import rio -from .common import ImageLike +from .utils import ImageLike from .self_serializing import SelfSerializing diff --git a/rio/cli/run_project/arbiter.py b/rio/cli/run_project/arbiter.py index a395b310..59a9c9cb 100644 --- a/rio/cli/run_project/arbiter.py +++ b/rio/cli/run_project/arbiter.py @@ -16,7 +16,7 @@ import rio.cli import rio.icon_registry import rio.snippets -from ... import common +from ... import utils from ...debug.monkeypatches import apply_monkeypatches from .. import nice_traceback, project from . import ( @@ -104,10 +104,10 @@ class Arbiter: # to connect to - this way old browser tabs don't get invalidated # constantly. if port is None: - if common.port_is_free(self._host, 8000): + if utils.port_is_free(self._host, 8000): self.port = 8000 else: - self.port = common.choose_free_port(self._host) + self.port = utils.choose_free_port(self._host) else: self.port = port diff --git a/rio/components/fundamental_component.py b/rio/components/fundamental_component.py index a4e4736b..7812fa1d 100644 --- a/rio/components/fundamental_component.py +++ b/rio/components/fundamental_component.py @@ -7,7 +7,7 @@ from uniserde import Jsonable, JsonDoc import rio -from .. import common, inspection +from .. import inspection, utils from .component import Component __all__ = [ @@ -88,7 +88,7 @@ class FundamentalComponent(Component): def __init_subclass__(cls): # Assign a unique id to this class. This allows the frontend to identify # components. - hash_ = common.secure_string_hash( + hash_ = utils.secure_string_hash( cls.__module__, cls.__qualname__, hash_length=12, diff --git a/rio/components/image.py b/rio/components/image.py index 7cafff7f..abebf696 100644 --- a/rio/components/image.py +++ b/rio/components/image.py @@ -6,7 +6,7 @@ from typing import Literal, final from uniserde import Jsonable, JsonDoc from .. import assets -from ..common import EventHandler, ImageLike +from ..utils import EventHandler, ImageLike from .fundamental_component import FundamentalComponent __all__ = ["Image"] diff --git a/rio/components/media_player.py b/rio/components/media_player.py index a743b1e1..a96c3378 100644 --- a/rio/components/media_player.py +++ b/rio/components/media_player.py @@ -8,7 +8,7 @@ from uniserde import JsonDoc import rio from .. import assets, color -from ..common import EventHandler +from ..utils import EventHandler from .fundamental_component import KeyboardFocusableFundamentalComponent __all__ = ["MediaPlayer"] diff --git a/rio/components/page_view.py b/rio/components/page_view.py index e448dc7e..8285eca0 100644 --- a/rio/components/page_view.py +++ b/rio/components/page_view.py @@ -6,7 +6,7 @@ from typing import * # type: ignore import rio -from .. import common +from .. import utils from .component import Component __all__ = [ @@ -163,10 +163,10 @@ class PageView(Component): if self.fallback_build is None: result = default_fallback_build(self.session) else: - result = common.safe_build(self.fallback_build) + result = utils.safe_build(self.fallback_build) result._rio_internal_ = False else: - result = common.safe_build(page.build) + result = utils.safe_build(page.build) result._rio_internal_ = False return result diff --git a/rio/components/root_components.py b/rio/components/root_components.py index 5e74d6aa..dbaede83 100644 --- a/rio/components/root_components.py +++ b/rio/components/root_components.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable from typing import * # type: ignore -from .. import common +from .. import utils from .component import Component from .fundamental_component import FundamentalComponent from .scroll_container import ScrollContainer @@ -41,11 +41,11 @@ class HighLevelRootComponent(Component): # User content should automatically scroll if it grows too large, so we # wrap it in a ScrollContainer - user_content = ScrollContainer(common.safe_build(self.build_function)) + user_content = ScrollContainer(utils.safe_build(self.build_function)) return FundamentalRootComponent( user_content, - common.safe_build(self.build_connection_lost_message_function), + utils.safe_build(self.build_connection_lost_message_function), debugger=debugger, ) diff --git a/rio/components/website.py b/rio/components/website.py index d4e95cfc..2639fcdb 100644 --- a/rio/components/website.py +++ b/rio/components/website.py @@ -1,6 +1,6 @@ from typing import final -from ..common import URL +from ..utils import URL from .fundamental_component import FundamentalComponent __all__ = [ diff --git a/rio/debug/dev_tools/component_details.py b/rio/debug/dev_tools/component_details.py index f22e0e64..28735110 100644 --- a/rio/debug/dev_tools/component_details.py +++ b/rio/debug/dev_tools/component_details.py @@ -4,7 +4,7 @@ from typing import * # type: ignore import rio import rio.docs -from ... import common +from ... import utils class ComponentDetails(rio.Component): @@ -55,25 +55,25 @@ class ComponentDetails(rio.Component): def _get_effective_margins(self) -> Tuple[float, float, float, float]: return ( - common.first_non_null( + utils.first_non_null( self.margin_left, self.margin_x, self.margin, 0, ), - common.first_non_null( + utils.first_non_null( self.margin_top, self.margin_y, self.margin, 0, ), - common.first_non_null( + utils.first_non_null( self.margin_right, self.margin_x, self.margin, 0, ), - common.first_non_null( + utils.first_non_null( self.margin_bottom, self.margin_y, self.margin, diff --git a/rio/fills.py b/rio/fills.py index 15b51d9f..4ab9095f 100644 --- a/rio/fills.py +++ b/rio/fills.py @@ -11,7 +11,7 @@ import rio from . import assets from .color import Color -from .common import ImageLike +from .utils import ImageLike from .self_serializing import SelfSerializing __all__ = [ diff --git a/rio/icon_registry.py b/rio/icon_registry.py index 4ed6d1f1..d09acea5 100644 --- a/rio/icon_registry.py +++ b/rio/icon_registry.py @@ -5,7 +5,7 @@ import tarfile from pathlib import Path from typing import * # type: ignore -from . import common +from . import utils from .errors import AssetError _icon_registry: IconRegistry | None = None @@ -40,15 +40,15 @@ class IconRegistry: # Register built-in icon sets _icon_registry.icon_set_archives["material"] = ( - common.RIO_ASSETS_DIR / "icon-sets" / "material.tar.xz" + utils.RIO_ASSETS_DIR / "icon-sets" / "material.tar.xz" ) _icon_registry.icon_set_archives["rio"] = ( - common.RIO_ASSETS_DIR / "icon-sets" / "rio.tar.xz" + utils.RIO_ASSETS_DIR / "icon-sets" / "rio.tar.xz" ) _icon_registry.icon_set_archives["styling"] = ( - common.RIO_ASSETS_DIR / "icon-sets" / "styling.tar.xz" + utils.RIO_ASSETS_DIR / "icon-sets" / "styling.tar.xz" ) # Use it @@ -107,7 +107,7 @@ class IconRegistry: Given the name of an icon set, return the directory where the icon set will be extracted to. The directory will be created if necessary. """ - return common.ASSET_MANGER.get_cache_path( + return utils.ASSET_MANGER.get_cache_path( Path("icon-sets") / icon_set, ) diff --git a/rio/routing.py b/rio/routing.py index e2bc0900..89f86f36 100644 --- a/rio/routing.py +++ b/rio/routing.py @@ -7,7 +7,7 @@ from typing import final import rio -from . import common +from . import utils from .errors import NavigationFailed __all__ = ["Page"] @@ -176,7 +176,7 @@ def check_page_guards( while True: # TODO: What if the URL is not a child of the base URL? i.e. redirecting # to a completely different site - target_url_relative = common.make_url_relative( + target_url_relative = utils.make_url_relative( sess._base_url, target_url_absolute ) diff --git a/rio/session.py b/rio/session.py index a27b1f00..74d021e4 100644 --- a/rio/session.py +++ b/rio/session.py @@ -30,7 +30,6 @@ import rio from . import ( app_server, assets, - common, errors, global_state, inspection, @@ -39,6 +38,7 @@ from . import ( text_style, theme, user_settings_module, + utils, ) from .components import fundamental_component, root_components from .state_properties import AttributeBinding @@ -474,13 +474,13 @@ class Session(unicall.Unicall): @overload async def _call_event_handler( - self, handler: common.EventHandler[[]], *, refresh: bool + self, handler: utils.EventHandler[[]], *, refresh: bool ) -> None: ... @overload async def _call_event_handler( self, - handler: common.EventHandler[[T]], + handler: utils.EventHandler[[T]], event_data: T, /, *, @@ -489,7 +489,7 @@ class Session(unicall.Unicall): async def _call_event_handler( self, - handler: common.EventHandler[...], + handler: utils.EventHandler[...], *event_data: object, refresh: bool, ) -> None: @@ -522,20 +522,20 @@ class Session(unicall.Unicall): @overload def _call_event_handler_sync( self, - handler: common.EventHandler[[]], + handler: utils.EventHandler[[]], ) -> None: ... @overload def _call_event_handler_sync( self, - handler: common.EventHandler[[T]], + handler: utils.EventHandler[[T]], event_data: T, /, ) -> None: ... def _call_event_handler_sync( self, - handler: common.EventHandler[...], + handler: utils.EventHandler[...], *event_data: object, ) -> None: """ @@ -638,7 +638,7 @@ class Session(unicall.Unicall): # Is this a page, or a full URL to another site? try: - common.make_url_relative( + utils.make_url_relative( self._base_url, target_url_absolute, ) @@ -806,7 +806,7 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url))}) global_state.currently_building_component = component global_state.currently_building_session = self - build_result = common.safe_build(component.build) + build_result = utils.safe_build(component.build) global_state.currently_building_component = None global_state.currently_building_session = None @@ -1760,7 +1760,7 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url))}) *, file_extensions: Iterable[str] | None = None, multiple: Literal[False] = False, - ) -> common.FileInfo: ... + ) -> utils.FileInfo: ... @overload async def file_chooser( @@ -1768,14 +1768,14 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url))}) *, file_extensions: Iterable[str] | None = None, multiple: Literal[True], - ) -> tuple[common.FileInfo, ...]: ... + ) -> tuple[utils.FileInfo, ...]: ... async def file_chooser( self, *, file_extensions: Iterable[str] | None = None, multiple: bool = False, - ) -> common.FileInfo | tuple[common.FileInfo, ...]: + ) -> utils.FileInfo | tuple[utils.FileInfo, ...]: """ Open a file chooser dialog. @@ -1799,7 +1799,7 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url))}) """ # Create a secret id and register the file upload with the app server upload_id = secrets.token_urlsafe() - future = asyncio.Future[list[common.FileInfo]]() + future = asyncio.Future[list[utils.FileInfo]]() self._app_server._pending_file_uploads[upload_id] = future diff --git a/rio/snippets/__init__.py b/rio/snippets/__init__.py index 0547bc64..8df8bade 100644 --- a/rio/snippets/__init__.py +++ b/rio/snippets/__init__.py @@ -11,7 +11,7 @@ from typing import * # type: ignore import uniserde -from .. import common +from .. import utils SECTION_PATTERN = re.compile(r" *#\s*<(\/?[\w-]+)>") @@ -188,7 +188,7 @@ def _get_all_snippet_paths() -> dict[str, dict[str, Path]]: # Scan all snippet directories. The first directory is used as a key, the # rest just for organization. - for group_dir in common.SNIPPETS_DIR.iterdir(): + for group_dir in utils.SNIPPETS_DIR.iterdir(): assert group_dir.is_dir(), group_dir scan_dir_recursively(group_dir.name, group_dir) diff --git a/rio/text_style.py b/rio/text_style.py index 77c5d5a0..161c3166 100644 --- a/rio/text_style.py +++ b/rio/text_style.py @@ -8,7 +8,7 @@ from uniserde import JsonDoc import rio -from . import common +from . import utils from .fills import FillLike from .self_serializing import SelfSerializing @@ -67,17 +67,17 @@ class Font(SelfSerializing): Font.ROBOTO = Font( - regular=common.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-Regular.ttf", - bold=common.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-Bold.ttf", - italic=common.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-Italic.ttf", - bold_italic=common.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-BoldItalic.ttf", + regular=utils.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-Regular.ttf", + bold=utils.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-Bold.ttf", + italic=utils.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-Italic.ttf", + bold_italic=utils.RIO_ASSETS_DIR / "fonts/Roboto/Roboto-BoldItalic.ttf", ) Font.ROBOTO_MONO = Font( - regular=common.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-Regular.ttf", - bold=common.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-Bold.ttf", - italic=common.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-Italic.ttf", - bold_italic=common.RIO_ASSETS_DIR + regular=utils.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-Regular.ttf", + bold=utils.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-Bold.ttf", + italic=utils.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-Italic.ttf", + bold_italic=utils.RIO_ASSETS_DIR / "fonts/Roboto Mono/RobotoMono-BoldItalic.ttf", ) @@ -133,7 +133,9 @@ 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, all_caps=self.all_caps if all_caps is None else all_caps, ) diff --git a/rio/theme.py b/rio/theme.py index f53215df..e56ef30a 100644 --- a/rio/theme.py +++ b/rio/theme.py @@ -8,7 +8,7 @@ from uniserde import Jsonable import rio -from . import color, common +from . import color, utils from . import text_style as text_style_module __all__ = [ @@ -139,14 +139,14 @@ class Palette: foreground: rio.Color | None = None, ) -> Palette: return Palette( - background=common.first_non_null(background, self.background), - background_variant=common.first_non_null( + background=utils.first_non_null(background, self.background), + background_variant=utils.first_non_null( background_variant, self.background_variant ), - background_active=common.first_non_null( + background_active=utils.first_non_null( background_active, self.background_active ), - foreground=common.first_non_null(foreground, self.foreground), + foreground=utils.first_non_null(foreground, self.foreground), ) @@ -738,54 +738,54 @@ class Theme: text_style: rio.TextStyle | None = None, ) -> Theme: return Theme._create_new( - primary_palette=common.first_non_null( + primary_palette=utils.first_non_null( primary_palette, self.primary_palette ), - secondary_palette=common.first_non_null( + secondary_palette=utils.first_non_null( secondary_palette, self.secondary_palette ), - background_palette=common.first_non_null( + background_palette=utils.first_non_null( background_palette, self.background_palette ), - neutral_palette=common.first_non_null( + neutral_palette=utils.first_non_null( neutral_palette, self.neutral_palette ), - hud_palette=common.first_non_null(hud_palette, self.hud_palette), - disabled_palette=common.first_non_null( + hud_palette=utils.first_non_null(hud_palette, self.hud_palette), + disabled_palette=utils.first_non_null( disabled_palette, self.disabled_palette ), - success_palette=common.first_non_null( + success_palette=utils.first_non_null( success_palette, self.success_palette ), - warning_palette=common.first_non_null( + warning_palette=utils.first_non_null( warning_palette, self.warning_palette ), - danger_palette=common.first_non_null( + danger_palette=utils.first_non_null( danger_palette, self.danger_palette ), - corner_radius_small=common.first_non_null( + corner_radius_small=utils.first_non_null( corner_radius_small, self.corner_radius_small ), - corner_radius_medium=common.first_non_null( + corner_radius_medium=utils.first_non_null( corner_radius_medium, self.corner_radius_medium ), - corner_radius_large=common.first_non_null( + corner_radius_large=utils.first_non_null( corner_radius_large, self.corner_radius_large ), - shadow_color=common.first_non_null(shadow_color, self.shadow_color), - monospace_font=common.first_non_null( + shadow_color=utils.first_non_null(shadow_color, self.shadow_color), + monospace_font=utils.first_non_null( monospace_font, self.monospace_font ), - heading1_style=common.first_non_null( + heading1_style=utils.first_non_null( heading1_style, self.heading1_style ), - heading2_style=common.first_non_null( + heading2_style=utils.first_non_null( heading2_style, self.heading2_style ), - heading3_style=common.first_non_null( + heading3_style=utils.first_non_null( heading3_style, self.heading3_style ), - text_style=common.first_non_null(text_style, self.text_style), + text_style=utils.first_non_null(text_style, self.text_style), ) @property diff --git a/rio/common.py b/rio/utils.py similarity index 100% rename from rio/common.py rename to rio/utils.py diff --git a/scripts/build_material_icon_set.py b/scripts/build_material_icon_set.py index a9005411..8637fd5e 100644 --- a/scripts/build_material_icon_set.py +++ b/scripts/build_material_icon_set.py @@ -25,7 +25,7 @@ SET_NAME = "material" # # Repo URL: https://github.com/marella/material-symbols INPUT_DIR = ( - rio.common.PROJECT_ROOT_DIR + rio.utils.PROJECT_ROOT_DIR / "thirdparty" / "material-symbols" / "svg" @@ -39,7 +39,7 @@ INPUT_NAME_PATTERN = r"(.+).svg" # Configure: The output file will be written into this directory as # .tar.xz -OUTPUT_DIR = rio.common.RIO_ASSETS_DIR / "icon-sets" +OUTPUT_DIR = rio.utils.RIO_ASSETS_DIR / "icon-sets" # For debugging: Stop after processing this many icons. Set to `None` for no # limit