From 0a65a265835bfe06f4eeea81be811e18275d6a02 Mon Sep 17 00:00:00 2001 From: Nathan LeRoy Date: Sat, 25 Mar 2023 11:43:58 -0400 Subject: [PATCH 1/2] add file-watch functionality --- .gitignore | 1 + markmeld/_version.py | 2 +- markmeld/cli.py | 28 +++++++++++++-- markmeld/watcher.py | 59 +++++++++++++++++++++++++++++++ requirements/requirements-all.txt | 1 + 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 markmeld/watcher.py diff --git a/.gitignore b/.gitignore index 360f4ed..7ea83a5 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ __pycache__/ *ipynb_checkpoints* hello_looper-master* +venv/ \ No newline at end of file diff --git a/markmeld/_version.py b/markmeld/_version.py index fa831be..0664c53 100644 --- a/markmeld/_version.py +++ b/markmeld/_version.py @@ -1 +1 @@ -__version__ = "0.2.0-dev" +__version__ = "0.3.0-dev" diff --git a/markmeld/cli.py b/markmeld/cli.py index 62bd848..79c0fef 100644 --- a/markmeld/cli.py +++ b/markmeld/cli.py @@ -8,6 +8,7 @@ from .exceptions import * from .melder import MarkdownMelder +from .watcher import MarkmeldWatchDog from .utilities import load_config_file, get_file_open_cmd from ._version import __version__ @@ -48,12 +49,11 @@ def build_argparser(): "--init", dest="init", metavar="I", - nargs='?', + nargs="?", const="_markmeld.yaml", help="Initilize config file", ) - parser.add_argument( "-c", "--config", @@ -65,6 +65,14 @@ def build_argparser(): # position 1 parser.add_argument(dest="target", metavar="T", help="Target", nargs="?") + parser.add_argument( + "-w", + "--watch", + dest="watch", + help="Watch file for changes and autocompile", + action="store_true", + ) + parser.add_argument( "-l", "--list", @@ -161,6 +169,22 @@ def main(test_args=None): _LOGGER.error(f" {k}: {v}") sys.exit(0) + # meld it and watch + if args.watch: + watcher = MarkmeldWatchDog( + ".", cfg, args.target, print_only=args.print, vardump=args.dump + ) + watcher.start() + try: + while watcher.is_alive(): + watcher.join(1) + except KeyboardInterrupt: + _LOGGER.info("Stopping...") + finally: + watcher.stop() + watcher.join() + return + _LOGGER.debug("Melding...") # Meld it! mm = MarkdownMelder(cfg) built_target = mm.build_target( diff --git a/markmeld/watcher.py b/markmeld/watcher.py new file mode 100644 index 0000000..c980f36 --- /dev/null +++ b/markmeld/watcher.py @@ -0,0 +1,59 @@ +import logging +from pathlib import Path +from typing import Union +from watchdog.observers import Observer +from watchdog.events import LoggingEventHandler, DirModifiedEvent, FileModifiedEvent + +from .melder import Target +from .melder import MarkdownMelder + +_LOGGER = logging.getLogger(__name__) + + +class MarkmeldWatchDog(Observer): + """ + Watchdog observer to watch for file changes. + """ + + def __init__( + self, + path: str, + cfg: dict, + target: str, + print_only: bool = False, + vardump: bool = False, + ): + super().__init__() + _LOGGER.info(f"Watching {path} for changes...") + self._mm = MarkdownMelder(cfg) + self.target = Target(cfg, target) + self.path = path + self.print_only = print_only + self.vardump = vardump + + # init the ignore files list (just the output files) + if "output_file" in self.target.root_cfg["targets"][target]: + ignore_file_name = Path(self.target.root_cfg["targets"][target]["output_file"]).name + self.ignore_files = [ignore_file_name] + else: + self.ignore_files = [] + + self.event_handler = LoggingEventHandler() + self.event_handler.on_modified = self.on_modified + self.schedule(self.event_handler, path, recursive=True) + + def on_modified(self, event: Union[DirModifiedEvent, FileModifiedEvent]): + """ + Check for file or directory modification and then rerun the melder. + """ + p = Path(event.src_path) + + # dont rebuild if the modified file or directory is the output file + # otherwise this causes an infinite loop + if p.name in self.ignore_files: + return + + _LOGGER.info(f"File modified: {event.src_path}") + self._mm.build_target( + self.target.target_name, print_only=self.print_only, vardump=self.vardump + ) diff --git a/requirements/requirements-all.txt b/requirements/requirements-all.txt index 823574c..b070e98 100644 --- a/requirements/requirements-all.txt +++ b/requirements/requirements-all.txt @@ -4,3 +4,4 @@ logmuse pyyaml requests ubiquerg +watchdog \ No newline at end of file From e4b0604419975733ab76d3faf66526e9e6f99e1b Mon Sep 17 00:00:00 2001 From: Nathan LeRoy Date: Sat, 25 Mar 2023 11:51:11 -0400 Subject: [PATCH 2/2] lint --- markmeld/melder.py | 1 - markmeld/watcher.py | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/markmeld/melder.py b/markmeld/melder.py index 32ccfef..e74d07f 100644 --- a/markmeld/melder.py +++ b/markmeld/melder.py @@ -330,7 +330,6 @@ def resolve_target_inheritance(self, target_name): return accumulated - class MarkdownMelder(object): def __init__(self, cfg): """ diff --git a/markmeld/watcher.py b/markmeld/watcher.py index c980f36..38abd55 100644 --- a/markmeld/watcher.py +++ b/markmeld/watcher.py @@ -33,11 +33,13 @@ def __init__( # init the ignore files list (just the output files) if "output_file" in self.target.root_cfg["targets"][target]: - ignore_file_name = Path(self.target.root_cfg["targets"][target]["output_file"]).name + ignore_file_name = Path( + self.target.root_cfg["targets"][target]["output_file"] + ).name self.ignore_files = [ignore_file_name] else: self.ignore_files = [] - + self.event_handler = LoggingEventHandler() self.event_handler.on_modified = self.on_modified self.schedule(self.event_handler, path, recursive=True)