# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['inboard',
 'inboard.app',
 'inboard.app.base',
 'inboard.app.fastapibase',
 'inboard.app.starlettebase']

package_data = \
{'': ['*']}

install_requires = \
['gunicorn>=20,<21', 'uvicorn>=0.11.8,<0.12.0']

extras_require = \
{'fastapi': ['fastapi>=0.61,<0.62'],
 'requests': ['requests>=2.24,<3.0'],
 'starlette': ['starlette>=0.13,<0.14']}

setup_kwargs = {
    'name': 'inboard',
    'version': '0.2.1',
    'description': 'Docker images to power your Python APIs and help you ship faster.',
    'long_description': '# 🚢 inboard 🐳\n\n_Docker images to power your Python APIs and help you ship faster. With support for Uvicorn, Gunicorn, Starlette, and FastAPI._\n\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)\n[![PyPI](https://img.shields.io/pypi/v/inboard?color=success)](https://pypi.org/project/inboard/)\n[![builds](https://github.com/br3ndonland/inboard/workflows/builds/badge.svg)](https://github.com/br3ndonland/inboard/actions)\n[![hooks](https://github.com/br3ndonland/inboard/workflows/hooks/badge.svg)](https://github.com/br3ndonland/inboard/actions)\n[![tests](https://github.com/br3ndonland/inboard/workflows/tests/badge.svg)](https://github.com/br3ndonland/inboard/actions)\n\nBrendon Smith ([br3ndonland](https://github.com/br3ndonland/))\n\n## Table of Contents <!-- omit in toc -->\n\n- [Description](#description)\n- [Instructions](#instructions)\n  - [Configure Docker for GitHub Container Registry](#configure-docker-for-github-container-registry)\n  - [Pull images](#pull-images)\n  - [Use images in a _Dockerfile_](#use-images-in-a-dockerfile)\n  - [Run containers](#run-containers)\n- [Configuration](#configuration)\n  - [General](#general)\n  - [Gunicorn and Uvicorn](#gunicorn-and-uvicorn)\n  - [Logging](#logging)\n- [Development](#development)\n  - [Code style](#code-style)\n  - [Building development images](#building-development-images)\n  - [Running development containers](#running-development-containers)\n\n## Description\n\nThis repo is inspired by [tiangolo/uvicorn-gunicorn-docker](https://github.com/tiangolo/uvicorn-gunicorn-docker), with the following advantages:\n\n- **One repo**. The tiangolo/uvicorn-gunicorn images are in at least three separate repos ([tiangolo/uvicorn-gunicorn-docker](https://github.com/tiangolo/uvicorn-gunicorn-docker), [tiangolo/uvicorn-gunicorn-fastapi-docker](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker), and [tiangolo/uvicorn-gunicorn-starlette-docker](https://github.com/tiangolo/uvicorn-gunicorn-starlette-docker)), with large amounts of code duplication, making maintenance difficult for an [already-busy maintainer](https://github.com/encode/uvicorn/pull/705#issuecomment-660042305). This repo combines three into one.\n- **One _Dockerfile_.** This repo leverages [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) to produce multiple Docker images from one _Dockerfile_.\n- **One Python requirements file.** This repo uses [Poetry](https://github.com/python-poetry/poetry) with Poetry Extras for dependency management with a single _pyproject.toml_.\n- **One logging configuration.** Logging a Uvicorn+Gunicorn+Starlette/FastAPI stack is unnecessarily complicated. Uvicorn and Gunicorn use different logging configurations, and it can be difficult to unify the log streams. In this repo, Uvicorn, Gunicorn, and FastAPI log streams are propagated to the root logger, and handled by the custom root logging config. Developers can also supply their own custom logging configurations.\n- **One programming language.** Pure Python with no shell scripts.\n- **One platform.** You\'re already on GitHub. Why not [pull Docker images from GitHub Container Registry](https://github.blog/2020-09-01-introducing-github-container-registry/)?\n\n## Instructions\n\n### Configure Docker for GitHub Container Registry\n\n[GitHub Container Registry](https://docs.github.com/en/packages/getting-started-with-github-container-registry) (GHCR) is a Docker registry. Follow the instructions in the GitHub docs on [configuring Docker for use with GHCR](https://docs.github.com/en/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images).\n\nYou\'ll need to [create a personal access token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). On GitHub, navigate to _Settings -> Developer settings -> Personal access tokens_ ([github.com/settings/tokens](https://github.com/settings/tokens)), then click "Generate new token." The token should have `read:packages` scope. You can then copy the token and use it with [`docker login`](https://docs.docker.com/engine/reference/commandline/login/):\n\n```sh\n# create PAT in GitHub and copy to clipboard\n\n# transfer PAT from clipboard to file\npbpaste > pat-ghcr.txt\n\n# log in with file\ncat pat-ghcr.txt | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin\n```\n\nIf you don\'t want to store your PAT in plain text, encrypt it with PGP instead. [GPG](https://www.gnupg.org/) or [Keybase](https://keybase.io) can be used for this. Here\'s how to do it with Keybase:\n\n```sh\n# create PAT in GitHub and copy to clipboard\n\n# transfer PAT from clipboard to encrypted file\npbpaste | keybase encrypt -o pat-ghcr.asc $YOUR_USERNAME\n\n# decrypt and log in\nkeybase decrypt -i pat-ghcr.asc | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin\n\n# can also use keybase pgp encrypt and keybase pgp decrypt, but must export PGP key\n```\n\n### Pull images\n\nAfter logging in, you can then pull images from `ghcr.io`.\n\n```sh\n# Pull most recent version of each image\ndocker pull ghcr.io/br3ndonland/inboard:base\ndocker pull ghcr.io/br3ndonland/inboard:fastapi\ndocker pull ghcr.io/br3ndonland/inboard:starlette\n\n# Pull image from specific release\ndocker pull ghcr.io/br3ndonland/inboard:base-0.2.0\ndocker pull ghcr.io/br3ndonland/inboard:fastapi-0.2.0\ndocker pull ghcr.io/br3ndonland/inboard:starlette-0.2.0\n```\n\nThe FastAPI image is also tagged with `latest`. Docker uses the `latest` tag by default, so simply running `docker pull ghcr.io/br3ndonland/inboard` will pull the FastAPI image.\n\n### Use images in a _Dockerfile_\n\nFor a [Poetry](https://github.com/python-poetry/poetry) project with the following directory structure:\n\n- `repo`\n  - `package`\n    - `main.py`\n    - `prestart.py`\n  - `Dockerfile`\n  - `poetry.lock`\n  - `pyproject.toml`\n\nThe _Dockerfile_ could look like this:\n\n```dockerfile\nFROM ghcr.io/br3ndonland/inboard:fastapi\n\n# Install Python requirements\nCOPY poetry.lock pyproject.toml /app/\nWORKDIR /app/\nRUN poetry install --no-dev --no-interaction --no-root\n\n# Install Python app\nCOPY package /app/package\nENV APP_MODULE=package.main:app\n# RUN command already included in base image\n```\n\nOrganizing the _Dockerfile_ this way helps [leverage the Docker build cache](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache). Files and commands that change most frequently are added last to the _Dockerfile_. Next time the image is built, Docker will skip any layers that didn\'t change, speeding up builds.\n\nFor a standard `pip` install:\n\n- `repo`\n  - `package`\n    - `main.py`\n    - `prestart.py`\n  - `Dockerfile`\n  - `requirements.txt`\n\n```dockerfile\nFROM ghcr.io/br3ndonland/inboard:fastapi\n\n# Install Python requirements\nCOPY requirements.txt /app/\nWORKDIR /app/\nRUN python -m pip install -r requirements.txt\n\n# Install Python app\nCOPY package /app/package\nENV APP_MODULE=package.main:app\n# RUN command already included in base image\n```\n\nThe image could then be built with:\n\n```sh\ncd /path/to/repo\ndocker build . -t imagename:latest\n```\n\nThe final argument is the Docker image name (`imagename` in this example). Replace with your image name.\n\n### Run containers\n\nRun container:\n\n```sh\ndocker run -d -p 80:80 imagename\n```\n\nRun container with mounted volume and Uvicorn reloading for development:\n\n```sh\ncd /path/to/repo\ndocker run -d -p 80:80 \\\n  -e "LOG_LEVEL=debug" -e "PROCESS_MANAGER=uvicorn" -e "WITH_RELOAD=true" \\\n  -v $(pwd)/package:/app/package imagename\n```\n\nDetails on the `docker run` command:\n\n- `-e "PROCESS_MANAGER=uvicorn" -e "WITH_RELOAD=true"` will instruct `start.py` to run Uvicorn with reloading and without Gunicorn. The Gunicorn configuration won\'t apply, but these environment variables will still work as [described](#configuration):\n  - `APP_MODULE`\n  - `HOST`\n  - `PORT`\n  - `LOG_COLORS`\n  - `LOG_FORMAT`\n  - `LOG_LEVEL`\n- `-v $(pwd)/package:/app/package`: the specified directory (`/path/to/repo/package` in this example) will be [mounted as a volume](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems) inside of the container at `/app/package`. When files in the working directory change, Docker and Uvicorn will sync the files to the running Docker container.\n\nHit an API endpoint:\n\n```sh\ndocker pull ghcr.io/br3ndonland/inboard:fastapi\ndocker run -d -p 80:80 ghcr.io/br3ndonland/inboard:fastapi\nhttp :80  # HTTPie: https://httpie.org/\n```\n\n```text\nHTTP/1.1 200 OK\ncontent-length: 17\ncontent-type: application/json\ndate: Wed, 02 Sep 2020 00:31:01 GMT\nserver: uvicorn\n\n{\n    "Hello": "World"\n}\n```\n\n## Configuration\n\nTo set environment variables when starting the Docker image:\n\n```sh\ndocker run -d -p 80:80 -e APP_MODULE="package.custom.module:api" -e WORKERS_PER_CORE="2" myimage\n```\n\nTo set environment variables within a _Dockerfile_:\n\n```dockerfile\nFROM ghcr.io/br3ndonland/inboard:fastapi\nENV APP_MODULE="package.custom.module:api" WORKERS_PER_CORE="2"\n```\n\n### General\n\n- `APP_MODULE`: Python module with app instance. Note that the base image sets the environment variable `PYTHONPATH=/app`, so the module name will be relative to `/app` unless you supply a custom [`PYTHONPATH`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH).\n\n  - Default: The appropriate app module from inboard.\n  - Custom: For a module at `/app/package/custom/module.py` and app instance object `api`, `APP_MODULE="package.custom.module:api"`\n\n    ```py\n    # /app/package/custom/module.py\n    from fastapi import FastAPI\n\n    api = FastAPI()\n\n    @api.get("/")\n    def read_root():\n        return {"message": "Hello World!"}\n    ```\n\n- `PRE_START_PATH`: Path to a pre-start script. Add a file `prestart.py` or `prestart.sh` to the application directory, and copy the directory into the Docker image as described (for a project with the Python application in `repo/package`, `COPY package /app/package`). The container will automatically detect and run the prestart script before starting the web server.\n\n  - Default: `"/app/inboard/prestart.py"` (the default file provided with the Docker image)\n  - Custom: `PRE_START_PATH="/app/package/custom_script.sh"`\n\n- [`PYTHONPATH`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH): Python\'s search path for module files.\n  - Default: `PYTHONPATH="/app"`\n  - Custom: `PYTHONPATH="/app/custom"`\n\n### Gunicorn and Uvicorn\n\n- `GUNICORN_CONF`: Path to a [Gunicorn configuration file](https://docs.gunicorn.org/en/latest/settings.html#config-file).\n  - Default:\n    - `"/app/inboard/gunicorn_conf.py"` (the default file provided with the Docker image)\n  - Custom:\n    - `GUNICORN_CONF="/app/package/custom_gunicorn_conf.py"`\n    - Feel free to use the [`gunicorn_conf.py`](./inboard/gunicorn_conf.py) from this repo as a starting point for your own custom configuration.\n- `HOST`: Host IP address (inside of the container) where Gunicorn will listen for requests.\n  - Default: `"0.0.0.0"`\n  - Custom: _TODO_\n- `PORT`: Port the container should listen on.\n  - Default: `"80"`\n  - Custom: `PORT="8080"`\n- [`BIND`](https://docs.gunicorn.org/en/latest/settings.html#server-socket): The actual host and port passed to Gunicorn.\n  - Default: `HOST:PORT` (`"0.0.0.0:80"`)\n  - Custom: `BIND="0.0.0.0:8080"`\n- `PROCESS_MANAGER`: Manager for Uvicorn worker processes. As described in the [Uvicorn docs](https://www.uvicorn.org), "Uvicorn includes a Gunicorn worker class allowing you to run ASGI applications, with all of Uvicorn\'s performance benefits, while also giving you Gunicorn\'s fully-featured process management."\n  - Default: `"gunicorn"` (run Uvicorn with Gunicorn as the process manager)\n  - Custom: `"uvicorn"` (run Uvicorn alone for local development)\n- [`WORKER_CLASS`](https://docs.gunicorn.org/en/latest/settings.html#worker-processes): The class to be used by Gunicorn for the workers.\n  - Default: `uvicorn.workers.UvicornWorker`\n  - Custom: For the alternate Uvicorn worker, `WORKER_CLASS="uvicorn.workers.UvicornH11Worker"`\n- [`WORKERS_PER_CORE`](https://docs.gunicorn.org/en/latest/settings.html#worker-processes): Number of Gunicorn workers per CPU core.\n  - Default: `"1"`\n  - Custom: `WORKERS_PER_CORE="2"`\n  - Notes:\n    - This image will check how many CPU cores are available in the current server running your container. It will set the number of workers to the number of CPU cores multiplied by this value.\n    - On a server with 2 CPU cores, `WORKERS_PER_CORE="3"` will run 6 worker processes.\n    - Floating point values are permitted. If you have a powerful server (let\'s say, with 8 CPU cores) running several applications, including an ASGI application that won\'t need high performance, but you don\'t want to waste server resources, you could set the environment variable to `WORKERS_PER_CORE="0.5"`. A server with 8 CPU cores would start only 4 worker processes.\n    - By default, if `WORKERS_PER_CORE="1"` and the server has only 1 CPU core, 2 workers will be started instead of 1, to avoid poor performance and blocking applications. This behavior can be overridden using `WEB_CONCURRENCY`.\n- `MAX_WORKERS`: Maximum number of workers to use, independent of number of CPU cores.\n  - Default: unlimited (not set)\n  - Custom: `MAX_WORKERS="24"`\n- [`WEB_CONCURRENCY`](https://docs.gunicorn.org/en/latest/settings.html#worker-processes): Set number of workers independently of number of CPU cores.\n  - Default:\n    - Number of CPU cores multiplied by the environment variable `WORKERS_PER_CORE`.\n    - In a server with 2 cores and default `WORKERS_PER_CORE="1"`, default `"2"`.\n  - Custom: To have 4 workers, `WEB_CONCURRENCY="4"`\n- [`TIMEOUT`](https://docs.gunicorn.org/en/stable/settings.html#timeout): Workers silent for more than this many seconds are killed and restarted.\n  - Default: `"120"`\n  - Custom: `TIMEOUT="20"`\n- [`GRACEFUL_TIMEOUT`](https://docs.gunicorn.org/en/stable/settings.html#graceful-timeout): Number of seconds to allow workers finish serving requests before restart.\n  - Default: `"120"`\n  - Custom: `GRACEFUL_TIMEOUT="20"`\n- [`KEEP_ALIVE`](https://docs.gunicorn.org/en/stable/settings.html#keepalive): Number of seconds to wait for requests on a Keep-Alive connection.\n  - Default: `"2"`\n  - Custom: `KEEP_ALIVE="20"`\n- `GUNICORN_CMD_ARGS`: Additional [command-line arguments for Gunicorn](https://docs.gunicorn.org/en/stable/settings.html). Gunicorn looks for the `GUNICORN_CMD_ARGS` environment variable automatically, and gives these settings precedence over other environment variables and Gunicorn config files.\n  - Custom: To use a custom TLS certificate, copy or mount the certificate and private key into the Docker image, and set [`--keyfile` and `--certfile`](http://docs.gunicorn.org/en/latest/settings.html#ssl) to the location of the files.\n    ```sh\n    docker run -d -p 443:443 \\\n      -e GUNICORN_CMD_ARGS="--keyfile=/secrets/key.pem --certfile=/secrets/cert.pem" \\\n      -e PORT=443 myimage\n    ```\n\n### Logging\n\n- `LOGGING_CONF`: Path to a [Python logging configuration file](https://docs.python.org/3/library/logging.config.html). The configuration must be a new-style `.py` file, containing a configuration dictionary object named `LOGGING_CONFIG`. The `LOGGING_CONFIG` dictionary will be passed to [`logging.config.dictConfig()`](https://docs.python.org/3/library/logging.config.html). See [br3ndonland/inboard#3](https://github.com/br3ndonland/inboard/pull/3) for more details on this design choice.\n  - Default:\n    - `"/app/inboard/logging_conf.py"` (the default file provided with the Docker image)\n  - Custom:\n    - `LOGGING_CONF="/app/package/custom_logging.py"`\n- `LOG_COLORS`: Whether or not to color log messages. Currently only supported for `LOG_FORMAT="uvicorn"`.\n  - Default:\n    - Auto-detected based on [`sys.stdout.isatty()`](https://docs.python.org/3/library/sys.html#sys.stdout).\n  - Custom:\n    - `LOG_COLORS="true"`\n    - `LOG_COLORS="false"`\n- `LOG_FORMAT`: [Python logging format](https://docs.python.org/3/library/logging.html#formatter-objects).\n  - Default:\n    - `"simple"`: Simply the log level and message.\n  - Custom:\n    - `"verbose"`: The most informative format, with the first 80 characters providing metadata, and the remainder supplying the log message.\n    - `"gunicorn"`: Gunicorn\'s default format.\n    - `"uvicorn"`: Uvicorn\'s default format, similar to `simple`, with support for `LOG_COLORS`. Note that Uvicorn\'s `access` formatter is not supported here, because it frequently throws errors related to [ASGI scope](https://asgi.readthedocs.io/en/latest/specs/lifespan.html).\n  ```sh\n  # simple\n  INFO       Started server process [19012]\n  # verbose\n  2020-08-19 21:07:31 -0400      19012      uvicorn.error   main            INFO       Started server process [19012]\n  # gunicorn\n  [2020-08-19 21:07:31 -0400] [19012] [INFO] Started server process [19012]\n  # uvicorn (can also be colored)\n  INFO:     Started server process [19012]\n  ```\n- `LOG_LEVEL`: Log level for [Gunicorn](https://docs.gunicorn.org/en/latest/settings.html#logging) or [Uvicorn](https://www.uvicorn.org/settings/#logging).\n  - Default: `"info"`\n  - Custom (organized from greatest to least amount of logging):\n    - `LOG_LEVEL="debug"`\n    - `LOG_LEVEL="info"`\n    - `LOG_LEVEL="warning"`\n    - `LOG_LEVEL="error"`\n    - `LOG_LEVEL="critical"`\n- `ACCESS_LOG`: Access log file to which to write.\n  - Default: `"-"` (`stdout`, print in Docker logs)\n  - Custom:\n    - `ACCESS_LOG="./path/to/accesslogfile.txt"`\n    - `ACCESS_LOG=` (set to an empty value) to disable\n- `ERROR_LOG`: Error log file to which to write.\n  - Default: `"-"` (`stdout`, print in Docker logs)\n  - Custom:\n    - `ERROR_LOG="./path/to/errorlogfile.txt"`\n    - `ERROR_LOG=` (set to an empty value) to disable\n\nFor more information on Python logging configuration, see the [Python `logging` how-to](https://docs.python.org/3/howto/logging.html), [Python `logging` cookbook](https://docs.python.org/3/howto/logging-cookbook.html), [Python `logging` module docs](https://docs.python.org/3/library/logging.html), and [Python `logging.config` module docs](https://docs.python.org/3/library/logging.config.html). Also consider [Loguru](https://loguru.readthedocs.io/en/stable/index.html), an alternative logging module with many improvements over the standard library `logging` module.\n\n## Development\n\n### Code style\n\n- Python code is formatted with [Black](https://black.readthedocs.io/en/stable/). Configuration for Black is stored in _[pyproject.toml](pyproject.toml)_.\n- Python imports are organized automatically with [isort](https://timothycrosley.github.io/isort/).\n  - The isort package organizes imports in three sections:\n    1. Standard library\n    2. Dependencies\n    3. Project\n  - Within each of those groups, `import` statements occur first, then `from` statements, in alphabetical order.\n  - You can run isort from the command line with `poetry run isort .`.\n  - Configuration for isort is stored in _[pyproject.toml](pyproject.toml)_.\n- Other web code (JSON, Markdown, YAML) is formatted with [Prettier](https://prettier.io/).\n\n### Building development images\n\nTo build the Docker images for each stage:\n\n```sh\ngit clone git@github.com:br3ndonland/inboard.git\n\ncd inboard\n\ndocker build . --rm --target base -t localhost/br3ndonland/inboard:base && \\\ndocker build . --rm --target fastapi -t localhost/br3ndonland/inboard:fastapi && \\\ndocker build . --rm --target starlette -t localhost/br3ndonland/inboard:starlette\n```\n\n### Running development containers\n\n```sh\n# Run Docker container with Uvicorn and reloading\ncd inboard\n\ndocker run -d -p 80:80 \\\n  -e "LOG_LEVEL=debug" -e "PROCESS_MANAGER=uvicorn" -e "WITH_RELOAD=true" \\\n  -v $(pwd)/inboard:/app/inboard localhost/br3ndonland/inboard:base\n\ndocker run -d -p 80:80 \\\n  -e "LOG_LEVEL=debug" -e "PROCESS_MANAGER=uvicorn" -e "WITH_RELOAD=true" \\\n  -v $(pwd)/inboard:/app/inboard localhost/br3ndonland/inboard:fastapi\n\ndocker run -d -p 80:80 \\\n  -e "LOG_LEVEL=debug" -e "PROCESS_MANAGER=uvicorn" -e "WITH_RELOAD=true" \\\n  -v $(pwd)/inboard:/app/inboard localhost/br3ndonland/inboard:starlette\n\n# Run Docker container with Gunicorn and Uvicorn\ndocker run -d -p 80:80 localhost/br3ndonland/inboard:base\ndocker run -d -p 80:80 localhost/br3ndonland/inboard:fastapi\ndocker run -d -p 80:80 localhost/br3ndonland/inboard:starlette\n\n# Test HTTP Basic Auth when running the FastAPI or Starlette images:\nhttp :80/status --auth-type=basic --auth=test_username:plunge-germane-tribal-pillar\n```\n\nChange the port numbers to run multiple containers simultaneously (`-p 81:80`).\n',
    'author': 'Brendon Smith',
    'author_email': 'br3ndonland@protonmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/br3ndonland/inboard',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
