WIP
This commit is contained in:
parent
e70a0584c9
commit
a7a5b5c8ea
43 changed files with 5531 additions and 9 deletions
112
config/dev_autoreload.py
Normal file
112
config/dev_autoreload.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import django_watchfiles
|
||||
from django.utils import autoreload
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Sequence
|
||||
|
||||
from watchfiles import Change
|
||||
|
||||
IGNORED_PROJECT_ROOT_NAMES: frozenset[str] = frozenset({
|
||||
".git",
|
||||
".venv",
|
||||
"__pycache__",
|
||||
"firecracker",
|
||||
})
|
||||
|
||||
|
||||
def _resolve_path(path: Path) -> Path:
|
||||
try:
|
||||
return path.resolve()
|
||||
except OSError:
|
||||
return path.absolute()
|
||||
|
||||
|
||||
def _is_relative_to(path: Path, parent: Path) -> bool:
|
||||
try:
|
||||
path.relative_to(parent)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def build_project_watch_roots(
|
||||
project_root: Path,
|
||||
*,
|
||||
ignored_root_names: Sequence[str] = tuple(IGNORED_PROJECT_ROOT_NAMES),
|
||||
) -> tuple[Path, ...]:
|
||||
"""Return top-level project directories worth watching during development."""
|
||||
ignored_names = set(ignored_root_names)
|
||||
resolved_project_root = _resolve_path(project_root)
|
||||
return tuple(
|
||||
sorted(
|
||||
_resolve_path(child)
|
||||
for child in resolved_project_root.iterdir()
|
||||
if child.is_dir() and not child.name.startswith(".") and child.name not in ignored_names
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TussilagoWatchfilesReloader(django_watchfiles.WatchfilesReloader):
|
||||
"""Use child directories instead of repo root for dev autoreload watches."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
project_root: Path,
|
||||
ignored_paths: Sequence[Path] | None = None,
|
||||
) -> None:
|
||||
"""Store project-specific watch settings before watcher startup."""
|
||||
self.project_root = _resolve_path(project_root)
|
||||
self.ignored_paths = tuple(
|
||||
_resolve_path(path) for path in (ignored_paths or (self.project_root / "firecracker",))
|
||||
)
|
||||
self.project_watch_roots = build_project_watch_roots(self.project_root)
|
||||
super().__init__()
|
||||
|
||||
def file_filter(self, change: Change, filename: str) -> bool:
|
||||
"""Drop file events from ignored paths such as the firecracker tree.
|
||||
|
||||
Returns:
|
||||
True when the file event should still be watched.
|
||||
"""
|
||||
resolved_path = _resolve_path(Path(filename))
|
||||
if any(_is_relative_to(resolved_path, ignored_path) for ignored_path in self.ignored_paths):
|
||||
return False
|
||||
return super().file_filter(change, filename)
|
||||
|
||||
def watched_roots(self, watched_files: Iterable[Path]) -> frozenset[Path]:
|
||||
"""Replace repo-root watches with top-level child directories.
|
||||
|
||||
Returns:
|
||||
Watch roots with the project root expanded into child directories.
|
||||
"""
|
||||
roots = {_resolve_path(root) for root in super().watched_roots(watched_files)}
|
||||
filtered_roots = {
|
||||
root
|
||||
for root in roots
|
||||
if not any(_is_relative_to(root, ignored_path) for ignored_path in self.ignored_paths)
|
||||
}
|
||||
if self.project_root in filtered_roots:
|
||||
filtered_roots.remove(self.project_root)
|
||||
filtered_roots.update(self.project_watch_roots)
|
||||
return frozenset(filtered_roots)
|
||||
|
||||
|
||||
def install_watchfiles_reloader_patch(*, project_root: Path) -> None:
|
||||
"""Install project-specific watchfiles reloader for dev and test runtimes."""
|
||||
resolved_project_root = _resolve_path(project_root)
|
||||
|
||||
class ProjectWatchfilesReloader(TussilagoWatchfilesReloader):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(project_root=resolved_project_root)
|
||||
|
||||
def replaced_get_reloader() -> autoreload.BaseReloader:
|
||||
return ProjectWatchfilesReloader()
|
||||
|
||||
autoreload.get_reloader = replaced_get_reloader
|
||||
Loading…
Add table
Add a link
Reference in a new issue