Parcourir la source

UI context properties (#2905)

* provide ui.context

* use ui.context instead of page injector

* add `context` to `__all__`

* remove unnecessary file

* introduce a context class

* deprecate getter methods in favor of properties

* fix pytests

* remove context imports and use ui.context instead

* replace some outdated calls to get_client()

---------

Co-authored-by: Rodja Trappe <rodja@zauberzeug.com>
Falko Schindler il y a 1 an
Parent
commit
f7c08cfd97

+ 1 - 1
examples/chat_app/main.py

@@ -36,7 +36,7 @@ async def main():
         ui.markdown('simple chat app built with [NiceGUI](https://nicegui.io)') \
             .classes('text-xs self-end mr-8 m-[-1em] text-primary')
 
-    await ui.context.get_client().connected()  # chat_messages(...) uses run_javascript which is only possible after connecting
+    await ui.context.client.connected()  # chat_messages(...) uses run_javascript which is only possible after connecting
     with ui.column().classes('w-full max-w-2xl mx-auto items-stretch'):
         chat_messages(user_id)
 

+ 1 - 1
examples/descope_auth/user.py

@@ -61,7 +61,7 @@ class page(ui.page):
                     const sessionToken = sdk.getSessionToken()
                 </script>
             ''')
-            await ui.context.get_client().connected()
+            await ui.context.client.connected()
             if await self._is_logged_in():
                 if self.path == self.LOGIN_PATH:
                     self._refresh()

+ 1 - 1
examples/download_text_as_file/main.py

@@ -21,7 +21,7 @@ async def index():
     ui.button('Download', on_click=lambda: ui.download(download_path))
 
     # cleanup the download route after the client disconnected
-    await ui.context.get_client().disconnected()
+    await ui.context.client.disconnected()
     app.routes[:] = [route for route in app.routes if route.path != download_path]
 
 ui.run()

+ 1 - 1
examples/infinite_scroll/main.py

@@ -9,7 +9,7 @@ async def page():
     async def check():
         if await ui.run_javascript('window.pageYOffset >= document.body.offsetHeight - 2 * window.innerHeight'):
             ui.image(f'https://picsum.photos/640/360?{time.time()}')
-    await ui.context.get_client().connected()
+    await ui.context.client.connected()
     ui.timer(0.1, check)
 
 

+ 2 - 1
nicegui/__init__.py

@@ -1,7 +1,8 @@
-from . import context, elements, run, ui
+from . import elements, run, ui
 from .api_router import APIRouter
 from .app.app import App
 from .client import Client
+from .context import context
 from .nicegui import app
 from .tailwind import Tailwind
 from .version import __version__

+ 36 - 14
nicegui/context.py

@@ -2,27 +2,49 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING, List
 
+from .logging import log
 from .slot import Slot
 
 if TYPE_CHECKING:
     from .client import Client
 
 
-def get_slot_stack() -> List[Slot]:
-    """Return the slot stack of the current asyncio task."""
-    return Slot.get_stack()
+class Context:
 
+    def get_slot_stack(self) -> List[Slot]:
+        """Return the slot stack of the current asyncio task. (DEPRECATED, use context.slot_stack instead)"""
+        log.warning('context.get_slot_stack() is deprecated, use context.slot_stack instead')
+        return self.slot_stack
 
-def get_slot() -> Slot:
-    """Return the current slot."""
-    slot_stack = get_slot_stack()
-    if not slot_stack:
-        raise RuntimeError('The current slot cannot be determined because the slot stack for this task is empty.\n'
-                           'This may happen if you try to create UI from a background task.\n'
-                           'To fix this, enter the target slot explicitly using `with container_element:`.')
-    return slot_stack[-1]
+    def get_slot(self) -> Slot:
+        """Return the current slot. (DEPRECATED, use context.slot instead)"""
+        log.warning('context.get_slot() is deprecated, use context.slot instead')
+        return self.slot
 
+    def get_client(self) -> Client:
+        """Return the current client. (DEPRECATED, use context.client instead)"""
+        log.warning('context.get_client() is deprecated, use context.client instead')
+        return self.client
 
-def get_client() -> Client:
-    """Return the current client."""
-    return get_slot().parent.client
+    @property
+    def slot_stack(self) -> List[Slot]:
+        """Return the slot stack of the current asyncio task."""
+        return Slot.get_stack()
+
+    @property
+    def slot(self) -> Slot:
+        """Return the current slot."""
+        slot_stack = self.slot_stack
+        if not slot_stack:
+            raise RuntimeError('The current slot cannot be determined because the slot stack for this task is empty.\n'
+                               'This may happen if you try to create UI from a background task.\n'
+                               'To fix this, enter the target slot explicitly using `with container_element:`.')
+        return slot_stack[-1]
+
+    @property
+    def client(self) -> Client:
+        """Return the current client."""
+        return self.slot.parent.client
+
+
+context = Context()

+ 4 - 3
nicegui/element.py

@@ -9,8 +9,9 @@ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Iterator, List,
 
 from typing_extensions import Self
 
-from . import context, core, events, helpers, json, storage
+from . import core, events, helpers, json, storage
 from .awaitable_response import AwaitableResponse, NullResponse
+from .context import context
 from .dependencies import Component, Library, register_library, register_resource, register_vue_component
 from .elements.mixins.visibility import Visibility
 from .event_listener import EventListener
@@ -72,7 +73,7 @@ class Element(Visibility):
         :param _client: client for this element (for internal use only)
         """
         super().__init__()
-        self.client = _client or context.get_client()
+        self.client = _client or context.client
         self.id = self.client.next_element_id
         self.client.next_element_id += 1
         self.tag = tag if tag else self.component.tag if self.component else 'div'
@@ -92,7 +93,7 @@ class Element(Visibility):
 
         self.client.elements[self.id] = self
         self.parent_slot: Optional[Slot] = None
-        slot_stack = context.get_slot_stack()
+        slot_stack = context.slot_stack
         if slot_stack:
             self.parent_slot = slot_stack[-1]
             self.parent_slot.children.append(self)

+ 2 - 2
nicegui/elements/carousel.py

@@ -2,7 +2,7 @@ from __future__ import annotations
 
 from typing import Any, Callable, Optional, Union, cast
 
-from .. import context
+from ..context import context
 from .mixins.disableable_element import DisableableElement
 from .mixins.value_element import ValueElement
 
@@ -62,7 +62,7 @@ class CarouselSlide(DisableableElement):
         :param name: name of the slide (will be the value of the `ui.carousel` element, auto-generated if `None`)
         """
         super().__init__(tag='q-carousel-slide')
-        self.carousel = cast(ValueElement, context.get_slot().parent)
+        self.carousel = cast(ValueElement, context.slot.parent)
         name = name or f'slide_{len(self.carousel.default_slot.children)}'
         self._props['name'] = name
         self._classes.append('nicegui-carousel-slide')

+ 2 - 2
nicegui/elements/notification.py

@@ -1,6 +1,6 @@
 from typing import Any, Literal, Optional, Union
 
-from .. import context
+from ..context import context
 from ..element import Element
 from .timer import Timer
 
@@ -57,7 +57,7 @@ class Notification(Element, component='notification.js'):
 
         Note: You can pass additional keyword arguments according to `Quasar's Notify API <https://quasar.dev/quasar-plugins/notify#notify-api>`_.
         """
-        with context.get_client().layout:
+        with context.client.layout:
             super().__init__()
         self._props['options'] = {
             'message': str(message),

+ 2 - 2
nicegui/elements/query.py

@@ -2,7 +2,7 @@ from typing import Optional
 
 from typing_extensions import Self
 
-from .. import context
+from ..context import context
 from ..element import Element
 
 
@@ -64,7 +64,7 @@ class Query:
 
         :param selector: the CSS selector (e.g. "body", "#my-id", ".my-class", "div > p")
         """
-        for element in context.get_client().elements.values():
+        for element in context.client.elements.values():
             if isinstance(element, QueryElement) and element._props['selector'] == selector:  # pylint: disable=protected-access
                 self.element = element
                 break

+ 2 - 2
nicegui/elements/stepper.py

@@ -2,7 +2,7 @@ from __future__ import annotations
 
 from typing import Any, Callable, Optional, Union, cast
 
-from .. import context
+from ..context import context
 from ..element import Element
 from .mixins.disableable_element import DisableableElement
 from .mixins.value_element import ValueElement
@@ -69,7 +69,7 @@ class Step(DisableableElement):
         self._classes.append('nicegui-step')
         if icon:
             self._props['icon'] = icon
-        self.stepper = cast(ValueElement, context.get_slot().parent)
+        self.stepper = cast(ValueElement, context.slot.parent)
         if self.stepper.value is None:
             self.stepper.value = name
 

+ 2 - 2
nicegui/elements/tabs.py

@@ -2,7 +2,7 @@ from __future__ import annotations
 
 from typing import Any, Callable, Optional, Union
 
-from .. import context
+from ..context import context
 from .mixins.disableable_element import DisableableElement
 from .mixins.value_element import ValueElement
 
@@ -44,7 +44,7 @@ class Tab(DisableableElement):
         self._props['label'] = label if label is not None else name
         if icon:
             self._props['icon'] = icon
-        self.tabs = context.get_slot().parent
+        self.tabs = context.slot.parent
 
 
 class TabPanels(ValueElement):

+ 3 - 2
nicegui/functions/download.py

@@ -1,7 +1,8 @@
 from pathlib import Path
 from typing import Optional, Union
 
-from .. import context, core, helpers
+from .. import core, helpers
+from ..context import context
 
 
 def download(src: Union[str, Path, bytes], filename: Optional[str] = None, media_type: str = '') -> None:
@@ -18,4 +19,4 @@ def download(src: Union[str, Path, bytes], filename: Optional[str] = None, media
             src = core.app.add_static_file(local_file=src, single_use=True)
         else:
             src = str(src)
-    context.get_client().download(src, filename, media_type)
+    context.client.download(src, filename, media_type)

+ 3 - 3
nicegui/functions/html.py

@@ -1,5 +1,5 @@
-from .. import context
 from ..client import Client
+from ..context import context
 
 
 def add_head_html(code: str, *, shared: bool = False) -> None:
@@ -11,7 +11,7 @@ def add_head_html(code: str, *, shared: bool = False) -> None:
     if shared:
         Client.shared_head_html += code + '\n'
     else:
-        client = context.get_client()
+        client = context.client
         if client.has_socket_connection:
             client.run_javascript(f'document.head.insertAdjacentHTML("beforeend", {code!r});')
         client._head_html += code + '\n'  # pylint: disable=protected-access
@@ -26,7 +26,7 @@ def add_body_html(code: str, *, shared: bool = False) -> None:
     if shared:
         Client.shared_body_html += code + '\n'
     else:
-        client = context.get_client()
+        client = context.client
         if client.has_socket_connection:
             client.run_javascript(f'document.querySelector("#app").insertAdjacentHTML("beforebegin", {code!r});')
         client._body_html += code + '\n'  # pylint: disable=protected-access

+ 2 - 2
nicegui/functions/javascript.py

@@ -1,7 +1,7 @@
 from typing import Optional
 
-from .. import context
 from ..awaitable_response import AwaitableResponse
+from ..context import context
 
 
 def run_javascript(code: str, *,
@@ -21,4 +21,4 @@ def run_javascript(code: str, *,
 
     :return: AwaitableResponse that can be awaited to get the result of the JavaScript code
     """
-    return context.get_client().run_javascript(code, respond=respond, timeout=timeout, check_interval=check_interval)
+    return context.client.run_javascript(code, respond=respond, timeout=timeout, check_interval=check_interval)

+ 2 - 2
nicegui/functions/navigate.py

@@ -1,7 +1,7 @@
 from typing import Any, Callable, Union
 
-from .. import context
 from ..client import Client
+from ..context import context
 from ..element import Element
 from .javascript import run_javascript
 
@@ -64,4 +64,4 @@ class Navigate:
             path = f'#c{target.id}'
         elif callable(target):
             path = Client.page_routes[target]
-        context.get_client().open(path, new_tab)
+        context.client.open(path, new_tab)

+ 2 - 2
nicegui/functions/notify.py

@@ -1,6 +1,6 @@
 from typing import Any, Literal, Optional, Union
 
-from .. import context
+from ..context import context
 
 ARG_MAP = {
     'close_button': 'closeBtn',
@@ -49,5 +49,5 @@ def notify(message: Any, *,
     options = {ARG_MAP.get(key, key): value for key, value in locals().items() if key != 'kwargs' and value is not None}
     options['message'] = str(message)
     options.update(kwargs)
-    client = context.get_client()
+    client = context.client
     client.outbox.enqueue_message('notify', options, client.id)

+ 3 - 3
nicegui/functions/on.py

@@ -1,6 +1,6 @@
 from typing import Any, Callable, Optional, Sequence, Union
 
-from .. import context
+from ..context import context
 
 
 def on(type: str,  # pylint: disable=redefined-builtin
@@ -19,5 +19,5 @@ def on(type: str,  # pylint: disable=redefined-builtin
     :param leading_events: whether to trigger the event handler immediately upon the first event occurrence (default: `True`)
     :param trailing_events: whether to trigger the event handler after the last event occurrence (default: `True`)
     """
-    context.get_client().layout.on(type, handler, args,
-                                   throttle=throttle, leading_events=leading_events, trailing_events=trailing_events)
+    context.client.layout.on(type, handler, args,
+                             throttle=throttle, leading_events=leading_events, trailing_events=trailing_events)

+ 3 - 2
nicegui/functions/page_title.py

@@ -1,4 +1,5 @@
-from .. import context, json
+from .. import json
+from ..context import context
 
 
 def page_title(title: str) -> None:
@@ -8,7 +9,7 @@ def page_title(title: str) -> None:
 
     :param title: page title
     """
-    client = context.get_client()
+    client = context.client
     client.title = title
     if client.has_socket_connection:
         client.run_javascript(f'document.title = {json.dumps(title)}')

+ 5 - 5
nicegui/page_layout.py

@@ -1,6 +1,6 @@
 from typing import Literal, Optional
 
-from . import context
+from .context import context
 from .element import Element
 from .elements.mixins.value_element import ValueElement
 from .functions.html import add_body_html
@@ -45,7 +45,7 @@ class Header(ValueElement):
         :param add_scroll_padding: whether to automatically prevent link targets from being hidden behind the header (default: `True`)
         """
         _check_current_slot(self)
-        with context.get_client().layout:
+        with context.client.layout:
             super().__init__(tag='q-header', value=value, on_value_change=None)
         self._classes.append('nicegui-header')
         self._props['bordered'] = bordered
@@ -109,7 +109,7 @@ class Drawer(Element):
         :param bottom_corner: whether the drawer expands into the bottom corner (default: `False`)
         """
         _check_current_slot(self)
-        with context.get_client().layout:
+        with context.client.layout:
             super().__init__('q-drawer')
         if value is None:
             self._props['show-if-above'] = True
@@ -228,7 +228,7 @@ class Footer(ValueElement):
         :param wrap: whether the footer should wrap its content (default: `True`)
         """
         _check_current_slot(self)
-        with context.get_client().layout:
+        with context.client.layout:
             super().__init__(tag='q-footer', value=value, on_value_change=None)
         self.classes('nicegui-footer')
         self._props['bordered'] = bordered
@@ -271,7 +271,7 @@ class PageSticky(Element):
 
 
 def _check_current_slot(element: Element) -> None:
-    parent = context.get_slot().parent
+    parent = context.slot.parent
     if parent != parent.client.content:
         log.warning(f'Found top level layout element "{element.__class__.__name__}" inside element "{parent.__class__.__name__}". '
                     'Top level layout elements should not be nested but must be direct children of the page content. '

+ 6 - 5
nicegui/storage.py

@@ -15,7 +15,8 @@ from starlette.middleware.sessions import SessionMiddleware
 from starlette.requests import Request
 from starlette.responses import Response
 
-from . import background_tasks, context, core, json, observables
+from . import background_tasks, core, json, observables
+from .context import context
 from .logging import log
 from .observables import ObservableDict
 
@@ -148,7 +149,7 @@ class Storage:
     @staticmethod
     def _is_in_auto_index_context() -> bool:
         try:
-            return context.get_client().is_auto_index_client
+            return context.client.is_auto_index_client
         except RuntimeError:
             return False  # no client
 
@@ -167,7 +168,7 @@ class Storage:
         if self._is_in_auto_index_context():
             raise RuntimeError('app.storage.client can only be used with page builder functions '
                                '(https://nicegui.io/documentation/page)')
-        return context.get_client().storage
+        return context.client.storage
 
     @property
     def tab(self) -> observables.ObservableDict:
@@ -175,7 +176,7 @@ class Storage:
         if self._is_in_auto_index_context():
             raise RuntimeError('app.storage.tab can only be used with page builder functions '
                                '(https://nicegui.io/documentation/page)')
-        client = context.get_client()
+        client = context.client
         if not client.has_socket_connection:
             raise RuntimeError('app.storage.tab can only be used with a client connection; '
                                'see https://nicegui.io/documentation/page#wait_for_client_connection to await it')
@@ -197,7 +198,7 @@ class Storage:
         self._general.clear()
         self._users.clear()
         try:
-            client = context.get_client()
+            client = context.client
         except RuntimeError:
             pass  # no client, could be a pytest
         else:

+ 1 - 1
nicegui/ui.py

@@ -122,7 +122,7 @@ __all__ = [
     'run_with',
 ]
 
-from . import context
+from .context import context
 from .element import Element as element
 from .elements.aggrid import AgGrid as aggrid
 from .elements.audio import Audio as audio

+ 2 - 2
tests/test_auto_context.py

@@ -2,7 +2,7 @@ import asyncio
 
 from selenium.webdriver.common.by import By
 
-from nicegui import Client, background_tasks, ui
+from nicegui import background_tasks, ui
 from nicegui.testing import Screen
 
 
@@ -52,7 +52,7 @@ def test_autoupdate_after_connected(screen: Screen):
     @ui.page('/')
     async def page():
         ui.label('before connected')
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         ui.label('after connected')
         await asyncio.sleep(1)
         ui.label('one')

+ 1 - 1
tests/test_interactive_image.py

@@ -20,7 +20,7 @@ def test_set_source_in_tab(screen: Screen):
                 img = ui.interactive_image()
             with ui.tab_panel('B'):
                 ui.label('Tab B')
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         img.set_source('https://picsum.photos/id/29/640/360')
 
     screen.open('/')

+ 1 - 1
tests/test_javascript.py

@@ -16,7 +16,7 @@ def test_run_javascript_on_value_change(screen: Screen):
     @ui.page('/')
     async def page():
         ui.radio(['A', 'B'], on_change=lambda e: ui.run_javascript(f'document.title = "Page {e.value}"'))
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         ui.run_javascript('document.title = "Initial Title"')
 
     screen.open('/')

+ 1 - 1
tests/test_lifecycle.py

@@ -35,7 +35,7 @@ def test_connect_disconnect_is_called_for_each_client(screen: Screen):
 
     @ui.page('/', reconnect_timeout=0)
     def page():
-        ui.label(f'client id: {ui.context.get_client().id}')
+        ui.label(f'client id: {ui.context.client.id}')
     app.on_connect(lambda: events.append('connect'))
     app.on_disconnect(lambda: events.append('disconnect'))
 

+ 9 - 9
tests/test_page.py

@@ -120,7 +120,7 @@ def test_wait_for_connected(screen: Screen):
     async def page():
         nonlocal label
         label = ui.label()
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         await load()
 
     screen.open('/')
@@ -132,9 +132,9 @@ def test_wait_for_disconnect(screen: Screen):
 
     @ui.page('/', reconnect_timeout=0)
     async def page():
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         events.append('connected')
-        await ui.context.get_client().disconnected()
+        await ui.context.client.disconnected()
         events.append('disconnected')
 
     screen.open('/')
@@ -149,7 +149,7 @@ def test_wait_for_disconnect_without_awaiting_connected(screen: Screen):
 
     @ui.page('/', reconnect_timeout=0)
     async def page():
-        await ui.context.get_client().disconnected()
+        await ui.context.client.disconnected()
         events.append('disconnected')
 
     screen.open('/')
@@ -163,7 +163,7 @@ def test_adding_elements_after_connected(screen: Screen):
     @ui.page('/')
     async def page():
         ui.label('before')
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         ui.label('after')
 
     screen.open('/')
@@ -185,7 +185,7 @@ def test_exception(screen: Screen):
 def test_exception_after_connected(screen: Screen):
     @ui.page('/')
     async def page():
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         ui.label('this is shown')
         raise RuntimeError('some exception')
 
@@ -207,7 +207,7 @@ def test_adding_elements_during_onconnect(screen: Screen):
     @ui.page('/')
     def page():
         ui.label('Label 1')
-        ui.context.get_client().on_connect(lambda: ui.label('Label 2'))
+        ui.context.client.on_connect(lambda: ui.label('Label 2'))
 
     screen.open('/')
     screen.should_contain('Label 2')
@@ -219,7 +219,7 @@ def test_async_connect_handler(screen: Screen):
         async def run_js():
             result.text = await ui.run_javascript('41 + 1')
         result = ui.label()
-        ui.context.get_client().on_connect(run_js)
+        ui.context.client.on_connect(run_js)
 
     screen.open('/')
     screen.should_contain('42')
@@ -292,7 +292,7 @@ def test_returning_custom_response_async(screen: Screen):
 def test_warning_about_to_late_responses(screen: Screen):
     @ui.page('/')
     async def page():
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         ui.label('NiceGUI page')
         return PlainTextResponse('custom response')
 

+ 6 - 6
tests/test_storage.py

@@ -4,7 +4,7 @@ from pathlib import Path
 import httpx
 import pytest
 
-from nicegui import Client, app, background_tasks, context, ui
+from nicegui import app, background_tasks, context, ui
 from nicegui import storage as storage_module
 from nicegui.testing import Screen
 
@@ -50,7 +50,7 @@ def test_browser_storage_supports_asyncio(screen: Screen):
 def test_browser_storage_modifications_after_page_load_are_forbidden(screen: Screen):
     @ui.page('/')
     async def page():
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         try:
             app.storage.browser['test'] = 'data'
         except TypeError as e:
@@ -65,7 +65,7 @@ def test_user_storage_modifications(screen: Screen):
     @ui.page('/')
     async def page(delayed: bool = False):
         if delayed:
-            await ui.context.get_client().connected()
+            await ui.context.client.connected()
         app.storage.user['count'] = app.storage.user.get('count', 0) + 1
         ui.label().bind_text_from(app.storage.user, 'count')
 
@@ -170,7 +170,7 @@ def test_rapid_storage(screen: Screen):
 def test_tab_storage_is_local(screen: Screen):
     @ui.page('/')
     async def page():
-        await context.get_client().connected()
+        await context.client.connected()
         app.storage.tab['count'] = app.storage.tab.get('count', 0) + 1
         ui.label().bind_text_from(app.storage.tab, 'count')
 
@@ -194,7 +194,7 @@ def test_tab_storage_is_auto_removed(screen: Screen):
 
     @ui.page('/')
     async def page():
-        await context.get_client().connected()
+        await context.client.connected()
         app.storage.tab['count'] = app.storage.tab.get('count', 0) + 1
         ui.label().bind_text_from(app.storage.tab, 'count')
 
@@ -213,7 +213,7 @@ def test_clear_tab_storage(screen: Screen):
 
     @ui.page('/')
     async def page():
-        await context.get_client().connected()
+        await context.client.connected()
         app.storage.tab['test'] = '123'
         ui.button('clear', on_click=app.storage.clear)
 

+ 2 - 2
website/documentation/content/generic_events_documentation.py

@@ -1,4 +1,4 @@
-from nicegui import context, ui
+from nicegui import ui
 
 from . import doc
 
@@ -116,7 +116,7 @@ async def custom_events() -> None:
     #     </script>
     # ''')
     # END OF DEMO
-    await context.get_client().connected()
+    await ui.context.client.connected()
     ui.run_javascript('''
         document.addEventListener('visibilitychange', () => {
             if (document.visibilityState === 'visible') {

+ 2 - 2
website/documentation/content/page_documentation.py

@@ -45,10 +45,10 @@ def wait_for_connected_demo():
     @ui.page('/wait_for_connection')
     async def wait_for_connection():
         ui.label('This text is displayed immediately.')
-        await ui.context.get_client().connected()
+        await ui.context.client.connected()
         await asyncio.sleep(2)
         ui.label('This text is displayed 2 seconds after the page has been fully loaded.')
-        ui.label(f'The IP address {ui.context.get_client().ip} was obtained from the websocket.')
+        ui.label(f'The IP address {ui.context.client.ip} was obtained from the websocket.')
 
     ui.link('wait for connection', wait_for_connection)
 

+ 3 - 3
website/documentation/content/query_documentation.py

@@ -1,4 +1,4 @@
-from nicegui import context, ui
+from nicegui import ui
 
 from . import doc
 
@@ -22,7 +22,7 @@ def main_demo() -> None:
 def background_image():
     # ui.query('body').classes('bg-gradient-to-t from-blue-400 to-blue-100')
     # END OF DEMO
-    context.get_slot_stack()[-1].parent.classes('bg-gradient-to-t from-blue-400 to-blue-100')
+    ui.context.slot_stack[-1].parent.classes('bg-gradient-to-t from-blue-400 to-blue-100')
 
 
 @doc.demo('Modify default page padding', '''
@@ -31,7 +31,7 @@ def background_image():
 ''')
 def remove_padding():
     # ui.query('.nicegui-content').classes('p-0')
-    context.get_slot_stack()[-1].parent.classes(remove='p-4')  # HIDE
+    ui.context.slot_stack[-1].parent.classes(remove='p-4')  # HIDE
     # with ui.column().classes('h-screen w-full bg-gray-400 justify-between'):
     with ui.column().classes('h-full w-full bg-gray-400 justify-between'):  # HIDE
         ui.label('top left')

+ 1 - 1
website/documentation/content/storage_documentation.py

@@ -122,7 +122,7 @@ def tab_storage():
 
     # @ui.page('/')
     # async def index():
-    #     await ui.context.get_client().connected()
+    #     await ui.context.client.connected()
     with ui.column():  # HIDE
         app.storage.tab['count'] = app.storage.tab.get('count', 0) + 1
         ui.label(f'Tab reloaded {app.storage.tab["count"]} times')

+ 2 - 2
website/main_page.py

@@ -1,4 +1,4 @@
-from nicegui import context, ui
+from nicegui import ui
 
 from . import documentation, example_card, svg
 from .examples import examples
@@ -8,7 +8,7 @@ from .style import example_link, features, heading, link_target, section_heading
 
 def create() -> None:
     """Create the content of the main page."""
-    context.get_client().content.classes('p-0 gap-0')
+    ui.context.client.content.classes('p-0 gap-0')
     add_head_html()
     add_header()
 

+ 2 - 2
website/style.py

@@ -1,7 +1,7 @@
 import re
 from typing import List, Optional
 
-from nicegui import context, ui
+from nicegui import ui
 
 from .examples import Example
 
@@ -73,7 +73,7 @@ def subheading(text: str, *, link: Optional[str] = None, major: bool = False, an
             ui.label(text).classes(classes)
         with ui.link(target=f'#{name}').classes('absolute').style('transform: translateX(-150%)'):
             ui.icon('link', size='sm').classes('opacity-10 hover:opacity-80')
-    drawers = [element for element in context.get_client().elements.values() if isinstance(element, ui.left_drawer)]
+    drawers = [element for element in ui.context.client.elements.values() if isinstance(element, ui.left_drawer)]
     if drawers:
         menu = drawers[0]
         with menu: