Source code for next.apps.autoreload

"""Patch Django's autoreload to use next-dj's reloader and watch specs.

Django does not expose a setting for overriding the reloader class, so
this module still swaps `autoreload.StatReloader`. The swap is kept
idempotent and records the class it replaces so tests (or other
packages) can restore it. A warning is logged if another library has
replaced `StatReloader` with a class that is not a `StatReloader`
subclass, which suggests an incompatible override and makes our patch
unsafe to apply.
"""

from __future__ import annotations

import logging

from django.utils import autoreload
from django.utils.autoreload import StatReloader, autoreload_started

from next.server import NextStatReloader, iter_all_autoreload_watch_specs


logger = logging.getLogger(__name__)

_ORIGINAL_STAT_RELOADER: type[StatReloader] | None = None
_WATCHER_CONNECTED = False


[docs] def install() -> None: """Swap `StatReloader` for `NextStatReloader` and wire watch specs. Safe to call more than once: subsequent calls are no-ops once the current `autoreload.StatReloader` is already our subclass or one of its descendants. """ global _ORIGINAL_STAT_RELOADER, _WATCHER_CONNECTED # noqa: PLW0603 current = autoreload.StatReloader if issubclass(current, NextStatReloader): pass elif issubclass(current, StatReloader): _ORIGINAL_STAT_RELOADER = current autoreload.StatReloader = NextStatReloader # type: ignore[misc] else: logger.warning( "autoreload.StatReloader has been replaced by %r which is not a " "StatReloader subclass, so next-dj will not override it.", current, ) if not _WATCHER_CONNECTED: autoreload_started.connect(_watch_next_filesystem) _WATCHER_CONNECTED = True
[docs] def uninstall() -> None: """Restore the previous `StatReloader` class if `install()` swapped it.""" global _ORIGINAL_STAT_RELOADER, _WATCHER_CONNECTED # noqa: PLW0603 if _ORIGINAL_STAT_RELOADER is not None: autoreload.StatReloader = _ORIGINAL_STAT_RELOADER # type: ignore[misc] _ORIGINAL_STAT_RELOADER = None if _WATCHER_CONNECTED: autoreload_started.disconnect(_watch_next_filesystem) _WATCHER_CONNECTED = False
def _watch_next_filesystem(sender: object, **_: object) -> None: for path, glob in iter_all_autoreload_watch_specs(): sender.watch_dir(path, glob) # type: ignore[attr-defined] __all__ = ["install", "uninstall"]