An open API service indexing awesome lists of open source software.

https://github.com/adamchainz/django-watchfiles

Use watchfiles in Django’s autoreloader.
https://github.com/adamchainz/django-watchfiles

Last synced: 6 months ago
JSON representation

Use watchfiles in Django’s autoreloader.

Awesome Lists containing this project

README

          

=================
django-watchfiles
=================

.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/django-watchfiles/main.yml.svg?branch=main&style=for-the-badge
:target: https://github.com/adamchainz/django-watchfiles/actions?workflow=CI

.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
:target: https://github.com/adamchainz/django-watchfiles/actions?workflow=CI

.. image:: https://img.shields.io/pypi/v/django-watchfiles.svg?style=for-the-badge
:target: https://pypi.org/project/django-watchfiles/

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
:target: https://github.com/psf/black

.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
:target: https://github.com/pre-commit/pre-commit
:alt: 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.10 to 3.14 supported.

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

.. |Path.full_match()| replace:: ``Path.full_match()``
__ https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.full_match

Django 4.2 to 6.0 supported.

Installation
------------

1. Install with **pip**:

.. code-block:: sh

python -m pip install django-watchfiles

2. Add django-watchfiles to your ``INSTALLED_APPS``:

.. code-block:: python

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:

.. |runserver command| replace:: ``runserver`` command
__ https://docs.djangoproject.com/en/stable/ref/django-admin/#runserver

.. code-block:: shell

$ ./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.

.. |the example directory| replace:: the ``example/`` directory
__ https://github.com/adamchainz/django-watchfiles/tree/main/example

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.