gui.workers.build_worker
1import sys 2import traceback 3from pathlib import Path 4from types import SimpleNamespace 5 6from PySide6.QtCore import QObject, Signal 7 8from gui.utils.path_setup import setup_path 9setup_path() 10 11from cli.__main__ import ( 12 generar, _cmd_clean, _cmd_version, _cmd_new, 13 _cmd_generate, _cmd_import, 14) 15 16 17class _StdoutCapture: 18 """Redirige sys.stdout → emite cada línea al callback.""" 19 20 def __init__(self, callback): 21 self._cb = callback 22 self._buf = '' 23 self._orig = sys.stdout 24 25 def write(self, text: str) -> None: 26 self._buf += text 27 while '\n' in self._buf: 28 line, self._buf = self._buf.split('\n', 1) 29 self._cb(line) 30 31 def flush(self) -> None: 32 pass 33 34 def __enter__(self): 35 sys.stdout = self 36 return self 37 38 def __exit__(self, *_) -> None: 39 if self._buf: 40 self._cb(self._buf) 41 self._buf = '' 42 sys.stdout = self._orig 43 44 45class BuildWorker(QObject): 46 """Ejecuta operaciones de build/clean/version/new en un QThread. 47 48 Uso: 49 worker = BuildWorker('build', yamls=[Path(...)]) 50 thread = QThread() 51 worker.moveToThread(thread) 52 thread.started.connect(worker.run) 53 worker.finished.connect(thread.quit) 54 thread.start() 55 """ 56 57 log_line = Signal(str, str) # (texto, nivel: 'info' | 'ok' | 'error') 58 finished = Signal(bool) # True = éxito 59 progress = Signal(int, int) # (actual, total) 60 files_generated = Signal(list) # list[tuple[Path, Path | None]] 61 62 def __init__(self, task: str, **kwargs): 63 super().__init__() 64 self._task = task 65 self._kwargs = kwargs 66 67 # ── Punto de entrada del hilo ───────────────────────────────────────────── 68 69 def run(self) -> None: 70 try: 71 with _StdoutCapture(self._on_stdout): 72 self._dispatch() 73 self.finished.emit(True) 74 except SystemExit as exc: 75 code = exc.code 76 if code and code != 0: 77 self.log_line.emit(f'Error: código de salida {code}', 'error') 78 self.finished.emit(False) 79 else: 80 self.finished.emit(True) 81 except Exception: 82 for line in traceback.format_exc().splitlines(): 83 self.log_line.emit(line, 'error') 84 self.finished.emit(False) 85 86 # ── Despacho de tareas ──────────────────────────────────────────────────── 87 88 def _dispatch(self) -> None: 89 task = self._task 90 91 if task == 'build': 92 yamls = self._kwargs.get('yamls', []) 93 _cmd_generate( 94 SimpleNamespace(targets=yamls, template=None), 95 progress_callback=lambda cur, tot: self.progress.emit(cur, tot), 96 results_callback=lambda r: self.files_generated.emit(r), 97 ) 98 99 elif task == 'clean': 100 _cmd_clean(SimpleNamespace( 101 targets=self._kwargs.get('course_dirs', []), 102 tipo=self._kwargs.get('tipo', None), 103 )) 104 105 elif task == 'version': 106 _cmd_version(SimpleNamespace( 107 targets=self._kwargs.get('course_dirs', []), 108 )) 109 110 elif task == 'new': 111 _cmd_new(SimpleNamespace(name=self._kwargs['name'])) 112 113 elif task == 'import_odp': 114 _cmd_import(Path(self._kwargs['odp_path'])) 115 116 elif task == 'watch': 117 self._run_watch() 118 119 else: 120 raise ValueError(f'Tarea desconocida: {self._task!r}') 121 122 def _run_watch(self) -> None: 123 """Watch reactivo: regenera YAMLs al guardar usando lib.watcher.""" 124 from lib.watcher import start_watch, WatchdogNotAvailable 125 from cli.__main__ import generar, _course_dir_from_yaml 126 127 yamls: list[Path] = [Path(y) for y in self._kwargs.get('yamls', [])] 128 no_pdf = self._kwargs.get('no_pdf', False) 129 template = self._kwargs.get('template', None) 130 131 def _on_change(yaml_path: Path) -> None: 132 self.log_line.emit(f'[watch] {yaml_path.name}', 'info') 133 try: 134 generar(yaml_path, template=template, no_pdf=no_pdf) 135 self.log_line.emit(f'✓ {yaml_path.name}', 'ok') 136 except Exception as exc: 137 self.log_line.emit(f'Error: {exc}', 'error') 138 139 try: 140 start_watch(yamls, _on_change) 141 except WatchdogNotAvailable as exc: 142 self.log_line.emit(str(exc), 'error') 143 raise SystemExit(1) 144 145 # ── Clasificador de nivel de log ────────────────────────────────────────── 146 147 def _on_stdout(self, line: str) -> None: 148 if not line.strip(): 149 return 150 low = line.lower() 151 if any(k in low for k in ('error', 'traceback', 'exception')): 152 level = 'error' 153 elif any(k in low for k in ('generado', 'success', 'ok', 'creado', 'versión')): 154 level = 'ok' 155 else: 156 level = 'info' 157 self.log_line.emit(line, level)
class
BuildWorker(PySide6.QtCore.QObject):
46class BuildWorker(QObject): 47 """Ejecuta operaciones de build/clean/version/new en un QThread. 48 49 Uso: 50 worker = BuildWorker('build', yamls=[Path(...)]) 51 thread = QThread() 52 worker.moveToThread(thread) 53 thread.started.connect(worker.run) 54 worker.finished.connect(thread.quit) 55 thread.start() 56 """ 57 58 log_line = Signal(str, str) # (texto, nivel: 'info' | 'ok' | 'error') 59 finished = Signal(bool) # True = éxito 60 progress = Signal(int, int) # (actual, total) 61 files_generated = Signal(list) # list[tuple[Path, Path | None]] 62 63 def __init__(self, task: str, **kwargs): 64 super().__init__() 65 self._task = task 66 self._kwargs = kwargs 67 68 # ── Punto de entrada del hilo ───────────────────────────────────────────── 69 70 def run(self) -> None: 71 try: 72 with _StdoutCapture(self._on_stdout): 73 self._dispatch() 74 self.finished.emit(True) 75 except SystemExit as exc: 76 code = exc.code 77 if code and code != 0: 78 self.log_line.emit(f'Error: código de salida {code}', 'error') 79 self.finished.emit(False) 80 else: 81 self.finished.emit(True) 82 except Exception: 83 for line in traceback.format_exc().splitlines(): 84 self.log_line.emit(line, 'error') 85 self.finished.emit(False) 86 87 # ── Despacho de tareas ──────────────────────────────────────────────────── 88 89 def _dispatch(self) -> None: 90 task = self._task 91 92 if task == 'build': 93 yamls = self._kwargs.get('yamls', []) 94 _cmd_generate( 95 SimpleNamespace(targets=yamls, template=None), 96 progress_callback=lambda cur, tot: self.progress.emit(cur, tot), 97 results_callback=lambda r: self.files_generated.emit(r), 98 ) 99 100 elif task == 'clean': 101 _cmd_clean(SimpleNamespace( 102 targets=self._kwargs.get('course_dirs', []), 103 tipo=self._kwargs.get('tipo', None), 104 )) 105 106 elif task == 'version': 107 _cmd_version(SimpleNamespace( 108 targets=self._kwargs.get('course_dirs', []), 109 )) 110 111 elif task == 'new': 112 _cmd_new(SimpleNamespace(name=self._kwargs['name'])) 113 114 elif task == 'import_odp': 115 _cmd_import(Path(self._kwargs['odp_path'])) 116 117 elif task == 'watch': 118 self._run_watch() 119 120 else: 121 raise ValueError(f'Tarea desconocida: {self._task!r}') 122 123 def _run_watch(self) -> None: 124 """Watch reactivo: regenera YAMLs al guardar usando lib.watcher.""" 125 from lib.watcher import start_watch, WatchdogNotAvailable 126 from cli.__main__ import generar, _course_dir_from_yaml 127 128 yamls: list[Path] = [Path(y) for y in self._kwargs.get('yamls', [])] 129 no_pdf = self._kwargs.get('no_pdf', False) 130 template = self._kwargs.get('template', None) 131 132 def _on_change(yaml_path: Path) -> None: 133 self.log_line.emit(f'[watch] {yaml_path.name}', 'info') 134 try: 135 generar(yaml_path, template=template, no_pdf=no_pdf) 136 self.log_line.emit(f'✓ {yaml_path.name}', 'ok') 137 except Exception as exc: 138 self.log_line.emit(f'Error: {exc}', 'error') 139 140 try: 141 start_watch(yamls, _on_change) 142 except WatchdogNotAvailable as exc: 143 self.log_line.emit(str(exc), 'error') 144 raise SystemExit(1) 145 146 # ── Clasificador de nivel de log ────────────────────────────────────────── 147 148 def _on_stdout(self, line: str) -> None: 149 if not line.strip(): 150 return 151 low = line.lower() 152 if any(k in low for k in ('error', 'traceback', 'exception')): 153 level = 'error' 154 elif any(k in low for k in ('generado', 'success', 'ok', 'creado', 'versión')): 155 level = 'ok' 156 else: 157 level = 'info' 158 self.log_line.emit(line, level)
Ejecuta operaciones de build/clean/version/new en un QThread.
Uso:
worker = BuildWorker('build', yamls=[Path(...)]) thread = QThread() worker.moveToThread(thread) thread.started.connect(worker.run) worker.finished.connect(thread.quit) thread.start()
def
run(self) -> None:
70 def run(self) -> None: 71 try: 72 with _StdoutCapture(self._on_stdout): 73 self._dispatch() 74 self.finished.emit(True) 75 except SystemExit as exc: 76 code = exc.code 77 if code and code != 0: 78 self.log_line.emit(f'Error: código de salida {code}', 'error') 79 self.finished.emit(False) 80 else: 81 self.finished.emit(True) 82 except Exception: 83 for line in traceback.format_exc().splitlines(): 84 self.log_line.emit(line, 'error') 85 self.finished.emit(False)
staticMetaObject =
PySide6.QtCore.QMetaObject("BuildWorker" inherits "QObject":
Methods:
#4 type=Signal, signature=log_line(QString,QString), parameters=QString, QString
#5 type=Signal, signature=finished(bool), parameters=bool
#6 type=Signal, signature=progress(int,int), parameters=int, int
#7 type=Signal, signature=files_generated(QVariantList), parameters=QVariantList
)