Skip to content

Commit 43277b7

Browse files
create a proof-of-concept for fast cpu_bound with lazy import
1 parent d87ee22 commit 43277b7

File tree

7 files changed

+83
-10
lines changed

7 files changed

+83
-10
lines changed

demo.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python3
2+
import time
3+
4+
from nicegui import run, ui # sets NICEGUI_HOST environment variable; if NICEGUI_HOST is set, imports a hull
5+
6+
7+
# unpickling does not import NiceGUI because `run` is implemented outside of nicegui module
8+
def simulate_work():
9+
print('Working...')
10+
time.sleep(1.0)
11+
print('Done.')
12+
13+
14+
# TODO: NiceGUI main_guard to selectively run user code?
15+
# if main_guard:
16+
# print('Doing heavy work...')
17+
18+
ui.button('Work', on_click=lambda: run.cpu_bound(simulate_work))
19+
20+
ui.run(reload=False) # does nothing if NICEGUI_HOST is set
21+
# ui.run() # TODO

nicegui/__init__.py

+48-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,51 @@
1-
from . import binding, elements, html, run, storage, ui
1+
# fmt: off
2+
# ruff: noqa: E402
3+
# pylint: disable=C0413
4+
import os
5+
import sys
6+
from types import ModuleType
7+
8+
9+
class WhateverModule(ModuleType):
10+
11+
def __getattr__(self, name):
12+
return Whatever()
13+
14+
class Whatever:
15+
16+
def __init__(self, *args, **kwargs):
17+
pass
18+
19+
def __getattr__(self, name):
20+
return Whatever()
21+
22+
def __call__(self, *args, **kwargs):
23+
return Whatever()
24+
25+
def __mro_entries__(self, *args, **kwargs):
26+
return (Whatever, )
27+
28+
def __add__(self, other):
29+
return Whatever()
30+
31+
if os.environ.get('NICEGUI_HOST'):
32+
sys.modules['nicegui.binding'] = WhateverModule('nicegui.binding')
33+
sys.modules['nicegui.elements'] = WhateverModule('nicegui.elements')
34+
sys.modules['nicegui.html'] = WhateverModule('nicegui.html')
35+
sys.modules['nicegui.storage'] = WhateverModule('nicegui.storage')
36+
sys.modules['nicegui.ui'] = WhateverModule('nicegui.ui')
37+
sys.modules['nicegui.api_router'] = WhateverModule('nicegui.api_router')
38+
sys.modules['nicegui.app.app'] = WhateverModule('nicegui.app.app')
39+
sys.modules['nicegui.client'] = WhateverModule('nicegui.client')
40+
sys.modules['nicegui.context'] = WhateverModule('nicegui.context')
41+
sys.modules['nicegui.element_filter'] = WhateverModule('nicegui.element_filter')
42+
sys.modules['nicegui.nicegui'] = WhateverModule('nicegui.nicegui')
43+
sys.modules['nicegui.tailwind'] = WhateverModule('nicegui.tailwind')
44+
sys.modules['nicegui.version'] = WhateverModule('nicegui.version')
45+
46+
from nicegui_run import run
47+
48+
from . import binding, elements, html, storage, ui
249
from .api_router import APIRouter
350
from .app.app import App
451
from .client import Client

nicegui/native/native.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from multiprocessing import Queue
55
from typing import Any, Callable, Tuple
66

7-
from .. import run
7+
from nicegui_run import run
8+
89
from ..logging import log
910

1011
method_queue: Queue = Queue()

nicegui/nicegui.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
from fastapi import HTTPException, Request
1010
from fastapi.responses import FileResponse, Response
1111

12-
from . import air, background_tasks, binding, core, favicon, helpers, json, run, welcome
12+
from nicegui_run import run
13+
14+
from . import air, background_tasks, binding, core, favicon, helpers, json, welcome
1315
from .app import App
1416
from .client import Client
1517
from .dependencies import js_components, libraries, resources

nicegui/welcome.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
import ifaddr
55

6-
from . import core, run
6+
from nicegui_run import run
7+
8+
from . import core
79

810

911
def _get_all_ips() -> List[str]:

nicegui_run/__init__.py

Whitespace-only changes.

nicegui/run.py nicegui_run/run.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
from typing_extensions import ParamSpec
99

10-
from . import core, helpers
11-
1210
process_pool = ProcessPoolExecutor()
1311
thread_pool = ThreadPoolExecutor()
1412

@@ -45,8 +43,9 @@ def safe_callback(callback: Callable, *args, **kwargs) -> Any:
4543

4644

4745
async def _run(executor: Any, callback: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
48-
if core.app.is_stopping:
49-
return # type: ignore # the assumption is that the user's code no longer cares about this value
46+
# TODO
47+
# if core.app.is_stopping:
48+
# return # type: ignore # the assumption is that the user's code no longer cares about this value
5049
try:
5150
loop = asyncio.get_running_loop()
5251
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)
7675

7776
def tear_down() -> None:
7877
"""Kill all processes and threads."""
79-
if helpers.is_pytest():
80-
return
78+
# TODO
79+
# if helpers.is_pytest():
80+
# return
8181
for p in process_pool._processes.values(): # pylint: disable=protected-access
8282
p.kill()
8383
kwargs = {'cancel_futures': True} if sys.version_info >= (3, 9) else {}

0 commit comments

Comments
 (0)