Skip to content

Commit

Permalink
#338 introduce outbox; update elements during initialization, not whe…
Browse files Browse the repository at this point in the history
…n leaving container
  • Loading branch information
falkoschindler committed Feb 7, 2023
1 parent 216d315 commit 3c00a86
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 93 deletions.
30 changes: 0 additions & 30 deletions nicegui/async_updater.py

This file was deleted.

6 changes: 3 additions & 3 deletions nicegui/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from fastapi.responses import Response
from fastapi.templating import Jinja2Templates

from . import background_tasks, globals
from . import globals, outbox
from .dependencies import generate_js_imports, generate_vue_content
from .element import Element
from .favicon import get_favicon_url
Expand Down Expand Up @@ -114,7 +114,7 @@ async def run_javascript(self, code: str, *,
'code': code,
'request_id': request_id if respond else None,
}
background_tasks.create(globals.sio.emit('run_javascript', command, room=self.id))
outbox.enqueue_message('run_javascript', command, self.id)
if not respond:
return None
deadline = time.time() + timeout
Expand All @@ -126,7 +126,7 @@ async def run_javascript(self, code: str, *,

def open(self, target: Union[Callable, str]) -> None:
path = target if isinstance(target, str) else globals.page_routes[target]
background_tasks.create(globals.sio.emit('open', path, room=self.id))
outbox.enqueue_message('open', path, self.id)

def on_connect(self, handler: Union[Callable, Awaitable]) -> None:
self.connect_handlers.append(handler)
Expand Down
11 changes: 7 additions & 4 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import re
from abc import ABC
from copy import deepcopy
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union

from . import background_tasks, binding, events, globals, updates
from . import binding, events, globals, outbox
from .elements.mixins.visibility import Visibility
from .event_listener import EventListener
from .slot import Slot
Expand Down Expand Up @@ -39,6 +39,9 @@ def __init__(self, tag: str, *, _client: Optional[Client] = None) -> None:
if slot_stack:
self.parent_slot = slot_stack[-1]
self.parent_slot.children.append(self)
outbox.enqueue_update(self.parent_slot.parent)

outbox.enqueue_update(self)

def add_slot(self, name: str) -> Slot:
self.slots[name] = Slot(self, name)
Expand Down Expand Up @@ -180,13 +183,13 @@ def collect_descendant_ids(self) -> List[int]:
return ids

def update(self) -> None:
updates.enqueue(self)
outbox.enqueue_update(self)

def run_method(self, name: str, *args: Any) -> None:
if not globals.loop:
return
data = {'id': self.id, 'name': name, 'args': args}
background_tasks.create(globals.sio.emit('run_method', data, room=globals._socket_id or self.client.id))
outbox.enqueue_message('run_method', data, globals._socket_id or self.client.id)

def clear(self) -> None:
descendants = [self.client.elements[id] for id in self.collect_descendant_ids()[1:]]
Expand Down
2 changes: 1 addition & 1 deletion nicegui/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def handle_event(handler: Optional[Callable],
if is_coroutine(handler):
async def wait_for_result():
with sender.parent_slot:
await AsyncUpdater(result)
await result
if globals.loop and globals.loop.is_running():
background_tasks.create(wait_for_result(), name=str(handler))
else:
Expand Down
4 changes: 2 additions & 2 deletions nicegui/functions/notify.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional, Union

from .. import background_tasks, globals
from .. import globals, outbox


def notify(message: str, *,
Expand All @@ -23,4 +23,4 @@ def notify(message: str, *,
Note: You can pass additional keyword arguments according to `Quasar's Notify API <https://quasar.dev/quasar-plugins/notify#notify-api>`_.
"""
options = {key: value for key, value in locals().items() if not key.startswith('_') and value is not None}
background_tasks.create(globals.sio.emit('notify', options, room=globals.get_client().id))
outbox.enqueue_message('notify', options, globals.get_client().id)
2 changes: 1 addition & 1 deletion nicegui/functions/timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async def _invoke_callback(self) -> None:
try:
result = self.callback()
if is_coroutine(self.callback):
await AsyncUpdater(result)
await result
except Exception:
traceback.print_exc()

Expand Down
4 changes: 2 additions & 2 deletions nicegui/nicegui.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from fastapi.staticfiles import StaticFiles
from fastapi_socketio import SocketManager

from . import background_tasks, binding, globals, updates
from . import background_tasks, binding, globals, outbox
from .app import App
from .client import Client
from .dependencies import js_components, js_dependencies
Expand Down Expand Up @@ -61,7 +61,7 @@ def handle_startup(with_welcome_message: bool = True) -> None:
for t in globals.startup_handlers:
safe_invoke(t)
background_tasks.create(binding.loop())
background_tasks.create(updates.loop())
background_tasks.create(outbox.loop())
background_tasks.create(prune_clients())
background_tasks.create(prune_slot_stacks())
globals.state = globals.State.STARTED
Expand Down
38 changes: 38 additions & 0 deletions nicegui/outbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import asyncio
from collections import deque
from typing import TYPE_CHECKING, Any, Deque, Literal, Tuple

from . import globals

if TYPE_CHECKING:
from .element import Element
ClientId = int
MessageType = Literal['update', 'run_method', 'run_javascript', 'open', 'notify']
MessageGroup = Tuple[ClientId, MessageType, Any]

queue: Deque['MessageGroup'] = deque()


def enqueue_update(element: 'Element') -> None:
if queue:
client_id, message_type, argument = queue[-1]
if client_id == element.client.id and message_type == 'update':
elements: Deque[Element] = argument
elements.append(element)
return
queue.append((element.client.id, 'update', deque([element])))


def enqueue_message(message_type: 'MessageType', data: Any, client_id: 'ClientId') -> None:
queue.append((client_id, message_type, data))


async def loop() -> None:
while True:
while queue:
client_id, message_type, data = queue.popleft()
if message_type == 'update':
messages: Deque[Element] = data
data = {'elements': {e.id: e.to_dict() for e in messages}}
await globals.sio.emit(message_type, data, room=client_id)
await asyncio.sleep(0.01)
2 changes: 1 addition & 1 deletion nicegui/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def decorated(*dec_args, **dec_kwargs) -> Response:
if inspect.isawaitable(result):
async def wait_for_result() -> None:
with client:
await AsyncUpdater(result)
await result
task = background_tasks.create(wait_for_result())
deadline = time.time() + self.response_timeout
while task and not client.is_waiting_for_connection and not task.done():
Expand Down
8 changes: 0 additions & 8 deletions nicegui/slot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,11 @@ def __init__(self, parent: 'Element', name: str) -> None:
self.name = name
self.parent = parent
self.children: List['Element'] = []
self.child_count = 0

def __enter__(self):
self.child_count = len(self.children)
globals.get_slot_stack().append(self)
return self

def __exit__(self, *_):
globals.get_slot_stack().pop()
globals.prune_slot_stack()
self.lazy_update()

def lazy_update(self) -> None:
if self.child_count != len(self.children):
self.child_count = len(self.children)
self.parent.update()
41 changes: 0 additions & 41 deletions nicegui/updates.py

This file was deleted.

1 change: 1 addition & 0 deletions tests/test_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def test_open_close_dialog(screen: Screen):
screen.open('/')
screen.should_not_contain('Content')
screen.click('Open')
screen.wait(0.5)
screen.should_contain('Content')
screen.click('Close')
screen.wait(0.5)
Expand Down
3 changes: 3 additions & 0 deletions tests/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,19 @@ def test_remove_and_clear(screen: Screen):
screen.should_contain('Label C')

screen.click('Remove B')
screen.wait(0.5)
screen.should_contain('Label A')
screen.should_not_contain('Label B')
screen.should_contain('Label C')

screen.click('Remove 0')
screen.wait(0.5)
screen.should_not_contain('Label A')
screen.should_not_contain('Label B')
screen.should_contain('Label C')

screen.click('Clear')
screen.wait(0.5)
screen.should_not_contain('Label A')
screen.should_not_contain('Label B')
screen.should_not_contain('Label C')
1 change: 1 addition & 0 deletions tests/test_expansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def test_open_close_expansion(screen: Screen):
screen.should_contain('Expansion')
screen.should_not_contain('Content')
screen.click('Open')
screen.wait(0.5)
screen.should_contain('Content')
screen.click('Close')
screen.wait(0.5)
Expand Down
3 changes: 3 additions & 0 deletions tests/test_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def test_password(screen: Screen):
assert element.get_attribute('value') == '123456'

element.send_keys('789')
screen.wait(0.5)
assert element.get_attribute('value') == '123456789'


Expand All @@ -44,7 +45,9 @@ def test_toggle_button(screen: Screen):
assert element.get_attribute('value') == '123456'

screen.click('visibility_off')
screen.wait(0.5)
assert element.get_attribute('type') == 'text'

screen.click('visibility')
screen.wait(0.5)
assert element.get_attribute('type') == 'password'

0 comments on commit 3c00a86

Please sign in to comment.