Search code examples
pythonfastapiuvicorn

Uvicorn reload options are not being followed


I have three directories: app, config, and private

I'm running uvicorn programmatically like this with WatchFiles installed:

uvicorn.run(
        "app.main:fast",
        host=host,
        port=port,
        log_level=log_level,
        reload=reload,
        reload_includes=["app/*", "config/*", "manage.py", ".env"],
    )

But for some reason, the directory private is also watched and reloads. I tried doing this:

uvicorn.run(
        "app.main:fast",
        host=host,
        port=port,
        log_level=log_level,
        reload=reload,
        reload_dirs=["app", "config"],
        reload_includes=["app/*.py", "config/*.py", "manage.py", ".env"],
        reload_excludes=["*.py"]
    )

But this just ignores all of the python files. How do I just watch the directories app and config.


Solution

  • (I'm assuming you have watchfiles installed)

    TL;DR

    Use recurring Unix's filename pattern matching to exclude all '.py' the add the ones you need to be watched one by one:

    uvicorn.run(
        "app.main:fast",
        host=host,
        port=port,
        log_level=log_level,
        reload=reload,
        reload_includes=[
            "app/**/*.py",
            "config/**/*",
            "manage.py",
            ".env"
        ],
        reload_excludes=[
            "./**/*.py",
        ]
    )
    

    Pattern matching

    Firstly, if the directory named private have .py files, it'll be watched by default see Uvicorn documentation. You need to give a list of Unix's filename pattern matching to both reload_excludes and reload_includes. The patterns you are providing only matches directories and files in first level.

    For instance, run:

    from pathlib import Path
    current_dir = Path.cwd()
    for match in current_dir.glob("app/*"):
        print(match)
    

    and you'll see that it will output all directories and files inside "app" directory, but not it's sub-folders or files in them.

    Recursively targeting a files extension

    For recurring file matching, say you want to target all html files inside the directory named 'templates'. Use the following filename pattern matching: "templates/**/*.html".

    Now say you also want to target all .js and .css files inside a 'staticfiles' directory.

    uvicorn.run(
        "app.main:fast",
        host=host,
        port=port,
        log_level=log_level,
        reload=reload,
        reload_includes=[
            "templates/**/*.html",
            "staticfiles/**/*.css",
            "staticfiles/**/*.js"
        ]
    )
    

    Excluding .py files from inside a folder

    Now say you also want you want to exclude all '.py' files inside 'private' directory. Even if you pass "private/**/*.py" to reload_excludes argument, since **/*.py is included by default you'll have to exclude patterns **/*.py and add all the other Python file pattern one by one:

    uvicorn.run(
        "app.main:fast",
        host=host,
        port=port,
        log_level=log_level,
        reload=reload,
        reload_includes=[
            "templates/**/*.html",
            "staticfiles/**/*.css",
            "staticfiles/**/*.js"
            "app/**/*.py",
            "config/**/*",
            "manage.py",
            ".env",
            reload_excludes=[
                "./**/*.py",
            ]
        ]
    )