Pārlūkot izejas kodu

run.py: little refactoring to gain basic android compatibility (#4683)

This PR refactors the initialization of the ThreadPoolExecutor and the
ProcessPoolExecutor in `run.py` a little. As a result they get
initialized on the first call to `io_bound` respectively `cpu_bound` on
demand.

This is necessary to achieve basic android compatibility (#578) since
named semaphores are not available on android which the named Executors
use and therefor throw an exception on initialization.

With this change nicegui runs on android under the following conditions:
- calling `run.io_bound` and `run.cpu_bound` will throw exceptions
(named semaphores not available)
- host must not equal '0.0.0.0' for now (because otherwise `welcome.py`
makes an `io_bound` call)

`run.io_bound` and `run.cpu_bound` support might follow in a separate
PR.

With this PR the basic example app at https://github.com/jeff-dh/niceApp
works on android (at least in my environment)

---------

Co-authored-by: Rodja Trappe <rodja@zauberzeug.com>
jeff 1 nedēļu atpakaļ
vecāks
revīzija
97fbf7a6b2
1 mainītis faili ar 13 papildinājumiem un 5 dzēšanām
  1. 13 5
      nicegui/run.py

+ 13 - 5
nicegui/run.py

@@ -1,4 +1,5 @@
 import asyncio
+import logging
 import sys
 import traceback
 from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
@@ -19,7 +20,10 @@ R = TypeVar('R')
 def setup() -> None:
     """Setup the process pool. (For internal use only.)"""
     global process_pool  # pylint: disable=global-statement # noqa: PLW0603
-    process_pool = ProcessPoolExecutor()
+    try:
+        process_pool = ProcessPoolExecutor()
+    except NotImplementedError:
+        logging.warning('Failed to initialize ProcessPoolExecutor')
 
 
 class SubprocessException(Exception):
@@ -72,6 +76,9 @@ async def cpu_bound(callback: Callable[P, R], *args: P.args, **kwargs: P.kwargs)
     It is encouraged to create static methods (or free functions) which get all the data as simple parameters (eg. no class/ui logic)
     and return the result (instead of writing it in class properties or global variables).
     """
+    if process_pool is None:
+        raise RuntimeError('Process pool not set up.')
+
     return await _run(process_pool, safe_callback, callback, *args, **kwargs)
 
 
@@ -84,9 +91,10 @@ def tear_down() -> None:
     """Kill all processes and threads."""
     if helpers.is_pytest():
         return
-    assert process_pool is not None
-    for p in process_pool._processes.values():  # pylint: disable=protected-access
-        p.kill()
+
     kwargs = {'cancel_futures': True} if sys.version_info >= (3, 9) else {}
-    process_pool.shutdown(wait=True, **kwargs)
+    if process_pool is not None:
+        for p in process_pool._processes.values():  # pylint: disable=protected-access
+            p.kill()
+        process_pool.shutdown(wait=True, **kwargs)
     thread_pool.shutdown(wait=False, **kwargs)