From 4af18b6c70149a3fb76e3855da36931aea7cf80f Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Fri, 12 Mar 2021 22:53:02 +0100 Subject: [PATCH] Implemented live development server --- src/cpl_cli/cli.py | 2 + src/cpl_cli/command/start_service.py | 68 +++++++++++++++++++ src/cpl_cli/live_server/__init__.py | 0 .../live_server/file_change_handler.py | 23 +++++++ src/cpl_cli/live_server/live_server.py | 38 +++++++++++ src/cpl_cli/startup.py | 3 + src/tests/custom/general/application.py | 10 ++- 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/cpl_cli/live_server/__init__.py create mode 100644 src/cpl_cli/live_server/file_change_handler.py create mode 100644 src/cpl_cli/live_server/live_server.py diff --git a/src/cpl_cli/cli.py b/src/cpl_cli/cli.py index 3b722432..004916c3 100644 --- a/src/cpl_cli/cli.py +++ b/src/cpl_cli/cli.py @@ -5,6 +5,7 @@ from cpl_cli.command.build_service import BuildService from cpl_cli.command.generate_service import GenerateService from cpl_cli.command.new_service import NewService from cpl_cli.command.publish_service import PublishService +from cpl_cli.command.start_service import StartService from cpl_cli.command_handler_service import CommandHandler from cpl_cli.command_model import CommandModel from cpl_cli.error import Error @@ -27,6 +28,7 @@ class CLI(ApplicationABC): self._command_handler.add_command(CommandModel('help', ['h', 'H'], HelpService, False)) self._command_handler.add_command(CommandModel('new', ['n', 'N'], NewService, False)) self._command_handler.add_command(CommandModel('publish', ['p', 'P'], PublishService, True)) + self._command_handler.add_command(CommandModel('start', ['s', 'S'], StartService, True)) self._command_handler.add_command(CommandModel('version', ['v', 'V'], VersionService, False)) def main(self): diff --git a/src/cpl_cli/command/start_service.py b/src/cpl_cli/command/start_service.py index e69de29b..90d1d844 100644 --- a/src/cpl_cli/command/start_service.py +++ b/src/cpl_cli/command/start_service.py @@ -0,0 +1,68 @@ +import os +import signal +import subprocess +import time + +import psutil as psutil +from watchdog.events import FileSystemEventHandler +from watchdog.observers import Observer + +from cpl.application import ApplicationRuntimeABC +from cpl.console.console import Console +from cpl_cli.command_abc import CommandABC +from cpl_cli.configuration import BuildSettings +from cpl_cli.live_server.live_server import LiveServerThread + + +class StartService(CommandABC, FileSystemEventHandler): + + def __init__(self, runtime: ApplicationRuntimeABC, build_settings: BuildSettings): + CommandABC.__init__(self) + FileSystemEventHandler.__init__(self) + + self._runtime = runtime + self._build_settings = build_settings + + self._src_dir = os.path.join(self._runtime.working_directory, self._build_settings.source_path) + self._live_server = LiveServerThread(self._src_dir) + self._observer = None + + def _start_observer(self): + self._observer = Observer() + self._observer.schedule(self, path=self._src_dir, recursive=True) + self._observer.start() + + def _restart(self): + for proc in psutil.process_iter(): + try: + if proc.cmdline() == self._live_server.command: + os.system(f'pkill -f {self._live_server.main}') + except Exception as e: + pass + + Console.write_line('Restart\n') + + while self._live_server.is_alive(): + time.sleep(1) + + self._live_server = LiveServerThread(self._src_dir) + self._live_server.start() + + self._start_observer() + + def on_modified(self, event): + if event.is_directory: + return None + + # Event is modified, you can process it now + if str(event.src_path).endswith('.py'): + self._observer.stop() + self._restart() + + def run(self, args: list[str]): + Console.write_line('** CPL live development server is running **') + self._start_observer() + self._live_server.start() + + Console.close() + Console.write('\n') diff --git a/src/cpl_cli/live_server/__init__.py b/src/cpl_cli/live_server/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/cpl_cli/live_server/file_change_handler.py b/src/cpl_cli/live_server/file_change_handler.py new file mode 100644 index 00000000..9cb71617 --- /dev/null +++ b/src/cpl_cli/live_server/file_change_handler.py @@ -0,0 +1,23 @@ +from watchdog.events import FileSystemEventHandler + +from cpl.console.console import Console +from cpl_cli.live_server.live_server import LiveServerThread + + +class FileChangeHandler(FileSystemEventHandler): + + def __init__(self, live_server: LiveServerThread): + FileSystemEventHandler.__init__(self) + + self._live_server = live_server + + def on_any_event(self, event): + if event.is_directory: + return None + + elif event.event_type == 'modified': + # Event is modified, you can process it now + if str(event.src_path).endswith('.py'): + Console.write_line(f'Detected change in {event.src_path}') + self._live_server.kill_application() + self._live_server.start() diff --git a/src/cpl_cli/live_server/live_server.py b/src/cpl_cli/live_server/live_server.py new file mode 100644 index 00000000..4f64ed49 --- /dev/null +++ b/src/cpl_cli/live_server/live_server.py @@ -0,0 +1,38 @@ +import os +import subprocess +import sys +import threading +from datetime import datetime + +from cpl.console import Console + + +class LiveServerThread(threading.Thread): + + def __init__(self, path: str): + threading.Thread.__init__(self) + + self._path = path + self._main = '' + self._command = [] + + @property + def command(self) -> list[str]: + return self._command + + @property + def main(self) -> str: + return self._main + + def run(self): + self._main = os.path.join(self._path, 'main.py') + if not os.path.isfile(self._main): + Console.error('Entry point main.py does not exist') + return + + Console.write_line('Read successfully') + now = datetime.now() + Console.write_line(f'Started at {now.strftime("%Y-%m-%d %H:%M:%S")}\n\n') + + self._command = [sys.executable, self._main, ''.join(sys.argv[2:])] + subprocess.run(self._command) diff --git a/src/cpl_cli/startup.py b/src/cpl_cli/startup.py index 2909e614..acc29898 100644 --- a/src/cpl_cli/startup.py +++ b/src/cpl_cli/startup.py @@ -10,6 +10,7 @@ from cpl_cli.command.build_service import BuildService from cpl_cli.command.generate_service import GenerateService from cpl_cli.command.new_service import NewService from cpl_cli.command.publish_service import PublishService +from cpl_cli.command.start_service import StartService from cpl_cli.command_handler_service import CommandHandler from cpl_cli.command.help_service import HelpService from cpl_cli.command.version_service import VersionService @@ -56,6 +57,7 @@ class Startup(StartupABC): ConsoleArgument('', 'console', ['c', 'C'], ' ') ])) self._configuration.add_console_argument(ConsoleArgument('', 'publish', ['p', 'P'], '')) + self._configuration.add_console_argument(ConsoleArgument('', 'start', ['s', 'S'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'version', ['v', 'V'], '')) self._configuration.add_console_arguments() @@ -71,6 +73,7 @@ class Startup(StartupABC): self._services.add_transient(HelpService) self._services.add_transient(NewService) self._services.add_transient(PublishService) + self._services.add_transient(StartService) self._services.add_transient(VersionService) return self._services diff --git a/src/tests/custom/general/application.py b/src/tests/custom/general/application.py index 2cdecb8c..b525dcd1 100644 --- a/src/tests/custom/general/application.py +++ b/src/tests/custom/general/application.py @@ -1,3 +1,4 @@ +import time from typing import Optional from cpl.application.application_abc import ApplicationABC @@ -19,7 +20,7 @@ class Application(ApplicationABC): mail.add_header('Mime-Version: 1.0') mail.add_header('Content-Type: text/plain; charset=utf-8') mail.add_header('Content-Transfer-Encoding: quoted-printable') - mail.add_receiver('sven.heidemann@sh-edraft.de') + mail.add_receiver(' sven.heidemann@sh-edraft.de') mail.subject = f'Test - {self._configuration.environment.host_name}' mail.body = 'Dies ist ein Test :D' self._mailer.send_mail(mail) @@ -28,11 +29,15 @@ class Application(ApplicationABC): self._logger.debug(__name__, 'Started console_old test') Console.write_line('Hello World') Console.write('\nName: ') - Console.write_line('Hello', Console.read_line()) + Console.write_line(' Hello', Console.read_line()) Console.clear() Console.write_at(5, 5, 'at 5, 5') Console.write_at(10, 10, 'at 10, 10') + @staticmethod + def _wait(time_ms: int): + time.sleep(time_ms) + def configure(self): self._logger = self._services.get_service(LoggerABC) self._mailer = self._services.get_service(EMailClientABC) @@ -42,5 +47,6 @@ class Application(ApplicationABC): self._logger.debug(__name__, f'Host: {self._configuration.environment.host_name}') self._logger.debug(__name__, f'Environment: {self._configuration.environment.environment_name}') self._logger.debug(__name__, f'Customer: {self._configuration.environment.customer}') + Console.spinner('Test', self._wait, 999999, spinner_foreground_color='red') # self.test_send_mail() # self.test_console()