🚒 Docker images and utilities to power your Python APIs and help you ship faster. With support for Uvicorn, Gunicorn, Starlette, and FastAPI.

Overview

🚒 inboard 🐳

inboard logo

Docker images and utilities to power your Python APIs and help you ship faster.

PyPI GitHub Container Registry Imports: isort Code style: black

builds hooks tests codecov

Mentioned in Awesome FastAPI

Description

This repository provides Docker images and a PyPI package with useful utilities for Python web servers. It runs Uvicorn with Gunicorn, and can be used to build applications with Starlette and FastAPI.

Quickstart

Get started with Docker, pull and run an image, and try an API endpoint.

docker pull ghcr.io/br3ndonland/inboard
docker run -d -p 80:80 ghcr.io/br3ndonland/inboard
http :80  # HTTPie: https://httpie.io/

Documentation

Documentation is built with Material for MkDocs, deployed on Vercel, and available at inboard.bws.bio and inboard.vercel.app.

Vercel build configuration:

  • Build command: python3 -m pip install 'mkdocs-material>=7.0.0,<=8.0.0' && mkdocs build --site-dir public
  • Output directory: public (default)

Vercel site configuration is specified in vercel.json.

Comments
  • Update poetry to 1.2.2 and regenerate lockfile

    Update poetry to 1.2.2 and regenerate lockfile

    inboard depends on gunicorn, which in turn depends on setuptools for pkg_resources. Unfortunately this sub-dependency is not mentioned in the poetry.lock file. That means when building a docker file as described in the docs, RUN poetry install will actually uninstall setuptools.

       - Removing setuptools (65.4.1)
    

    That in turn breaks guincorn.

    Traceback (most recent call last):
    ModuleNotFoundError: No module named 'pkg_resources'
        import pkg_resources
      File "/app/.venv/lib/python3.10/site-packages/gunicorn/util.py", line 25, in <module>
        from gunicorn import util
      File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/base.py", line 11, in <module>
        from gunicorn.app.base import Application
      File "/app/.venv/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py", line 9, in <module>
        from gunicorn.app.wsgiapp import run
      File "/app/.venv/bin/gunicorn", line 5, in <module>
    

    All I did for this PR is run poetry lock in the repository. It added setuptools in line 199 of poetry.lock as well as in the [metadata.files] section.

    opened by bodograumann 7
  • Add Python 3.10 support

    Add Python 3.10 support

    Description

    This PR will add Python 3.10 support to inboard.

    Changes

    GitHub Actions

    • [x] Add Python 3.10 to GitHub Actions workflows
    • [x] Update to stable Python 3.10 when it is released
    • [x] Update Python version syntax to enclose version numbers in quotes
      • Python versions must now be quoted in YAML. 3.10 (without quotes) is interpreted as a float and becomes 3.1, so "3.10" must be used instead.
      • https://github.com/actions/setup-python/issues/249#issuecomment-934299359
      • https://dev.to/hugovk/the-python-3-1-problem-85g

    Poetry

    • [x] Install Poetry successfully with Python 3.10: Poetry has introduced some breaking changes recently. To help buffer against these breaking changes, and to promote reproducible Poetry installations, br3ndonland/inboard#47 updated Poetry installations to use pipx instead of get-poetry.py or its replacement install-poetry.py. This change eliminated all of the issues seen with get-poetry.py and install-poetry.py.
    • [x] Install Poetry dependencies successfully with Python 3.10: Installing Poetry with pipx (#47) eliminated the issues seen when installing dependencies with Python 3.10.
    • [x] Ensure Python 3.10 classifier is included with Poetry package builds: Fixed as of Poetry 1.1.11 (python-poetry/poetry#4581).
    Expand this details element for details on some of the problems seen with get-poetry.py and install-poetry.py on Python 3.10.

    The previous get-poetry.py install script is not compatible with Python 3.10. Attempts to install Poetry with Python 3.10 and get-poetry.py may raise ModuleNotFoundError (python-poetry/poetry#3071, python-poetry/poetry#3345) and other errors:

     Traceback (most recent call last):
      File "/opt/poetry/bin/poetry", line 17, in <module>
        from poetry.console import main
      File "/opt/poetry/lib/poetry/console/__init__.py", line 1, in <module>
        from .application import Application
      File "/opt/poetry/lib/poetry/console/application.py", line 3, in <module>
        from cleo import Application as BaseApplication
    ModuleNotFoundError: No module named 'cleo'
    

    Poetry 1.1.11 claimed to resolve Python 3.10 issues, but it did not.

    The new install-poetry.py script (added in python-poetry/poetry#3706) must therefore be used, but it was problematic when it was introduced.

    install-poetry.py did not respect POETRY_VIRTUALENVS_CREATE

    As of Poetry 1.1.7, there may be complications when using install-poetry.py without a virtualenv (POETRY_VIRTUALENVS_CREATE=false). When installing dependencies, the following error is frequently seen:

    OSError
    
    Could not find a suitable TLS CA certificate bundle, invalid path:
    /opt/poetry/venv/lib/python3.9/site-packages/certifi/cacert.pem
    
    at /opt/poetry/venv/lib/python3.9/site-packages/requests/adapters.py:227
    in cert_verify
    

    In some situations, Poetry uninstalls its own dependencies. The OSError above occurs because Poetry uninstalls requests and certifi, then complains that it can't find them. See:

    • python-poetry/poetry#3139
    • python-poetry/poetry#3957
    • python-poetry/poetry#4195
    • python-poetry/poetry#4336
    • python-poetry/poetry#4470

    Poetry may have also been incorrectly attempting to read from its virtualenv (instead of site-packages) if it was not respecting POETRY_VIRTUALENVS_CREATE. Downstream steps also did not appear to respect POETRY_VIRTUALENVS_CREATE, so the application did not run. See:

    • python-poetry/poetry#3870
    • https://github.com/python-poetry/poetry/discussions/4271
    • python-poetry/poetry#4369
    • python-poetry/poetry#4414
    • https://github.com/python-poetry/poetry/issues/4429#issuecomment-903694778
    • python-poetry/poetry#4430
    • python-poetry/poetry#4433

    Poetry did not install packages correctly with install-poetry.py, POETRY_VIRTUALENVS_CREATE, and Python 3.10

    Poetry errored out with a JSONDecodeError when attempting to install packages with Python 3.10. See python-poetry/poetry#4210. This appeared to be resolved as of Poetry 1.2.0a2.

    There were also errors with Poetry's own virtualenv when using install-poetry.py, POETRY_VIRTUALENVS_CREATE, and multiple versions of Python, including 3.8 and 3.9. Error tracebacks typically reported a traceback sequence of CalledProcessError -> EnvCommandError -> PoetryException for each package, and appeared to involve virtualenv and pip. These errors may have been related to Poetry's "new installer", which can be disabled with the command poetry config experimental.new-installer false or the environment variable POETRY_EXPERIMENTAL_NEW_INSTALLER=false.

    Example traceback from a GitHub Actions run:

      CalledProcessError
    
      Command '['/opt/poetry/venv/bin/python', '/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip', 'install', '--disable-pip-version-check', '--prefix', '/opt/hostedtoolcache/Python/3.8.11/x64', '--no-deps', 'file:/home/runner/.cache/pypoetry/artifacts/94/aa/c7/f2115774653c0d0bf72ce93092eb73de70854164adf9f00749a952ba9c/websockets-9.1-cp38-cp38-manylinux2010_x86_64.whl']' returned non-zero exit status 1.
    
      at /opt/hostedtoolcache/Python/3.8.11/x64/lib/python3.8/subprocess.py:516 in run
           512β”‚             # We don't call process.wait() as .__exit__ does that for us.
           513β”‚             raise
           514β”‚         retcode = process.poll()
           515β”‚         if check and retcode:
        β†’  516β”‚             raise CalledProcessError(retcode, process.args,
           517β”‚                                      output=stdout, stderr=stderr)
           518β”‚     return CompletedProcess(process.args, retcode, stdout, stderr)
           519β”‚
           520β”‚
    
    The following error occurred when trying to handle this error:
    
    
      EnvCommandError
    
      Command ['/opt/poetry/venv/bin/python', '/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip', 'install', '--disable-pip-version-check', '--prefix', '/opt/hostedtoolcache/Python/3.8.11/x64', '--no-deps', 'file:/home/runner/.cache/pypoetry/artifacts/94/aa/c7/f2115774653c0d0bf72ce93092eb73de70854164adf9f00749a952ba9c/websockets-9.1-cp38-cp38-manylinux2010_x86_64.whl'] errored with the following return code 1, and output:
      Traceback (most recent call last):
        File "/opt/hostedtoolcache/Python/3.8.11/x64/lib/python3.8/runpy.py", line 194, in _run_module_as_main
          return _run_code(code, main_globals, None,
        File "/opt/hostedtoolcache/Python/3.8.11/x64/lib/python3.8/runpy.py", line 87, in _run_code
          exec(code, run_globals)
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/__main__.py", line 24, in <module>
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/cli/main.py", line 71, in main
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/commands/__init__.py", line 96, in create_command
        File "/opt/hostedtoolcache/Python/3.8.11/x64/lib/python3.8/importlib/__init__.py", line 127, in import_module
          return _bootstrap._gcd_import(name[level:], package, level)
        File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/commands/install.py", line 15, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/cli/req_command.py", line 21, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/req/__init__.py", line 8, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/req/req_install.py", line 32, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_internal/pyproject.py", line 4, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_vendor/toml/__init__.py", line 6, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_vendor/toml/encoder.py", line 6, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
        File "<frozen importlib._bootstrap>", line 618, in _load_backward_compatible
        File "<frozen zipimport>", line 259, in load_module
        File "/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl/pip/_vendor/toml/decoder.py", line 7, in <module>
        File "<frozen importlib._bootstrap>", line 991, in _find_and_load
        File "<frozen importlib._bootstrap>", line 971, in _find_and_load_unlocked
        File "<frozen importlib._bootstrap>", line 914, in _find_spec
        File "<frozen importlib._bootstrap_external>", line 1407, in find_spec
        File "<frozen importlib._bootstrap_external>", line 1381, in _get_spec
        File "<frozen importlib._bootstrap_external>", line 1362, in _legacy_get_spec
        File "<frozen importlib._bootstrap>", line 414, in spec_from_loader
        File "<frozen importlib._bootstrap_external>", line 709, in spec_from_file_location
        File "<frozen zipimport>", line 191, in get_filename
        File "<frozen zipimport>", line 709, in _get_module_code
        File "<frozen zipimport>", line 536, in _get_data
      FileNotFoundError: [Errno 2] No such file or directory: '/opt/poetry/venv/lib/python3.8/site-packages/virtualenv/seed/wheels/embed/pip-21.0.1-py3-none-any.whl'
    
    
      at /opt/poetry/venv/lib/python3.8/site-packages/poetry/utils/env.py:1300 in _run
          1296β”‚                 output = subprocess.check_output(
          1297β”‚                     cmd, stderr=subprocess.STDOUT, env=env, **kwargs
          1298β”‚                 )
          1299β”‚         except CalledProcessError as e:
        β†’ 1300β”‚             raise EnvCommandError(e, input=input_)
          1301β”‚
          1302β”‚         return decode(output)
          1303β”‚
          1304β”‚     def execute(self, bin: str, *args: str, **kwargs: Any) -> Optional[int]:
    
    The following error occurred when trying to handle this error:
    
    
      PoetryException
    
      Failed to install file:/home/runner/.cache/pypoetry/artifacts/94/aa/c7/f2115774653c0d0bf72ce93092eb73de70854164adf9f00749a952ba9c/websockets-9.1-cp38-cp38-manylinux2010_x86_64.whl
    
      at /opt/poetry/venv/lib/python3.8/site-packages/poetry/utils/pip.py:60 in pip_install
           56β”‚                     *env.get_pip_command(),
           57β”‚                     *args,
           58β”‚                     env={**os.environ, "PYTHONPATH": str(env.purelib)},
           59β”‚                 )
        β†’  60β”‚         raise PoetryException(f"Failed to install {path.as_posix()}") from e
           61β”‚
           62β”‚
           63β”‚ def pip_editable_install(directory: Path, environment: Env) -> Union[int, str]:
           64β”‚     return pip_install(
    
    

    May be somewhat related to:

    • python-poetry/poetry#3336
    • python-poetry/install.python-poetry.org#61
    • python-poetry/poetry#4195
    • python-poetry/poetry#4463

    install-poetry.py was not present in stable releases

    Poetry 1.1 releases did not include install-poetry.py in their source tree. This could be a problem for projects that fetch install-poetry.py from a specific Git tag, as this project does (br3ndonland/inboard#44).

    For example:

    • https://raw.githubusercontent.com/python-poetry/poetry/1.1.11/get-poetry.py returns the get-poetry.py install script
    • https://raw.githubusercontent.com/python-poetry/poetry/1.1.11/install-poetry.py returns 404

    pydantic and typing

    • [x] Ensure pydantic provides Python package wheels for Python 3.10: Python 3.10 support was added in pydantic 1.9.0.
    • [x] Ensure pydantic models are compatible with Python 3.10 type annotations:
      • The Python 3.10 union operator (the pipe, like str | None) will not be used on pydantic models. This project still supports Python 3.8 and 3.9. If running Python 3.9 or below, pydantic is not compatible with the union operator, even if annotations are imported with from __future__ import annotations.
      • PEP 604: Allow writing union types as X | Y
      • https://github.com/samuelcolvin/pydantic/issues/2597#issuecomment-1086194186
      • https://github.com/samuelcolvin/pydantic/pull/2609#issuecomment-1084341812
      • https://github.com/samuelcolvin/pydantic/issues/3300#issuecomment-1034007897
    • [x] Update type annotations for Python 3.10 (all except the union types on pydantic model fields)

    Related

    GitHub Actions

    • actions/setup-python#249

    Poetry

    • br3ndonland/inboard#44
    • br3ndonland/inboard#47
    • python-poetry/poetry#3071
    • python-poetry/poetry#3139
    • python-poetry/poetry#3336
    • python-poetry/poetry#3345
    • python-poetry/poetry#3706
    • python-poetry/poetry#3870
    • python-poetry/poetry#3957
    • python-poetry/poetry#4056
    • python-poetry/poetry#4195
    • python-poetry/poetry#4210
    • https://github.com/python-poetry/poetry/discussions/4271
    • python-poetry/poetry#4336
    • python-poetry/poetry#4369
    • python-poetry/poetry#4414
    • python-poetry/poetry#4429
    • python-poetry/poetry#4430
    • python-poetry/poetry#4433
    • python-poetry/poetry#4463
    • python-poetry/poetry#4470
    • python-poetry/poetry#4581

    pydantic and typing

    opened by br3ndonland 4
  • Migrate from Poetry 1.1 to Hatch

    Migrate from Poetry 1.1 to Hatch

    Description

    This PR will migrate inboard from Poetry 1.1 to Hatch. See br3ndonland/inboard#56 for more details and context around the motivations for this change.

    Note that projects using inboard are not required to migrate to Hatch. The Docker images will retain Poetry 1.1 for backwards compatibility for now. Poetry 1.1 is unmaintained, and so it will eventually need to be removed, but there will be notice given at least one minor version ahead of time. If projects using inboard require poetry>1.2, they can add pipx upgrade poetry or pipx install poetry>1.2 --force to their Dockerfiles as described in the updated docs in this PR (on the Docker page, under "Docker and Poetry").

    Changes

    Update configuration files for Hatch

    • Remove poetry.lock
    • Remove poetry.toml
    • Remove [tool.poetry] sections from pyproject.toml
    • Update [build-system] section in pyproject.toml for Hatchling
    • Move version number to inboard/__init__.py, which allows Python projects to import it with import inboard then inboard.__version__
    • Add project metadata to new [project] section in pyproject.toml, bringing this project in line with Python project metadata standards
    • Add optional dependencies to [project.optional-dependencies] groups
    • Pin asgiref and mypy to avoid breaking changes to type checking that could be introduced by changes to mypy strict mode (2cbc99c) or by changes to asgiref TypedDict keys (d729fdf)
    • Add environments to [tool.hatch.envs.*] tables, using features for optional dependency groups, and path = ".venv" for an in-project virtualenv in the same location as a Poetry in-project virtualenv
    • Add Hatch environment names to .gitignore

    Update Dockerfile for Hatch

    • Remove poetry.lock and poetry.toml from COPY step
    • Add README.md to COPY step (README.md is part of the Python package)
    • Break ARG and ENV commands onto multiple lines for readability
    • Add HATCH_VERSION build arg
    • Set HATCH_ENV_TYPE_VIRTUAL_PATH=".venv" to ensure consistent in-project virtualenv location
    • Install Hatch with pipx
    • Continue installing Poetry 1.1 with pipx for backwards compatibility for now. Poetry 1.1 is unmaintained, and so it will eventually need to be removed, but there will be notice given at least one minor version ahead of time.
    • Replace poetry install commands with hatch env create commands. There is no direct command to manually install or sync dependencies. The Hatch docs say, "You never need to manually create environments as spawning a shell or running commands within one will automatically trigger creation." The hatch env create command will create the virtual environment and install the appropriate dependencies. Note that commands for directly installing or syncing environments may be added in the future.
      • pypa/hatch#593
      • pypa/hatch#650

    Update GitHub Actions workflows for Hatch

    • Update test that ensures pipx installs correct version of Hatch
    • Update test for consistent in-project virtualenv directory (.venv)
    • Update caching for pip so that GitHub Actions caches dependencies
    • Update poetry run commands for Hatch, ensuring correct environment is selected with the HATCH_ENV environment variable
    • Update package build and PyPI publish commands for Hatch

    Update docs for Hatch

    • Add new user words to cspell.json
    • Add Hatch description and instructions to contributing.md
    • Update Poetry references in docs for Hatch
    • Move instructions for code quality checks below instructions for Hatch so that contributors understand how to set up the project prior to running code quality checks

    Related

    opened by br3ndonland 3
  • Add configurable logging filter (Sourcery refactored)

    Add configurable logging filter (Sourcery refactored)

    Pull Request #49 refactored by Sourcery.

    If you're happy with these changes, merge this Pull Request using the Squash and merge strategy.

    NOTE: As code is pushed to the original Pull Request, Sourcery will re-run and update (force-push) this Pull Request with new refactorings as necessary. If Sourcery finds no refactorings at any point, this Pull Request will be closed automatically.

    See our documentation here.

    Run Sourcery locally

    Reduce the feedback loop during development by using the Sourcery editor plugin:

    Review changes via command line

    To manually merge these changes, make sure you're on the logging-filter branch, then run:

    git fetch origin sourcery/logging-filter
    git merge --ff-only FETCH_HEAD
    git reset HEAD^
    

    Help us improve this pull request!

    opened by sourcery-ai[bot] 3
  • Add configurable logging filter

    Add configurable logging filter

    Description

    When applications with APIs are deployed, it is common to perform "health checks" on them. Health checks are usually performed by making HTTP requests to a designated API endpoint. These checks are made at frequent intervals, and so they can fill up the access logs with large numbers of unnecessary log records.

    To avoid logging health checks, it would be helpful to have a way to filter health checks out of the logs.

    Changes

    This PR will add a configurable logging filter to inboard.logging_conf.

    • Filters can be provided as a comma-separated string with the environment variable LOG_FILTERS, like LOG_FILTERS="/health, /heartbeat".
    • The environment variable value will be converted into a set of strings.
    • Each log message will then be checked for each filter in the set. If any matches are present in the log message, the logger will not log that message.

    There are multiple ways to match these filters. inboard simply performs a substring membership check to see if any of the filters in the set have a match in the log message. This should work for most use cases, but the match could be overly greedy. For example, if /health is in LOG_FILTERS, it would filter out calls to /health, but it would also filter out calls to /healthy.

    Another approach could be to operate on the LogRecord args, but the results would vary based on how the program supplies the log record.

    • As seen in the type stubs for the logging module, the args can be either tuple[object, ...] or Mapping[str, object]. The args could be converted to a set, and compared with the set of filters using isdisjoint. This set comparison approach is potentially more precise than string matching.
    • Gunicorn's access logger gunicorn.access supports several custom message fields, which it calls "atoms" in its source code, and "identifiers" in the docs. Gunicorn formats these atoms and supplies them as LogRecord args. The URL path could be logged with the %(U)s atom, and inboard could check to see if there is an exact match in the set of filters.
    • Uvicorn's access logger uvicorn.access automatically supplies the URL path as its own arg, which could also work nicely with this approach.
    • However, LogRecord args aren't always supplied as strings directly, so there would probably need to be isinstance checks added to check each of the args. For example, when watching files with watchgod (now renamed to watchfiles), Uvicorn logs the list of files being watched as a single arg. Lists are not hashable, so the list would need to be converted to another type (like set, or str as the logger does when formatting messages) in order to perform equality comparisons.

    Substring membership checks should be adequate for this feature, and they avoid the complications of working with LogRecord args directly.

    Related

    opened by br3ndonland 3
  • Drop `toml` dependency

    Drop `toml` dependency

    Description

    While the TOML spec is alive and well, the toml package that was used in this project is dead (uiri/toml#267).

    There are several alternatives:

    • rtoml is the fastest, but does not provide "round trip" guarantees (meaning that it can't load, then dump, and get an identical result). However, for simple loading and parsing it's fine. It also requires a Rust environment to compile from source, which may be a consideration on distributions like Alpine Linux.
    • tomli is another alternative, but it is read-only (writing requires a separate tomli-w package), and requires files to be opened in binary mode to parse TOML.
    • tomlkit is used by Poetry (and is written by the original author of Poetry), but is currently 70x slower than rtoml.

    This PR will remove toml from the fastapi Poetry extras group, and make the necessary source code changes.

    Rather than installing an alternative TOML package, the TOML logic will simply be removed from inboard, because the TOML parsing and settings loading was somewhat tangential to the focus of the project. A separate project, fastenv, is more focused on settings management, and may feature TOML support in the future.

    Note that toml is still a sub-dependency of some dev-dependencies, including pre-commit and pytest.

    Changes

    • Drop toml dependency (619f63c)

    Related

    uiri/toml#267 https://github.com/uiri/toml/pull/279#issuecomment-633012586

    opened by br3ndonland 3
  • Add `UVICORN_CONFIG_OPTIONS` environment variable for catch-all configuration

    Add `UVICORN_CONFIG_OPTIONS` environment variable for catch-all configuration

    Description

    Gunicorn supports a GUNICORN_CMD_ARGS catch-all environment variable for configuring settings. It looks for the GUNICORN_CMD_ARGS environment variable automatically, and gives these settings precedence over other environment variables and Gunicorn config files.

    It would be useful to have something similar for Uvicorn. This PR will add support for a UVICORN_CONFIG_OPTIONS environment variable. The idea here is to allow a catch-all Uvicorn config variable in the spirit of GUNICORN_CMD_ARGS, so that advanced users can specify the full range of Uvicorn options even if inboard has not directly implemented them. The inboard.start module will run the UVICORN_CONFIG_OPTIONS environment variable value through json.loads(), and then pass the resultant dictionary through to Uvicorn. If the same option is set with an individual environment variable (such as WITH_RELOAD) and with a JSON value in UVICORN_CONFIG_OPTIONS, the JSON value will take precedence.

    json.loads() converts data types from JSON to Python, and returns a Python dictionary. See the guide to understanding JSON schema for many helpful examples of how JSON data types correspond to Python data types. If the Uvicorn options are already available as a Python dictionary, dump them to a JSON-formatted string with json.dumps(), and set that as an environment variable.

    The UVICORN_CONFIG_OPTIONS environment variable is suggested for advanced usage, because it requires some knowledge of uvicorn.config.Config. Other than the JSON -> Python dictionary conversion, no additional type conversions or validations are performed on UVICORN_CONFIG_OPTIONS. All options should be able to be passed directly to uvicorn.config.Config.

    Changes

    • Use pytest fixtures for test Uvicorn configs (2964358)
    • Implement UVICORN_CONFIG_OPTIONS variable (a8773b6)
    • Document UVICORN_CONFIG_OPTIONS variable (509b4bf)

    Related

    br3ndonland/inboard#21 br3ndonland/inboard#39

    opened by br3ndonland 3
  • Upgrade to Uvicorn 0.15 and implement new reload options (Sourcery refactored)

    Upgrade to Uvicorn 0.15 and implement new reload options (Sourcery refactored)

    Pull Request #39 refactored by Sourcery.

    If you're happy with these changes, merge this Pull Request using the Squash and merge strategy.

    NOTE: As code is pushed to the original Pull Request, Sourcery will re-run and update (force-push) this Pull Request with new refactorings as necessary. If Sourcery finds no refactorings at any point, this Pull Request will be closed automatically.

    See our documentation here.

    Run Sourcery locally

    Reduce the feedback loop during development by using the Sourcery editor plugin:

    Review changes via command line

    To manually merge these changes, make sure you're on the uvicorn-0.15 branch, then run:

    git fetch origin sourcery/uvicorn-0.15
    git merge --ff-only FETCH_HEAD
    git reset HEAD^
    

    Help us improve this pull request!

    opened by sourcery-ai[bot] 3
  • Upgrade to Uvicorn 0.15 and implement new reload options

    Upgrade to Uvicorn 0.15 and implement new reload options

    Description

    PR #21 implemented the Uvicorn reload_dirs setting, enabling file watching and hot-reloading with watchgod for development.

    In encode/uvicorn#820 and Uvicorn 0.15, additional reload configuration options were added:

    • reload_delay (CLI equivalent: --reload-delay)
    • reload_excludes (CLI equivalent: --reload-exclude)
    • reload_includes (CLI equivalent: --reload-include)

    This PR will implement the new Uvicorn reload options. The options will be configured with the following environment variables:

    • RELOAD_DELAY
    • RELOAD_EXCLUDES
    • RELOAD_INCLUDES

    Changes

    • Upgrade to Uvicorn 0.15 (ffb47df)
    • Implement new Uvicorn 0.15 reload config options (f4428dd)
    • Document new Uvicorn 0.15 reload config options (95fe201)
    • Update #39 with Sourcery refactorings from #40 (75b81a4)

    Related

    br3ndonland/inboard#21 encode/uvicorn#820 https://github.com/encode/uvicorn/releases/tag/0.15.0

    opened by br3ndonland 3
  • Improve testing of server configuration

    Improve testing of server configuration

    Description

    Unit test coverage is at 100%, but testing doesn't stop there. It is still important to test that the Uvicorn and Gunicorn servers are being properly configured with environment variables and configuration files. This PR will provide improvements to the unit tests for the Uvicorn and Gunicorn servers, while maintaining test coverage at 100%.

    Changes

    Gunicorn

    • Properly mock Gunicorn and Uvicorn processes (9ddbc4b)
      • Add full process calls: verify that the server functions (uvicorn.run for Uvicorn, subprocess.run for Gunicorn) are called with expected arguments.
      • Add mock for subprocess.run(): similar to Uvicorn update in 3906b3d.
      • Provide Gunicorn with correct worker_tmp_dir: The Gunicorn server was previously exiting without need for a mock. However, this was actually because it couldn't find the hard-coded worker_tmp_dir (/dev/shm), as can be seen when invoking pytest with pytest -vv --capture=sys. The solution is to override the hard-coded worker_tmp_dir with GUNICORN_CMD_ARGS="--worker-tmp-dir $DIR". Although the full Gunicorn module is not mocked here, it's a good practice to properly configure the Gunicorn server.
    • Improve Gunicorn performance auto-tuning (02b249e)
      • The "auto-tuning" advertised in tiangolo/uvicorn-gunicorn-docker is basically a few lines of the gunicorn_conf.py that determine the number of Gunicorn workers to run. It would be helpful to write some unit test cases for this feature, but without being in a separate unit, it is difficult to unit test in isolation.
      • This commit will refactor the performance auto-tuning into a function, gunicorn_conf.calculate_workers, and will add unit test cases to test_start.py to verify the resulting number of worker processes.
    • Clarify Gunicorn worker calculation in README (2567156)

    General

    • Improve pytest fixtures for temporary paths (c1a6f3a)
      • Ensure Path() is used for shutil arguments instead of string
      • Create temporary directory for Gunicorn with tmp_path_factory
      • Copy gunicorn_conf.py to temporary directory
      • Update test_start.py with new Gunicorn paths
    • Set default process manager for base ASGI app (9d51de4)
      • 5e6af54 added a check for the PROCESS_MANAGER environment variable in inboard/app/base/main.py, but no default was set.
      • This commit will set Gunicorn as the default process manager, to avoid errors if PROCESS_MANAGER is not set.

    Related

    br3ndonland/inboard@3906b3d br3ndonland/inboard@cd7604c br3ndonland/inboard#8 https://github.com/tiangolo/uvicorn-gunicorn-docker/releases/tag/0.3.0 tiangolo/uvicorn-gunicorn-docker#5 tiangolo/uvicorn-gunicorn-starlette-docker#4 tiangolo/uvicorn-gunicorn-fastapi-docker#6

    opened by br3ndonland 3
  • Bump certifi from 2022.9.24 to 2022.12.7

    Bump certifi from 2022.9.24 to 2022.12.7

    Bumps certifi from 2022.9.24 to 2022.12.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 2
Owner
Brendon Smith
I build software to support the creation, curation, and application of scientific knowledge.
Brendon Smith
Opentracing support for Starlette and FastApi

Starlette-OpenTracing OpenTracing support for Starlette and FastApi. Inspired by: Flask-OpenTracing OpenTracing implementations exist for major distri

Rene Dohmen 63 Dec 30, 2022
Opentracing support for Starlette and FastApi

Starlette-OpenTracing OpenTracing support for Starlette and FastApi. Inspired by: Flask-OpenTracing OpenTracing implementations exist for major distri

Rene Dohmen 26 Feb 11, 2021
Prometheus exporter for Starlette and FastAPI

starlette_exporter Prometheus exporter for Starlette and FastAPI. The middleware collects basic metrics: Counter: starlette_requests_total Histogram:

Steve Hillier 225 Jan 5, 2023
A rate limiter for Starlette and FastAPI

SlowApi A rate limiting library for Starlette and FastAPI adapted from flask-limiter. Note: this is alpha quality code still, the API may change, and

Laurent Savaete 562 Jan 1, 2023
A rate limiter for Starlette and FastAPI

SlowApi A rate limiting library for Starlette and FastAPI adapted from flask-limiter. Note: this is alpha quality code still, the API may change, and

Laurent Savaete 154 Feb 16, 2021
Prometheus exporter for Starlette and FastAPI

starlette_exporter Prometheus exporter for Starlette and FastAPI. The middleware collects basic metrics: Counter: starlette_requests_total Histogram:

Steve Hillier 82 Feb 13, 2021
SQLAlchemy Admin for Starlette/FastAPI

SQLAlchemy Admin for Starlette/FastAPI SQLAdmin is a flexible Admin interface for SQLAlchemy models. Main features include: SQLAlchemy sync/async engi

Amin Alaee 683 Jan 3, 2023
FastAPI Server Session is a dependency-based extension for FastAPI that adds support for server-sided session management

FastAPI Server-sided Session FastAPI Server Session is a dependency-based extension for FastAPI that adds support for server-sided session management.

DevGuyAhnaf 5 Dec 23, 2022
Easy and secure implementation of Azure AD for your FastAPI APIs πŸ”’

FastAPI-Azure-auth Azure AD Authentication for FastAPI apps made easy. ?? Description FastAPI is a modern, fast (high-performance), web framework for

Intility 216 Dec 27, 2022
Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id.

starlette context Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automat

Tomasz WΓ³jcik 300 Dec 26, 2022
Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id.

starlette context Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automat

Tomasz WΓ³jcik 110 Feb 16, 2021
Sample-fastapi - A sample app using Fastapi that you can deploy on App Platform

Getting Started We provide a sample app using Fastapi that you can deploy on App

Erhan BÜTE 2 Jan 17, 2022
Opinionated set of utilities on top of FastAPI

FastAPI Contrib Opinionated set of utilities on top of FastAPI Free software: MIT license Documentation: https://fastapi-contrib.readthedocs.io. Featu

identix.one 543 Jan 5, 2023
Reusable utilities for FastAPI

Reusable utilities for FastAPI Documentation: https://fastapi-utils.davidmontague.xyz Source Code: https://github.com/dmontagu/fastapi-utils FastAPI i

David Montague 1.3k Jan 4, 2023
Opinionated set of utilities on top of FastAPI

FastAPI Contrib Opinionated set of utilities on top of FastAPI Free software: MIT license Documentation: https://fastapi-contrib.readthedocs.io. Featu

identix.one 281 Feb 15, 2021
Reusable utilities for FastAPI

Reusable utilities for FastAPI Documentation: https://fastapi-utils.davidmontague.xyz Source Code: https://github.com/dmontagu/fastapi-utils FastAPI i

David Montague 543 Feb 17, 2021
The template for building scalable web APIs based on FastAPI, Tortoise ORM and other.

FastAPI and Tortoise ORM. Powerful but simple template for web APIs w/ FastAPI (as web framework) and Tortoise-ORM (for working via database without h

prostomarkeloff 95 Jan 8, 2023
CLI and Streamlit applications to create APIs from Excel data files within seconds, using FastAPI

FastAPI-Wrapper CLI & APIness Streamlit App Arvindra Sehmi, Oxford Economics Ltd. | Website | LinkedIn (Updated: 21 April, 2021) fastapi-wrapper is mo

Arvindra 49 Dec 3, 2022
Dead simple CSRF security middleware for Starlette ⭐ and Fast API ⚑

csrf-starlette-fastapi Dead simple CSRF security middleware for Starlette ⭐ and Fast API ⚑ Will work with either a <input type="hidden"> field or ajax

Nathaniel Sabanski 9 Nov 20, 2022