Watch directory

This commit is contained in:
Ruben van de Ven 2025-03-26 09:27:26 +01:00
parent 78d3b40c03
commit 78f21a834e
5 changed files with 32 additions and 16 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.pyc

View file

@ -86,6 +86,7 @@ class SubprocessController():
@classmethod @classmethod
def from_toml(cls, filename: os.PathLike): def from_toml(cls, filename: os.PathLike):
logger.info(f"Load from TOML {filename}")
path = pathlib.Path(filename) path = pathlib.Path(filename)
name = path.stem name = path.stem
args = [] args = []
@ -93,7 +94,7 @@ class SubprocessController():
data = tomllib.load(fp) data = tomllib.load(fp)
print(data) print(data)
for arg in data['arguments']: for arg in data.get('arguments', []):
args.append(Argument.from_dict(arg)) args.append(Argument.from_dict(arg))
sc = cls( sc = cls(
@ -198,10 +199,34 @@ class Foucault():
def __init__(self): def __init__(self):
self.processes: list[SubprocessController] = [] self.processes: list[SubprocessController] = []
self.uis: list[SubprocessUI] = [] self.uis: list[SubprocessUI] = []
self.directories: list[pathlib.Path] = []
def add_process(self, sc: SubprocessController): def add_process(self, sc: SubprocessController):
logger.info(f"add process {sc.as_bash_string()}")
self.processes.append(sc) self.processes.append(sc)
self.uis.append(SubprocessUI(sc)) self.uis.append(SubprocessUI(sc))
self.ui.refresh()
def watch(self, path: os.PathLike):
path = pathlib.Path(path).resolve()
if path.is_dir():
if path in self.directories:
logger.warning(f"Path already watched {path}")
self.directories.append(path)
self.update_watched()
elif path.suffix == 'toml':
self.add_process(SubprocessController.from_toml(path))
else:
raise RuntimeError(f"Not a valid path {path}")
def update_watched(self):
# TODO)) It would be great to use e.g. Watchdog
fns = [scu.sc.filename for scu in self.uis]
for d in self.directories:
config_files = d.glob("**/*.toml")
for path in config_files:
if path not in fns:
self.add_process(SubprocessController.from_toml(path))
def run_all(self): def run_all(self):
for p in self.processes: for p in self.processes:
@ -217,6 +242,7 @@ class Foucault():
self.stop_all() self.stop_all()
self.run_all() self.run_all()
@ui.refreshable_method
def ui(self): def ui(self):
with ui.row(): with ui.row():
ui.button('', on_click=self.run_all) ui.button('', on_click=self.run_all)
@ -248,6 +274,7 @@ class Foucault():
i+=1 i+=1
# if we're only interested in running, we can use os.waitpid(), but we also check config changes # if we're only interested in running, we can use os.waitpid(), but we also check config changes
time.sleep(.3) time.sleep(.3)
self.update_watched()
for proc_ui in self.uis: for proc_ui in self.uis:
state = proc_ui.sc.state() state = proc_ui.sc.state()
if state != states[proc_ui.sc]: if state != states[proc_ui.sc]:
@ -262,7 +289,7 @@ class SubprocessUI:
@ui.refreshable_method @ui.refreshable_method
def ui(self): def ui(self):
card_class = "bg-teal" if self.sc.is_running() else ("bg-warning" if self.sc.return_code() else "bg-gray") card_class = "bg-teal" if self.sc.is_running() else ("bg-warning" if self.sc.return_code() and math.abs(self.sc.return_code()) != 15 else "bg-gray")
with ui.card().classes(card_class): with ui.card().classes(card_class):
with ui.row(align_items="stretch").classes('w-full'): with ui.row(align_items="stretch").classes('w-full'):
if self.sc.return_code(): if self.sc.return_code():
@ -276,7 +303,7 @@ class SubprocessUI:
ui.button('', on_click=self.sc.restart, color='red' if self.sc.is_stale() else "primary") ui.button('', on_click=self.sc.restart, color='red' if self.sc.is_stale() else "primary")
ui.label(f'Running: {self.sc.is_running()}') ui.label(f'Running: {self.sc.is_running()}' + f"({self.sc.return_code()})")
ui.code(f'{" ".join(self.sc.cmd)}') ui.code(f'{" ".join(self.sc.cmd)}')
ui.code(self.sc.as_bash_string()) ui.code(self.sc.as_bash_string())
ui.separator() ui.separator()

14
main.py
View file

@ -2,21 +2,9 @@ from pathlib import Path
from foucault.gui import * from foucault.gui import *
# print(sc)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
conductofconduct = Foucault() conductofconduct = Foucault()
sc = SubprocessController.from_toml(Path('processes/tail.toml')) conductofconduct.watch("processes")
sc2 = SubprocessController.from_toml(Path('processes/tail2.toml'))
sc3 = SubprocessController.from_toml(Path('processes/tail.toml'))
conductofconduct.add_process(sc)
conductofconduct.add_process(sc2)
conductofconduct.add_process(sc3)
# conductofconduct.add_process(SubprocessController("test1" , ["tail"], ['-f', "gui.py"]))
# conductofconduct.add_process(SubprocessController("test2" , ["tail"], ['-f', "gui.py"]))
# conductofconduct.add_process(SubprocessController("test3 broken" , ["tail"], ['-f', "nonexistent.py"]))
# conductofconduct.add_process(SubprocessController("system status" , ["uv run status.py"], ['-f', "nonexistent.py"]))
conductofconduct.run() conductofconduct.run()