-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
Session
abstract base class (#1331)
- Loading branch information
Showing
18 changed files
with
662 additions
and
359 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
from __future__ import annotations | ||
|
||
import textwrap | ||
from typing import TYPE_CHECKING, Awaitable, Callable, Literal, Optional | ||
|
||
from htmltools import TagChild | ||
|
||
from .._namespaces import Id, ResolvedId, Root | ||
from ..session import Inputs, Outputs, Session | ||
from ..session._session import SessionProxy | ||
|
||
if TYPE_CHECKING: | ||
from .._app import App | ||
from .._typing_extensions import Never | ||
from ..session._session import DownloadHandler, DynamicRouteHandler, RenderedDeps | ||
from ..types import Jsonifiable | ||
from ._run import AppOpts | ||
|
||
all = ("ExpressStubSession",) | ||
|
||
|
||
class ExpressStubSession(Session): | ||
""" | ||
A very bare-bones stub session class that is used only in shiny.express's UI | ||
rendering phase. | ||
Note that this class is also used to hold application-level options that are set via | ||
the `app_opts()` function. | ||
""" | ||
|
||
def __init__(self, ns: ResolvedId = Root): | ||
self.ns = ns | ||
self.input = Inputs({}) | ||
self.output = Outputs(self, self.ns, outputs={}) | ||
|
||
# Set these values to None just to satisfy the abstract base class to make this | ||
# code run -- these things should not be used at run time, so None will work as | ||
# a placeholder. But we also need to tell pyright to ignore that the Nones don't | ||
# match the type declared in the Session abstract base class. | ||
self._outbound_message_queues = None # pyright: ignore | ||
self._downloads = None # pyright: ignore | ||
|
||
# Application-level (not session-level) options that may be set via app_opts(). | ||
self.app_opts: AppOpts = {} | ||
|
||
def is_stub_session(self) -> Literal[True]: | ||
return True | ||
|
||
@property | ||
def id(self) -> str: | ||
self._not_implemented("id") | ||
|
||
@id.setter | ||
def id(self, value: str) -> None: # pyright: ignore | ||
self._not_implemented("id") | ||
|
||
@property | ||
def app(self) -> App: | ||
self._not_implemented("app") | ||
|
||
@app.setter | ||
def app(self, value: App) -> None: # pyright: ignore | ||
self._not_implemented("app") | ||
|
||
async def close(self, code: int = 1001) -> None: | ||
return | ||
|
||
# This is needed so that Outputs don't throw an error. | ||
def _is_hidden(self, name: str) -> bool: | ||
return False | ||
|
||
def on_ended( | ||
self, | ||
fn: Callable[[], None] | Callable[[], Awaitable[None]], | ||
) -> Callable[[], None]: | ||
return lambda: None | ||
|
||
def make_scope(self, id: Id) -> Session: | ||
ns = self.ns(id) | ||
return SessionProxy(parent=self, ns=ns) | ||
|
||
def root_scope(self) -> ExpressStubSession: | ||
return self | ||
|
||
def _process_ui(self, ui: TagChild) -> RenderedDeps: | ||
return {"deps": [], "html": ""} | ||
|
||
def send_input_message(self, id: str, message: dict[str, object]) -> None: | ||
return | ||
|
||
def _send_insert_ui( | ||
self, selector: str, multiple: bool, where: str, content: RenderedDeps | ||
) -> None: | ||
return | ||
|
||
def _send_remove_ui(self, selector: str, multiple: bool) -> None: | ||
return | ||
|
||
def _send_progress(self, type: str, message: object) -> None: | ||
return | ||
|
||
async def send_custom_message(self, type: str, message: dict[str, object]) -> None: | ||
return | ||
|
||
def set_message_handler( | ||
self, | ||
name: str, | ||
handler: ( | ||
Callable[..., Jsonifiable] | Callable[..., Awaitable[Jsonifiable]] | None | ||
), | ||
*, | ||
_handler_session: Optional[Session] = None, | ||
) -> str: | ||
return "" | ||
|
||
async def _send_message(self, message: dict[str, object]) -> None: | ||
return | ||
|
||
def _send_message_sync(self, message: dict[str, object]) -> None: | ||
return | ||
|
||
def on_flush( | ||
self, | ||
fn: Callable[[], None] | Callable[[], Awaitable[None]], | ||
once: bool = True, | ||
) -> Callable[[], None]: | ||
return lambda: None | ||
|
||
def on_flushed( | ||
self, | ||
fn: Callable[[], None] | Callable[[], Awaitable[None]], | ||
once: bool = True, | ||
) -> Callable[[], None]: | ||
return lambda: None | ||
|
||
def dynamic_route(self, name: str, handler: DynamicRouteHandler) -> str: | ||
return "" | ||
|
||
async def _unhandled_error(self, e: Exception) -> None: | ||
return | ||
|
||
def download( | ||
self, | ||
id: Optional[str] = None, | ||
filename: Optional[str | Callable[[], str]] = None, | ||
media_type: None | str | Callable[[], str] = None, | ||
encoding: str = "utf-8", | ||
) -> Callable[[DownloadHandler], None]: | ||
return lambda x: None | ||
|
||
def _not_implemented(self, name: str) -> Never: | ||
raise NotImplementedError( | ||
textwrap.dedent( | ||
f""" | ||
The session attribute `{name}` is not yet available for use. Since this code | ||
will run again when the session is initialized, you can use `if not session.is_stub_session():` | ||
to only run this code when the session is established. | ||
""" | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.