From 11ebc24d7026d84c8151bedd155971a2a13f1be7 Mon Sep 17 00:00:00 2001 From: Jakob Pinterits Date: Thu, 7 Nov 2024 23:01:48 +0100 Subject: [PATCH] webview import fixes --- rio/app.py | 8 ++++---- rio/arequests.py | 6 +++--- rio/assets.py | 2 +- rio/cli/rio_api.py | 4 ++-- rio/cli/run_project/arbiter.py | 23 ++++++++--------------- rio/cli/run_project/webview_worker.py | 19 +++++++------------ rio/components/dropdown.py | 9 +-------- rio/debug/layouter.py | 2 +- rio/session.py | 18 ++++++++---------- rio/utils.py | 23 ----------------------- rio/{webview.py => webview_shim.py} | 18 ++++++++++++++++-- 11 files changed, 51 insertions(+), 81 deletions(-) rename rio/{webview.py => webview_shim.py} (57%) diff --git a/rio/app.py b/rio/app.py index 68a42130..85b0eac5 100644 --- a/rio/app.py +++ b/rio/app.py @@ -679,7 +679,7 @@ class App: Error messages will be printed regardless of this setting. """ try: - from .webview import webview + from . import webview_shim except ImportError: raise Exception( "The `window` extra is required to use `App.run_in_window`." @@ -723,7 +723,7 @@ class App: # Problem: width and height are given in rem, but we need them in # pixels. We'll use pywebview's execute_js to find out as soon as the # window has been created, and then update the window size accordingly. - def update_window_size(): + def update_window_size() -> None: if width is None and height is None: return @@ -753,13 +753,13 @@ pixels_per_rem # Start the webview try: - window = webview.create_window( + window = webview_shim.create_window( self.name, url, maximized=maximized, fullscreen=fullscreen, ) - webview.start( + webview_shim.start( update_window_size, debug=os.environ.get("RIO_WEBVIEW_DEBUG") == "1", ) diff --git a/rio/arequests.py b/rio/arequests.py index c8e3b18b..3b8dadeb 100644 --- a/rio/arequests.py +++ b/rio/arequests.py @@ -70,7 +70,7 @@ class HttpResponse: def _request_sync( - method: t.Literal["GET", "POST"], + method: t.Literal["get", "post"], url: str, *, content: str | bytes | None = None, @@ -134,7 +134,7 @@ def _request_sync( async def request( - method: t.Literal["GET", "POST"], + method: t.Literal["get", "post"], url: str, *, content: bytes | None = None, @@ -158,7 +158,7 @@ async def request( async def main() -> None: response = await request( - "GET", + "get", "https://postman-echo.com/get?foo=bar", ) diff --git a/rio/assets.py b/rio/assets.py index 7be375da..4a7c8a7a 100644 --- a/rio/assets.py +++ b/rio/assets.py @@ -257,7 +257,7 @@ class UrlAsset(Asset): async def try_fetch_as_blob(self) -> tuple[bytes, str | None]: try: - response = await arequests.request("GET", str(self._url)) + response = await arequests.request("get", str(self._url)) content_type = response.headers.get("content-type") diff --git a/rio/cli/rio_api.py b/rio/cli/rio_api.py index 703b8a23..ef97b85a 100644 --- a/rio/cli/rio_api.py +++ b/rio/cli/rio_api.py @@ -62,7 +62,7 @@ class RioApi: self, endpoint: str, *, - method: t.Literal["get", "post", "delete"] = "get", + method: t.Literal["get", "post"] = "get", json: dict[str, t.Any] | None = None, file: t.BinaryIO | None = None, ) -> t.Any: @@ -87,7 +87,7 @@ class RioApi: f"{BASE_URL}/{endpoint}", headers=headers, json=json, - files=files, + # files=files, # TODO: ) # Handle errors diff --git a/rio/cli/run_project/arbiter.py b/rio/cli/run_project/arbiter.py index f06affba..2daa947b 100644 --- a/rio/cli/run_project/arbiter.py +++ b/rio/cli/run_project/arbiter.py @@ -29,15 +29,6 @@ from . import ( webview_worker, ) -try: - from ...webview import webview -except ImportError: - if t.TYPE_CHECKING: - from ...webview import webview - else: - webview = None - - # There is a hilarious problem with `watchfiles`: When a change occurs, # `watchfiles` logs that change. If Python is configured to log into the project # directory, then `watchfiles` will pick up on that new log line, thus @@ -188,7 +179,7 @@ class Arbiter: """ try: response = await arequests.request( - "GET", + "get", "https://pypi.org/pypi/rio-ui/json", ) @@ -429,11 +420,13 @@ class Arbiter: self._arbiter_task = asyncio.current_task() # Make sure the webview module is available - if self.run_in_window and webview is None: - revel.fatal( - "The `window` extra is required to run apps inside of a window." - """ Run `pip install "rio-ui[[window]"` to install it.""" - ) + if self.run_in_window: + try: + from ... import webview_shim as webview_shim + except ImportError: + revel.fatal( + """The `window` extra is required to run apps inside of a window. Run `pip install "rio-ui[[window]"` to install it.""" + ) # Make sure the app is cleanly shut down, even if the arbiter crashes # for whichever reason. diff --git a/rio/cli/run_project/webview_worker.py b/rio/cli/run_project/webview_worker.py index 33d08381..e9cd0eac 100644 --- a/rio/cli/run_project/webview_worker.py +++ b/rio/cli/run_project/webview_worker.py @@ -4,14 +4,6 @@ import typing as t from . import run_models -try: - from ...webview import webview -except ImportError: - if t.TYPE_CHECKING: - from ...webview import webview - else: - webview = None - class WebViewWorker: def __init__( @@ -21,12 +13,14 @@ class WebViewWorker: debug_mode: bool, url: str, ) -> None: + from ... import webview_shim + self.push_event = push_event self.debug_mode = debug_mode self.url = url # If running, this is the webview window - self.window: webview.Window | None = None + self.window: webview_shim.Window | None = None def run(self) -> None: """ @@ -34,7 +28,8 @@ class WebViewWorker: `request_stop` is called. This function must be called from the main thread. """ - assert webview is not None + from ... import webview_shim + assert self.window is None, "Already running" # Make sure this was called from the main thread. @@ -43,12 +38,12 @@ class WebViewWorker: ), "Must be called from the main thread" # Create the window - self.window = webview.create_window( + self.window = webview_shim.create_window( # TODO: Get the app's name, if possible "Rio (debug)" if self.debug_mode else "Rio", url=self.url, ) - webview.start() + webview_shim.start() # If we've reached this point, the window must have been closed. self.window = None diff --git a/rio/components/dropdown.py b/rio/components/dropdown.py index 16184902..73342c6b 100644 --- a/rio/components/dropdown.py +++ b/rio/components/dropdown.py @@ -18,13 +18,6 @@ __all__ = [ T = t.TypeVar("T") -class NotGiven: - pass - - -NOT_GIVEN = NotGiven() - - @t.final @rio.docs.mark_constructor_as_private @dataclass @@ -135,7 +128,7 @@ class Dropdown(FundamentalComponent, t.Generic[T]): _: KW_ONLY label: str style: t.Literal["underlined", "rounded", "pill"] - selected_value: T | utils.NotGiven = NOT_GIVEN + selected_value: T | utils.NotGiven = utils.NOT_GIVEN is_sensitive: bool is_valid: bool on_change: rio.EventHandler[DropdownChangeEvent[T]] diff --git a/rio/debug/layouter.py b/rio/debug/layouter.py index b3b65c48..49728b4d 100644 --- a/rio/debug/layouter.py +++ b/rio/debug/layouter.py @@ -851,7 +851,7 @@ class Layouter: round(self.window_width * pixels_per_unit), round(self.window_height * pixels_per_unit), ), - color="white", + color="white", # type: ignore ) draw = PIL.ImageDraw.Draw(image) diff --git a/rio/session.py b/rio/session.py index 009dc465..b0528fc2 100644 --- a/rio/session.py +++ b/rio/session.py @@ -637,11 +637,11 @@ window.resizeTo(screen.availWidth, screen.availHeight); # Close the tab / window if close_remote_session: if self.running_in_window: - from .webview import webview + from . import webview_shim # It's possible that the session is being closed because the # user closed the window, so the window may not exist anymore - window = webview.active_window() + window = webview_shim.active_window() if window is not None: window.destroy() else: @@ -674,7 +674,7 @@ window.resizeTo(screen.availWidth, screen.availHeight); return low_level_root.content async def _get_webview_window(self): - from .webview import webview + from . import webview_shim assert ( self.running_in_window @@ -682,7 +682,7 @@ window.resizeTo(screen.availWidth, screen.availHeight); # The window may not have opened yet, so we'll wait until it exists while True: - window = webview.active_window() + window = webview_shim.active_window() if window is not None: return window @@ -2023,9 +2023,7 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url.relative()))}) `title`: The new window title. """ if self.running_in_window: - import webview.util # type: ignore - - from .webview import webview + from . import webview_shim window = await self._get_webview_window() @@ -2033,7 +2031,7 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url.relative()))}) try: window.set_title(title) break - except webview.util.WebViewException: + except webview_shim.util.WebViewException: await asyncio.sleep(0.2) else: await self._remote_set_title(title) @@ -2184,11 +2182,11 @@ window.history.{method}(null, "", {json.dumps(str(active_page_url.relative()))}) # way to open a file dialog without blocking the event loop. import aiofiles - from .webview import webview + from . import webview_shim window = await self._get_webview_window() destinations = window.create_file_dialog( - webview.SAVE_DIALOG, + webview_shim.SAVE_DIALOG, directory="" if directory is None else str(directory), save_filename=file_name, ) diff --git a/rio/utils.py b/rio/utils.py index 4f792631..401e3fbd 100644 --- a/rio/utils.py +++ b/rio/utils.py @@ -1,6 +1,5 @@ from __future__ import annotations -import asyncio import hashlib import mimetypes import os @@ -9,7 +8,6 @@ import secrets import socket import typing as t from dataclasses import dataclass -from http.client import HTTPSConnection from io import BytesIO, StringIO from pathlib import Path @@ -567,24 +565,3 @@ def soft_sort( for element, _, _ in keyed_elements: elements.append(element) - - -async def async_http_request(url: str) -> Tuple[Dict[str, str], bytes]: - """Performs an async HTTP GET request to the given URL and returns headers and content blob.""" - loop = asyncio.get_running_loop() - host, path = url.split("/", 1) - - def fetch() -> Tuple[Dict[str, str], bytes]: - conn = HTTPSConnection(host) - conn.request("GET", f"/{path}") - response = conn.getresponse() - headers = dict(response.getheaders()) - blob = response.read() - conn.close() - return headers, blob - - return await loop.run_in_executor(None, fetch) - - -# Example usage: -# headers, blob = asyncio.run(async_http_request("example.com/some-path")) diff --git a/rio/webview.py b/rio/webview_shim.py similarity index 57% rename from rio/webview.py rename to rio/webview_shim.py index cb1e1c85..6b63d92a 100644 --- a/rio/webview.py +++ b/rio/webview_shim.py @@ -1,4 +1,11 @@ -__all__ = ["webview"] +__all__ = [ + "active_window", + "create_window", + "SAVE_DIALOG", + "start", + "util", + "Window", +] # There is an issue with `rye test`. rye passes a `--rootdir` argument to @@ -11,7 +18,14 @@ argv = sys.argv sys.argv = argv[:1] try: - import webview + from webview import ( + SAVE_DIALOG, + Window, + active_window, + create_window, + start, + util, + ) except ImportError: pass finally: