Skip to content

Make Django’s autoreloader more efficient by watching for changes with watchfiles.

License

Notifications You must be signed in to change notification settings

adamchainz/django-watchfiles

Repository files navigation

django-watchfiles

https://img.shields.io/github/actions/workflow/status/adamchainz/django-watchfiles/main.yml.svg?branch=main&style=for-the-badge https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge https://img.shields.io/pypi/v/django-watchfiles.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

Make Django’s autoreloader more efficient by watching for changes with watchfiles.

Read more in the introductory post, or below.


Improve your Django and Git skills with my books.


Requirements

Python 3.9 to 3.14 supported.

Correct behaviour for globs requires Python 3.13+ for Path.full_match(), per Issue #91.

Django 4.2 to 6.0 supported.

Installation

  1. Install with pip:

    python -m pip install django-watchfiles
  2. Add django-watchfiles to your INSTALLED_APPS:

    INSTALLED_APPS = [
        ...,
        "django_watchfiles",
        ...,
    ]

That’s it! 😅

Try installing django-browser-reload as well, to make your browser automatically reload the page when changes are detected.

Usage

django-watchfiles will be automatically used by Django’s runserver command. You can tell this because runserver will list WatchfilesReloader as the watcher class:

$ ./manage.py runserver
Watching for file changes with WatchfilesReloader
...

(Rather than the default StatReloader.)

WatchfilesReloader provides the following advantages:

  • Much lower CPU usage

    Django’s default StatReloader works by polling all files for changes, sleeping for one second, and looping. Meanwhile, WatchfilesReloader avoids polling; instead, it asks the operating system to report any changes to the watched files.

    The difference can be stark and save you significant battery when developing on a device that isn’t connected to a power source. A quick benchmark on a medium-sized project (385,000 lines plus 206 installed packages) using an M1 MacBook showed StatReloader using ~10% of a CPU every other second, while WatchfilesReloader uses 0%.

  • Reduced reload time

    StatReloader can take one second or more to detect changes, while WatchfilesReloader can take as little as 50 milliseconds. This means that runserver starts reloading your code more quickly, and you can iterate more rapidly.

  • Batched reloads

    Sometimes multiple file changes can occur in quick succession, such as when one file is saved and then updated by a formatter, or when multiple files are changed when you git switch to another branch. In such cases, StatReloader can trigger multiple reloads, unnecessarily slowing down progress, or it can miss some changes, leading to old code being left running. WatchfilesReloader instead batches changes, using watchfiles’ debounce feature, so that multiple changes will only trigger a single reload.

    WatchfilesReloader uses watchfiles’ defaults here, waiting for changes within a 50 millisecond window, and repeating this wait for up to 1600 milliseconds, as long as changes keep occurring. These values provide a good balance between responsiveness and batching.

On some platforms (Windows Subsystem for Linux, or WSL), watchfiles will fall back to a polling approach. This is still more efficient than StatReloader though, because it’s implemented in Rust.

watchfiles has some configuration options which can be configured through environment variables. See the watchfiles documentation for details.

Example project

To demonstrate and test django-watchfiles in various situations, there is an example project included in the repository. Open the example/ directory, follow the instructions in its README, and try it out.

History

Django’s runserver started with only the logic for StatReloader, because it’s simple and works on all platforms.

In Django 1.7 (2014), Django gained support for using the Linux file-watching API inotify, through the pyinotify package. This provided efficient reloading, but was limited to Linux. This was thanks to Unai Zalakain, Chris Lamb, and Pascal Hartig for that work in Ticket #9722.

In Django 2.2 (2019), Django gained support for Watchman, a cross-platform file-watching service from Facebook, via the pywatchman package. This provides efficient reloading on Linux and macOS, but requires developers to install and run the Watchman service separately. Thanks to Tom Forbes for that work in Ticket #27685.

Unfortunately, the pywatchman package stopped working on Python 3.10, as reported in its Issue #970 (2021). This issue remained unfixed for a long time, until March 2024, after the release of Python 3.12. It appears that Watchman and pywatchman are not a priority for maintenance by Facebook.

In 2022, Samel Colvin released watchfiles, a new cross-platform file-watching library for Python. It is powered by Notify, a popular and established Rust crate. (watchfiles is also the Rust-powered rebuild of Samuel’s earlier watchgod package (2017).)

I created django-watchfiles in 2022 to integrate watchfiles with Django’s autoreloader. The inspiration came from writing about using Watchman in Boost Your Django DX and feeling a bit dismayed that it wasn’t particularly easy, and that it wasn’t (yet) working on Python 3.10.

django-watchfiles had its first stable release in 2024. I may propose integrating it with Django core at some point, when it’s more established.

About

Make Django’s autoreloader more efficient by watching for changes with watchfiles.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

No packages published

Contributors 8