nib run¶
Run a Nib application in development mode with automatic hot reload on file changes.
Usage¶
nib run [script.py] [-r] [-v | -vv]
If no script is specified, nib run reads the entry point from pyproject.toml:
nib run # Uses [tool.nib] entry (defaults to src/main.py)
nib run src/main.py # Explicit script path
nib run my_project/ # Runs my_project/src/main.py (enables recursive watching)
Options¶
| Flag | Description |
|---|---|
-r, --recursive |
Watch subdirectories for changes |
-v, --verbose |
Info-level logs. -vv for debug-level logs |
How it works¶
When you run nib run, the following happens:
- The Swift runtime (
nib-runtime) is launched as a separate process - A Unix socket connection is established between Python and Swift
- Your Python script is executed and the UI is rendered
- A file watcher monitors
.pyfiles for changes
On every file save, only the Python side restarts. The Swift runtime stays alive. This means the menu bar icon persists and UI updates are near-instant.
Save file --> Python reloads --> new UI sent to Swift runtime (still running)
Hot reload¶
The watcher triggers a reload whenever a .py file is modified, created or moved into place (editors that save atomically rename a temporary file over the original). The reload process:
- Calls the previous app's
on_quitcallback and sets itsis_runningtoFalse - Clears cached user modules from
sys.modules - Re-executes your script from scratch
- Sends the updated view tree to the Swift runtime
A file saved while a reload is in progress queues one more reload. Reloads run on the main thread.
Background threads
A thread started by your app keeps running across reloads unless it stops itself. Loop on app.is_running, or stop it from app.on_quit:
python
def main(app: nib.App):
def tick():
while app.is_running:
time.sleep(1)
threading.Thread(target=tick, daemon=True).start()
Debouncing
Rapid saves are coalesced with a 100ms debounce window. If you save multiple times in quick succession, only one reload is triggered.
Excluded directories¶
The watcher ignores changes in these directories, matched relative to the script's directory (a project that itself lives under build/ or venv/ still reloads):
__pycache__.git.venv,venvnode_modules.mypy_cache,.pytest_cache,.ruff_cachedist,build.eggs
Recursive watching¶
By default, only the script's parent directory is watched (non-recursively). Use -r to watch all subdirectories:
nib run src/main.py -r # Watches src/ and all its subdirectories
When you pass a directory instead of a script, recursive watching is enabled automatically:
nib run my_project/ # Equivalent to: nib run my_project/src/main.py -r
Error handling¶
Syntax errors and runtime exceptions are displayed in the terminal. The Swift runtime keeps running, so you can fix the error and save to trigger another reload.
$ nib run
File changed: main.py
Reloading...
Syntax error in /path/to/main.py:12
if True
^
expected ':'
Fix the error and save to reload...
No need to restart. Just fix the problem and save.
Stopping¶
Press Ctrl+C to stop both the Python process and the Swift runtime. The Unix socket is cleaned up automatically.
Related¶
- Quick Start:
nib create,nib run,nib build - nib build
- pyproject.toml Config:
entryand other defaults - Logging:
NIB_LOG_LEVEL, log file