Restart the app on changes

This commit is contained in:
Isaiah Odhner 2023-04-19 12:00:34 -04:00
parent adcaed47cd
commit 140ff15ff1
5 changed files with 64 additions and 6 deletions

9
.vscode/launch.json vendored
View File

@ -27,6 +27,15 @@
"args": ["run", "--dev", "paint.py --clear-screen --inspect-layout LICENSE.txt"],
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Debug --restart-on-changes file matching",
"type": "python",
"request": "launch",
"program": "${userHome}/.local/bin/textual",
"args": ["run", "--dev", "paint.py --clear-screen --restart-on-changes"],
"console": "integratedTerminal",
"justMyCode": false
}
]
}

View File

@ -69,8 +69,8 @@ textual-paint
```
$ python3 paint.py --help
usage: paint.py [-h] [--theme {light,dark}] [--language {ar,cs,da,de,el,en,es,fi,fr,he,hu,it,ja,ko,nl,no,pl,pt,pt-br,ru,sk,sl,sv,tr,zh,zh-simplified}]
[--ascii-only-icons] [--inspect-layout] [--clear-screen]
usage: paint.py [-h] [--theme {light,dark}] [--language {ar,cs,da,de,el,en,es,fi,fr,he,hu,it,ja,ko,nl,no,pl,pt,pt-br,ru,sk,sl,sv,tr,zh,zh-simplified}] [--ascii-only-icons]
[--inspect-layout] [--clear-screen] [--restart-on-changes]
[filename]
Paint in the terminal.
@ -86,6 +86,7 @@ options:
--ascii-only-icons Use only ASCII characters for tool icons
--inspect-layout Inspect the layout with middle click, for development
--clear-screen Clear the screen before starting; useful for development, to avoid seeing fixed errors
--restart-on-changes Restart the app when the source code is changed, for development
```
### Keyboard Shortcuts
@ -113,21 +114,30 @@ Install Textual and other dependencies:
pip install "textual[dev]" stransi psutil
```
Run via Textual's CLI for live-reloading CSS support:
Run via Textual's CLI for live-reloading CSS support, and enable other development features:
```bash
textual run --dev "paint.py --clear-screen"
textual run --dev "paint.py --clear-screen --inspect-layout --restart-on-changes"
```
Or run more basically:
```bash
python paint.py --clear-screen
python paint.py
```
`--clear-screen` is useful for development, because it's sometimes jarring to see error messages that have actually been fixed, when exiting the program.
`--inspect-layout` lets you middle click to visualize the layout breakdown by labeling each widget in the hierarchy, and coloring their regions. The labels affect the layout, so you can also hold Ctrl to only colorize, and you can remember how the colors correspond to the labels.
`--restart-on-changes` automatically restarts the program when any Python files change. This works by the program restarting itself directly. (Programs like `modd` or `nodemon` that run your program in a subprocess don't work well with Textual's escape sequences.)
There are also launch tasks configured for VS Code, so you can run the program from the Run and Debug panel.
I tried running via `modd` to automatically reload the program when (non-CSS) files change, but it doesn't handle ANSI escape sequences well. I wonder if it would work better now with the `--clear-screen` option. (I could also look for another tool that's more part of the Python ecosystem.)
### Update Dependencies
```bash
python -m pipreqs.pipreqs --ignore .history --force
```
## License

View File

@ -25,6 +25,7 @@
"llpaper",
"modd",
"Odhner",
"pipreqs",
"Playscii",
"psutil",
"pycache",

View File

@ -7,6 +7,8 @@ import asyncio
from enum import Enum
from random import randint, random
from typing import List, Optional
from watchdog.events import PatternMatchingEventHandler, FileSystemEvent, EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED
from watchdog.observers import Observer
import stransi
from rich.segment import Segment
from rich.style import Style
@ -52,6 +54,36 @@ def restart_program():
# os.execl(python, python, *sys.argv)
os.execl(sys.executable, *sys.orig_argv)
class RestartHandler(PatternMatchingEventHandler):
"""A handler for file changes"""
def on_any_event(self, event: FileSystemEvent):
if event.event_type in (EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED):
# These seem like they'd just cause trouble... they're not changes, are they?
return
restart_program()
def restart_on_changes():
"""Restarts the current program when a file is changed"""
observer = Observer()
observer.schedule(RestartHandler(
# Don't need to restart on changes to .css, since Textual will reload them in --dev mode
# Could include localization files, but I'm not actively localizing this app at this point.
# WET: WatchDog doesn't match zero directories for **, so we have to split up any patterns that use it.
patterns=[
"**/*.py", "*.py"
],
ignore_patterns=[
".history/**/*", ".history/*",
".vscode/**/*", ".vscode/*",
".git/**/*", ".git/*",
"node_modules/**/*", "node_modules/*",
"__pycache__/**/*", "__pycache__/*",
"venv/**/*", "venv/*",
],
ignore_directories=True,
), path='.', recursive=True)
observer.start()
# These can go away now that args are parsed up top
ascii_only_icons = False
@ -70,7 +102,9 @@ parser.add_argument('--inspect-layout', action='store_true', help='Inspect the l
# There are enough ACTUAL "that should have worked!!" moments to deal with.
# I really don't want false ones mixed in. You want to reward your brain for finding good solutions, after all.
parser.add_argument('--clear-screen', action='store_true', help='Clear the screen before starting; useful for development, to avoid seeing fixed errors')
parser.add_argument('--restart-on-changes', action='store_true', help='Restart the app when the source code is changed, for development')
parser.add_argument('filename', nargs='?', default=None, help='File to open')
if __name__ == "<run_path>":
# Arguments have to be passed like `textual run --dev "paint.py LICENSE.txt"`
# so we need to look for an argument starting with "paint.py",
@ -85,6 +119,9 @@ else:
load_language(args.language)
if args.restart_on_changes:
restart_on_changes()
# Most arguments are handled at the end of the file.
class Tool(Enum):

View File

@ -2,3 +2,4 @@ psutil==5.9.0
rich==13.3.4
stransi==0.3.0
textual==0.20.1
watchdog==3.0.0