Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Change reload to be configurable with glob patterns #820

Merged
merged 43 commits into from Aug 8, 2021

Conversation

Roang-zero1
Copy link
Contributor

@Roang-zero1 Roang-zero1 commented Oct 16, 2020

With this change it is possible to control which files will trigger a reload of uvicorn.

Features:

  • New Option reload-include is a repeatable list of unix style glob patterns pathlib.Path.glob that will be included in reload watch.
  • New Option reload-exclude is a repeatable list of unix style glob patterns pathlib.Path.glob that will be excluded in reload watch.
  • Common Watchgod and Startreload configuration:
    • Both reloaders get their directories from the new configurations
  • Improved Watchgod implementation:
    • Use should_watch_dir to reduce setup overhead
    • Use should_watch_file with pathlib to verify included paths
    • Only watch .py files by default
  • Improved Startreload implementation:
    • Use pathlib glob to search for .py files
  • Updated documentation
  • Updated tests

Edit by @Kludex:

Copy link
Member

@euri10 euri10 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for this !

I begun reviewing this and realize it would be simpler to split it in 2:

  1. the test refactor
  2. the added functionality

in no particular order, this one seems simpler from my point of view.

Sorry but cant wrap my head around reviewing that much to be honest, and in any case it's a separate issue.

I left my comments anyway since I begun but the split would be much appreciated

docs/settings.md Outdated Show resolved Hide resolved
docs/settings.md Show resolved Hide resolved
tests/supervisors/test_statreload.py Outdated Show resolved Hide resolved
tests/supervisors/test_statreload.py Outdated Show resolved Hide resolved
docs/settings.md Outdated Show resolved Hide resolved
docs/deployment.md Outdated Show resolved Hide resolved
docs/index.md Outdated Show resolved Hide resolved
tests/supervisors/test_statreload.py Outdated Show resolved Hide resolved
tests/supervisors/test_watchgodreload.py Outdated Show resolved Hide resolved
tests/supervisors/test_watchgodreload.py Outdated Show resolved Hide resolved
@Roang-zero1
Copy link
Contributor Author

Sure, I'll start the work on separating out the test refactor.
I'll keep this PR for the reimplementation of the reload function and start a new one for the test refactor.

@Roang-zero1 Roang-zero1 marked this pull request as draft October 20, 2020 14:57
@Roang-zero1 Roang-zero1 marked this pull request as ready for review October 23, 2020 17:09
Copy link
Contributor Author

@Roang-zero1 Roang-zero1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some points of consideration

uvicorn/supervisors/watchgodreload.py Show resolved Hide resolved
uvicorn/supervisors/basereload.py Outdated Show resolved Hide resolved
uvicorn/supervisors/statreload.py Show resolved Hide resolved
@Roang-zero1
Copy link
Contributor Author

As requested I've separated out the test class refactor to #833.

Ready for new round of review. Added some comments to point out where feedback is requesed.

@euri10
Copy link
Member

euri10 commented Oct 23, 2020 via email

@hanneskuettner
Copy link

Any updates on this PR?

@Roang-zero1
Copy link
Contributor Author

Just got the notification. I would be willing to revisit this PR, just let me know what is needed.

@Kludex
Copy link
Sponsor Member

Kludex commented Jun 11, 2021

Hi @Roang-zero1 👋

I'm going to review it over the weekend. If you'd like, merging master would be great! 😄

Sorry for the delay on it!

@kennylajara
Copy link

I'm going to review it over the weekend.

Bump @Kludex

Roang-zero1 and others added 9 commits August 4, 2021 01:09
* Use per test parameter to enable in depth testing
* Use Path instead of os.path for all manipulations
* Use glob pattern to search for python files
* Use should_watch_dir to filter directories
* Use glob patterns to filter files
Fix copy paste errors

Co-authored-by: euri10 <euri10@users.noreply.github.com>
Fix docs error

Co-authored-by: euri10 <euri10@users.noreply.github.com>
Co-authored-by: euri10 <euri10@users.noreply.github.com>
First reload test was working without the prepared directory.
Therefore it initialized on the repository itself, thereby being slow.
@Roang-zero1
Copy link
Contributor Author

I've added the changes, unfortunately this meant reworking large parts of this PR 🔨. (Sorry @euri10 for the additional work)
There is now only one watcher for the current working directory. This enables watchgod to pick up new files and directories that match the globs that were not present before starting the server.

I think I've added test cases for all new functionality.
I am a bit on the fence as there is some feature duplication between config.py and the watchgodreload.py, but I believe this to be unavoidable.

I've added a cache to the watchers, this reduces the CPU load, but costs memory. Please indicate if that is ok.

There is now a fixture for testing all the reload related use cases, it is currently scoped for package, as I don't have a good enough understanding please verify it the scope is correct.

@Roang-zero1
Copy link
Contributor Author

Just saw the failed test. I'll look into what the problem with Windows is tomorrow.

Windows raises an OSError when a path is not found.
This change introduces a helper function to mitigate this.
Windows paths were incorrect in the tests, this fixes these messages.
@euri10
Copy link
Member

euri10 commented Aug 6, 2021

this is a subtle one:
this test works fine with this logger: logger.info(f"Will watch for changes in these directories: {list(map(str, self.reload_dirs))}")

        assert (
            caplog.records[-1].message
            == f"Will watch for changes in these directories: {[str(app_dir)]}"
        )

you cannot build manually the list [] I think that was what caused the double escaping

tests/conftest.py Outdated Show resolved Hide resolved
tests/supervisors/test_reload.py Outdated Show resolved Hide resolved
tests/supervisors/test_reload.py Outdated Show resolved Hide resolved
tests/supervisors/test_reload.py Show resolved Hide resolved
tests/test_config.py Outdated Show resolved Hide resolved
uvicorn/config.py Show resolved Hide resolved
uvicorn/config.py Outdated Show resolved Hide resolved
uvicorn/supervisors/basereload.py Show resolved Hide resolved
uvicorn/supervisors/statreload.py Show resolved Hide resolved
uvicorn/supervisors/watchgodreload.py Show resolved Hide resolved
@euri10
Copy link
Member

euri10 commented Aug 7, 2021

for the failing test, let's deal with that sorting the files in the output for predictable behaviour:

diff --git a/tests/test_config.py b/tests/test_config.py
index a999b33..73f2e5f 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -225,8 +225,10 @@ def test_reload_includes_exclude_dir_patterns_are_matched(
                 "Will watch for changes in these directories: "
                 in caplog.records[-1].message
             )
-            assert str(first_app_dir) in caplog.records[-1].message
-            assert str(second_app_dir) in caplog.records[-1].message
+            assert (
+                    caplog.records[-1].message
+                    == f"Will watch for changes in these directories: {sorted([str(first_app_dir), str(second_app_dir)])}"
+            )
             assert frozenset(config.reload_dirs) == frozenset(
                 [first_app_dir, second_app_dir]
             )

             )
             )
diff --git a/uvicorn/config.py b/uvicorn/config.py
index 4fae430..13f5898 100644
--- a/uvicorn/config.py
+++ b/uvicorn/config.py
@@ -331,7 +331,7 @@ class Config:

             logger.info(
                 "Will watch for changes in these directories: %s",
-                list(map(str, self.reload_dirs)),
+                sorted(list(map(str, self.reload_dirs))),
             )

         if env_file is not None:

Log messages were printed for excluded directories, even if these
directories were not in any of the watched reload_dirs.
@euri10
Copy link
Member

euri10 commented Aug 8, 2021

thanks a lot @Roang-zero1 for this, this is a very nice enhancement !

@euri10 euri10 merged commit ad76136 into encode:master Aug 8, 2021
@euri10 euri10 mentioned this pull request Aug 11, 2021
1 task
Kludex pushed a commit that referenced this pull request Nov 17, 2021
* Update reload test class

* Use per test parameter to enable in depth testing

* Add tests for include and exclude

* Add configuration for include and exclude

* Update startreload

* Use Path instead of os.path for all manipulations
* Use glob pattern to search for python files

* Update watchgodreload

* Use should_watch_dir to filter directories
* Use glob patterns to filter files

* Update documentation

* Apply suggestions from code review

Fix copy paste errors

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* Update docs/settings.md

Fix docs error

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* Update docs/settings.md

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* Add test for StartReload output

* Update types

* Add coverage notation for TYPE_CHECKING

* Fix documentation linting

* Fix documentation for linting

* Removed tmpdir fixture

* Changed context manager to take path arg

* Update config tests with as_cwd util

* Move as_cwd to test utils
* Update test_config tests with new util
* Remove usage of tmpdir

* Remove debug print statement

* Fix reload_includes arbitrarliy dropping entries

* Add additional configuration tests

* Fix mypy error for mkdir

* Remove installation hint for watchgod

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* ➕ Add warning for invalid reload config

If any of the reload settings are present, but the app would not reload
there will not be a warning informing the user.

* ➕ Add reload_dir result as log message

* ➕ Add reload_directory_structure fixture

Use one directory structure for all reload test.

* ➕ Add reload defaults to cli help

* ➕ Add parsing of reload dirs from glob patterns

Glob patterns are now matched agains directories which will
be added to reload_dirs if not present.
This also extends to excluded directories.
Exclude patterns now take precedence over includes.

* 🐛 Fix incorrect init in StatReload

* 🔨 Refactor should_watch_dir for WatchGodReload

Uses a global dict to cache results.
Prioritizes exclude directories and patterns.
Finds additional reload/exclude directories if they are created under an
existing watcher.

* ➕ Add result cache for should_watch_*

* 🐛 Fix slow first reload test

First reload test was working without the prepared directory.
Therefore it initialized on the repository itself, thereby being slow.

* 🐛 Fix directory checks for Windows

Windows raises an OSError when a path is not found.
This change introduces a helper function to mitigate this.

* 🐛 Fix reload warning message tests

Windows paths were incorrect in the tests, this fixes these messages.

* Update tests/supervisors/test_reload.py

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* 🔥 Remove debug print statement

* 🚨 Fix test linting

* ➕ Add description for reload_directory_structure fixture

* 🐛 Fix path error in windows tests

* 🐛 Fix path test again

* 🔥 Remove duplicate test case

* ➕ Add sorting to reload_dirs list

* 🐛 Fix redundant excluded directory log output

Log messages were printed for excluded directories, even if these
directories were not in any of the watched reload_dirs.

Co-authored-by: euri10 <euri10@users.noreply.github.com>
Co-authored-by: euri10 <benoit.barthelet@gmail.com>
@HansBrende
Copy link
Contributor

@Roang-zero1 @euri10 @Kludex this PR introduced a regression and deleted the test for it (test_reload_dir_is_set()). Previously you could pass reload_dirs a string instead of a list. Now:

Config('myapp:app', reload=True, reload_dirs='src')

prints:

WARNING:  Provided reload directories ['r', 'c', 's'] did not contain valid directories, watching current working directory.

https://github.com/encode/uvicorn/pull/820/files#diff-6982a5e270879281f9182303a9da9776148857b46325afb230e50ef8eb3264c9R294
(Of course, set expects an iterable... and strings are iterables of chars.)

Kludex pushed a commit to sephioh/uvicorn that referenced this pull request Oct 29, 2022
* Update reload test class

* Use per test parameter to enable in depth testing

* Add tests for include and exclude

* Add configuration for include and exclude

* Update startreload

* Use Path instead of os.path for all manipulations
* Use glob pattern to search for python files

* Update watchgodreload

* Use should_watch_dir to filter directories
* Use glob patterns to filter files

* Update documentation

* Apply suggestions from code review

Fix copy paste errors

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* Update docs/settings.md

Fix docs error

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* Update docs/settings.md

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* Add test for StartReload output

* Update types

* Add coverage notation for TYPE_CHECKING

* Fix documentation linting

* Fix documentation for linting

* Removed tmpdir fixture

* Changed context manager to take path arg

* Update config tests with as_cwd util

* Move as_cwd to test utils
* Update test_config tests with new util
* Remove usage of tmpdir

* Remove debug print statement

* Fix reload_includes arbitrarliy dropping entries

* Add additional configuration tests

* Fix mypy error for mkdir

* Remove installation hint for watchgod

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* ➕ Add warning for invalid reload config

If any of the reload settings are present, but the app would not reload
there will not be a warning informing the user.

* ➕ Add reload_dir result as log message

* ➕ Add reload_directory_structure fixture

Use one directory structure for all reload test.

* ➕ Add reload defaults to cli help

* ➕ Add parsing of reload dirs from glob patterns

Glob patterns are now matched agains directories which will
be added to reload_dirs if not present.
This also extends to excluded directories.
Exclude patterns now take precedence over includes.

* 🐛 Fix incorrect init in StatReload

* 🔨 Refactor should_watch_dir for WatchGodReload

Uses a global dict to cache results.
Prioritizes exclude directories and patterns.
Finds additional reload/exclude directories if they are created under an
existing watcher.

* ➕ Add result cache for should_watch_*

* 🐛 Fix slow first reload test

First reload test was working without the prepared directory.
Therefore it initialized on the repository itself, thereby being slow.

* 🐛 Fix directory checks for Windows

Windows raises an OSError when a path is not found.
This change introduces a helper function to mitigate this.

* 🐛 Fix reload warning message tests

Windows paths were incorrect in the tests, this fixes these messages.

* Update tests/supervisors/test_reload.py

Co-authored-by: euri10 <euri10@users.noreply.github.com>

* 🔥 Remove debug print statement

* 🚨 Fix test linting

* ➕ Add description for reload_directory_structure fixture

* 🐛 Fix path error in windows tests

* 🐛 Fix path test again

* 🔥 Remove duplicate test case

* ➕ Add sorting to reload_dirs list

* 🐛 Fix redundant excluded directory log output

Log messages were printed for excluded directories, even if these
directories were not in any of the watched reload_dirs.

Co-authored-by: euri10 <euri10@users.noreply.github.com>
Co-authored-by: euri10 <benoit.barthelet@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support --ignore or --reload-file option Ignore changes to any top-level directory starting with a "."
6 participants