Add watcher for development
This commit is contained in:
parent
d9cc5211e8
commit
7081d4cc5f
6 changed files with 123 additions and 47 deletions
|
@ -12,8 +12,8 @@ COPY site/ site/
|
||||||
COPY .git/ .git/
|
COPY .git/ .git/
|
||||||
|
|
||||||
RUN poetry config virtualenvs.create false
|
RUN poetry config virtualenvs.create false
|
||||||
RUN poetry install --no-interaction --no-root
|
RUN poetry install --no-interaction --no-root --without dev
|
||||||
RUN poetry run python3 generator/ site/
|
RUN poetry run python3 generator/ generate site/
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,19 @@ import fire
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
import process
|
import process
|
||||||
import functools
|
import functools
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
def make_absurl_filter(site_conf: any) -> Callable[[str], str]:
|
def make_absurl_filter(site_conf: any) -> Callable[[str], str]:
|
||||||
return functools.partial(get_absolute_url, site_conf)
|
return functools.partial(get_absolute_url, site_conf)
|
||||||
|
|
||||||
|
class CLI:
|
||||||
|
@staticmethod
|
||||||
|
def g(*args, **kwargs):
|
||||||
|
return CLI.generate(*args, **kwargs)
|
||||||
|
|
||||||
def run(base_dir: str, output_dir: str = "_dist"):
|
@staticmethod
|
||||||
|
def generate(base_dir: str, output_dir: str = "_dist"):
|
||||||
base_dir = Path(base_dir)
|
base_dir = Path(base_dir)
|
||||||
output_dir = Path(output_dir)
|
output_dir = Path(output_dir)
|
||||||
html_dir = output_dir / "html"
|
html_dir = output_dir / "html"
|
||||||
|
@ -52,10 +58,34 @@ def run(base_dir: str, output_dir: str = "_dist"):
|
||||||
s = "" if count == 1 else "s"
|
s = "" if count == 1 else "s"
|
||||||
res_parts.append(f"{key} {count} {thing_overrides.get(key, 'file')}{s}")
|
res_parts.append(f"{key} {count} {thing_overrides.get(key, 'file')}{s}")
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
rprint(INFO_LEADER + "Finished working, " + ", ".join(res_parts))
|
rprint(INFO_LEADER + "Finished working, " + ", ".join(res_parts))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def watch(base_dir: str, output_dir: str = "_dist"):
|
||||||
|
import pyinotify
|
||||||
|
|
||||||
|
if os.path.exists(output_dir):
|
||||||
|
rprint(WARN_LEADER + f"{output_dir} already exists and will be clobbered when regeneration is triggered")
|
||||||
|
|
||||||
|
@debounce(1)
|
||||||
|
def run():
|
||||||
|
if os.path.exists(output_dir):
|
||||||
|
shutil.rmtree(output_dir)
|
||||||
|
reset_counts() # boys and girls, this is why global state is bad
|
||||||
|
CLI.generate(base_dir, output_dir=output_dir)
|
||||||
|
|
||||||
|
class OnWriteHandler(pyinotify.ProcessEvent):
|
||||||
|
def process_IN_MODIFY(self, event):
|
||||||
|
rprint(INFO_LEADER + f"Change detected in {event.pathname}")
|
||||||
|
run()
|
||||||
|
|
||||||
|
wm = pyinotify.WatchManager()
|
||||||
|
notifier = pyinotify.Notifier(wm, default_proc_fun=OnWriteHandler())
|
||||||
|
wm.add_watch(base_dir, pyinotify.ALL_EVENTS, rec=True, auto_add=True)
|
||||||
|
|
||||||
|
rprint(INFO_LEADER + f"Watching {base_dir}")
|
||||||
|
notifier.loop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
fire.Fire(run)
|
fire.Fire(CLI)
|
||||||
|
|
|
@ -77,11 +77,11 @@ def content(base_dir: Path, output_dir: Path, jinja_env: Environment, site_confi
|
||||||
)
|
)
|
||||||
os.makedirs(target_path.parent, exist_ok=True)
|
os.makedirs(target_path.parent, exist_ok=True)
|
||||||
|
|
||||||
rprint(
|
# rprint(
|
||||||
INFO_LEADER
|
# INFO_LEADER
|
||||||
+ f"Rendering [bold]{fpath.relative_to(base_dir)}[/bold]"
|
# + f"Rendering [bold]{fpath.relative_to(base_dir)}[/bold]"
|
||||||
f"[white] => {target_path}[/white]"
|
# f"[white] => {target_path}[/white]"
|
||||||
)
|
# )
|
||||||
|
|
||||||
ctx = {"site": site_config}
|
ctx = {"site": site_config}
|
||||||
_template_frontmatter(tpl_frontmatter, jinja_env, ctx)
|
_template_frontmatter(tpl_frontmatter, jinja_env, ctx)
|
||||||
|
@ -172,11 +172,11 @@ def blog(base_dir: Path, output_dir: Path, jinja_env: Environment, site_config:
|
||||||
target_path = output_dir / "blog" / post_slug / "index.html"
|
target_path = output_dir / "blog" / post_slug / "index.html"
|
||||||
os.makedirs(target_path.parent, exist_ok=True)
|
os.makedirs(target_path.parent, exist_ok=True)
|
||||||
|
|
||||||
rprint(
|
# rprint(
|
||||||
INFO_LEADER
|
# INFO_LEADER
|
||||||
+ f"Rendering [bold]{fpath.relative_to(base_dir)}[/bold]"
|
# + f"Rendering [bold]{fpath.relative_to(base_dir)}[/bold]"
|
||||||
f"[white] => {target_path}[/white]"
|
# f"[white] => {target_path}[/white]"
|
||||||
)
|
# )
|
||||||
|
|
||||||
if "updatedDate" in post_frontmatter:
|
if "updatedDate" in post_frontmatter:
|
||||||
post_frontmatter["updatedDate"] = list(
|
post_frontmatter["updatedDate"] = list(
|
||||||
|
|
|
@ -3,6 +3,7 @@ import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import yaml
|
import yaml
|
||||||
import mistune
|
import mistune
|
||||||
|
import threading
|
||||||
|
|
||||||
ERROR_LEADER = "[[red bold]FAIL[/bold red]] "
|
ERROR_LEADER = "[[red bold]FAIL[/bold red]] "
|
||||||
WARN_LEADER = "[[yellow bold]WARN[/bold yellow]] "
|
WARN_LEADER = "[[yellow bold]WARN[/bold yellow]] "
|
||||||
|
@ -20,6 +21,10 @@ def is_directory_empty(path: str | Path) -> bool:
|
||||||
_counts: dict[str, int] = {}
|
_counts: dict[str, int] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def reset_counts():
|
||||||
|
_counts = {}
|
||||||
|
|
||||||
|
|
||||||
def update_counts(key: str, delta: int):
|
def update_counts(key: str, delta: int):
|
||||||
_counts[key] = _counts.get(key, 0) + delta
|
_counts[key] = _counts.get(key, 0) + delta
|
||||||
|
|
||||||
|
@ -61,3 +66,30 @@ def render_markdown(raw_content: str, escape: bool = True) -> str:
|
||||||
escape=escape, plugins=["strikethrough", "table", "footnotes"]
|
escape=escape, plugins=["strikethrough", "table", "footnotes"]
|
||||||
)
|
)
|
||||||
return markdown(raw_content)
|
return markdown(raw_content)
|
||||||
|
|
||||||
|
|
||||||
|
def debounce(wait_time):
|
||||||
|
"""
|
||||||
|
https://stackoverflow.com/a/66907107
|
||||||
|
|
||||||
|
Decorator that will debounce a function so that it is called after wait_time seconds
|
||||||
|
If it is called multiple times, will wait for the last call to be debounced and run only this one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(function):
|
||||||
|
def debounced(*args, **kwargs):
|
||||||
|
def call_function():
|
||||||
|
debounced._timer = None
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
# if we already have a call to the function currently waiting to be executed, reset the timer
|
||||||
|
if debounced._timer is not None:
|
||||||
|
debounced._timer.cancel()
|
||||||
|
|
||||||
|
# after wait_time, call the function provided to the decorator with its arguments
|
||||||
|
debounced._timer = threading.Timer(wait_time, call_function)
|
||||||
|
debounced._timer.start()
|
||||||
|
|
||||||
|
debounced._timer = None
|
||||||
|
return debounced
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
13
poetry.lock
generated
13
poetry.lock
generated
|
@ -158,6 +158,17 @@ files = [
|
||||||
plugins = ["importlib-metadata"]
|
plugins = ["importlib-metadata"]
|
||||||
windows-terminal = ["colorama (>=0.4.6)"]
|
windows-terminal = ["colorama (>=0.4.6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyinotify"
|
||||||
|
version = "0.9.6"
|
||||||
|
description = "Linux filesystem events monitoring"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "pyinotify-0.9.6.tar.gz", hash = "sha256:9c998a5d7606ca835065cdabc013ae6c66eb9ea76a00a1e3bc6e0cfe2b4f71f4"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyyaml"
|
name = "pyyaml"
|
||||||
version = "6.0.1"
|
version = "6.0.1"
|
||||||
|
@ -267,4 +278,4 @@ tests = ["pytest", "pytest-cov"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "a89183bd8594d0aa4c78441a7c0307ac71a0195e657ddfb94a033a5fe41cb300"
|
content-hash = "54f52a4692080706a62ade0de6c704d2919f5e5ae666f4769061b3b99d197889"
|
||||||
|
|
|
@ -14,6 +14,9 @@ rich = "^13.7.0"
|
||||||
mistune = "^3.0.2"
|
mistune = "^3.0.2"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pyinotify = "^0.9.6"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue