cengal.parallel_execution.coroutines.integrations.nicegui.versions.v_0.nicegui

Module Docstring Docstrings: http://www.python.org/dev/peps/pep-0257/

   1#!/usr/bin/env python
   2# coding=utf-8
   3
   4# Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space>
   5# 
   6# Licensed under the Apache License, Version 2.0 (the "License");
   7# you may not use this file except in compliance with the License.
   8# You may obtain a copy of the License at
   9# 
  10#     http://www.apache.org/licenses/LICENSE-2.0
  11# 
  12# Unless required by applicable law or agreed to in writing, software
  13# distributed under the License is distributed on an "AS IS" BASIS,
  14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15# See the License for the specific language governing permissions and
  16# limitations under the License.
  17
  18"""
  19Module Docstring
  20Docstrings: http://www.python.org/dev/peps/pep-0257/
  21"""
  22
  23__author__ = "ButenkoMS <gtalk@butenkoms.space>"
  24__copyright__ = "Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space>"
  25__credits__ = ["ButenkoMS <gtalk@butenkoms.space>", ]
  26__license__ = "Apache License, Version 2.0"
  27__version__ = "4.4.1"
  28__maintainer__ = "ButenkoMS <gtalk@butenkoms.space>"
  29__email__ = "gtalk@butenkoms.space"
  30# __status__ = "Prototype"
  31__status__ = "Development"
  32# __status__ = "Production"
  33
  34
  35__all__ = ['ClientHandlers', 'PageItems', 'nicegui_page_sync_coro', 'sync_like_page', 
  36           'sl_page', 'nicegui_page_async_coro', 'async_page', 'apage',
  37           'nicegui_page_class_async_coro', 'async_page_class', 'apage_class', 
  38           'nicegui_event_handler_func_async_coro', 'async_event_handler_func', 
  39           'aevent_handler_func', 'nicegui_event_handler_method_async_coro', 
  40           'async_event_handler_method', 'aevent_handler_method', 'PageContextBase', 
  41           'run']
  42
  43
  44import asyncio
  45
  46
  47class CurrentTask:
  48    def __init__(self, original) -> None:
  49        self.original = original
  50        self.new = original
  51
  52    def patch(self, new):
  53        self.new = new
  54
  55    def restore(self):
  56        self.new = self.original
  57
  58    def __call__(self, *args, **kwds):
  59        return self.new(*args, **kwds)
  60
  61
  62current_task = CurrentTask(asyncio.current_task)
  63asyncio.current_task = current_task
  64
  65
  66from cengal.parallel_execution.coroutines.integrations.uvloop import uvloop_install
  67uvloop_install()
  68
  69
  70from cengal.parallel_execution.asyncio.init_loop import init_loop
  71init_loop()
  72
  73
  74if True:
  75    # increasing max decode packets to be able to transfer images
  76    # see https://github.com/miguelgrinberg/python-engineio/issues/142
  77    from engineio.payload import Payload
  78    Payload.max_decode_packets = 500
  79
  80
  81import inspect
  82from time import perf_counter
  83from typing import Set, Tuple, Optional, Union, Dict, Callable, Any, Hashable, cast, List, Coroutine
  84import warnings
  85from cengal.parallel_execution.asyncio.atasks import create_task
  86from cengal.parallel_execution.coroutines.coro_scheduler import Interface, AnyWorker, cs_acoro
  87from cengal.parallel_execution.coroutines.coro_standard_services.async_event_bus import (AsyncEventBus,
  88                                                                                         AsyncEventBusRequest)
  89from cengal.parallel_execution.coroutines.coro_standard_services.put_coro import PutCoro, put_coro_to
  90from cengal.parallel_execution.coroutines.coro_standard_services.run_coro import RunCoro, arun_coro_fast, run_coro_fast
  91from cengal.parallel_execution.coroutines.coro_standard_services.db import Db, DbRequest, EnvId, EnvInfo
  92from cengal.parallel_execution.coroutines.coro_standard_services.simple_yield import Yield
  93from cengal.parallel_execution.coroutines.coro_standard_services.sleep import Sleep
  94from cengal.parallel_execution.coroutines.coro_standard_services.asyncio_loop import AsyncioLoop, AsyncioLoopRequest
  95from cengal.parallel_execution.coroutines.coro_standard_services.shutdown_loop import ShutdownLoop
  96from cengal.parallel_execution.coroutines.coro_standard_services.shutdown_on_keyboard_interrupt import ShutdownOnKeyboardInterrupt
  97from cengal.parallel_execution.coroutines.coro_standard_services.simple_yield import Yield
  98from cengal.parallel_execution.coroutines.coro_standard_services.instance import Instance, InstanceRequest, afast_wait, fast_get_explicit, fast_set_explicit
  99from cengal.parallel_execution.coroutines.coro_standard_services.loop_yield import agly_patched
 100from cengal.parallel_execution.coroutines.coro_standard_services.log import LogRequest
 101from cengal.parallel_execution.coroutines.coro_tools.await_coro import await_coro_prim
 102from cengal.parallel_execution.coroutines.coro_tools.run_in_loop import (arun_in_fast_loop, arun_in_loop, run_in_loop)
 103from cengal.parallel_execution.coroutines.coro_tools.lock import Lock
 104from cengal.code_flow_control.args_manager import args_kwargs, EntityArgsHolder, EntityArgsHolderExplicit
 105from cengal.io.serve_free_ports import simple_port_search
 106from uuid import uuid4
 107from inspect import signature, Signature, isclass
 108from cengal.parallel_execution.coroutines.coro_scheduler import *
 109from cengal.data_manipulation.conversion.reinterpret_cast import \
 110    reinterpret_cast
 111from cengal.parallel_execution.coroutines.coro_scheduler import (
 112    CoroWrapperAsyncAwait, Interface)
 113from cengal.parallel_execution.coroutines.coro_tools.await_coro import \
 114    await_coro_prim
 115from cengal.parallel_execution.coroutines.coro_tools.wait_coro import sync_coro_param
 116from cengal.parallel_execution.asyncio.atasks import create_task
 117
 118
 119from nicegui import app, ui, Client
 120# from nicegui.binding import loop
 121# agly_patched(loop)
 122from cengal.parallel_execution.asyncio.timed_yield import TimedYield
 123import nicegui.binding
 124original__propagate = nicegui.binding.propagate
 125class PropagateMock:
 126    def __init__(self) -> None:
 127        self.ty: TimedYield = TimedYield(0.01)
 128    
 129    def __call__(self, source_obj: Any, source_name: str, visited: Optional[Set[Tuple[int, str]]] = None) -> None:
 130        return original__propagate(source_obj, source_name, visited)
 131
 132
 133nicegui.binding.propagate = PropagateMock()
 134from nicegui.events import handle_event, GenericEventArguments, UiEventArguments
 135from nicegui.dataclasses import KWONLY_SLOTS
 136from nicegui.slot import Slot
 137from nicegui.globals import slot_stacks
 138from fastapi import Request as FastAPIRequest
 139from .text_translation import setup_translation, create_translatable_text_element, TextTranslator, \
 140    TranslationLanguageMapper, TranslationLanguageChooser, TranslatableTextElement, TTE, NiceguiTranslatableTextElement, NTTE
 141from cengal.parallel_execution.coroutines.coro_tools.await_coro import asyncio_coro
 142from cengal.file_system.app_fs_structure.app_dir_path import AppDirPath, AppDirectoryType
 143from cengal.file_system.path_manager import RelativePath
 144from cengal.web_tools.detect_browsers_host_device_type.by_http_headers import ClientViewType, client_view_type
 145from cengal.web_tools.detect_browsers_language.by_http_headers import parse_accept_language, optimize_accept_language, match_langs, normalize_lang
 146from cengal.math.numbers import RationalNumber
 147from cengal.base.exceptions import CengalError
 148from cengal.introspection.inspect import entity_name, entity_repr
 149from functools import partial
 150from collections import OrderedDict
 151from datetime import datetime, timedelta
 152from starlette.datastructures import Address, URL
 153from starlette.responses import Response, RedirectResponse
 154from aioipinfo import IPInfoClient, IPInfoError, IPInfoResponse
 155from dataclasses import dataclass
 156from contextlib import nullcontext
 157import os
 158import hashlib
 159import logging
 160
 161
 162DESTROY_CS_EVENT = f'DESTROY_CS_EVENT__{uuid4()}'
 163CS_PREPARED_FLAG = f'CS_PREPARED_FLAG'
 164CS_DESTROY_TIMEOUT = 3.0
 165
 166
 167text_translator: TextTranslator = None
 168translation_language_mapper: TranslationLanguageMapper = None
 169session_clients: Dict[Hashable, Set[Hashable]] = dict()
 170translatable_text_element_per_session: Dict[Hashable, NiceguiTranslatableTextElement] = dict()
 171translatable_text_element_per_client: Dict[Hashable, NiceguiTranslatableTextElement] = dict()
 172
 173known_translation_param_names: Set[str] = {'_t', '_T', '__'}
 174known_page_context_param_names: Set[str] = {'page_context', 'pc', 'self'}
 175db_env_id: EnvId = 'cengal_nicegui_db_env'
 176db_request__logged_in_clients: DbRequest = DbRequest(db_env_id, 'logged_in_clients')
 177db_request__user_clients: DbRequest = DbRequest(db_env_id, 'user_clients')
 178db_request__logged_in_sessions: DbRequest = DbRequest(db_env_id, 'logged_in_sessions')
 179db_request__user_sessions: DbRequest = DbRequest(db_env_id, 'user_sessions')
 180db_request__ip_geolocation: DbRequest = DbRequest(db_env_id, 'ip_geolocation')
 181db_request__user_sign_in_info: DbRequest = DbRequest(db_env_id, 'user_sign_in_info')
 182db_request__credentials_by_user_id: DbRequest = DbRequest(db_env_id, 'credentials_by_user_id')
 183db_request__user_sign_up_info: DbRequest = DbRequest(db_env_id, 'user_sign_up_info')
 184signed_in_session_timeout: timedelta = timedelta(days=90)
 185ipinfo_io_token: str = os.environ.get('CENGAL_NICEGUI__IPINFO_IO_TOKEN', None)
 186
 187
 188class CengalNiceguiException(CengalError):
 189    pass
 190
 191
 192class UserNotFoundError(CengalNiceguiException):
 193    pass
 194
 195
 196class PasswordIsNotCorrectError(CengalNiceguiException):
 197    pass
 198
 199
 200class CoroWrapperNiceGuiPageAsyncAwait(CoroWrapperAsyncAwait):
 201    def reinit_for_nicegui(self):
 202        self.subtype = self._setup_subtype()
 203        self._current_call_method = self._call_method  # type: Callable
 204        self.original__current_task = None
 205
 206    def mock__current_task(self, loop=None):
 207        return self.current_asyncio_task
 208
 209    def patch_asyncio_current_task(self):
 210        current_task.patch(self.mock__current_task)
 211
 212    def unpatch_asyncio_current_task(self):
 213        current_task.restore()
 214
 215    def _init_coroutine(self, *init_args, **init_kwargs):
 216        try:
 217            self.patch_asyncio_current_task()
 218            return super()._init_coroutine(*init_args, **init_kwargs)
 219        finally:
 220            self.unpatch_asyncio_current_task()
 221
 222    def _call_coroutine(self, *args, **kwargs):
 223        try:
 224            self.patch_asyncio_current_task()
 225            return super()._call_coroutine(*args, **kwargs)
 226        finally:
 227            self.unpatch_asyncio_current_task()
 228
 229    def _throw_coroutine(self, ex_type, ex_value=None, ex_traceback=None):
 230        try:
 231            self.patch_asyncio_current_task()
 232            return super()._throw_coroutine(ex_type, ex_value, ex_traceback)
 233        finally:
 234            self.unpatch_asyncio_current_task()
 235
 236    def _close_coroutine(self, *args, **kwargs):
 237        try:
 238            self.patch_asyncio_current_task()
 239            return super()._close_coroutine(*args, **kwargs)
 240        finally:
 241            self.unpatch_asyncio_current_task()
 242
 243
 244class CoroWrapperNiceGuiPageGreenlet(CoroWrapperGreenlet):
 245    def reinit_for_nicegui(self):
 246        self.subtype = self._setup_subtype()
 247        self._current_call_method = self._call_method  # type: Callable
 248        self.original__current_task = None
 249
 250    def mock__current_task(self, loop=None):
 251        return self.current_asyncio_task
 252
 253    def patch_asyncio_current_task(self):
 254        current_task.patch(self.mock__current_task)
 255
 256    def unpatch_asyncio_current_task(self):
 257        current_task.restore()
 258
 259    def _init_coroutine(self, *init_args, **init_kwargs):
 260        try:
 261            self.patch_asyncio_current_task()
 262            return super()._init_coroutine(*init_args, **init_kwargs)
 263        finally:
 264            self.unpatch_asyncio_current_task()
 265
 266    def _call_coroutine(self, *args, **kwargs):
 267        try:
 268            self.patch_asyncio_current_task()
 269            return super()._call_coroutine(*args, **kwargs)
 270        finally:
 271            self.unpatch_asyncio_current_task()
 272
 273    def _throw_coroutine(self, ex_type, ex_value=None, ex_traceback=None):
 274        try:
 275            self.patch_asyncio_current_task()
 276            return super()._throw_coroutine(ex_type, ex_value, ex_traceback)
 277        finally:
 278            self.unpatch_asyncio_current_task()
 279
 280    def _close_coroutine(self, *args, **kwargs):
 281        try:
 282            self.patch_asyncio_current_task()
 283            return super()._close_coroutine(*args, **kwargs)
 284        finally:
 285            self.unpatch_asyncio_current_task()
 286
 287
 288class ClientHandlers:
 289    def __init__(self, client: Optional[Client]) -> None:
 290        self.client: Optional[Client] = client
 291        self.on_connected_handlers: Set[Callable] = set()
 292        self.on_disconnected_handlers: Set[Callable] = set()
 293        self.is_connected: bool = False
 294        self.is_disconnected: bool = False
 295    
 296    @classmethod
 297    def check_args_factory(cls, signature: Signature, args, kwargs) -> Optional['ClientHandlers']:
 298        client: Optional[Client] = kwargs.get('client', None)
 299        if (client is None) or (not isinstance(client, Client)):
 300            return None
 301        else:
 302            return cls.install(client)
 303    
 304    @classmethod
 305    def install(cls, client: Client) -> 'ClientHandlers':
 306        client_handlers: ClientHandlers = cls(client)
 307        client.handlers = client_handlers
 308        client.on_connect(client_handlers._on_connected())
 309        client.on_disconnect(client_handlers._on_disconnected())
 310        return client_handlers
 311    
 312    def add_on_connected_handler(self, handler: Callable):
 313        if self.is_connected:
 314            handler()
 315        else:
 316            self.on_connected_handlers.add(handler)
 317    
 318    def remove_on_connected_handler(self, handler: Callable):
 319        self.on_connected_handlers.discard(handler)
 320    
 321    async def _on_connected(self):
 322        on_connected_handlers_buff = tuple(self.on_connected_handlers)
 323        self.on_connected_handlers = set()
 324        for index, handler in enumerate(on_connected_handlers_buff):
 325            try:
 326                handler()
 327            except:
 328                self.on_connected_handlers.update(on_connected_handlers_buff[index + 1:])
 329                create_task(self._on_connected)
 330                raise
 331    
 332    def add_on_disconnected_handler(self, handler: Callable):
 333        if self.is_disconnected:
 334            handler()
 335        else:
 336            self.on_disconnected_handlers.add(handler)
 337    
 338    def remove_on_disconnected_handler(self, handler: Callable):
 339        self.on_disconnected_handlers.discard(handler)
 340    
 341    async def _on_disconnected(self):
 342        on_disconnected_handlers_buff = tuple(self.on_disconnected_handlers)
 343        self.on_disconnected_handlers = set()
 344        for index, handler in enumerate(on_disconnected_handlers_buff):
 345            try:
 346                handler()
 347            except:
 348                self.on_disconnected_handlers.update(on_disconnected_handlers_buff[index + 1:])
 349                create_task(self._on_disconnected)
 350                raise
 351
 352
 353class PageItems:
 354    def __init__(self, client: Optional[Client]) -> None:
 355        self.client: Optional[Client] = client
 356        self.items: Set[Any] = set()
 357    
 358    @classmethod
 359    def check_args_factory(cls, signature: Signature, args, kwargs) -> Optional['PageItems']:
 360        client: Optional[Client] = kwargs.get('client', None)
 361        if (client is None) or (not isinstance(client, Client)):
 362            return None
 363        else:
 364            return cls.install(client)
 365    
 366    @classmethod
 367    def install(cls, client: Client) -> 'PageItems':
 368        page_items: PageItems = cls(client)
 369        client.page_items = page_items
 370        return page_items
 371    
 372    def add(self, item: Any):
 373        self.items.add(item)
 374    
 375    def remove(self, item: Any):
 376        self.items.discard(item)
 377    
 378    def __call__(self, item: Any) -> Any:
 379        return self.add(item)
 380
 381
 382async def get_ip_geolocation(ipinfo_io_token, host):
 383    async with IPInfoClient(ipinfo_io_token) as client:
 384        try:
 385            ip_geolocation = await client.ipinfo_dict(host)
 386            if ip_geolocation is not None:
 387                await i(db_request__ip_geolocation.put(host))
 388        except IPInfoError:
 389            ip_geolocation = None
 390        
 391        return ip_geolocation
 392
 393
 394def nicegui_event_handler_func_async_coro(coro_worker: Worker):
 395    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
 396
 397    Args:
 398        coro_worker (Worker): _description_
 399
 400    Raises:
 401        TypeError: _description_
 402
 403    Returns:
 404        _type_: _description_
 405    """    
 406    coro_worker_0 = coro_worker
 407    # async def wrapper(*args, **kwargs):
 408    async def wrapper(*args, **kwargs):
 409        await await_cs_initiated()
 410        coro_worker = coro_worker_0
 411        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
 412        coro_worker_type: CoroType = find_coro_type(coro_worker)
 413        if CoroType.awaitable == coro_worker_type:
 414            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
 415                                             coro_worker_with_args: EntityArgsHolderExplicit):
 416                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
 417                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
 418                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
 419                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
 420                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
 421                return await coro_worker(i, *args, **kwargs)
 422            
 423            coro_wrapper = awaitable_coro_wrapper
 424        elif CoroType.greenlet == coro_worker_type:
 425            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
 426                                      coro_worker_with_args: EntityArgsHolderExplicit):
 427                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
 428                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
 429                pretent_to_be_asyncio_coro(i, current_asyncio_task)
 430                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
 431                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
 432                return coro_worker(i, *args, **kwargs)
 433            
 434            coro_wrapper = greenlet_coro_wrapper
 435        else:
 436            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
 437
 438        current_asyncio_task = asyncio.current_task()
 439        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
 440                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
 441        
 442    coro_worker_sign: Signature = signature(coro_worker)
 443    wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
 444    wrapper.__signature__ = wrapper_signature
 445    
 446    return wrapper
 447
 448
 449async_event_handler_func = nicegui_event_handler_func_async_coro
 450aevent_handler_func = nicegui_event_handler_func_async_coro
 451
 452
 453def nicegui_event_handler_method_async_coro(coro_worker: Worker):
 454    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
 455
 456    Args:
 457        coro_worker (Worker): _description_
 458
 459    Raises:
 460        TypeError: _description_
 461
 462    Returns:
 463        _type_: _description_
 464    """    
 465    coro_worker_0 = coro_worker
 466    # async def wrapper(*args, **kwargs):
 467    async def wrapper(self, *args, **kwargs):
 468        await await_cs_initiated()
 469        coro_worker = coro_worker_0
 470        coro_worker_name: str = coro_worker.__name__
 471        unwrapped_coro_worker_name: str = f'_unwrapped_method__{coro_worker_name}'
 472        if not hasattr(self, unwrapped_coro_worker_name):
 473            bound_coro_worker = coro_worker.__get__(self, self.__class__)
 474            setattr(self, unwrapped_coro_worker_name, bound_coro_worker)
 475
 476        coro_worker = getattr(self, unwrapped_coro_worker_name)
 477        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
 478        coro_worker_type: CoroType = find_coro_type(coro_worker)
 479        if CoroType.awaitable == coro_worker_type:
 480            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
 481                                             coro_worker_with_args: EntityArgsHolderExplicit):
 482                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
 483                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
 484                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
 485                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
 486                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
 487                i.log.debug(f'{entity_repr(coro_worker)}')
 488                return await coro_worker(i, *args, **kwargs)
 489            
 490            coro_wrapper = awaitable_coro_wrapper
 491        elif CoroType.greenlet == coro_worker_type:
 492            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
 493                                      coro_worker_with_args: EntityArgsHolderExplicit):
 494                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
 495                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
 496                pretent_to_be_asyncio_coro(i, current_asyncio_task)
 497                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
 498                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
 499                return coro_worker(i, *args, **kwargs)
 500            
 501            coro_wrapper = greenlet_coro_wrapper
 502        else:
 503            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
 504
 505        current_asyncio_task = asyncio.current_task()
 506        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
 507                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
 508        
 509    coro_worker_sign: Signature = signature(coro_worker)
 510    new_parameters = list(coro_worker_sign.parameters.values())
 511    del new_parameters[1]
 512    wrapper_signature = coro_worker_sign.replace(parameters=tuple(new_parameters), return_annotation=coro_worker_sign.return_annotation)
 513    wrapper.__signature__ = wrapper_signature
 514    
 515    return wrapper
 516
 517
 518async_event_handler_method = nicegui_event_handler_method_async_coro
 519aevent_handler_method = nicegui_event_handler_method_async_coro
 520
 521
 522def get_task_slot_stack(task_id) -> List[Slot]:
 523    if task_id not in slot_stacks:
 524        slot_stacks[task_id] = []
 525    return slot_stacks[task_id]
 526
 527
 528class FakeElementForEvents:
 529    def __init__(self, task_id) -> None:
 530        self.is_ignoring_events: bool = False
 531        self.parent_slot: Union[Slot, nullcontext] = nullcontext()
 532        slot_stack = get_task_slot_stack(task_id)
 533        if slot_stack:
 534            self.parent_slot = slot_stack[-1]
 535
 536
 537@dataclass(**KWONLY_SLOTS)
 538class OnSignInEventArguments(UiEventArguments):
 539    user_id: Hashable
 540    user_sign_in_info: Union[Dict, Tuple, List]
 541
 542
 543@dataclass(**KWONLY_SLOTS)
 544class OnSignOutEventArguments(UiEventArguments):
 545    user_id: Hashable
 546
 547
 548class PageContextBase:
 549    def __init__(self, client: Client, request: FastAPIRequest, _t: NTTE, current_asyncio_task) -> None:
 550        self.client: Client = client
 551        self.request: FastAPIRequest = request
 552        self.client_host_port: Address = self.request.client
 553        self.client_id: Hashable = client.id
 554        self.session_id: Hashable = client.session_id
 555        self.user_id: Hashable = None
 556        self._t: NTTE = _t
 557        self.current_asyncio_task = current_asyncio_task
 558        self.current_asyncio_task_id = id(current_asyncio_task)
 559        self.client_view_type: ClientViewType = self._determine_client_view_type()
 560        self.better_lang: str = None
 561        self.featured_langs: OrderedDict[str, RationalNumber] = None
 562        self.langs: OrderedDict[str, RationalNumber] = None
 563        self.better_lang, self.featured_langs, self.langs = self._determine_client_languages()
 564        self._t.text_translation_language_chooser.lang = self.better_lang
 565    
 566    def _init(self, i: Interface):
 567        self.register_session_page(i)
 568        i(PutCoro, self._arequest_ip_geolocation, self.client_host_port[0])
 569        if self._find_user(i):
 570            i.log.debug(f'{self.session_id}, {self.client_id}: logged in')
 571        else:
 572            i.log.debug(f'{self.session_id}, {self.client_id}: logged out')
 573    
 574    async def _ainit(self, i: Interface):
 575        self.register_session_page(i)
 576        await i(PutCoro, self._arequest_ip_geolocation, self.client_host_port[0])
 577        if await self._afind_user(i):
 578            i.log.debug(f'{self.session_id}, {self.client_id}: logged in')
 579        else:
 580            i.log.debug(f'{self.session_id}, {self.client_id}: logged out')
 581    
 582    async def _arequest_ip_geolocation(self, i: Interface, host: str):
 583        ip_geolocation: Optional[Dict] = None
 584        need_to_request: bool = True
 585        try:
 586            ip_geolocation = await i(db_request__ip_geolocation.get(host))
 587            need_to_request = False
 588        except KeyError:
 589            pass
 590
 591        if need_to_request:
 592            ip_geolocation = await i(AsyncioLoop, AsyncioLoopRequest().wait(get_ip_geolocation(ipinfo_io_token, host)))
 593        
 594        await self.on_ip_geolocation_ready(i, ip_geolocation)
 595    
 596    def register_session_page(self, i: Interface):
 597        session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
 598        if self.session_id not in session_pages:
 599            session_pages[self.session_id] = set()
 600        
 601        session_pages[self.session_id].add(self)
 602    
 603    def _determine_client_view_type(self) -> ClientViewType:
 604        return client_view_type(self.request.headers)
 605    
 606    def _determine_client_languages(self):
 607        parsed_accept_language: Optional[OrderedDict[str, RationalNumber]] = optimize_accept_language(parse_accept_language(self.request.headers))
 608        translation_data: Dict = self._t.text_translator.decoded_data
 609        return match_langs(
 610                translation_data['default_language'], 
 611                set(translation_data['featured_languages']),
 612                set(translation_data['supported_languages']),
 613                translation_data['translation_language_map'],
 614                parsed_accept_language,
 615            )
 616    
 617    def _find_user(self, i: Interface) -> bool:
 618        if self.session_id is None:
 619            return False
 620        
 621        try:
 622            user_id, sign_in_date_time = i(db_request__logged_in_sessions.get(self.session_id))
 623            sign_in_date_time = datetime.fromisoformat(sign_in_date_time)
 624            if (datetime.now() - sign_in_date_time) > signed_in_session_timeout:
 625                i(db_request__logged_in_sessions.delete(self.session_id))
 626                return False
 627            
 628            self.user_id = user_id
 629            return True
 630        except KeyError:
 631            return False
 632    
 633    async def _afind_user(self, i: Interface) -> bool:
 634        if self.session_id is None:
 635            return False
 636        
 637        try:
 638            user_id, sign_in_date_time = await i(db_request__logged_in_sessions.get(self.session_id))
 639            sign_in_date_time = datetime.fromisoformat(sign_in_date_time)
 640            if (datetime.now() - sign_in_date_time) > signed_in_session_timeout:
 641                await i(db_request__logged_in_sessions.delete(self.session_id))
 642                return False
 643            
 644            self.user_id = user_id
 645            return True
 646        except KeyError:
 647            return False
 648    
 649    # def find_user(self, i: Interface, client_connection_check_interval: RationalNumber = 0.01) -> bool:
 650    #     if not self.client.has_socket_connection:
 651    #         if not self.client.is_waiting_for_connection:
 652    #             i(AsyncioLoop, AsyncioLoopRequest().wait(self.client.connected()))
 653        
 654    #     while self.client.is_waiting_for_connection:
 655    #         i(Sleep, client_connection_check_interval)
 656        
 657    #     return self._find_user(i)
 658    
 659    # async def afind_user(self, i: Interface, client_connection_check_interval: RationalNumber = 0.01) -> bool:
 660    #     if not self.client.has_socket_connection:
 661    #         if not self.client.is_waiting_for_connection:
 662    #             await i(AsyncioLoop, AsyncioLoopRequest().wait(self.client.connected()))
 663        
 664    #     while self.client.is_waiting_for_connection:
 665    #         await i(Sleep, client_connection_check_interval)
 666        
 667    #     return await self._afind_user(i)
 668    
 669    def try_sign_in(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float]) -> Optional[Union[Dict, Tuple, List]]:
 670        user_data: Union[Dict, Tuple, List] = None
 671
 672        try:
 673            user_data = i(db_request__user_sign_in_info.get(credentials))
 674        except KeyError:
 675            pass
 676        
 677        if user_data is None:
 678            return None
 679        
 680        user_id = user_data[0]
 681        if self.session_id is None:
 682            i(db_request__logged_in_clients.put(self.client_id, (user_id, datetime.now())))
 683            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
 684            with user_clients_lock:
 685                try:
 686                    user_clients: Set[Hashable] = set(i(db_request__user_clients.get(user_id)))
 687                except KeyError:
 688                    user_clients = set()
 689                
 690                user_clients.add(self.client_id)
 691                i(db_request__user_sessions.put(user_id, user_clients))
 692            
 693            i(PutCoro, self._on_signed_in, user_id)
 694        else:
 695            i(db_request__logged_in_sessions.put(self.session_id, (user_id, datetime.now())))
 696            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
 697            with user_sessions_lock:
 698                try:
 699                    user_sessions: Set[Hashable] = set(i(db_request__user_sessions.get(user_id)))
 700                except KeyError:
 701                    user_sessions = set()
 702                
 703                user_sessions.add(self.session_id)
 704                i(db_request__user_sessions.put(user_id, user_sessions))
 705            
 706            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
 707            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
 708            for session_page in current_session_pages:
 709                i(PutCoro, session_page._on_signed_in, user_id, user_data)
 710        
 711        return user_data
 712    
 713    async def atry_sign_in(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float]) -> Optional[Union[Dict, Tuple, List]]:
 714        user_data: Union[Dict, Tuple, List] = None
 715
 716        try:
 717            user_data = await i(db_request__user_sign_in_info.get(credentials))
 718        except KeyError:
 719            pass
 720        
 721        if user_data is None:
 722            return None
 723        
 724        user_id = user_data[0]
 725        if self.session_id is None:
 726            await i(db_request__logged_in_clients.put(self.client_id, (user_id, datetime.now())))
 727            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
 728            async with user_clients_lock:
 729                try:
 730                    user_clients: Set[Hashable] = set(await i(db_request__user_clients.get(user_id)))
 731                except KeyError:
 732                    user_clients = set()
 733                
 734                user_clients.add(self.client_id)
 735                await i(db_request__user_sessions.put(user_id, user_clients))
 736            
 737            await i(PutCoro, self._on_signed_in, user_id)
 738        else:
 739            await i(db_request__logged_in_sessions.put(self.session_id, (user_id, datetime.now())))
 740            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
 741            async with user_sessions_lock:
 742                try:
 743                    user_sessions: Set[Hashable] = set(await i(db_request__user_sessions.get(user_id)))
 744                except KeyError:
 745                    user_sessions = set()
 746                
 747                user_sessions.add(self.session_id)
 748                await i(db_request__user_sessions.put(user_id, user_sessions))
 749            
 750            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
 751            current_session_pages: Set[PageContextBase] = set(session_pages.get(self.session_id, set()))  # TODO: robust fix needed for `RuntimeError: Set changed size during iteration`
 752            for session_page in current_session_pages:
 753                await i(PutCoro, session_page._on_signed_in, user_id, user_data)
 754        
 755        return user_data
 756    
 757    def user_signed_up(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float],
 758                              user_sign_in_info: Union[Dict, Tuple, List]):
 759        i(db_request__user_sign_in_info.put(credentials, user_sign_in_info))
 760    
 761    async def auser_signed_up(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float],
 762                              user_sign_in_info: Union[Dict, Tuple, List]):
 763        await i(db_request__user_sign_in_info.put(credentials, user_sign_in_info))
 764
 765    def sign_out(self, i: Interface) -> bool:
 766        user_id = self.user_id
 767        if user_id is None:
 768            return False
 769        
 770        if self.session_id is None:
 771            try:
 772                i(db_request__logged_in_clients.delete(self.client_id))
 773            except KeyError:
 774                pass
 775
 776            i(PutCoro, self._on_signed_out)
 777            return True
 778        else:
 779            try:
 780                i(db_request__logged_in_sessions.delete(self.session_id))
 781            except KeyError:
 782                pass
 783
 784            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
 785            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
 786            for session_page in current_session_pages:
 787                i(PutCoro, session_page._on_signed_out)
 788
 789            return True
 790
 791    async def asign_out(self, i: Interface) -> bool:
 792        user_id = self.user_id
 793        if user_id is None:
 794            return False
 795        
 796        if self.session_id is None:
 797            try:
 798                await i(db_request__logged_in_clients.delete(self.client_id))
 799            except KeyError:
 800                pass
 801
 802            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
 803            async with user_clients_lock:
 804                try:
 805                    user_clients: Set[Hashable] = set(await i(db_request__user_clients.get(user_id)))
 806                except KeyError:
 807                    user_clients = set()
 808                
 809                user_clients.discard(self.client_id)
 810                await i(db_request__user_sessions.put(user_id, user_clients))
 811
 812            await i(PutCoro, self._on_signed_out)
 813            return True
 814        else:
 815            try:
 816                await i(db_request__logged_in_sessions.delete(self.session_id))
 817            except KeyError:
 818                pass
 819
 820            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
 821            async with user_sessions_lock:
 822                try:
 823                    user_sessions: Set[Hashable] = set(await i(db_request__user_sessions.get(user_id)))
 824                except KeyError:
 825                    user_sessions = set()
 826                
 827                user_sessions.discard(self.session_id)
 828                await i(db_request__user_sessions.put(user_id, user_sessions))
 829
 830            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
 831            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
 832            for session_page in current_session_pages:
 833                await i(PutCoro, session_page._on_signed_out)
 834
 835            return True
 836
 837    def try_sign_up__login_password(self, i: Interface, login: str, password: str, 
 838                                    user_sign_in_info_maker: Callable[[Interface, str, Hashable, datetime], Union[Dict, Tuple, List]]) -> bool:
 839        login_exists: bool = False
 840        try:
 841            i(db_request__user_sign_up_info.get(login))
 842            login_exists = True
 843        except KeyError:
 844            pass
 845
 846        if login_exists:
 847            return False
 848        
 849        password_bytes = password.encode('utf-8')
 850        salt = os.urandom(16)
 851        key_length = 64
 852        N = 16384  # CPU/memory cost factor
 853        r = 8     # Block size
 854        p = 1     # Parallelization factor
 855        dk = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
 856        i(db_request__user_sign_up_info.put(login, (dk, salt, N, r, p, key_length)))
 857        user_id: Hashable = uuid4()
 858        credentials: Tuple = (login, dk)
 859        i(db_request__credentials_by_user_id.put(user_id, credentials))
 860        user_sign_in_info: Union[Dict, Tuple, List] = run_coro_fast(i, user_sign_in_info_maker, login, user_id, datetime.now())
 861        self.user_signed_up(i, credentials, user_sign_in_info)
 862        return True
 863
 864    async def atry_sign_up__login_password(self, i: Interface, login: str, password: str, 
 865                                           user_sign_in_info_maker: Callable[[Interface, str, Hashable, datetime], Union[Dict, Tuple, List]]) -> bool:
 866        login_exists: bool = False
 867        try:
 868            await i(db_request__user_sign_up_info.get(login))
 869            login_exists = True
 870        except KeyError:
 871            pass
 872
 873        if login_exists:
 874            return False
 875        
 876        password_bytes = password.encode('utf-8')
 877        salt = os.urandom(16)
 878        key_length = 64
 879        N = 16384  # CPU/memory cost factor
 880        r = 8     # Block size
 881        p = 1     # Parallelization factor
 882        dk = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
 883        await i(db_request__user_sign_up_info.put(login, (dk, salt, N, r, p, key_length)))
 884        user_id: Hashable = uuid4().bytes
 885        credentials: Tuple = (login, dk)
 886        await i(db_request__credentials_by_user_id.put(user_id, credentials))
 887        user_sign_in_info: Union[Dict, Tuple, List] = await arun_coro_fast(i, user_sign_in_info_maker, login, user_id, datetime.now())
 888        await self.auser_signed_up(i, credentials, user_sign_in_info)
 889        return True
 890
 891    def try_sign_in__login_password(self, i: Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
 892        user_found: bool = False
 893        try:
 894            dk, salt, N, r, p, key_length = i(db_request__user_sign_up_info.get(login))
 895            user_found = True
 896        except KeyError:
 897            pass
 898
 899        if not user_found:
 900            raise UserNotFoundError
 901
 902        password_bytes = password.encode('utf-8')
 903        dk_0 = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
 904        if dk != dk_0:
 905            raise PasswordIsNotCorrectError
 906        
 907        credentials: Tuple = (login, dk)
 908        return self.try_sign_in(i, credentials)
 909
 910    async def atry_sign_in__login_password(self, i: Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
 911        user_found: bool = False
 912        try:
 913            dk, salt, N, r, p, key_length = await i(db_request__user_sign_up_info.get(login))
 914            user_found = True
 915        except KeyError:
 916            pass
 917
 918        if not user_found:
 919            raise UserNotFoundError
 920
 921        password_bytes = password.encode('utf-8')
 922        dk_0 = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
 923        if dk != dk_0:
 924            raise PasswordIsNotCorrectError
 925        
 926        credentials: Tuple = (login, dk)
 927        return await self.atry_sign_in(i, credentials)
 928
 929    def try_sign_up__email_password(self, i: Interface, email: str, password: str) -> bool:
 930        raise NotImplementedError
 931
 932    async def atry_sign_up__email_password(self, i: Interface, email: str, password: str) -> bool:
 933        raise NotImplementedError
 934
 935    async def _on_signed_in(self, i: Interface, user_id: Hashable, user_sign_in_info: Union[Dict, Tuple, List]) -> None:
 936        self.user_id = user_id
 937        handle_event(self._on_signed_in_impl, OnSignInEventArguments(sender=FakeElementForEvents(self.current_asyncio_task_id), client=self.client, user_id=user_id, user_sign_in_info=user_sign_in_info))
 938
 939    @aevent_handler_method
 940    async def _on_signed_in_impl(self, i: Interface, arguments: OnSignInEventArguments) -> None:
 941        return await self.on_signed_in(i, arguments.user_id, arguments.user_sign_in_info)
 942
 943    async def on_signed_in(self, i: Interface, user_id: Hashable, user_sign_in_info: Union[Dict, Tuple, List]) -> None:
 944        pass
 945
 946    async def _on_signed_out(self, i: Interface) -> None:
 947        user_id = self.user_id
 948        self.user_id = None
 949        handle_event(self._on_signed_out_impl, OnSignOutEventArguments(sender=FakeElementForEvents(self.current_asyncio_task_id), client=self.client, user_id=user_id))
 950
 951    @aevent_handler_method
 952    async def _on_signed_out_impl(self, i: Interface, arguments: OnSignOutEventArguments) -> None:
 953        return await self.on_signed_out(i, arguments.user_id)
 954
 955    async def on_signed_out(self, i: Interface, user_id: Hashable) -> None:
 956        pass
 957
 958    async def on_ip_geolocation_ready(self, i: Interface, ip_geolocation: Optional[Dict]) -> None:
 959        pass
 960
 961    def refferer(self) -> Optional[str]:
 962        return self.request.headers.get('referer', None)
 963
 964    def url(self) -> URL:
 965        return self.request.url
 966
 967    def open_page(self, target: Optional[Union[Callable[..., Any], str]] = None, new_tab: bool = False) -> Optional[RedirectResponse]:
 968        if target is None:
 969            return
 970        
 971        client = self.client
 972        if client.has_socket_connection:
 973            client.open(target, new_tab)
 974        else:
 975            return RedirectResponse(target)
 976
 977    def open_previous_page(self, default_target: Optional[Union[Callable[..., Any], str]] = None, new_tab: bool = False, can_be_current_page: bool = False) -> Optional[RedirectResponse]:
 978        target: str = self.refferer()
 979        current_url: str = str(self.url())
 980        if (target is None) or (False if can_be_current_page else (target == current_url)):
 981            if not isinstance(default_target, str):
 982                default_target = globals.page_routes[target]
 983            
 984            target = default_target if default_target is not None else '/'
 985        
 986        client = self.client
 987        if client.has_socket_connection:
 988            client.open(target, new_tab)
 989        else:
 990            return self.open_previous_page_http_response(default_target, can_be_current_page)
 991
 992    def open_previous_page_http_response(self, default_target: Optional[Union[Callable[..., Any], str]] = None, can_be_current_page: bool = False) -> RedirectResponse:
 993        refferer: str = self.refferer()
 994        current_url: str = str(self.url())
 995        if (refferer is None) or (False if can_be_current_page else (refferer == current_url)):
 996            refferer = default_target if default_target is not None else '/'
 997        
 998        return RedirectResponse(refferer)
 999
1000
1001def nicegui_page_sync_coro(*dargs, **dkwargs):
1002    """Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function.
1003    Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible.
1004    Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details
1005
1006    Returns:
1007        _type_: _description_
1008    """    
1009    dargs_0 = dargs
1010    dkwargs_0 = dkwargs
1011    def nicegui_page_sync_coro_impl(coro_worker: Worker):
1012        coro_worker_0 = coro_worker
1013        dargs_1 = dargs_0
1014        dkwargs_1 = dkwargs_0
1015        def wrapper(*args, **kwargs):
1016            coro_worker = coro_worker_0
1017            dargs = dargs_1
1018            dkwargs = dkwargs_1
1019            sync_coro_decorator = sync_coro_param(*dargs, **dkwargs)
1020            sync_coro_decorator_wrapper = sync_coro_decorator(coro_worker)
1021            ClientHandlers.check_args_factory(wrapper_signature, args, kwargs)
1022            PageItems.check_args_factory(wrapper_signature, args, kwargs)
1023            return sync_coro_decorator_wrapper(*args, **kwargs)
1024            
1025        coro_worker_sign: Signature = signature(coro_worker)
1026        wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
1027        wrapper.__signature__ = wrapper_signature
1028        
1029        return wrapper
1030    return nicegui_page_sync_coro_impl
1031
1032
1033sync_like_page = nicegui_page_sync_coro
1034sl_page = nicegui_page_sync_coro
1035
1036
1037async def await_cs_initiated():
1038    while (not hasattr(app, 'cs_initiated')) or (not app.cs_initiated):
1039        await asyncio.sleep(0.01)
1040
1041
1042async def apretent_to_be_asyncio_coro(i: Interface, asyncio_coro):
1043    reinterpret_cast(CoroWrapperNiceGuiPageAsyncAwait, i._coro)
1044    i._coro.current_asyncio_task = asyncio_coro
1045    i._coro.reinit_for_nicegui()
1046    await i(Yield)
1047
1048
1049def pretent_to_be_asyncio_coro(i: Interface, asyncio_coro):
1050    reinterpret_cast(CoroWrapperNiceGuiPageGreenlet, i._coro)
1051    i._coro.current_asyncio_task = asyncio_coro
1052    i._coro.reinit_for_nicegui()
1053    i(Yield)
1054
1055
1056def nicegui_page_async_coro_impl(page_class: Optional[Union[PageContextBase, EntityArgsHolder]], coro_worker: Worker):
1057    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
1058
1059    Args:
1060        coro_worker (Worker): _description_
1061
1062    Raises:
1063        TypeError: _description_
1064
1065    Returns:
1066        _type_: _description_
1067    """    
1068    coro_worker_0 = coro_worker
1069    page_class_0 = page_class
1070    # async def wrapper(*args, **kwargs):
1071    async def wrapper(request: FastAPIRequest, client: Client):
1072        await await_cs_initiated()
1073        request.app.cs.logger.debug(f'nicegui_page_async_coro_impl.wrapper - start: {request.url}')
1074        client_id = client.id
1075        session_id = request.session.get('id', None)
1076        client.session_id = session_id
1077        if session_id not in session_clients:
1078            session_clients[session_id] = set()
1079        
1080        session_clients[session_id].add(client_id)
1081
1082        if session_id is None:
1083            translatable_text_element: NiceguiTranslatableTextElement = create_translatable_text_element(text_translator, translation_language_mapper)
1084            # translatable_text_element: NiceguiTranslatableTextElement = create_translatable_text_element(
1085            #         await asyncio_coro(cs_acoro(afast_wait))('text_translator'), await asyncio_coro(cs_acoro(afast_wait))('translation_language_mapper')
1086            #     )
1087            translatable_text_element_per_client[client_id] = translatable_text_element
1088        else:
1089            if session_id in translatable_text_element_per_session:
1090                translatable_text_element = translatable_text_element_per_session[session_id]
1091            else:
1092                translatable_text_element: NiceguiTranslatableTextElement = create_translatable_text_element(text_translator, translation_language_mapper)
1093                # translatable_text_element: NiceguiTranslatableTextElement = create_translatable_text_element(
1094                #         await asyncio_coro(cs_acoro(afast_wait))('text_translator'), await asyncio_coro(cs_acoro(afast_wait))('translation_language_mapper')
1095                #     )
1096                translatable_text_element_per_session[session_id] = translatable_text_element
1097        
1098        client.translatable_text_element = translatable_text_element
1099        await asyncio_coro(cs_acoro(translatable_text_element.aregister_on_lang_changed_handler))()
1100        coro_worker = coro_worker_0
1101        coro_worker_type: CoroType = find_coro_type(coro_worker)
1102        if CoroType.awaitable == coro_worker_type:
1103            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, wrapper_signature, 
1104                                             coro_worker_with_args: EntityArgsHolderExplicit, page_context: PageContextBase):
1105                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
1106                i.log.debug(f'awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
1107                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
1108                ClientHandlers.check_args_factory(wrapper_signature, args, kwargs)
1109                PageItems.check_args_factory(wrapper_signature, args, kwargs)
1110                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
1111                await page_context._ainit(i)
1112                i.log.debug(f'awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
1113                return await coro_worker(i, *args, **kwargs)
1114            
1115            coro_wrapper = awaitable_coro_wrapper
1116        elif CoroType.greenlet == coro_worker_type:
1117            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, wrapper_signature, 
1118                                      coro_worker_with_args: EntityArgsHolderExplicit, page_context: PageContextBase):
1119                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
1120                i.log.debug(f'greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
1121                pretent_to_be_asyncio_coro(i, current_asyncio_task)
1122                ClientHandlers.check_args_factory(wrapper_signature, args, kwargs)
1123                PageItems.check_args_factory(wrapper_signature, args, kwargs)
1124                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
1125                page_context._init(i)
1126                i.log.debug(f'greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
1127                return coro_worker(i, *args, **kwargs)
1128            
1129            coro_wrapper = greenlet_coro_wrapper
1130        else:
1131            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
1132
1133        current_asyncio_task = asyncio.current_task()
1134        # args_kwargs: Optional[Tuple[Tuple, Dict]] = dict().get('args_kwargs', None)
1135        # if args_kwargs is None:
1136        #     args_kwargs = (tuple(), dict())
1137        
1138        args = tuple()
1139        kwargs = dict()
1140        coro_worker_param_names_set: Set[str] = set(inspect.signature(coro_worker).parameters.keys())
1141        if 'client' in coro_worker_param_names_set:
1142            kwargs['client'] = client
1143
1144        if 'request' in coro_worker_param_names_set:
1145            kwargs['request'] = request
1146        
1147        translation_params: Set[str] = known_translation_param_names & coro_worker_param_names_set
1148        if translation_params:
1149            for translation_param in translation_params:
1150                kwargs[translation_param] = translatable_text_element
1151        
1152        page_context_params: Set[str] = known_page_context_param_names & coro_worker_param_names_set
1153        if page_context_params:
1154            page_class = page_class_0
1155            if isclass(page_class):
1156                if issubclass(page_class, PageContextBase):
1157                    page_context: PageContextBase = page_class(client, request, translatable_text_element, current_asyncio_task)
1158                else:
1159                    raise TypeError(f'{page_class} is not a subclass of PageContextBase')
1160            elif isinstance(page_class, EntityArgsHolder):
1161                page_class = cast(EntityArgsHolder, page_class)
1162                page_class, page_class_own_args, page_class_own_kwargs = page_class.entity_args_kwargs()
1163                page_class_own_kwargs.update({
1164                    'client': client,
1165                    'request': request,
1166                    '_t': translatable_text_element,
1167                    'current_asyncio_task': current_asyncio_task,
1168                })
1169                page_context = page_class(*page_class_own_args, **page_class_own_kwargs)
1170            else:
1171                raise TypeError(f'{page_class} is not a subclass of PageContextBase nor an EntityArgsHolder')
1172
1173            for page_context_param in page_context_params:
1174                kwargs[page_context_param] = page_context
1175
1176        return await await_coro_prim(coro_wrapper, current_asyncio_task, wrapper_signature, 
1177                                     EntityArgsHolderExplicit(coro_worker, args, kwargs), page_context)
1178        
1179    wrapper_sign: Signature = signature(wrapper)
1180    coro_worker_sign: Signature = signature(coro_worker)
1181    # wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
1182    wrapper_signature = coro_worker_sign.replace(parameters=wrapper_sign.parameters.values(), return_annotation=coro_worker_sign.return_annotation)
1183    wrapper.__signature__ = wrapper_signature
1184    
1185    return wrapper
1186
1187
1188nicegui_page_async_coro = partial(nicegui_page_async_coro_impl, None)
1189
1190
1191async_page = nicegui_page_async_coro
1192apage = nicegui_page_async_coro
1193
1194
1195def nicegui_page_class_async_coro(page_class: Optional[Union[PageContextBase, EntityArgsHolder]] = PageContextBase):
1196    return partial(nicegui_page_async_coro_impl, page_class)
1197
1198
1199async_page_class = nicegui_page_class_async_coro
1200apage_class = nicegui_page_class_async_coro
1201
1202
1203async def init_cs(is_fast_loop: bool = True, main_coro: AnyWorker = None, app_args_kwargs = None):
1204    async def coro(i: Interface, main_coro: AnyWorker = None, app_args_kwargs = None):
1205        set_primary_coro_scheduler(i._loop)
1206        app.cs = i._loop
1207        app.cs_initiated = False
1208        app_name: str = app_args_kwargs[1]['app_name']
1209        app_name_for_fs: str = app_args_kwargs[1]['app_name_for_fs']
1210        app_version: str = app_args_kwargs[1]['app_version']
1211        app_version_str: str = app_args_kwargs[1]['app_version_str']
1212
1213        await i(Instance, InstanceRequest().set('app_name', app_name))
1214        await i(Instance, InstanceRequest().set('app_name_for_fs', app_name_for_fs))
1215        await i(Instance, InstanceRequest().set('app_version', app_version))
1216        await i(Instance, InstanceRequest().set('app_version_str', app_version_str))
1217
1218        i.log.setLevel(logging.INFO)
1219        await i(LogRequest().sync())
1220        i.log.debug('cengal.nicegui.init_cs - start')
1221        await i(AsyncioLoop, AsyncioLoopRequest().inherit_surrounding_loop())
1222        await i(AsyncioLoop, AsyncioLoopRequest().turn_on_loops_intercommunication(True))
1223        await i(ShutdownOnKeyboardInterrupt)
1224        await i(Instance, InstanceRequest().set('cengal_nicegui__app', app))
1225        await i(Instance, InstanceRequest().set('cengal_nicegui__app_args_kwargs', app_args_kwargs))
1226
1227        db_env_info: EnvInfo = await i(DbRequest().open_db_environment(db_env_id, None, False, max_dbs=20))
1228        await i(DbRequest(env_id=db_env_id).open_databases({
1229            'logged_in_clients',
1230            'user_clients',
1231            'logged_in_sessions',
1232            'user_sessions',
1233            'ip_geolocation',
1234            'user_sign_in_info',
1235            'credentials_by_user_id',
1236            'user_sign_up_info',
1237        }))
1238        await i(Instance, InstanceRequest().set('cengal_nicegui__db_env_info', db_env_info))
1239        await i(Instance, InstanceRequest().set('cengal_nicegui__session_pages', dict()))
1240        await i(Instance, InstanceRequest().set('cengal_nicegui__user_clients_lock', Lock(f'cengal_nicegui__user_clients_lock__{uuid4()}')))
1241        await i(Instance, InstanceRequest().set('cengal_nicegui__user_sessions_lock', Lock(f'cengal_nicegui__user_sessions_lock__{uuid4()}')))
1242
1243        # await init_translation(i, app_args_kwargs)
1244        if main_coro is not None:
1245            await i(PutCoro, main_coro, app_args_kwargs)
1246        
1247        app.cs_initiated = True
1248        i.log.debug('cengal.nicegui.init_cs - ready')
1249        await i(Instance, InstanceRequest().set(CS_PREPARED_FLAG, True))
1250        try:
1251            await i(AsyncEventBus, AsyncEventBusRequest().wait(DESTROY_CS_EVENT))
1252        finally:
1253            await i(DbRequest().close_db_environment(db_env_id))
1254            i.log.debug('cengal.nicegui.init_cs - shutting down loop')
1255            await i(ShutdownLoop)
1256
1257    if is_fast_loop:
1258        await arun_in_fast_loop(coro, main_coro, app_args_kwargs)
1259    else:
1260        await arun_in_loop(coro, main_coro, app_args_kwargs)
1261
1262
1263# # Async on_destroy handling is currently (08 Feb 2023) broken in NiceGUI: 
1264# #     they should await for handlers instead of current tasks creation approach
1265# async def destroy_cs():
1266#     async def coro(i: Interface):
1267#         await i(AsyncEventBus, AsyncEventBusRequest().send_event(DESTROY_CS_EVENT, None))
1268#         await i(Sleep, CS_DESTROY_TIMEOUT)
1269
1270#     await await_coro_prim(coro)
1271
1272
1273# Async on_destroy handling is currently (08 Feb 2023) broken in NiceGUI: 
1274#     they should await for handlers instead of current tasks creation approach
1275def destroy_cs():
1276    cs: CoroSchedulerType = get_available_coro_scheduler()
1277    cs_destroy_timeouted: bool = False
1278    if cs and (not cs._destroyed):
1279        async def coro(i: Interface):
1280            await i(AsyncEventBus, AsyncEventBusRequest().send_event(DESTROY_CS_EVENT, None))
1281
1282        put_coro_to(get_interface_and_loop_with_explicit_loop(cs), coro)
1283        start_time = perf_counter()
1284        while cs.iteration():
1285            if (perf_counter() - start_time) >= CS_DESTROY_TIMEOUT:
1286                cs_destroy_timeouted = True
1287                break
1288
1289    if cs_destroy_timeouted:
1290        warnings.warn(f'destroy_cs - not finished within timeout of {CS_DESTROY_TIMEOUT} sec.')
1291
1292
1293@asyncio_coro
1294async def on_disconnect_handler(i: Interface, client: Client):
1295    client_id = client.id
1296    if hasattr(client, 'session_id'):
1297        session_id = client.session_id
1298    else:
1299        session_id = None
1300    
1301    if session_id is None:
1302        translatable_text_element: NiceguiTranslatableTextElement = translatable_text_element_per_client.pop(client_id, None)
1303    else:
1304        translatable_text_element = translatable_text_element_per_session.pop(session_id, None)
1305    
1306    if hasattr(client, 'translatable_text_element'):
1307        if translatable_text_element is None:
1308            translatable_text_element = client.translatable_text_element
1309        
1310        delattr(client, 'translatable_text_element')
1311
1312    if translatable_text_element is not None:
1313        await translatable_text_element.aremove_on_lang_changed_handler()
1314    
1315    if session_id is not None:
1316        session_clients[session_id].discard(client_id)
1317        if not session_clients[session_id]:
1318            session_clients.pop(session_id, None)
1319
1320
1321async def init_translation(i: Interface, app_args_kwargs: Tuple[Tuple, Dict]):
1322    await i(ShutdownOnKeyboardInterrupt)
1323    app_dir_path: AppDirPath = await i(Instance, InstanceRequest().get(AppDirPath))
1324    app_data_dir_path_type: str = await i(Instance, InstanceRequest().get('app_data_dir_path_type'))
1325    app_name_for_fs: str = await i(Instance, InstanceRequest().get('app_name_for_fs'))
1326    app_name_for_fs = app_name_for_fs if app_name_for_fs else app_args_kwargs[1]['app_name_for_fs']
1327    app_data_dir_path_rel: RelativePath = RelativePath(app_dir_path(app_data_dir_path_type, app_name_for_fs))
1328    global text_translator
1329    global translation_language_mapper
1330    text_translator, translation_language_mapper = setup_translation(app_data_dir_path_rel('text_dictionary.json'))
1331    await i(Instance, InstanceRequest().set('cengal_nicegui__text_translator', text_translator))
1332    await i(Instance, InstanceRequest().set('cengal_nicegui__translation_language_mapper', translation_language_mapper))
1333
1334
1335def run(*,
1336        host: str = '0.0.0.0',
1337        port_or_range: Union[int, slice, Tuple[int, int]] = 8080,
1338        main_coro: AnyWorker = None,
1339        is_fast_loop: bool = True,
1340        app_name: str = str(),
1341        app_name_for_fs: str = str(),
1342        app_version: Tuple[int, int, int, Union[int, str]] = tuple(),
1343        app_version_str: str = str(),
1344        **kwargs
1345    ) -> None:
1346    """Prepares and starts NiceGUI. Saves initial args and kwargs parameters (as well as determined free port) into a tuple (Tuple[Tuple, Dict]) within an Instance service awailable by a 'cengal_nicegui__app_args_kwargs' string key 
1347
1348    Args:
1349        host (str, optional): _description_. Defaults to '0.0.0.0'.
1350        port_or_range (Union[int, slice, Tuple[int, int]], optional): _description_. Defaults to 8080.
1351        title (str, optional): _description_. Defaults to 'NiceGUI'.
1352        viewport (str, optional): _description_. Defaults to 'width=device-width, initial-scale=1'.
1353        favicon (Optional[str], optional): _description_. Defaults to None.
1354        dark (Optional[bool], optional): _description_. Defaults to False.
1355        binding_refresh_interval (float, optional): _description_. Defaults to 0.1.
1356        show (bool, optional): _description_. Defaults to True.
1357        reload (bool, optional): _description_. Defaults to True.
1358        uvicorn_logging_level (str, optional): _description_. Defaults to 'warning'.
1359        uvicorn_reload_dirs (str, optional): _description_. Defaults to '.'.
1360        uvicorn_reload_includes (str, optional): _description_. Defaults to '*.py'.
1361        uvicorn_reload_excludes (str, optional): _description_. Defaults to '.*, .py[cod], .sw.*, ~*'.
1362        tailwind (bool, optional): _description_. Defaults to True.
1363        is_fast_loop (bool, optional): _description_. Defaults to True.
1364        main_coro (AnyWorker, optional): _description_. Defaults to None.
1365    """
1366    port = simple_port_search(host, port_or_range)
1367    app_args_kwargs: Tuple[Tuple, Dict] = args_kwargs(
1368        host=host, 
1369        port_or_range=port_or_range, 
1370        port=port, 
1371        main_coro=main_coro,
1372        is_fast_loop=is_fast_loop,
1373        app_name=app_name,
1374        app_name_for_fs=app_name_for_fs,
1375        app_version=app_version,
1376        app_version_str=app_version_str,
1377        **kwargs,
1378    )
1379    run_in_loop(init_translation, app_args_kwargs)
1380    app.on_startup(init_cs(is_fast_loop, main_coro, app_args_kwargs))
1381    app.on_shutdown(destroy_cs)
1382    # app.on_connect(on_connect_handler)
1383    app.on_disconnect(on_disconnect_handler)
1384    ui.run(
1385        host=host,
1386        port=port,
1387        **kwargs,
1388        )
class ClientHandlers:
289class ClientHandlers:
290    def __init__(self, client: Optional[Client]) -> None:
291        self.client: Optional[Client] = client
292        self.on_connected_handlers: Set[Callable] = set()
293        self.on_disconnected_handlers: Set[Callable] = set()
294        self.is_connected: bool = False
295        self.is_disconnected: bool = False
296    
297    @classmethod
298    def check_args_factory(cls, signature: Signature, args, kwargs) -> Optional['ClientHandlers']:
299        client: Optional[Client] = kwargs.get('client', None)
300        if (client is None) or (not isinstance(client, Client)):
301            return None
302        else:
303            return cls.install(client)
304    
305    @classmethod
306    def install(cls, client: Client) -> 'ClientHandlers':
307        client_handlers: ClientHandlers = cls(client)
308        client.handlers = client_handlers
309        client.on_connect(client_handlers._on_connected())
310        client.on_disconnect(client_handlers._on_disconnected())
311        return client_handlers
312    
313    def add_on_connected_handler(self, handler: Callable):
314        if self.is_connected:
315            handler()
316        else:
317            self.on_connected_handlers.add(handler)
318    
319    def remove_on_connected_handler(self, handler: Callable):
320        self.on_connected_handlers.discard(handler)
321    
322    async def _on_connected(self):
323        on_connected_handlers_buff = tuple(self.on_connected_handlers)
324        self.on_connected_handlers = set()
325        for index, handler in enumerate(on_connected_handlers_buff):
326            try:
327                handler()
328            except:
329                self.on_connected_handlers.update(on_connected_handlers_buff[index + 1:])
330                create_task(self._on_connected)
331                raise
332    
333    def add_on_disconnected_handler(self, handler: Callable):
334        if self.is_disconnected:
335            handler()
336        else:
337            self.on_disconnected_handlers.add(handler)
338    
339    def remove_on_disconnected_handler(self, handler: Callable):
340        self.on_disconnected_handlers.discard(handler)
341    
342    async def _on_disconnected(self):
343        on_disconnected_handlers_buff = tuple(self.on_disconnected_handlers)
344        self.on_disconnected_handlers = set()
345        for index, handler in enumerate(on_disconnected_handlers_buff):
346            try:
347                handler()
348            except:
349                self.on_disconnected_handlers.update(on_disconnected_handlers_buff[index + 1:])
350                create_task(self._on_disconnected)
351                raise
ClientHandlers(client: typing.Union[nicegui.client.Client, NoneType])
290    def __init__(self, client: Optional[Client]) -> None:
291        self.client: Optional[Client] = client
292        self.on_connected_handlers: Set[Callable] = set()
293        self.on_disconnected_handlers: Set[Callable] = set()
294        self.is_connected: bool = False
295        self.is_disconnected: bool = False
client: Union[nicegui.client.Client, NoneType]
on_connected_handlers: Set[Callable]
on_disconnected_handlers: Set[Callable]
is_connected: bool
is_disconnected: bool
@classmethod
def check_args_factory( cls, signature: inspect.Signature, args, kwargs) -> Union[ClientHandlers, NoneType]:
297    @classmethod
298    def check_args_factory(cls, signature: Signature, args, kwargs) -> Optional['ClientHandlers']:
299        client: Optional[Client] = kwargs.get('client', None)
300        if (client is None) or (not isinstance(client, Client)):
301            return None
302        else:
303            return cls.install(client)
@classmethod
def install( cls, client: nicegui.client.Client) -> ClientHandlers:
305    @classmethod
306    def install(cls, client: Client) -> 'ClientHandlers':
307        client_handlers: ClientHandlers = cls(client)
308        client.handlers = client_handlers
309        client.on_connect(client_handlers._on_connected())
310        client.on_disconnect(client_handlers._on_disconnected())
311        return client_handlers
def add_on_connected_handler(self, handler: typing.Callable):
313    def add_on_connected_handler(self, handler: Callable):
314        if self.is_connected:
315            handler()
316        else:
317            self.on_connected_handlers.add(handler)
def remove_on_connected_handler(self, handler: typing.Callable):
319    def remove_on_connected_handler(self, handler: Callable):
320        self.on_connected_handlers.discard(handler)
def add_on_disconnected_handler(self, handler: typing.Callable):
333    def add_on_disconnected_handler(self, handler: Callable):
334        if self.is_disconnected:
335            handler()
336        else:
337            self.on_disconnected_handlers.add(handler)
def remove_on_disconnected_handler(self, handler: typing.Callable):
339    def remove_on_disconnected_handler(self, handler: Callable):
340        self.on_disconnected_handlers.discard(handler)
class PageItems:
354class PageItems:
355    def __init__(self, client: Optional[Client]) -> None:
356        self.client: Optional[Client] = client
357        self.items: Set[Any] = set()
358    
359    @classmethod
360    def check_args_factory(cls, signature: Signature, args, kwargs) -> Optional['PageItems']:
361        client: Optional[Client] = kwargs.get('client', None)
362        if (client is None) or (not isinstance(client, Client)):
363            return None
364        else:
365            return cls.install(client)
366    
367    @classmethod
368    def install(cls, client: Client) -> 'PageItems':
369        page_items: PageItems = cls(client)
370        client.page_items = page_items
371        return page_items
372    
373    def add(self, item: Any):
374        self.items.add(item)
375    
376    def remove(self, item: Any):
377        self.items.discard(item)
378    
379    def __call__(self, item: Any) -> Any:
380        return self.add(item)
PageItems(client: typing.Union[nicegui.client.Client, NoneType])
355    def __init__(self, client: Optional[Client]) -> None:
356        self.client: Optional[Client] = client
357        self.items: Set[Any] = set()
client: Union[nicegui.client.Client, NoneType]
items: Set[Any]
@classmethod
def check_args_factory( cls, signature: inspect.Signature, args, kwargs) -> Union[PageItems, NoneType]:
359    @classmethod
360    def check_args_factory(cls, signature: Signature, args, kwargs) -> Optional['PageItems']:
361        client: Optional[Client] = kwargs.get('client', None)
362        if (client is None) or (not isinstance(client, Client)):
363            return None
364        else:
365            return cls.install(client)
@classmethod
def install( cls, client: nicegui.client.Client) -> PageItems:
367    @classmethod
368    def install(cls, client: Client) -> 'PageItems':
369        page_items: PageItems = cls(client)
370        client.page_items = page_items
371        return page_items
def add(self, item: typing.Any):
373    def add(self, item: Any):
374        self.items.add(item)
def remove(self, item: typing.Any):
376    def remove(self, item: Any):
377        self.items.discard(item)
def nicegui_page_sync_coro(*dargs, **dkwargs):
1002def nicegui_page_sync_coro(*dargs, **dkwargs):
1003    """Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function.
1004    Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible.
1005    Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details
1006
1007    Returns:
1008        _type_: _description_
1009    """    
1010    dargs_0 = dargs
1011    dkwargs_0 = dkwargs
1012    def nicegui_page_sync_coro_impl(coro_worker: Worker):
1013        coro_worker_0 = coro_worker
1014        dargs_1 = dargs_0
1015        dkwargs_1 = dkwargs_0
1016        def wrapper(*args, **kwargs):
1017            coro_worker = coro_worker_0
1018            dargs = dargs_1
1019            dkwargs = dkwargs_1
1020            sync_coro_decorator = sync_coro_param(*dargs, **dkwargs)
1021            sync_coro_decorator_wrapper = sync_coro_decorator(coro_worker)
1022            ClientHandlers.check_args_factory(wrapper_signature, args, kwargs)
1023            PageItems.check_args_factory(wrapper_signature, args, kwargs)
1024            return sync_coro_decorator_wrapper(*args, **kwargs)
1025            
1026        coro_worker_sign: Signature = signature(coro_worker)
1027        wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
1028        wrapper.__signature__ = wrapper_signature
1029        
1030        return wrapper
1031    return nicegui_page_sync_coro_impl

Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function. Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible. Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details

Returns: _type_: _description_

def sync_like_page(*dargs, **dkwargs):
1002def nicegui_page_sync_coro(*dargs, **dkwargs):
1003    """Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function.
1004    Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible.
1005    Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details
1006
1007    Returns:
1008        _type_: _description_
1009    """    
1010    dargs_0 = dargs
1011    dkwargs_0 = dkwargs
1012    def nicegui_page_sync_coro_impl(coro_worker: Worker):
1013        coro_worker_0 = coro_worker
1014        dargs_1 = dargs_0
1015        dkwargs_1 = dkwargs_0
1016        def wrapper(*args, **kwargs):
1017            coro_worker = coro_worker_0
1018            dargs = dargs_1
1019            dkwargs = dkwargs_1
1020            sync_coro_decorator = sync_coro_param(*dargs, **dkwargs)
1021            sync_coro_decorator_wrapper = sync_coro_decorator(coro_worker)
1022            ClientHandlers.check_args_factory(wrapper_signature, args, kwargs)
1023            PageItems.check_args_factory(wrapper_signature, args, kwargs)
1024            return sync_coro_decorator_wrapper(*args, **kwargs)
1025            
1026        coro_worker_sign: Signature = signature(coro_worker)
1027        wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
1028        wrapper.__signature__ = wrapper_signature
1029        
1030        return wrapper
1031    return nicegui_page_sync_coro_impl

Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function. Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible. Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details

Returns: _type_: _description_

def sl_page(*dargs, **dkwargs):
1002def nicegui_page_sync_coro(*dargs, **dkwargs):
1003    """Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function.
1004    Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible.
1005    Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details
1006
1007    Returns:
1008        _type_: _description_
1009    """    
1010    dargs_0 = dargs
1011    dkwargs_0 = dkwargs
1012    def nicegui_page_sync_coro_impl(coro_worker: Worker):
1013        coro_worker_0 = coro_worker
1014        dargs_1 = dargs_0
1015        dkwargs_1 = dkwargs_0
1016        def wrapper(*args, **kwargs):
1017            coro_worker = coro_worker_0
1018            dargs = dargs_1
1019            dkwargs = dkwargs_1
1020            sync_coro_decorator = sync_coro_param(*dargs, **dkwargs)
1021            sync_coro_decorator_wrapper = sync_coro_decorator(coro_worker)
1022            ClientHandlers.check_args_factory(wrapper_signature, args, kwargs)
1023            PageItems.check_args_factory(wrapper_signature, args, kwargs)
1024            return sync_coro_decorator_wrapper(*args, **kwargs)
1025            
1026        coro_worker_sign: Signature = signature(coro_worker)
1027        wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
1028        wrapper.__signature__ = wrapper_signature
1029        
1030        return wrapper
1031    return nicegui_page_sync_coro_impl

Decorator. With an arguments. Gives ability to execute any decorated Cengal coroutine based page as a sync function. Can postpone execution to the actual loop when possible if None as an immediate result (no result) is acceptible. Can start own loop if needed. See sync_coro_param() decorator from cengal/parallel_execution/coroutines/coro_tools/wait_coro for more details

Returns: _type_: _description_

nicegui_page_async_coro = functools.partial(<function nicegui_page_async_coro_impl>, None)
async_page = functools.partial(<function nicegui_page_async_coro_impl>, None)
apage = functools.partial(<function nicegui_page_async_coro_impl>, None)
def nicegui_page_class_async_coro( page_class: typing.Union[PageContextBase, cengal.code_flow_control.args_manager.versions.v_0.args_manager.EntityArgsHolder, NoneType] = <class 'PageContextBase'>):
1196def nicegui_page_class_async_coro(page_class: Optional[Union[PageContextBase, EntityArgsHolder]] = PageContextBase):
1197    return partial(nicegui_page_async_coro_impl, page_class)
def async_page_class( page_class: typing.Union[PageContextBase, cengal.code_flow_control.args_manager.versions.v_0.args_manager.EntityArgsHolder, NoneType] = <class 'PageContextBase'>):
1196def nicegui_page_class_async_coro(page_class: Optional[Union[PageContextBase, EntityArgsHolder]] = PageContextBase):
1197    return partial(nicegui_page_async_coro_impl, page_class)
def apage_class( page_class: typing.Union[PageContextBase, cengal.code_flow_control.args_manager.versions.v_0.args_manager.EntityArgsHolder, NoneType] = <class 'PageContextBase'>):
1196def nicegui_page_class_async_coro(page_class: Optional[Union[PageContextBase, EntityArgsHolder]] = PageContextBase):
1197    return partial(nicegui_page_async_coro_impl, page_class)
def nicegui_event_handler_func_async_coro( coro_worker: typing.Union[collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]]):
395def nicegui_event_handler_func_async_coro(coro_worker: Worker):
396    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
397
398    Args:
399        coro_worker (Worker): _description_
400
401    Raises:
402        TypeError: _description_
403
404    Returns:
405        _type_: _description_
406    """    
407    coro_worker_0 = coro_worker
408    # async def wrapper(*args, **kwargs):
409    async def wrapper(*args, **kwargs):
410        await await_cs_initiated()
411        coro_worker = coro_worker_0
412        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
413        coro_worker_type: CoroType = find_coro_type(coro_worker)
414        if CoroType.awaitable == coro_worker_type:
415            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
416                                             coro_worker_with_args: EntityArgsHolderExplicit):
417                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
418                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
419                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
420                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
421                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
422                return await coro_worker(i, *args, **kwargs)
423            
424            coro_wrapper = awaitable_coro_wrapper
425        elif CoroType.greenlet == coro_worker_type:
426            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
427                                      coro_worker_with_args: EntityArgsHolderExplicit):
428                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
429                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
430                pretent_to_be_asyncio_coro(i, current_asyncio_task)
431                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
432                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
433                return coro_worker(i, *args, **kwargs)
434            
435            coro_wrapper = greenlet_coro_wrapper
436        else:
437            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
438
439        current_asyncio_task = asyncio.current_task()
440        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
441                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
442        
443    coro_worker_sign: Signature = signature(coro_worker)
444    wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
445    wrapper.__signature__ = wrapper_signature
446    
447    return wrapper

Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine

Args: coro_worker (Worker): _description_

Raises: TypeError: _description_

Returns: _type_: _description_

def async_event_handler_func( coro_worker: typing.Union[collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]]):
395def nicegui_event_handler_func_async_coro(coro_worker: Worker):
396    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
397
398    Args:
399        coro_worker (Worker): _description_
400
401    Raises:
402        TypeError: _description_
403
404    Returns:
405        _type_: _description_
406    """    
407    coro_worker_0 = coro_worker
408    # async def wrapper(*args, **kwargs):
409    async def wrapper(*args, **kwargs):
410        await await_cs_initiated()
411        coro_worker = coro_worker_0
412        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
413        coro_worker_type: CoroType = find_coro_type(coro_worker)
414        if CoroType.awaitable == coro_worker_type:
415            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
416                                             coro_worker_with_args: EntityArgsHolderExplicit):
417                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
418                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
419                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
420                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
421                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
422                return await coro_worker(i, *args, **kwargs)
423            
424            coro_wrapper = awaitable_coro_wrapper
425        elif CoroType.greenlet == coro_worker_type:
426            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
427                                      coro_worker_with_args: EntityArgsHolderExplicit):
428                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
429                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
430                pretent_to_be_asyncio_coro(i, current_asyncio_task)
431                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
432                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
433                return coro_worker(i, *args, **kwargs)
434            
435            coro_wrapper = greenlet_coro_wrapper
436        else:
437            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
438
439        current_asyncio_task = asyncio.current_task()
440        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
441                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
442        
443    coro_worker_sign: Signature = signature(coro_worker)
444    wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
445    wrapper.__signature__ = wrapper_signature
446    
447    return wrapper

Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine

Args: coro_worker (Worker): _description_

Raises: TypeError: _description_

Returns: _type_: _description_

def aevent_handler_func( coro_worker: typing.Union[collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]]):
395def nicegui_event_handler_func_async_coro(coro_worker: Worker):
396    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
397
398    Args:
399        coro_worker (Worker): _description_
400
401    Raises:
402        TypeError: _description_
403
404    Returns:
405        _type_: _description_
406    """    
407    coro_worker_0 = coro_worker
408    # async def wrapper(*args, **kwargs):
409    async def wrapper(*args, **kwargs):
410        await await_cs_initiated()
411        coro_worker = coro_worker_0
412        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
413        coro_worker_type: CoroType = find_coro_type(coro_worker)
414        if CoroType.awaitable == coro_worker_type:
415            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
416                                             coro_worker_with_args: EntityArgsHolderExplicit):
417                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
418                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
419                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
420                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
421                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
422                return await coro_worker(i, *args, **kwargs)
423            
424            coro_wrapper = awaitable_coro_wrapper
425        elif CoroType.greenlet == coro_worker_type:
426            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
427                                      coro_worker_with_args: EntityArgsHolderExplicit):
428                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
429                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
430                pretent_to_be_asyncio_coro(i, current_asyncio_task)
431                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
432                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
433                return coro_worker(i, *args, **kwargs)
434            
435            coro_wrapper = greenlet_coro_wrapper
436        else:
437            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
438
439        current_asyncio_task = asyncio.current_task()
440        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
441                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
442        
443    coro_worker_sign: Signature = signature(coro_worker)
444    wrapper_signature = coro_worker_sign.replace(parameters=tuple(coro_worker_sign.parameters.values())[1:], return_annotation=coro_worker_sign.return_annotation)
445    wrapper.__signature__ = wrapper_signature
446    
447    return wrapper

Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine

Args: coro_worker (Worker): _description_

Raises: TypeError: _description_

Returns: _type_: _description_

def nicegui_event_handler_method_async_coro( coro_worker: typing.Union[collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]]):
454def nicegui_event_handler_method_async_coro(coro_worker: Worker):
455    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
456
457    Args:
458        coro_worker (Worker): _description_
459
460    Raises:
461        TypeError: _description_
462
463    Returns:
464        _type_: _description_
465    """    
466    coro_worker_0 = coro_worker
467    # async def wrapper(*args, **kwargs):
468    async def wrapper(self, *args, **kwargs):
469        await await_cs_initiated()
470        coro_worker = coro_worker_0
471        coro_worker_name: str = coro_worker.__name__
472        unwrapped_coro_worker_name: str = f'_unwrapped_method__{coro_worker_name}'
473        if not hasattr(self, unwrapped_coro_worker_name):
474            bound_coro_worker = coro_worker.__get__(self, self.__class__)
475            setattr(self, unwrapped_coro_worker_name, bound_coro_worker)
476
477        coro_worker = getattr(self, unwrapped_coro_worker_name)
478        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
479        coro_worker_type: CoroType = find_coro_type(coro_worker)
480        if CoroType.awaitable == coro_worker_type:
481            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
482                                             coro_worker_with_args: EntityArgsHolderExplicit):
483                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
484                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
485                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
486                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
487                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
488                i.log.debug(f'{entity_repr(coro_worker)}')
489                return await coro_worker(i, *args, **kwargs)
490            
491            coro_wrapper = awaitable_coro_wrapper
492        elif CoroType.greenlet == coro_worker_type:
493            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
494                                      coro_worker_with_args: EntityArgsHolderExplicit):
495                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
496                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
497                pretent_to_be_asyncio_coro(i, current_asyncio_task)
498                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
499                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
500                return coro_worker(i, *args, **kwargs)
501            
502            coro_wrapper = greenlet_coro_wrapper
503        else:
504            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
505
506        current_asyncio_task = asyncio.current_task()
507        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
508                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
509        
510    coro_worker_sign: Signature = signature(coro_worker)
511    new_parameters = list(coro_worker_sign.parameters.values())
512    del new_parameters[1]
513    wrapper_signature = coro_worker_sign.replace(parameters=tuple(new_parameters), return_annotation=coro_worker_sign.return_annotation)
514    wrapper.__signature__ = wrapper_signature
515    
516    return wrapper

Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine

Args: coro_worker (Worker): _description_

Raises: TypeError: _description_

Returns: _type_: _description_

def async_event_handler_method( coro_worker: typing.Union[collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]]):
454def nicegui_event_handler_method_async_coro(coro_worker: Worker):
455    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
456
457    Args:
458        coro_worker (Worker): _description_
459
460    Raises:
461        TypeError: _description_
462
463    Returns:
464        _type_: _description_
465    """    
466    coro_worker_0 = coro_worker
467    # async def wrapper(*args, **kwargs):
468    async def wrapper(self, *args, **kwargs):
469        await await_cs_initiated()
470        coro_worker = coro_worker_0
471        coro_worker_name: str = coro_worker.__name__
472        unwrapped_coro_worker_name: str = f'_unwrapped_method__{coro_worker_name}'
473        if not hasattr(self, unwrapped_coro_worker_name):
474            bound_coro_worker = coro_worker.__get__(self, self.__class__)
475            setattr(self, unwrapped_coro_worker_name, bound_coro_worker)
476
477        coro_worker = getattr(self, unwrapped_coro_worker_name)
478        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
479        coro_worker_type: CoroType = find_coro_type(coro_worker)
480        if CoroType.awaitable == coro_worker_type:
481            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
482                                             coro_worker_with_args: EntityArgsHolderExplicit):
483                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
484                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
485                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
486                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
487                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
488                i.log.debug(f'{entity_repr(coro_worker)}')
489                return await coro_worker(i, *args, **kwargs)
490            
491            coro_wrapper = awaitable_coro_wrapper
492        elif CoroType.greenlet == coro_worker_type:
493            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
494                                      coro_worker_with_args: EntityArgsHolderExplicit):
495                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
496                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
497                pretent_to_be_asyncio_coro(i, current_asyncio_task)
498                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
499                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
500                return coro_worker(i, *args, **kwargs)
501            
502            coro_wrapper = greenlet_coro_wrapper
503        else:
504            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
505
506        current_asyncio_task = asyncio.current_task()
507        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
508                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
509        
510    coro_worker_sign: Signature = signature(coro_worker)
511    new_parameters = list(coro_worker_sign.parameters.values())
512    del new_parameters[1]
513    wrapper_signature = coro_worker_sign.replace(parameters=tuple(new_parameters), return_annotation=coro_worker_sign.return_annotation)
514    wrapper.__signature__ = wrapper_signature
515    
516    return wrapper

Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine

Args: coro_worker (Worker): _description_

Raises: TypeError: _description_

Returns: _type_: _description_

def aevent_handler_method( coro_worker: typing.Union[collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]]):
454def nicegui_event_handler_method_async_coro(coro_worker: Worker):
455    """Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine
456
457    Args:
458        coro_worker (Worker): _description_
459
460    Raises:
461        TypeError: _description_
462
463    Returns:
464        _type_: _description_
465    """    
466    coro_worker_0 = coro_worker
467    # async def wrapper(*args, **kwargs):
468    async def wrapper(self, *args, **kwargs):
469        await await_cs_initiated()
470        coro_worker = coro_worker_0
471        coro_worker_name: str = coro_worker.__name__
472        unwrapped_coro_worker_name: str = f'_unwrapped_method__{coro_worker_name}'
473        if not hasattr(self, unwrapped_coro_worker_name):
474            bound_coro_worker = coro_worker.__get__(self, self.__class__)
475            setattr(self, unwrapped_coro_worker_name, bound_coro_worker)
476
477        coro_worker = getattr(self, unwrapped_coro_worker_name)
478        app.cs.logger.debug(f'nicegui_event_handler_async_coro.wrapper - start: {entity_name(coro_worker)}')
479        coro_worker_type: CoroType = find_coro_type(coro_worker)
480        if CoroType.awaitable == coro_worker_type:
481            async def awaitable_coro_wrapper(i: Interface, current_asyncio_task, 
482                                             coro_worker_with_args: EntityArgsHolderExplicit):
483                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
484                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - start: {entity_name(coro_worker)}')
485                await apretent_to_be_asyncio_coro(i, current_asyncio_task)
486                # await i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
487                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.awaitable_coro_wrapper - ready: {entity_name(coro_worker)}')
488                i.log.debug(f'{entity_repr(coro_worker)}')
489                return await coro_worker(i, *args, **kwargs)
490            
491            coro_wrapper = awaitable_coro_wrapper
492        elif CoroType.greenlet == coro_worker_type:
493            def greenlet_coro_wrapper(i: Interface, current_asyncio_task, 
494                                      coro_worker_with_args: EntityArgsHolderExplicit):
495                coro_worker, args, kwargs = coro_worker_with_args.entity_args_kwargs()
496                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - start: {entity_name(coro_worker)}')
497                pretent_to_be_asyncio_coro(i, current_asyncio_task)
498                # i(Instance, InstanceRequest().wait(CS_PREPARED_FLAG))
499                i.log.debug(f'nicegui_event_handler_async_coro.wrapper.greenlet_coro_wrapper - ready: {entity_name(coro_worker)}')
500                return coro_worker(i, *args, **kwargs)
501            
502            coro_wrapper = greenlet_coro_wrapper
503        else:
504            raise TypeError(f'{coro_worker} is neither an awaitable nor a greenlet')
505
506        current_asyncio_task = asyncio.current_task()
507        return await await_coro_prim(coro_wrapper, current_asyncio_task, 
508                                     EntityArgsHolderExplicit(coro_worker, args, kwargs))
509        
510    coro_worker_sign: Signature = signature(coro_worker)
511    new_parameters = list(coro_worker_sign.parameters.values())
512    del new_parameters[1]
513    wrapper_signature = coro_worker_sign.replace(parameters=tuple(new_parameters), return_annotation=coro_worker_sign.return_annotation)
514    wrapper.__signature__ = wrapper_signature
515    
516    return wrapper

Decorator. Without arguments. Makes a proper, fully functional async Page from any decorated Cengal coroutine

Args: coro_worker (Worker): _description_

Raises: TypeError: _description_

Returns: _type_: _description_

class PageContextBase:
549class PageContextBase:
550    def __init__(self, client: Client, request: FastAPIRequest, _t: NTTE, current_asyncio_task) -> None:
551        self.client: Client = client
552        self.request: FastAPIRequest = request
553        self.client_host_port: Address = self.request.client
554        self.client_id: Hashable = client.id
555        self.session_id: Hashable = client.session_id
556        self.user_id: Hashable = None
557        self._t: NTTE = _t
558        self.current_asyncio_task = current_asyncio_task
559        self.current_asyncio_task_id = id(current_asyncio_task)
560        self.client_view_type: ClientViewType = self._determine_client_view_type()
561        self.better_lang: str = None
562        self.featured_langs: OrderedDict[str, RationalNumber] = None
563        self.langs: OrderedDict[str, RationalNumber] = None
564        self.better_lang, self.featured_langs, self.langs = self._determine_client_languages()
565        self._t.text_translation_language_chooser.lang = self.better_lang
566    
567    def _init(self, i: Interface):
568        self.register_session_page(i)
569        i(PutCoro, self._arequest_ip_geolocation, self.client_host_port[0])
570        if self._find_user(i):
571            i.log.debug(f'{self.session_id}, {self.client_id}: logged in')
572        else:
573            i.log.debug(f'{self.session_id}, {self.client_id}: logged out')
574    
575    async def _ainit(self, i: Interface):
576        self.register_session_page(i)
577        await i(PutCoro, self._arequest_ip_geolocation, self.client_host_port[0])
578        if await self._afind_user(i):
579            i.log.debug(f'{self.session_id}, {self.client_id}: logged in')
580        else:
581            i.log.debug(f'{self.session_id}, {self.client_id}: logged out')
582    
583    async def _arequest_ip_geolocation(self, i: Interface, host: str):
584        ip_geolocation: Optional[Dict] = None
585        need_to_request: bool = True
586        try:
587            ip_geolocation = await i(db_request__ip_geolocation.get(host))
588            need_to_request = False
589        except KeyError:
590            pass
591
592        if need_to_request:
593            ip_geolocation = await i(AsyncioLoop, AsyncioLoopRequest().wait(get_ip_geolocation(ipinfo_io_token, host)))
594        
595        await self.on_ip_geolocation_ready(i, ip_geolocation)
596    
597    def register_session_page(self, i: Interface):
598        session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
599        if self.session_id not in session_pages:
600            session_pages[self.session_id] = set()
601        
602        session_pages[self.session_id].add(self)
603    
604    def _determine_client_view_type(self) -> ClientViewType:
605        return client_view_type(self.request.headers)
606    
607    def _determine_client_languages(self):
608        parsed_accept_language: Optional[OrderedDict[str, RationalNumber]] = optimize_accept_language(parse_accept_language(self.request.headers))
609        translation_data: Dict = self._t.text_translator.decoded_data
610        return match_langs(
611                translation_data['default_language'], 
612                set(translation_data['featured_languages']),
613                set(translation_data['supported_languages']),
614                translation_data['translation_language_map'],
615                parsed_accept_language,
616            )
617    
618    def _find_user(self, i: Interface) -> bool:
619        if self.session_id is None:
620            return False
621        
622        try:
623            user_id, sign_in_date_time = i(db_request__logged_in_sessions.get(self.session_id))
624            sign_in_date_time = datetime.fromisoformat(sign_in_date_time)
625            if (datetime.now() - sign_in_date_time) > signed_in_session_timeout:
626                i(db_request__logged_in_sessions.delete(self.session_id))
627                return False
628            
629            self.user_id = user_id
630            return True
631        except KeyError:
632            return False
633    
634    async def _afind_user(self, i: Interface) -> bool:
635        if self.session_id is None:
636            return False
637        
638        try:
639            user_id, sign_in_date_time = await i(db_request__logged_in_sessions.get(self.session_id))
640            sign_in_date_time = datetime.fromisoformat(sign_in_date_time)
641            if (datetime.now() - sign_in_date_time) > signed_in_session_timeout:
642                await i(db_request__logged_in_sessions.delete(self.session_id))
643                return False
644            
645            self.user_id = user_id
646            return True
647        except KeyError:
648            return False
649    
650    # def find_user(self, i: Interface, client_connection_check_interval: RationalNumber = 0.01) -> bool:
651    #     if not self.client.has_socket_connection:
652    #         if not self.client.is_waiting_for_connection:
653    #             i(AsyncioLoop, AsyncioLoopRequest().wait(self.client.connected()))
654        
655    #     while self.client.is_waiting_for_connection:
656    #         i(Sleep, client_connection_check_interval)
657        
658    #     return self._find_user(i)
659    
660    # async def afind_user(self, i: Interface, client_connection_check_interval: RationalNumber = 0.01) -> bool:
661    #     if not self.client.has_socket_connection:
662    #         if not self.client.is_waiting_for_connection:
663    #             await i(AsyncioLoop, AsyncioLoopRequest().wait(self.client.connected()))
664        
665    #     while self.client.is_waiting_for_connection:
666    #         await i(Sleep, client_connection_check_interval)
667        
668    #     return await self._afind_user(i)
669    
670    def try_sign_in(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float]) -> Optional[Union[Dict, Tuple, List]]:
671        user_data: Union[Dict, Tuple, List] = None
672
673        try:
674            user_data = i(db_request__user_sign_in_info.get(credentials))
675        except KeyError:
676            pass
677        
678        if user_data is None:
679            return None
680        
681        user_id = user_data[0]
682        if self.session_id is None:
683            i(db_request__logged_in_clients.put(self.client_id, (user_id, datetime.now())))
684            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
685            with user_clients_lock:
686                try:
687                    user_clients: Set[Hashable] = set(i(db_request__user_clients.get(user_id)))
688                except KeyError:
689                    user_clients = set()
690                
691                user_clients.add(self.client_id)
692                i(db_request__user_sessions.put(user_id, user_clients))
693            
694            i(PutCoro, self._on_signed_in, user_id)
695        else:
696            i(db_request__logged_in_sessions.put(self.session_id, (user_id, datetime.now())))
697            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
698            with user_sessions_lock:
699                try:
700                    user_sessions: Set[Hashable] = set(i(db_request__user_sessions.get(user_id)))
701                except KeyError:
702                    user_sessions = set()
703                
704                user_sessions.add(self.session_id)
705                i(db_request__user_sessions.put(user_id, user_sessions))
706            
707            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
708            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
709            for session_page in current_session_pages:
710                i(PutCoro, session_page._on_signed_in, user_id, user_data)
711        
712        return user_data
713    
714    async def atry_sign_in(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float]) -> Optional[Union[Dict, Tuple, List]]:
715        user_data: Union[Dict, Tuple, List] = None
716
717        try:
718            user_data = await i(db_request__user_sign_in_info.get(credentials))
719        except KeyError:
720            pass
721        
722        if user_data is None:
723            return None
724        
725        user_id = user_data[0]
726        if self.session_id is None:
727            await i(db_request__logged_in_clients.put(self.client_id, (user_id, datetime.now())))
728            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
729            async with user_clients_lock:
730                try:
731                    user_clients: Set[Hashable] = set(await i(db_request__user_clients.get(user_id)))
732                except KeyError:
733                    user_clients = set()
734                
735                user_clients.add(self.client_id)
736                await i(db_request__user_sessions.put(user_id, user_clients))
737            
738            await i(PutCoro, self._on_signed_in, user_id)
739        else:
740            await i(db_request__logged_in_sessions.put(self.session_id, (user_id, datetime.now())))
741            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
742            async with user_sessions_lock:
743                try:
744                    user_sessions: Set[Hashable] = set(await i(db_request__user_sessions.get(user_id)))
745                except KeyError:
746                    user_sessions = set()
747                
748                user_sessions.add(self.session_id)
749                await i(db_request__user_sessions.put(user_id, user_sessions))
750            
751            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
752            current_session_pages: Set[PageContextBase] = set(session_pages.get(self.session_id, set()))  # TODO: robust fix needed for `RuntimeError: Set changed size during iteration`
753            for session_page in current_session_pages:
754                await i(PutCoro, session_page._on_signed_in, user_id, user_data)
755        
756        return user_data
757    
758    def user_signed_up(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float],
759                              user_sign_in_info: Union[Dict, Tuple, List]):
760        i(db_request__user_sign_in_info.put(credentials, user_sign_in_info))
761    
762    async def auser_signed_up(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float],
763                              user_sign_in_info: Union[Dict, Tuple, List]):
764        await i(db_request__user_sign_in_info.put(credentials, user_sign_in_info))
765
766    def sign_out(self, i: Interface) -> bool:
767        user_id = self.user_id
768        if user_id is None:
769            return False
770        
771        if self.session_id is None:
772            try:
773                i(db_request__logged_in_clients.delete(self.client_id))
774            except KeyError:
775                pass
776
777            i(PutCoro, self._on_signed_out)
778            return True
779        else:
780            try:
781                i(db_request__logged_in_sessions.delete(self.session_id))
782            except KeyError:
783                pass
784
785            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
786            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
787            for session_page in current_session_pages:
788                i(PutCoro, session_page._on_signed_out)
789
790            return True
791
792    async def asign_out(self, i: Interface) -> bool:
793        user_id = self.user_id
794        if user_id is None:
795            return False
796        
797        if self.session_id is None:
798            try:
799                await i(db_request__logged_in_clients.delete(self.client_id))
800            except KeyError:
801                pass
802
803            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
804            async with user_clients_lock:
805                try:
806                    user_clients: Set[Hashable] = set(await i(db_request__user_clients.get(user_id)))
807                except KeyError:
808                    user_clients = set()
809                
810                user_clients.discard(self.client_id)
811                await i(db_request__user_sessions.put(user_id, user_clients))
812
813            await i(PutCoro, self._on_signed_out)
814            return True
815        else:
816            try:
817                await i(db_request__logged_in_sessions.delete(self.session_id))
818            except KeyError:
819                pass
820
821            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
822            async with user_sessions_lock:
823                try:
824                    user_sessions: Set[Hashable] = set(await i(db_request__user_sessions.get(user_id)))
825                except KeyError:
826                    user_sessions = set()
827                
828                user_sessions.discard(self.session_id)
829                await i(db_request__user_sessions.put(user_id, user_sessions))
830
831            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
832            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
833            for session_page in current_session_pages:
834                await i(PutCoro, session_page._on_signed_out)
835
836            return True
837
838    def try_sign_up__login_password(self, i: Interface, login: str, password: str, 
839                                    user_sign_in_info_maker: Callable[[Interface, str, Hashable, datetime], Union[Dict, Tuple, List]]) -> bool:
840        login_exists: bool = False
841        try:
842            i(db_request__user_sign_up_info.get(login))
843            login_exists = True
844        except KeyError:
845            pass
846
847        if login_exists:
848            return False
849        
850        password_bytes = password.encode('utf-8')
851        salt = os.urandom(16)
852        key_length = 64
853        N = 16384  # CPU/memory cost factor
854        r = 8     # Block size
855        p = 1     # Parallelization factor
856        dk = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
857        i(db_request__user_sign_up_info.put(login, (dk, salt, N, r, p, key_length)))
858        user_id: Hashable = uuid4()
859        credentials: Tuple = (login, dk)
860        i(db_request__credentials_by_user_id.put(user_id, credentials))
861        user_sign_in_info: Union[Dict, Tuple, List] = run_coro_fast(i, user_sign_in_info_maker, login, user_id, datetime.now())
862        self.user_signed_up(i, credentials, user_sign_in_info)
863        return True
864
865    async def atry_sign_up__login_password(self, i: Interface, login: str, password: str, 
866                                           user_sign_in_info_maker: Callable[[Interface, str, Hashable, datetime], Union[Dict, Tuple, List]]) -> bool:
867        login_exists: bool = False
868        try:
869            await i(db_request__user_sign_up_info.get(login))
870            login_exists = True
871        except KeyError:
872            pass
873
874        if login_exists:
875            return False
876        
877        password_bytes = password.encode('utf-8')
878        salt = os.urandom(16)
879        key_length = 64
880        N = 16384  # CPU/memory cost factor
881        r = 8     # Block size
882        p = 1     # Parallelization factor
883        dk = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
884        await i(db_request__user_sign_up_info.put(login, (dk, salt, N, r, p, key_length)))
885        user_id: Hashable = uuid4().bytes
886        credentials: Tuple = (login, dk)
887        await i(db_request__credentials_by_user_id.put(user_id, credentials))
888        user_sign_in_info: Union[Dict, Tuple, List] = await arun_coro_fast(i, user_sign_in_info_maker, login, user_id, datetime.now())
889        await self.auser_signed_up(i, credentials, user_sign_in_info)
890        return True
891
892    def try_sign_in__login_password(self, i: Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
893        user_found: bool = False
894        try:
895            dk, salt, N, r, p, key_length = i(db_request__user_sign_up_info.get(login))
896            user_found = True
897        except KeyError:
898            pass
899
900        if not user_found:
901            raise UserNotFoundError
902
903        password_bytes = password.encode('utf-8')
904        dk_0 = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
905        if dk != dk_0:
906            raise PasswordIsNotCorrectError
907        
908        credentials: Tuple = (login, dk)
909        return self.try_sign_in(i, credentials)
910
911    async def atry_sign_in__login_password(self, i: Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
912        user_found: bool = False
913        try:
914            dk, salt, N, r, p, key_length = await i(db_request__user_sign_up_info.get(login))
915            user_found = True
916        except KeyError:
917            pass
918
919        if not user_found:
920            raise UserNotFoundError
921
922        password_bytes = password.encode('utf-8')
923        dk_0 = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
924        if dk != dk_0:
925            raise PasswordIsNotCorrectError
926        
927        credentials: Tuple = (login, dk)
928        return await self.atry_sign_in(i, credentials)
929
930    def try_sign_up__email_password(self, i: Interface, email: str, password: str) -> bool:
931        raise NotImplementedError
932
933    async def atry_sign_up__email_password(self, i: Interface, email: str, password: str) -> bool:
934        raise NotImplementedError
935
936    async def _on_signed_in(self, i: Interface, user_id: Hashable, user_sign_in_info: Union[Dict, Tuple, List]) -> None:
937        self.user_id = user_id
938        handle_event(self._on_signed_in_impl, OnSignInEventArguments(sender=FakeElementForEvents(self.current_asyncio_task_id), client=self.client, user_id=user_id, user_sign_in_info=user_sign_in_info))
939
940    @aevent_handler_method
941    async def _on_signed_in_impl(self, i: Interface, arguments: OnSignInEventArguments) -> None:
942        return await self.on_signed_in(i, arguments.user_id, arguments.user_sign_in_info)
943
944    async def on_signed_in(self, i: Interface, user_id: Hashable, user_sign_in_info: Union[Dict, Tuple, List]) -> None:
945        pass
946
947    async def _on_signed_out(self, i: Interface) -> None:
948        user_id = self.user_id
949        self.user_id = None
950        handle_event(self._on_signed_out_impl, OnSignOutEventArguments(sender=FakeElementForEvents(self.current_asyncio_task_id), client=self.client, user_id=user_id))
951
952    @aevent_handler_method
953    async def _on_signed_out_impl(self, i: Interface, arguments: OnSignOutEventArguments) -> None:
954        return await self.on_signed_out(i, arguments.user_id)
955
956    async def on_signed_out(self, i: Interface, user_id: Hashable) -> None:
957        pass
958
959    async def on_ip_geolocation_ready(self, i: Interface, ip_geolocation: Optional[Dict]) -> None:
960        pass
961
962    def refferer(self) -> Optional[str]:
963        return self.request.headers.get('referer', None)
964
965    def url(self) -> URL:
966        return self.request.url
967
968    def open_page(self, target: Optional[Union[Callable[..., Any], str]] = None, new_tab: bool = False) -> Optional[RedirectResponse]:
969        if target is None:
970            return
971        
972        client = self.client
973        if client.has_socket_connection:
974            client.open(target, new_tab)
975        else:
976            return RedirectResponse(target)
977
978    def open_previous_page(self, default_target: Optional[Union[Callable[..., Any], str]] = None, new_tab: bool = False, can_be_current_page: bool = False) -> Optional[RedirectResponse]:
979        target: str = self.refferer()
980        current_url: str = str(self.url())
981        if (target is None) or (False if can_be_current_page else (target == current_url)):
982            if not isinstance(default_target, str):
983                default_target = globals.page_routes[target]
984            
985            target = default_target if default_target is not None else '/'
986        
987        client = self.client
988        if client.has_socket_connection:
989            client.open(target, new_tab)
990        else:
991            return self.open_previous_page_http_response(default_target, can_be_current_page)
992
993    def open_previous_page_http_response(self, default_target: Optional[Union[Callable[..., Any], str]] = None, can_be_current_page: bool = False) -> RedirectResponse:
994        refferer: str = self.refferer()
995        current_url: str = str(self.url())
996        if (refferer is None) or (False if can_be_current_page else (refferer == current_url)):
997            refferer = default_target if default_target is not None else '/'
998        
999        return RedirectResponse(refferer)
PageContextBase( client: nicegui.client.Client, request: starlette.requests.Request, _t: cengal.parallel_execution.coroutines.integrations.nicegui.versions.v_0.text_translation.NiceguiTranslatableTextElement, current_asyncio_task)
550    def __init__(self, client: Client, request: FastAPIRequest, _t: NTTE, current_asyncio_task) -> None:
551        self.client: Client = client
552        self.request: FastAPIRequest = request
553        self.client_host_port: Address = self.request.client
554        self.client_id: Hashable = client.id
555        self.session_id: Hashable = client.session_id
556        self.user_id: Hashable = None
557        self._t: NTTE = _t
558        self.current_asyncio_task = current_asyncio_task
559        self.current_asyncio_task_id = id(current_asyncio_task)
560        self.client_view_type: ClientViewType = self._determine_client_view_type()
561        self.better_lang: str = None
562        self.featured_langs: OrderedDict[str, RationalNumber] = None
563        self.langs: OrderedDict[str, RationalNumber] = None
564        self.better_lang, self.featured_langs, self.langs = self._determine_client_languages()
565        self._t.text_translation_language_chooser.lang = self.better_lang
client: nicegui.client.Client
request: starlette.requests.Request
client_host_port: starlette.datastructures.Address
client_id: Hashable
session_id: Hashable
user_id: Hashable
current_asyncio_task
current_asyncio_task_id
client_view_type: cengal.web_tools.detect_browsers_host_device_type.by_http_headers.versions.v_0.by_http_headers.ClientViewType
better_lang: str
featured_langs: 'OrderedDict[(str, RationalNumber)]'
langs: 'OrderedDict[(str, RationalNumber)]'
def register_session_page( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface):
597    def register_session_page(self, i: Interface):
598        session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
599        if self.session_id not in session_pages:
600            session_pages[self.session_id] = set()
601        
602        session_pages[self.session_id].add(self)
def try_sign_in( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, credentials: typing.Union[typing.Dict, typing.Tuple, typing.List, str, bytes, int, float]) -> Union[Dict, Tuple, List, NoneType]:
670    def try_sign_in(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float]) -> Optional[Union[Dict, Tuple, List]]:
671        user_data: Union[Dict, Tuple, List] = None
672
673        try:
674            user_data = i(db_request__user_sign_in_info.get(credentials))
675        except KeyError:
676            pass
677        
678        if user_data is None:
679            return None
680        
681        user_id = user_data[0]
682        if self.session_id is None:
683            i(db_request__logged_in_clients.put(self.client_id, (user_id, datetime.now())))
684            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
685            with user_clients_lock:
686                try:
687                    user_clients: Set[Hashable] = set(i(db_request__user_clients.get(user_id)))
688                except KeyError:
689                    user_clients = set()
690                
691                user_clients.add(self.client_id)
692                i(db_request__user_sessions.put(user_id, user_clients))
693            
694            i(PutCoro, self._on_signed_in, user_id)
695        else:
696            i(db_request__logged_in_sessions.put(self.session_id, (user_id, datetime.now())))
697            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
698            with user_sessions_lock:
699                try:
700                    user_sessions: Set[Hashable] = set(i(db_request__user_sessions.get(user_id)))
701                except KeyError:
702                    user_sessions = set()
703                
704                user_sessions.add(self.session_id)
705                i(db_request__user_sessions.put(user_id, user_sessions))
706            
707            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
708            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
709            for session_page in current_session_pages:
710                i(PutCoro, session_page._on_signed_in, user_id, user_data)
711        
712        return user_data
async def atry_sign_in( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, credentials: typing.Union[typing.Dict, typing.Tuple, typing.List, str, bytes, int, float]) -> Union[Dict, Tuple, List, NoneType]:
714    async def atry_sign_in(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float]) -> Optional[Union[Dict, Tuple, List]]:
715        user_data: Union[Dict, Tuple, List] = None
716
717        try:
718            user_data = await i(db_request__user_sign_in_info.get(credentials))
719        except KeyError:
720            pass
721        
722        if user_data is None:
723            return None
724        
725        user_id = user_data[0]
726        if self.session_id is None:
727            await i(db_request__logged_in_clients.put(self.client_id, (user_id, datetime.now())))
728            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
729            async with user_clients_lock:
730                try:
731                    user_clients: Set[Hashable] = set(await i(db_request__user_clients.get(user_id)))
732                except KeyError:
733                    user_clients = set()
734                
735                user_clients.add(self.client_id)
736                await i(db_request__user_sessions.put(user_id, user_clients))
737            
738            await i(PutCoro, self._on_signed_in, user_id)
739        else:
740            await i(db_request__logged_in_sessions.put(self.session_id, (user_id, datetime.now())))
741            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
742            async with user_sessions_lock:
743                try:
744                    user_sessions: Set[Hashable] = set(await i(db_request__user_sessions.get(user_id)))
745                except KeyError:
746                    user_sessions = set()
747                
748                user_sessions.add(self.session_id)
749                await i(db_request__user_sessions.put(user_id, user_sessions))
750            
751            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
752            current_session_pages: Set[PageContextBase] = set(session_pages.get(self.session_id, set()))  # TODO: robust fix needed for `RuntimeError: Set changed size during iteration`
753            for session_page in current_session_pages:
754                await i(PutCoro, session_page._on_signed_in, user_id, user_data)
755        
756        return user_data
def user_signed_up( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, credentials: typing.Union[typing.Dict, typing.Tuple, typing.List, str, bytes, int, float], user_sign_in_info: typing.Union[typing.Dict, typing.Tuple, typing.List]):
758    def user_signed_up(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float],
759                              user_sign_in_info: Union[Dict, Tuple, List]):
760        i(db_request__user_sign_in_info.put(credentials, user_sign_in_info))
async def auser_signed_up( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, credentials: typing.Union[typing.Dict, typing.Tuple, typing.List, str, bytes, int, float], user_sign_in_info: typing.Union[typing.Dict, typing.Tuple, typing.List]):
762    async def auser_signed_up(self, i: Interface, credentials: Union[Dict, Tuple, List, str, bytes, int, float],
763                              user_sign_in_info: Union[Dict, Tuple, List]):
764        await i(db_request__user_sign_in_info.put(credentials, user_sign_in_info))
def sign_out( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface) -> bool:
766    def sign_out(self, i: Interface) -> bool:
767        user_id = self.user_id
768        if user_id is None:
769            return False
770        
771        if self.session_id is None:
772            try:
773                i(db_request__logged_in_clients.delete(self.client_id))
774            except KeyError:
775                pass
776
777            i(PutCoro, self._on_signed_out)
778            return True
779        else:
780            try:
781                i(db_request__logged_in_sessions.delete(self.session_id))
782            except KeyError:
783                pass
784
785            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
786            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
787            for session_page in current_session_pages:
788                i(PutCoro, session_page._on_signed_out)
789
790            return True
async def asign_out( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface) -> bool:
792    async def asign_out(self, i: Interface) -> bool:
793        user_id = self.user_id
794        if user_id is None:
795            return False
796        
797        if self.session_id is None:
798            try:
799                await i(db_request__logged_in_clients.delete(self.client_id))
800            except KeyError:
801                pass
802
803            user_clients_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_clients_lock')
804            async with user_clients_lock:
805                try:
806                    user_clients: Set[Hashable] = set(await i(db_request__user_clients.get(user_id)))
807                except KeyError:
808                    user_clients = set()
809                
810                user_clients.discard(self.client_id)
811                await i(db_request__user_sessions.put(user_id, user_clients))
812
813            await i(PutCoro, self._on_signed_out)
814            return True
815        else:
816            try:
817                await i(db_request__logged_in_sessions.delete(self.session_id))
818            except KeyError:
819                pass
820
821            user_sessions_lock: Lock = fast_get_explicit(i, 'cengal_nicegui__user_sessions_lock')
822            async with user_sessions_lock:
823                try:
824                    user_sessions: Set[Hashable] = set(await i(db_request__user_sessions.get(user_id)))
825                except KeyError:
826                    user_sessions = set()
827                
828                user_sessions.discard(self.session_id)
829                await i(db_request__user_sessions.put(user_id, user_sessions))
830
831            session_pages: Dict[Hashable, Set[PageContextBase]] = fast_get_explicit(i, 'cengal_nicegui__session_pages')
832            current_session_pages: Set[PageContextBase] = session_pages.get(self.session_id, set())
833            for session_page in current_session_pages:
834                await i(PutCoro, session_page._on_signed_out)
835
836            return True
def try_sign_up__login_password( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, login: str, password: str, user_sign_in_info_maker: typing.Callable[[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, str, typing.Hashable, datetime.datetime], typing.Union[typing.Dict, typing.Tuple, typing.List]]) -> bool:
838    def try_sign_up__login_password(self, i: Interface, login: str, password: str, 
839                                    user_sign_in_info_maker: Callable[[Interface, str, Hashable, datetime], Union[Dict, Tuple, List]]) -> bool:
840        login_exists: bool = False
841        try:
842            i(db_request__user_sign_up_info.get(login))
843            login_exists = True
844        except KeyError:
845            pass
846
847        if login_exists:
848            return False
849        
850        password_bytes = password.encode('utf-8')
851        salt = os.urandom(16)
852        key_length = 64
853        N = 16384  # CPU/memory cost factor
854        r = 8     # Block size
855        p = 1     # Parallelization factor
856        dk = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
857        i(db_request__user_sign_up_info.put(login, (dk, salt, N, r, p, key_length)))
858        user_id: Hashable = uuid4()
859        credentials: Tuple = (login, dk)
860        i(db_request__credentials_by_user_id.put(user_id, credentials))
861        user_sign_in_info: Union[Dict, Tuple, List] = run_coro_fast(i, user_sign_in_info_maker, login, user_id, datetime.now())
862        self.user_signed_up(i, credentials, user_sign_in_info)
863        return True
async def atry_sign_up__login_password( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, login: str, password: str, user_sign_in_info_maker: typing.Callable[[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, str, typing.Hashable, datetime.datetime], typing.Union[typing.Dict, typing.Tuple, typing.List]]) -> bool:
865    async def atry_sign_up__login_password(self, i: Interface, login: str, password: str, 
866                                           user_sign_in_info_maker: Callable[[Interface, str, Hashable, datetime], Union[Dict, Tuple, List]]) -> bool:
867        login_exists: bool = False
868        try:
869            await i(db_request__user_sign_up_info.get(login))
870            login_exists = True
871        except KeyError:
872            pass
873
874        if login_exists:
875            return False
876        
877        password_bytes = password.encode('utf-8')
878        salt = os.urandom(16)
879        key_length = 64
880        N = 16384  # CPU/memory cost factor
881        r = 8     # Block size
882        p = 1     # Parallelization factor
883        dk = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
884        await i(db_request__user_sign_up_info.put(login, (dk, salt, N, r, p, key_length)))
885        user_id: Hashable = uuid4().bytes
886        credentials: Tuple = (login, dk)
887        await i(db_request__credentials_by_user_id.put(user_id, credentials))
888        user_sign_in_info: Union[Dict, Tuple, List] = await arun_coro_fast(i, user_sign_in_info_maker, login, user_id, datetime.now())
889        await self.auser_signed_up(i, credentials, user_sign_in_info)
890        return True
def try_sign_in__login_password( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
892    def try_sign_in__login_password(self, i: Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
893        user_found: bool = False
894        try:
895            dk, salt, N, r, p, key_length = i(db_request__user_sign_up_info.get(login))
896            user_found = True
897        except KeyError:
898            pass
899
900        if not user_found:
901            raise UserNotFoundError
902
903        password_bytes = password.encode('utf-8')
904        dk_0 = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
905        if dk != dk_0:
906            raise PasswordIsNotCorrectError
907        
908        credentials: Tuple = (login, dk)
909        return self.try_sign_in(i, credentials)
async def atry_sign_in__login_password( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
911    async def atry_sign_in__login_password(self, i: Interface, login: str, password: str) -> Union[Dict, Tuple, List]:
912        user_found: bool = False
913        try:
914            dk, salt, N, r, p, key_length = await i(db_request__user_sign_up_info.get(login))
915            user_found = True
916        except KeyError:
917            pass
918
919        if not user_found:
920            raise UserNotFoundError
921
922        password_bytes = password.encode('utf-8')
923        dk_0 = hashlib.scrypt(password_bytes, salt=salt, n=N, r=r, p=p, dklen=key_length)
924        if dk != dk_0:
925            raise PasswordIsNotCorrectError
926        
927        credentials: Tuple = (login, dk)
928        return await self.atry_sign_in(i, credentials)
def try_sign_up__email_password( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, email: str, password: str) -> bool:
930    def try_sign_up__email_password(self, i: Interface, email: str, password: str) -> bool:
931        raise NotImplementedError
async def atry_sign_up__email_password( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, email: str, password: str) -> bool:
933    async def atry_sign_up__email_password(self, i: Interface, email: str, password: str) -> bool:
934        raise NotImplementedError
async def on_signed_in( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, user_id: typing.Hashable, user_sign_in_info: typing.Union[typing.Dict, typing.Tuple, typing.List]) -> None:
944    async def on_signed_in(self, i: Interface, user_id: Hashable, user_sign_in_info: Union[Dict, Tuple, List]) -> None:
945        pass
async def on_signed_out( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, user_id: typing.Hashable) -> None:
956    async def on_signed_out(self, i: Interface, user_id: Hashable) -> None:
957        pass
async def on_ip_geolocation_ready( self, i: cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, ip_geolocation: typing.Union[typing.Dict, NoneType]) -> None:
959    async def on_ip_geolocation_ready(self, i: Interface, ip_geolocation: Optional[Dict]) -> None:
960        pass
def refferer(self) -> Union[str, NoneType]:
962    def refferer(self) -> Optional[str]:
963        return self.request.headers.get('referer', None)
def url(self) -> starlette.datastructures.URL:
965    def url(self) -> URL:
966        return self.request.url
def open_page( self, target: typing.Union[typing.Callable[..., typing.Any], str, NoneType] = None, new_tab: bool = False) -> Union[starlette.responses.RedirectResponse, NoneType]:
968    def open_page(self, target: Optional[Union[Callable[..., Any], str]] = None, new_tab: bool = False) -> Optional[RedirectResponse]:
969        if target is None:
970            return
971        
972        client = self.client
973        if client.has_socket_connection:
974            client.open(target, new_tab)
975        else:
976            return RedirectResponse(target)
def open_previous_page( self, default_target: typing.Union[typing.Callable[..., typing.Any], str, NoneType] = None, new_tab: bool = False, can_be_current_page: bool = False) -> Union[starlette.responses.RedirectResponse, NoneType]:
978    def open_previous_page(self, default_target: Optional[Union[Callable[..., Any], str]] = None, new_tab: bool = False, can_be_current_page: bool = False) -> Optional[RedirectResponse]:
979        target: str = self.refferer()
980        current_url: str = str(self.url())
981        if (target is None) or (False if can_be_current_page else (target == current_url)):
982            if not isinstance(default_target, str):
983                default_target = globals.page_routes[target]
984            
985            target = default_target if default_target is not None else '/'
986        
987        client = self.client
988        if client.has_socket_connection:
989            client.open(target, new_tab)
990        else:
991            return self.open_previous_page_http_response(default_target, can_be_current_page)
def open_previous_page_http_response( self, default_target: typing.Union[typing.Callable[..., typing.Any], str, NoneType] = None, can_be_current_page: bool = False) -> starlette.responses.RedirectResponse:
993    def open_previous_page_http_response(self, default_target: Optional[Union[Callable[..., Any], str]] = None, can_be_current_page: bool = False) -> RedirectResponse:
994        refferer: str = self.refferer()
995        current_url: str = str(self.url())
996        if (refferer is None) or (False if can_be_current_page else (refferer == current_url)):
997            refferer = default_target if default_target is not None else '/'
998        
999        return RedirectResponse(refferer)
def run( *, host: str = '0.0.0.0', port_or_range: typing.Union[int, slice, typing.Tuple[int, int]] = 8080, main_coro: typing.Union[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.ExplicitWorker, collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Any], collections.abc.Callable[cengal.parallel_execution.coroutines.coro_scheduler.versions.v_0.coro_scheduler.Interface, typing.Awaitable[typing.Any]]] = None, is_fast_loop: bool = True, app_name: str = '', app_name_for_fs: str = '', app_version: typing.Tuple[int, int, int, typing.Union[int, str]] = (), app_version_str: str = '', **kwargs) -> None:
1336def run(*,
1337        host: str = '0.0.0.0',
1338        port_or_range: Union[int, slice, Tuple[int, int]] = 8080,
1339        main_coro: AnyWorker = None,
1340        is_fast_loop: bool = True,
1341        app_name: str = str(),
1342        app_name_for_fs: str = str(),
1343        app_version: Tuple[int, int, int, Union[int, str]] = tuple(),
1344        app_version_str: str = str(),
1345        **kwargs
1346    ) -> None:
1347    """Prepares and starts NiceGUI. Saves initial args and kwargs parameters (as well as determined free port) into a tuple (Tuple[Tuple, Dict]) within an Instance service awailable by a 'cengal_nicegui__app_args_kwargs' string key 
1348
1349    Args:
1350        host (str, optional): _description_. Defaults to '0.0.0.0'.
1351        port_or_range (Union[int, slice, Tuple[int, int]], optional): _description_. Defaults to 8080.
1352        title (str, optional): _description_. Defaults to 'NiceGUI'.
1353        viewport (str, optional): _description_. Defaults to 'width=device-width, initial-scale=1'.
1354        favicon (Optional[str], optional): _description_. Defaults to None.
1355        dark (Optional[bool], optional): _description_. Defaults to False.
1356        binding_refresh_interval (float, optional): _description_. Defaults to 0.1.
1357        show (bool, optional): _description_. Defaults to True.
1358        reload (bool, optional): _description_. Defaults to True.
1359        uvicorn_logging_level (str, optional): _description_. Defaults to 'warning'.
1360        uvicorn_reload_dirs (str, optional): _description_. Defaults to '.'.
1361        uvicorn_reload_includes (str, optional): _description_. Defaults to '*.py'.
1362        uvicorn_reload_excludes (str, optional): _description_. Defaults to '.*, .py[cod], .sw.*, ~*'.
1363        tailwind (bool, optional): _description_. Defaults to True.
1364        is_fast_loop (bool, optional): _description_. Defaults to True.
1365        main_coro (AnyWorker, optional): _description_. Defaults to None.
1366    """
1367    port = simple_port_search(host, port_or_range)
1368    app_args_kwargs: Tuple[Tuple, Dict] = args_kwargs(
1369        host=host, 
1370        port_or_range=port_or_range, 
1371        port=port, 
1372        main_coro=main_coro,
1373        is_fast_loop=is_fast_loop,
1374        app_name=app_name,
1375        app_name_for_fs=app_name_for_fs,
1376        app_version=app_version,
1377        app_version_str=app_version_str,
1378        **kwargs,
1379    )
1380    run_in_loop(init_translation, app_args_kwargs)
1381    app.on_startup(init_cs(is_fast_loop, main_coro, app_args_kwargs))
1382    app.on_shutdown(destroy_cs)
1383    # app.on_connect(on_connect_handler)
1384    app.on_disconnect(on_disconnect_handler)
1385    ui.run(
1386        host=host,
1387        port=port,
1388        **kwargs,
1389        )

Prepares and starts NiceGUI. Saves initial args and kwargs parameters (as well as determined free port) into a tuple (Tuple[Tuple, Dict]) within an Instance service awailable by a 'cengal_nicegui__app_args_kwargs' string key

Args: host (str, optional): _description_. Defaults to '0.0.0.0'. port_or_range (Union[int, slice, Tuple[int, int]], optional): _description_. Defaults to 8080. title (str, optional): _description_. Defaults to 'NiceGUI'. viewport (str, optional): _description_. Defaults to 'width=device-width, initial-scale=1'. favicon (Optional[str], optional): _description_. Defaults to None. dark (Optional[bool], optional): _description_. Defaults to False. binding_refresh_interval (float, optional): _description_. Defaults to 0.1. show (bool, optional): _description_. Defaults to True. reload (bool, optional): _description_. Defaults to True. uvicorn_logging_level (str, optional): _description_. Defaults to 'warning'. uvicorn_reload_dirs (str, optional): _description_. Defaults to '.'. uvicorn_reload_includes (str, optional): _description_. Defaults to '.py'. uvicorn_reload_excludes (str, optional): _description_. Defaults to '., .py[cod], .sw., ~'. tailwind (bool, optional): _description_. Defaults to True. is_fast_loop (bool, optional): _description_. Defaults to True. main_coro (AnyWorker, optional): _description_. Defaults to None.