Explorar o código

create a proof-of-concept for fast cpu_bound with lazy import

Falko Schindler hai 2 meses
pai
achega
43277b747e
Modificáronse 7 ficheiros con 83 adicións e 10 borrados
  1. 21 0
      demo.py
  2. 48 1
      nicegui/__init__.py
  3. 2 1
      nicegui/native/native.py
  4. 3 1
      nicegui/nicegui.py
  5. 3 1
      nicegui/welcome.py
  6. 0 0
      nicegui_run/__init__.py
  7. 6 6
      nicegui_run/run.py

+ 21 - 0
demo.py

@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+import time
+
+from nicegui import run, ui  # sets NICEGUI_HOST environment variable; if NICEGUI_HOST is set, imports a hull
+
+
+# unpickling does not import NiceGUI because `run` is implemented outside of nicegui module
+def simulate_work():
+    print('Working...')
+    time.sleep(1.0)
+    print('Done.')
+
+
+# TODO: NiceGUI main_guard to selectively run user code?
+# if main_guard:
+#     print('Doing heavy work...')
+
+ui.button('Work', on_click=lambda: run.cpu_bound(simulate_work))
+
+ui.run(reload=False)  # does nothing if NICEGUI_HOST is set
+# ui.run()  # TODO

+ 48 - 1
nicegui/__init__.py

@@ -1,4 +1,51 @@
-from . import binding, elements, html, run, storage, ui
+# fmt: off
+# ruff: noqa: E402
+# pylint: disable=C0413
+import os
+import sys
+from types import ModuleType
+
+
+class WhateverModule(ModuleType):
+
+    def __getattr__(self, name):
+        return Whatever()
+
+class Whatever:
+
+    def __init__(self, *args, **kwargs):
+        pass
+
+    def __getattr__(self, name):
+        return Whatever()
+
+    def __call__(self, *args, **kwargs):
+        return Whatever()
+
+    def __mro_entries__(self, *args, **kwargs):
+        return (Whatever, )
+
+    def __add__(self, other):
+        return Whatever()
+
+if os.environ.get('NICEGUI_HOST'):
+    sys.modules['nicegui.binding'] = WhateverModule('nicegui.binding')
+    sys.modules['nicegui.elements'] = WhateverModule('nicegui.elements')
+    sys.modules['nicegui.html'] = WhateverModule('nicegui.html')
+    sys.modules['nicegui.storage'] = WhateverModule('nicegui.storage')
+    sys.modules['nicegui.ui'] = WhateverModule('nicegui.ui')
+    sys.modules['nicegui.api_router'] = WhateverModule('nicegui.api_router')
+    sys.modules['nicegui.app.app'] = WhateverModule('nicegui.app.app')
+    sys.modules['nicegui.client'] = WhateverModule('nicegui.client')
+    sys.modules['nicegui.context'] = WhateverModule('nicegui.context')
+    sys.modules['nicegui.element_filter'] = WhateverModule('nicegui.element_filter')
+    sys.modules['nicegui.nicegui'] = WhateverModule('nicegui.nicegui')
+    sys.modules['nicegui.tailwind'] = WhateverModule('nicegui.tailwind')
+    sys.modules['nicegui.version'] = WhateverModule('nicegui.version')
+
+from nicegui_run import run
+
+from . import binding, elements, html, storage, ui
 from .api_router import APIRouter
 from .app.app import App
 from .client import Client

+ 2 - 1
nicegui/native/native.py

@@ -4,7 +4,8 @@ import warnings
 from multiprocessing import Queue
 from typing import Any, Callable, Tuple
 
-from .. import run
+from nicegui_run import run
+
 from ..logging import log
 
 method_queue: Queue = Queue()

+ 3 - 1
nicegui/nicegui.py

@@ -9,7 +9,9 @@ import socketio
 from fastapi import HTTPException, Request
 from fastapi.responses import FileResponse, Response
 
-from . import air, background_tasks, binding, core, favicon, helpers, json, run, welcome
+from nicegui_run import run
+
+from . import air, background_tasks, binding, core, favicon, helpers, json, welcome
 from .app import App
 from .client import Client
 from .dependencies import js_components, libraries, resources

+ 3 - 1
nicegui/welcome.py

@@ -3,7 +3,9 @@ from typing import List
 
 import ifaddr
 
-from . import core, run
+from nicegui_run import run
+
+from . import core
 
 
 def _get_all_ips() -> List[str]:

+ 0 - 0
nicegui_run/__init__.py


+ 6 - 6
nicegui/run.py → nicegui_run/run.py

@@ -7,8 +7,6 @@ from typing import Any, Callable, TypeVar
 
 from typing_extensions import ParamSpec
 
-from . import core, helpers
-
 process_pool = ProcessPoolExecutor()
 thread_pool = ThreadPoolExecutor()
 
@@ -45,8 +43,9 @@ def safe_callback(callback: Callable, *args, **kwargs) -> Any:
 
 
 async def _run(executor: Any, callback: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
-    if core.app.is_stopping:
-        return  # type: ignore  # the assumption is that the user's code no longer cares about this value
+    # TODO
+    # if core.app.is_stopping:
+    #     return  # type: ignore  # the assumption is that the user's code no longer cares about this value
     try:
         loop = asyncio.get_running_loop()
         return await loop.run_in_executor(executor, partial(callback, *args, **kwargs))
@@ -76,8 +75,9 @@ async def io_bound(callback: Callable[P, R], *args: P.args, **kwargs: P.kwargs)
 
 def tear_down() -> None:
     """Kill all processes and threads."""
-    if helpers.is_pytest():
-        return
+    # TODO
+    # if helpers.is_pytest():
+    #     return
     for p in process_pool._processes.values():  # pylint: disable=protected-access
         p.kill()
     kwargs = {'cancel_futures': True} if sys.version_info >= (3, 9) else {}