patx/veela
Sync source comments and clarify worker documentation
Commit 21e6819 · patx · 2026-10-13T23:17:57-04:00
Comments
No comments yet.
Diff
diff --git a/README.md b/README.md
index 118e1c3..86410f6 100644
--- a/README.md
+++ b/README.md
@@ -154,11 +154,17 @@ Four workers. One file. Please allow the procurement department a moment.
The default is `127.0.0.1:8000` with one worker. No
`if __name__ == '__main__':` guard is needed, including with multiple workers.
An existing guard around `app.run()` is fine too. Multiple workers need an app
-defined at module level outside that guard, and a Python script or importable
-module (not a REPL or `python -c`). Start them from the main thread.
-Workers re-execute application setup and have separate memory.
+defined at module level: put `app = App()` and your route registrations directly
+in the file, outside functions and the main guard, as in the example above.
+Each worker loads that named app in its own process, re-executes application
+setup, and has separate memory.
+
+`workers` must be a positive integer. Start multiple workers from the main
+thread using a Python script or `python -m your_module` (including packages),
+not a REPL or `python -c`.
Ctrl+C and SIGTERM stop the server, join workers, and remove the temporary
server archive before `run()` returns.
+
The bundled server uses asyncio and h11, with WebSockets and lifespan disabled.
`app.run()` also needs `uvicorn`, `h11`, and `click` not to be imported already.
The magic prefers to make its own introductions.
diff --git a/source/wred.py b/source/wred.py
index e2507de..e2a1c61 100644
--- a/source/wred.py
+++ b/source/wred.py
@@ -114,9 +114,6 @@ class App:
import threading
from functools import partial
- # Spawn re-executes the entry module before loading the worker target.
- # Python exposes no public bootstrap-state check; this is the same flag
- # multiprocessing uses to detect an unsafe recursive start.
if _loading_worker_app or getattr(multiprocessing.current_process(), '_inheriting', False):
return
if type(workers) is not int or workers < 1:
@@ -133,7 +130,6 @@ class App:
if module == '__main__':
spec = namespace.get('__spec__')
if spec is not None and spec.name.endswith('.__main__'):
- # multiprocessing does not re-execute package __main__ modules.
module = spec.name
elif not Path(namespace.get('__file__', '')).is_file():
raise ValueError('Multiple workers require a Python script or importable module')
@@ -167,7 +163,6 @@ class App:
try:
supervisor.run()
except BaseException:
- # Uvicorn's supervisor does not clean up a partial start.
supervisor.terminate_all()
supervisor.join_all()
raise
@@ -184,7 +179,6 @@ class App:
if path == archive or path.startswith((archive + '/', archive + '\\')):
del sys.path_importer_cache[path]
except KeyboardInterrupt:
- # Also cover Ctrl+C during imports and startup, before Uvicorn owns it.
pass
finally:
for sig, handler in handlers.items():
diff --git a/wred.py b/wred.py
index 745b620..20ebfac 100644
--- a/wred.py
+++ b/wred.py
@@ -114,9 +114,6 @@ class App:
import threading
from functools import partial
- # Spawn re-executes the entry module before loading the worker target.
- # Python exposes no public bootstrap-state check; this is the same flag
- # multiprocessing uses to detect an unsafe recursive start.
if _loading_worker_app or getattr(multiprocessing.current_process(), '_inheriting', False):
return
if type(workers) is not int or workers < 1:
@@ -133,7 +130,6 @@ class App:
if module == '__main__':
spec = namespace.get('__spec__')
if spec is not None and spec.name.endswith('.__main__'):
- # multiprocessing does not re-execute package __main__ modules.
module = spec.name
elif not Path(namespace.get('__file__', '')).is_file():
raise ValueError('Multiple workers require a Python script or importable module')
@@ -167,7 +163,6 @@ class App:
try:
supervisor.run()
except BaseException:
- # Uvicorn's supervisor does not clean up a partial start.
supervisor.terminate_all()
supervisor.join_all()
raise
@@ -184,7 +179,6 @@ class App:
if path == archive or path.startswith((archive + '/', archive + '\\')):
del sys.path_importer_cache[path]
except KeyboardInterrupt:
- # Also cover Ctrl+C during imports and startup, before Uvicorn owns it.
pass
finally:
for sig, handler in handlers.items():