Repository: encode/uvicorn

Issue:
Add support for --reload to monitor additional file types.
The "reload" process currently only monitors ".py" files in various directories. I have a changes that will pass in a list of additional "reload_suffixes" that the process will monitor. This allows the service to monitor data files in addition to code files.

Any feedback on whether this is useful to others?

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15ffc8d..67020e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ## Unreleased
 
 * Use `watchgod`, if installed, for watching code changes.
+* Reload application when any files in watched directories change, not just `.py` files.
 
 ## 0.11.3
 
diff --git a/uvicorn/supervisors/statreload.py b/uvicorn/supervisors/statreload.py
index 3209c2c..03c96be 100644
--- a/uvicorn/supervisors/statreload.py
+++ b/uvicorn/supervisors/statreload.py
@@ -14,7 +14,7 @@ class StatReload(BaseReload):
         self.mtimes = {}
 
     def should_restart(self):
-        for filename in self.iter_py_files():
+        for filename in self.iter_files():
             try:
                 mtime = os.path.getmtime(filename)
             except OSError:  # pragma: nocover
@@ -33,9 +33,9 @@ class StatReload(BaseReload):
                 return True
         return False
 
-    def iter_py_files(self):
+    def iter_files(self):
         for reload_dir in self.config.reload_dirs:
             for subdir, dirs, files in os.walk(reload_dir):
                 for file in files:
-                    if file.endswith(".py"):
+                    if not file.startswith("."):
                         yield subdir + os.sep + file


Test patch:
diff --git a/tests/supervisors/__init__.py b/tests/supervisors/__init__.py
new file mode 100644
index 0000000..2def547
--- /dev/null
+++ b/tests/supervisors/__init__.py
@@ -0,0 +1,5 @@
+WATCHED_FILES = (
+    "example.py",
+    "example.html",
+    "example.graphql",
+)
diff --git a/tests/supervisors/test_statreload.py b/tests/supervisors/test_statreload.py
index 891a4fc..49a5ba3 100644
--- a/tests/supervisors/test_statreload.py
+++ b/tests/supervisors/test_statreload.py
@@ -3,9 +3,13 @@ import signal
 import time
 from pathlib import Path
 
+import pytest
+
 from uvicorn.config import Config
 from uvicorn.supervisors.statreload import StatReload
 
+from . import WATCHED_FILES
+
 
 def run(sockets):
     pass
@@ -24,8 +28,9 @@ def test_statreload():
     reloader.run()
 
 
-def test_should_reload(tmpdir):
-    update_file = Path(os.path.join(str(tmpdir), "example.py"))
+@pytest.mark.parametrize("filename", WATCHED_FILES)
+def test_should_reload_when_watched_file_is_changed(tmpdir, filename):
+    update_file = Path(tmpdir) / filename
     update_file.touch()
 
     working_dir = os.getcwd()
@@ -45,3 +50,26 @@ def test_should_reload(tmpdir):
         reloader.shutdown()
     finally:
         os.chdir(working_dir)
+
+
+def test_should_not_reload_when_dot_file_is_changed(tmpdir):
+    update_file = Path(tmpdir) / ".dotted"
+    update_file.touch()
+
+    working_dir = os.getcwd()
+    os.chdir(str(tmpdir))
+    try:
+        config = Config(app=None, reload=True)
+        reloader = StatReload(config, target=run, sockets=[])
+        reloader.signal_handler(sig=signal.SIGINT, frame=None)
+        reloader.startup()
+
+        assert not reloader.should_restart()
+        time.sleep(0.1)
+        update_file.touch()
+        assert not reloader.should_restart()
+
+        reloader.restart()
+        reloader.shutdown()
+    finally:
+        os.chdir(working_dir)
diff --git a/tests/supervisors/test_watchgodreload.py b/tests/supervisors/test_watchgodreload.py
index f8bc774..4bba1c2 100644
--- a/tests/supervisors/test_watchgodreload.py
+++ b/tests/supervisors/test_watchgodreload.py
@@ -3,9 +3,13 @@ import signal
 import time
 from pathlib import Path
 
+import pytest
+
 from uvicorn.config import Config
 from uvicorn.supervisors.watchgodreload import WatchGodReload
 
+from . import WATCHED_FILES
+
 
 def run(sockets):
     pass
@@ -18,9 +22,9 @@ def test_watchgodreload(certfile_and_keyfile):
     reloader.run()
 
 
-def test_should_reload_when_python_file_is_changed(tmpdir):
-    file = "example.py"
-    update_file = Path(os.path.join(str(tmpdir), file))
+@pytest.mark.parametrize("filename", WATCHED_FILES)
+def test_should_reload_when_file_is_changed(tmpdir, filename):
+    update_file = Path(tmpdir) / filename
     update_file.touch()
 
     working_dir = os.getcwd()
@@ -43,8 +47,7 @@ def test_should_reload_when_python_file_is_changed(tmpdir):
 
 
 def test_should_not_reload_when_dot_file_is_changed(tmpdir):
-    file = ".dotted"
-    update_file = Path(os.path.join(str(tmpdir), file))
+    update_file = Path(tmpdir) / ".dotted"
     update_file.touch()
 
     working_dir = os.getcwd()