From 02ce6c5c6406e5ec1028d9b0f8a706dd42249d3a Mon Sep 17 00:00:00 2001 From: bot Date: Sun, 14 Jun 2026 15:24:16 +0000 Subject: [PATCH] chore: bump zhurnal version to 0.2.0 and fix dependency specifier spacing in requires.txt --- dist/zhurnal-0.2.0-py3-none-any.whl | Bin 0 -> 7679 bytes dist/zhurnal-0.2.0.tar.gz | Bin 0 -> 8228 bytes src/zhurnal.egg-info/PKG-INFO | 4 +-- src/zhurnal.egg-info/requires.txt | 2 +- src/zhurnal/app.py | 40 ++++++++++++++++------------ 5 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 dist/zhurnal-0.2.0-py3-none-any.whl create mode 100644 dist/zhurnal-0.2.0.tar.gz diff --git a/dist/zhurnal-0.2.0-py3-none-any.whl b/dist/zhurnal-0.2.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d495dd6c2f06b4a0ee9b72a8fe519dbb6eb6ac69 GIT binary patch literal 7679 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 F`XBUm2`B&n literal 0 HcmV?d00001 diff --git a/src/zhurnal.egg-info/PKG-INFO b/src/zhurnal.egg-info/PKG-INFO index 14c61d2..8a6522d 100644 --- a/src/zhurnal.egg-info/PKG-INFO +++ b/src/zhurnal.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: zhurnal -Version: 0.1.0 +Version: 0.2.0 Summary: Process monitoring and management web application Author: retoor Author-email: retoor@molodetz.nl @@ -8,7 +8,7 @@ Requires-Python: >=3.8 Description-Content-Type: text/markdown Requires-Dist: aiohttp Requires-Dist: setuptools -Requires-Dist: app@ git+https://retoor.molodetz.nl/retoor/app.git +Requires-Dist: app @ git+https://retoor.molodetz.nl/retoor/app.git # Zhurnal: Real-Time Process Monitoring Web Interface 🚀 diff --git a/src/zhurnal.egg-info/requires.txt b/src/zhurnal.egg-info/requires.txt index 95a12c3..b326dce 100644 --- a/src/zhurnal.egg-info/requires.txt +++ b/src/zhurnal.egg-info/requires.txt @@ -1,3 +1,3 @@ aiohttp setuptools -app@ git+https://retoor.molodetz.nl/retoor/app.git +app @ git+https://retoor.molodetz.nl/retoor/app.git diff --git a/src/zhurnal/app.py b/src/zhurnal/app.py index 8e4f65b..b418b85 100644 --- a/src/zhurnal/app.py +++ b/src/zhurnal/app.py @@ -209,15 +209,15 @@ class Zhurnal(BaseApplication): self.process_tasks = {} self.shutdown_event = asyncio.Event() super().__init__(*args, **kwargs) - + # Register signal handlers for graceful shutdown loop = asyncio.get_event_loop() for sig in (signal.SIGINT, signal.SIGTERM): loop.add_signal_handler(sig, self.handle_exit) - + self.on_startup.append(self.start_processes) self.on_cleanup.append(self.cleanup_processes) - + self.clients = [] self.router.add_get("/ws", self.websocket_handler) self.router.add_get("/", self.index_handler) @@ -231,12 +231,12 @@ class Zhurnal(BaseApplication): async def cleanup_processes(self, app): """Cleanup all running subprocesses.""" log.info("Cleaning up processes...") - + # Terminate all running processes for command, process_info in list(self.processes.items()): try: # Get the subprocess - process = process_info.get('process') + process = process_info.get("process") if process and process.returncode is None: log.info(f"Terminating process: {command}") try: @@ -250,7 +250,7 @@ class Zhurnal(BaseApplication): process.kill() except Exception as e: log.error(f"Error cleaning up process {command}: {e}") - + # Cancel any running tasks for task in list(self.process_tasks.values()): if not task.done(): @@ -297,13 +297,10 @@ class Zhurnal(BaseApplication): stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) - + # Store process information - self.processes[command] = { - 'process': process, - 'name': process_name - } - + self.processes[command] = {"process": process, "name": process_name} + log.info(f"Running process {process_name}: {command}") async def read_output(app, name, process, f, is_stderr=False): @@ -313,15 +310,17 @@ class Zhurnal(BaseApplication): # Check if shutdown is requested if self.shutdown_event.is_set(): break - + time_current = time.time() time_elapsed = round( time_previous and time_current - time_previous or 0, 4 ) decoded_line = line.decode("utf-8", "ignore").strip() print(decoded_line) - decoded_line = "".join(c for c in decoded_line if c.isprintable()) - + decoded_line = "".join( + c for c in decoded_line if c.isprintable() + ) + # Broadcast to all WebSocket clients for client in app.clients: await client.send_str( @@ -346,7 +345,13 @@ class Zhurnal(BaseApplication): # Read stdout and stderr concurrently await asyncio.gather( read_output(self, f"{process_name}:stdout", process, process.stdout), - read_output(self, f"{process_name}:stderr", process, process.stderr, is_stderr=True) + read_output( + self, + f"{process_name}:stderr", + process, + process.stderr, + is_stderr=True, + ), ) # Wait for process to complete @@ -361,7 +366,7 @@ class Zhurnal(BaseApplication): log.error( f"Process {process_name}:{command} exited with non-zero status {process.returncode}." ) - + except Exception as e: log.error(f"Error running process {process_name}: {e}") finally: @@ -422,5 +427,6 @@ def cli(): app.run(host=args.host, port=args.port) + if __name__ == "__main__": cli()