Skip to content

Commit 5192884

Browse files
committed
First pass of using ._session._parent.bookmark instead of ._root_bookmark; "Exclude" todo remaining
1 parent 1daae23 commit 5192884

File tree

4 files changed

+144
-97
lines changed

4 files changed

+144
-97
lines changed

shiny/bookmark/_bookmark.py

Lines changed: 126 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import TYPE_CHECKING, Awaitable, Callable, Literal, NoReturn
77

88
from .._utils import AsyncCallbacks, CancelCallback, wrap_async
9-
from ..types import MISSING, MISSING_TYPE
109
from ._button import BOOKMARK_ID
1110
from ._restore_state import RestoreState
1211
from ._save_state import BookmarkState
@@ -62,41 +61,33 @@
6261
if TYPE_CHECKING:
6362
from ..express._stub_session import ExpressStubSession
6463
from ..module import ResolvedId
65-
from ..session import Session
66-
from ..session._session import SessionProxy
64+
from ..session._session import AppSession, SessionProxy
6765
from . import RestoreContext
6866
else:
6967
from typing import Any
7068

7169
RestoreContext = Any
72-
Session = Any
7370
SessionProxy = Any
71+
AppSession = Any
7472
ResolvedId = Any
7573
ExpressStubSession = Any
7674

7775

78-
# TODO: future - Local storage Bookmark class!
79-
# * Needs a consistent id for storage.
80-
# * Needs ways to clean up other storage
81-
# * Needs ways to see available IDs
82-
83-
8476
class Bookmark(ABC):
8577

86-
_session_root: Session
87-
"""
88-
The root session object (most likely a `AppSession` object).
89-
"""
90-
91-
_store: BookmarkStore | MISSING_TYPE
92-
"""
93-
Session specific bookmark store value.
78+
_proxy_exclude_fns: list[Callable[[], list[str]]]
79+
"""Callbacks that BookmarkProxy classes utilize to help determine the list of inputs to exclude from bookmarking."""
80+
exclude: list[str]
81+
"""A list of scoped Input names to exclude from bookmarking."""
9482

95-
This value could help determine how session state is saved. However, app authors will not be able to change how the session is restored as the server function will run after the session has been restored.
96-
"""
83+
_on_bookmark_callbacks: AsyncCallbacks
84+
_on_bookmarked_callbacks: AsyncCallbacks
85+
_on_restore_callbacks: AsyncCallbacks
86+
_on_restored_callbacks: AsyncCallbacks
9787

9888
# Making this a read only property as app authors will not be able to change how the session is restored as the server function will run after the session has been restored.
9989
@property
90+
@abstractmethod
10091
def store(self) -> BookmarkStore:
10192
"""
10293
App's bookmark store value
@@ -106,47 +97,31 @@ def store(self) -> BookmarkStore:
10697
* `"server"`: Save / reload the bookmark state on the server.
10798
* `"disable"` (default): Bookmarking is diabled.
10899
"""
109-
110-
# Read from the App's bookmark store value.
111-
return self._session_root.app.bookmark_store
112-
113-
_proxy_exclude_fns: list[Callable[[], list[str]]]
114-
"""Callbacks that BookmarkProxy classes utilize to help determine the list of inputs to exclude from bookmarking."""
115-
exclude: list[str]
116-
"""A list of scoped Input names to exclude from bookmarking."""
117-
118-
_on_bookmark_callbacks: AsyncCallbacks
119-
_on_bookmarked_callbacks: AsyncCallbacks
120-
_on_restore_callbacks: AsyncCallbacks
121-
_on_restored_callbacks: AsyncCallbacks
122-
123-
_restore_context_value: RestoreContext | None
124-
"""
125-
Placeholder value that should only be manually set within the session's `init` websocket message.
126-
"""
100+
...
127101

128102
@property
103+
@abstractmethod
129104
def _restore_context(self) -> RestoreContext | None:
130105
"""
131106
A read-only value of the session's RestoreContext object.
132107
"""
133-
return self._root_bookmark._restore_context_value
108+
...
134109

135-
async def __call__(self) -> None:
136-
await self._root_bookmark.do_bookmark()
110+
@abstractmethod
111+
def _set_restore_context(self, restore_context: RestoreContext):
112+
"""
113+
Set the session's RestoreContext object.
137114
138-
@property
139-
def _root_bookmark(self) -> "Bookmark":
140-
"""The base session's bookmark object."""
141-
return self._session_root.bookmark
115+
This should only be done within the `init` websocket message.
116+
"""
117+
...
142118

143-
def __init__(self, session_root: Session):
144-
# from ._restore_state import RestoreContext
119+
async def __call__(self) -> None:
120+
await self.do_bookmark()
121+
122+
def __init__(self):
145123

146124
super().__init__()
147-
self._session_root = session_root
148-
self._restore_context_value = None
149-
self._store = MISSING
150125

151126
self._proxy_exclude_fns = []
152127
self.exclude = []
@@ -288,8 +263,47 @@ def on_restored(
288263

289264

290265
class BookmarkApp(Bookmark):
291-
def __init__(self, session_root: Session):
292-
super().__init__(session_root)
266+
_session: AppSession
267+
"""
268+
The root session object (most likely a `AppSession` object).
269+
"""
270+
_restore_context_value: RestoreContext
271+
"""
272+
Placeholder value that should only be manually set within the session's `init` websocket message.
273+
"""
274+
275+
def __init__(self, session: AppSession):
276+
from ..session._session import AppSession
277+
278+
assert isinstance(session, AppSession)
279+
super().__init__()
280+
281+
self._session = session
282+
# self._restore_context_value = None
283+
284+
# Making this a read only property as app authors will not be able to change how the session is restored as the server function will run after the session has been restored.
285+
@property
286+
def store(self) -> BookmarkStore:
287+
"""
288+
App's bookmark store value
289+
290+
Possible values:
291+
* `"url"`: Save / reload the bookmark state in the URL.
292+
* `"server"`: Save / reload the bookmark state on the server.
293+
* `"disable"` (default): Bookmarking is diabled.
294+
"""
295+
296+
return self._session.app.bookmark_store
297+
298+
@property
299+
def _restore_context(self) -> RestoreContext | None:
300+
"""
301+
A read-only value of the session's RestoreContext object.
302+
"""
303+
return self._restore_context_value
304+
305+
def _set_restore_context(self, restore_context: RestoreContext):
306+
self._restore_context_value = restore_context
293307

294308
def _create_effects(self) -> None:
295309
"""
@@ -305,7 +319,7 @@ def _create_effects(self) -> None:
305319
if self.store == "disable":
306320
return
307321

308-
session = self._session_root
322+
session = self._session
309323

310324
from .. import reactive
311325
from ..session import session_context
@@ -432,7 +446,7 @@ async def update_query_string(
432446
) -> None:
433447
if mode not in {"replace", "push"}:
434448
raise ValueError(f"Invalid mode: {mode}")
435-
await self._session_root._send_message(
449+
await self._session._send_message(
436450
{
437451
"updateQueryString": {
438452
"queryString": query_string,
@@ -462,17 +476,17 @@ async def do_bookmark(self) -> None:
462476
from ..session import session_context
463477

464478
async def root_state_on_save(state: BookmarkState) -> None:
465-
with session_context(self._session_root):
479+
with session_context(self._session):
466480
await self._on_bookmark_callbacks.invoke(state)
467481

468482
root_state = BookmarkState(
469-
input=self._session_root.input,
483+
input=self._session.input,
470484
exclude=self._get_bookmark_exclude(),
471485
on_save=root_state_on_save,
472486
)
473487

474488
if self.store == "server":
475-
query_string = await root_state._save_state(app=self._session_root.app)
489+
query_string = await root_state._save_state(app=self._session.app)
476490
elif self.store == "url":
477491
query_string = await root_state._encode_state()
478492
# # Can we have browser storage?
@@ -484,7 +498,7 @@ async def root_state_on_save(state: BookmarkState) -> None:
484498
else:
485499
raise ValueError("Unknown bookmark store: " + self.store)
486500

487-
clientdata = self._session_root.clientdata
501+
clientdata = self._session.clientdata
488502

489503
port = str(clientdata.url_port())
490504
full_url = "".join(
@@ -503,7 +517,7 @@ async def root_state_on_save(state: BookmarkState) -> None:
503517
# If onBookmarked callback was provided, invoke it; if not call
504518
# the default.
505519
if self._on_bookmarked_callbacks.count() > 0:
506-
with session_context(self._session_root):
520+
with session_context(self._session):
507521
await self._on_bookmarked_callbacks.invoke(full_url)
508522
else:
509523
# `session.bookmark.show_modal(url)`
@@ -523,14 +537,20 @@ async def root_state_on_save(state: BookmarkState) -> None:
523537
class BookmarkProxy(Bookmark):
524538

525539
_ns: ResolvedId
540+
_session: SessionProxy
526541

527542
def __init__(self, session_proxy: SessionProxy):
528-
super().__init__(session_proxy.root_scope())
543+
from ..session._session import SessionProxy
544+
545+
assert isinstance(session_proxy, SessionProxy)
546+
super().__init__()
529547

530548
self._ns = session_proxy.ns
531-
self._session_proxy = session_proxy
549+
self._session = session_proxy
532550

533-
self._session_root.bookmark._proxy_exclude_fns.append(
551+
# TODO: Barret - This isn't getting to the root
552+
# Maybe `._get_bookmark_exclude()` should be used instead of`proxy_exclude_fns`?
553+
self._session._parent.bookmark._proxy_exclude_fns.append(
534554
lambda: [str(self._ns(name)) for name in self.exclude]
535555
)
536556

@@ -540,39 +560,39 @@ def __init__(self, session_proxy: SessionProxy):
540560

541561
# The goal of this method is to save the scope's values. All namespaced inputs
542562
# will already exist within the `root_state`.
543-
@self._root_bookmark.on_bookmark
563+
@self._session._parent.bookmark.on_bookmark
544564
async def scoped_on_bookmark(root_state: BookmarkState) -> None:
545565
return await self._scoped_on_bookmark(root_state)
546566

547567
from ..session import session_context
548568

549-
@self._root_bookmark.on_bookmarked
569+
@self._session._parent.bookmark.on_bookmarked
550570
async def scoped_on_bookmarked(url: str) -> None:
551571
if self._on_bookmarked_callbacks.count() == 0:
552572
return
553573

554-
with session_context(self._session_proxy):
574+
with session_context(self._session):
555575
await self._on_bookmarked_callbacks.invoke(url)
556576

557577
ns_prefix = str(self._ns + self._ns._sep)
558578

559-
@self._root_bookmark.on_restore
579+
@self._session._parent.bookmark.on_restore
560580
async def scoped_on_restore(restore_state: RestoreState) -> None:
561581
if self._on_restore_callbacks.count() == 0:
562582
return
563583

564584
scoped_restore_state = restore_state._state_within_namespace(ns_prefix)
565585

566-
with session_context(self._session_proxy):
586+
with session_context(self._session):
567587
await self._on_restore_callbacks.invoke(scoped_restore_state)
568588

569-
@self._root_bookmark.on_restored
589+
@self._session._parent.bookmark.on_restored
570590
async def scoped_on_restored(restore_state: RestoreState) -> None:
571591
if self._on_restored_callbacks.count() == 0:
572592
return
573593

574594
scoped_restore_state = restore_state._state_within_namespace(ns_prefix)
575-
with session_context(self._session_proxy):
595+
with session_context(self._session):
576596
await self._on_restored_callbacks.invoke(scoped_restore_state)
577597

578598
async def _scoped_on_bookmark(self, root_state: BookmarkState) -> None:
@@ -583,8 +603,8 @@ async def _scoped_on_bookmark(self, root_state: BookmarkState) -> None:
583603
from ..bookmark._bookmark import BookmarkState
584604

585605
scoped_state = BookmarkState(
586-
input=self._session_root.input,
587-
exclude=self._root_bookmark.exclude,
606+
input=self._session.input,
607+
exclude=self.exclude,
588608
on_save=None,
589609
)
590610

@@ -603,7 +623,7 @@ async def _scoped_on_bookmark(self, root_state: BookmarkState) -> None:
603623
# Invoke the callback on the scopeState object
604624
from ..session import session_context
605625

606-
with session_context(self._session_proxy):
626+
with session_context(self._session):
607627
await self._on_bookmark_callbacks.invoke(scoped_state)
608628

609629
# Copy `values` from scoped_state to root_state (adding namespace)
@@ -613,6 +633,19 @@ async def _scoped_on_bookmark(self, root_state: BookmarkState) -> None:
613633
raise ValueError("All scope values must be named.")
614634
root_state.values[str(self._ns(key))] = value
615635

636+
@property
637+
def store(self) -> BookmarkStore:
638+
return self._session._parent.bookmark.store
639+
640+
@property
641+
def _restore_context(self) -> RestoreContext | None:
642+
return self._session._parent.bookmark._restore_context
643+
644+
def _set_restore_context(self, restore_context: RestoreContext) -> NoReturn:
645+
raise NotImplementedError(
646+
"The `RestoreContext` should only be set on the root session object."
647+
)
648+
616649
def _create_effects(self) -> NoReturn:
617650
raise NotImplementedError(
618651
"Please call `._create_effects()` from the root session only."
@@ -635,21 +668,18 @@ def on_bookmarked(
635668
return self._on_bookmarked_callbacks.register(wrap_async(callback))
636669

637670
def _get_bookmark_exclude(self) -> NoReturn:
671+
638672
raise NotImplementedError(
639673
"Please call `._get_bookmark_exclude()` from the root session only."
640674
)
641675

642676
async def update_query_string(
643677
self, query_string: str, mode: Literal["replace", "push"] = "replace"
644678
) -> None:
645-
await self._root_bookmark.update_query_string(query_string, mode)
679+
await self._session._parent.bookmark.update_query_string(query_string, mode)
646680

647681
async def do_bookmark(self) -> None:
648-
await self._root_bookmark.do_bookmark()
649-
650-
@property
651-
def store(self) -> BookmarkStore:
652-
return self._root_bookmark.store
682+
await self._session._parent.bookmark.do_bookmark()
653683

654684
def on_restore(
655685
self,
@@ -670,8 +700,24 @@ def on_restored(
670700

671701
class BookmarkExpressStub(Bookmark):
672702

673-
def __init__(self, session_root: ExpressStubSession) -> None:
674-
super().__init__(session_root)
703+
def __init__(self, session: ExpressStubSession) -> None:
704+
super().__init__()
705+
706+
from ..express._stub_session import ExpressStubSession
707+
708+
assert isinstance(session, ExpressStubSession)
709+
self._session = session
710+
711+
@property
712+
def store(self) -> BookmarkStore:
713+
return "disable"
714+
715+
@property
716+
def _restore_context(self) -> RestoreContext | None:
717+
return None
718+
719+
def _set_restore_context(self, restore_context: RestoreContext) -> None:
720+
return None
675721

676722
def _create_effects(self) -> NoReturn:
677723
raise NotImplementedError(

0 commit comments

Comments
 (0)