diff --git a/CHANGELOG.md b/CHANGELOG.md index c8c9584..d2f5b42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## unreleased +## [unreleased] +* Fixed devsever so it does not crash anymore when the (re-)build fails * dropped support for Python 3.8 and 3.9 -* added Python 3.13 to CI test suite +* updated dependencies ## [2.2.1] -- 2023-11-11 diff --git a/blag/devserver.py b/blag/devserver.py index a67b365..1945740 100644 --- a/blag/devserver.py +++ b/blag/devserver.py @@ -49,7 +49,7 @@ def get_last_modified(dirs: list[str]) -> float: return last_mtime -def autoreload(args: argparse.Namespace) -> NoReturn: +def autoreload(args: argparse.Namespace, wait: int=1) -> NoReturn: """Start the autoreloader. This method monitors the given directories for changes (i.e. the @@ -63,6 +63,9 @@ def autoreload(args: argparse.Namespace) -> NoReturn: ---------- args contains the input-, template- and static dir + wait + number of seconds the devsever waits before checking for updated + content """ dirs = [args.input_dir, args.template_dir, args.static_dir] @@ -71,12 +74,18 @@ def autoreload(args: argparse.Namespace) -> NoReturn: # loop to avoid serving stale contents last_mtime = 0.0 while True: - mtime = get_last_modified(dirs) - if mtime > last_mtime: - last_mtime = mtime - logger.info("Change detected, rebuilding...") - blag.build(args) - time.sleep(1) + # make sure the devsever does not crash when the build fails with an + # exception + try: + mtime = get_last_modified(dirs) + if mtime > last_mtime: + last_mtime = mtime + logger.info("Change detected, rebuilding...") + blag.build(args) + time.sleep(wait) + except Exception: + logger.exception("Error occurred during rebuild:") + logger.info("Devserver did not crash, you may continue editing.") def serve(args: argparse.Namespace) -> None: diff --git a/tests/test_devserver.py b/tests/test_devserver.py index a10c4d9..df7b69f 100644 --- a/tests/test_devserver.py +++ b/tests/test_devserver.py @@ -4,10 +4,10 @@ import threading import time from argparse import Namespace -import pytest - from blag import devserver +WAITTIME = 0.1 + def test_get_last_modified(cleandir: str) -> None: """Test get_last_modified.""" @@ -15,13 +15,13 @@ def test_get_last_modified(cleandir: str) -> None: t1 = devserver.get_last_modified(["content"]) # wait a bit, create a file and measure again - time.sleep(0.1) + time.sleep(WAITTIME) with open("content/test", "w") as fh: fh.write("boo") t2 = devserver.get_last_modified(["content"]) # wait a bit and take time again - time.sleep(0.1) + time.sleep(WAITTIME) t3 = devserver.get_last_modified(["content"]) assert t2 > t1 @@ -36,14 +36,14 @@ def test_autoreload_builds_immediately(args: Namespace) -> None: t = threading.Thread( target=devserver.autoreload, - args=(args,), + args=(args, WAITTIME), daemon=True, ) t0 = devserver.get_last_modified(["build"]) t.start() # try for 5 seconds... for i in range(5): - time.sleep(1) + time.sleep(WAITTIME) t1 = devserver.get_last_modified(["build"]) print(t1) if t1 > t0: @@ -51,14 +51,11 @@ def test_autoreload_builds_immediately(args: Namespace) -> None: assert t1 > t0 -@pytest.mark.filterwarnings( - "ignore::pytest.PytestUnhandledThreadExceptionWarning" -) def test_autoreload(args: Namespace) -> None: """Test autoreload.""" t = threading.Thread( target=devserver.autoreload, - args=(args,), + args=(args, WAITTIME), daemon=True, ) t.start() @@ -71,8 +68,32 @@ def test_autoreload(args: Namespace) -> None: # try for 5 seconds to see if we rebuild once... for i in range(5): - time.sleep(1) + time.sleep(WAITTIME) t1 = devserver.get_last_modified(["build"]) if t1 > t0: break assert t1 > t0 + + +def test_autoreload_does_not_crash(args: Namespace) -> None: + """Test autoreload does not crash if build fails.""" + t = threading.Thread( + target=devserver.autoreload, + args=(args, WAITTIME), + daemon=True, + ) + t.start() + + t0 = devserver.get_last_modified(["build"]) + + # create a file that causes build to crash + with open("content/test.md", "w") as fh: + fh.write("date: ") + + # try for 5 seconds to see if we rebuild once... + for i in range(5): + time.sleep(WAITTIME) + t1 = devserver.get_last_modified(["build"]) + if t1 > t0: + break + assert t.is_alive()