aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.dockerignore3
-rw-r--r--.github/CONTRIBUTING.md85
-rw-r--r--.github/workflows/lint.yaml50
-rw-r--r--.github/workflows/test.yaml5
-rw-r--r--.pre-commit-config.yaml35
-rw-r--r--Dockerfile18
-rw-r--r--Makefile44
-rw-r--r--Pipfile54
-rw-r--r--Pipfile.lock480
-rw-r--r--requirements/coverage.in3
-rw-r--r--requirements/coverage.pip10
-rw-r--r--requirements/coveralls.in3
-rw-r--r--requirements/coveralls.pip28
-rw-r--r--requirements/lint.in4
-rw-r--r--requirements/lint.pip28
-rw-r--r--requirements/pip-tools.in6
-rw-r--r--requirements/pip-tools.pip22
-rw-r--r--requirements/requirements.in7
-rw-r--r--requirements/requirements.pip29
-rw-r--r--snekbox/api/snekapi.py4
-rw-r--r--tests/api/test_eval.py2
21 files changed, 259 insertions, 661 deletions
diff --git a/.dockerignore b/.dockerignore
index d030b2f..30ecfd4 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -4,7 +4,6 @@
# Make exceptions for what's needed
!snekbox
!config/
+!requirements/
!tests
-!Pipfile
-!Pipfile.lock
!LICENSE
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index ddb1c5c..b15a4ba 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -6,83 +6,34 @@ The Contributing Guidelines for Python Discord projects can be found [on our web
## Initial Setup
-A Python 3.10 interpreter and the [pipenv] package are required. Once those requirements are satisfied, install the project's dependencies:
+A Python 3.10 interpreter and `make` are required. A virtual environment is also recommended. Once that is set up, install the project's dependencies with `make setup`.
-```
-pipenv sync --dev
-```
-
-Follow that up with setting up the pre-commit hook:
-
-```
-pipenv run precommit
-```
-
-Now Flake8 will run and lint staged changes whenever an attempt to commit the changes is made. Flake8 can still be invoked manually:
-
-```
-pipenv run lint
-```
+This also installs a git pre-commit hook so that the linter runs upon a commit.
+Manual invocation is still possible with `make lint`.
## Running snekbox
-Use Docker Compose to start snekbox in development mode. The optional `--build` argument can be passed to force the image to be rebuilt.
-
-```
-docker-compose up
-```
+Use `docker-compose up` to start snekbox in development mode. The optional `--build` argument can be passed to force the image to be rebuilt.
The container has all development dependencies. The repository on the host is mounted within the container; changes made to local files will also affect the container.
-To build a normal container that can be used in production, run
-
-```
-pipenv run build
-```
+To build a normal container that can be used in production, run `make build`.
Refer to the [README] for how to run the container normally.
## Running Tests
-Tests are run through coverage.py using unittest. To run the tests within a development container, run
-
-```
-pipenv run test
-```
+Tests are run through coverage.py using unittest. To run the tests within a development container, run `make test`.
## Coverage
-To see a coverage report, run
+To see a coverage report, run `make report`.
-```
-pipenv run report
-```
-
-Alternatively, a report can be generated as HTML with
-
-```
-pipenv run coverage html
-```
-
-The HTML will output to `./htmlcov/` by default.
-
-The report cannot be generated on Windows directly due to the difference in file separators in the paths. Instead, launch a shell in the container (see below) and use `coverage report` or `coverage html` (do not invoke through Pipenv).
+The report cannot be generated on Windows directly due to the difference in file separators in the paths. Instead, launch a shell in the container (see below) and use `coverage report`.
## Launching a Shell in the Container
-A bash shell can be launched in the development container using
-
-```
-pipenv run devsh
-```
-
-This creates a new container which will get deleted once the shell session ends.
-
-It's possible to run a command directly; it supports the same arguments that `bash` supports.
-
-```bash
-pipenv run devsh -c 'echo hello'
-```
+A bash shell can be launched in the development container using `make devsh`. This creates a new container which will get deleted once the shell session ends.
### Invoking NsJail
@@ -92,17 +43,23 @@ NsJail can be invoked in a more direct manner that does not require using a web
python -m snekbox 'print("hello world!")' --time_limit 0 --- -m timeit
```
-With this command, NsJail uses the same configuration normally used through the web API. It also has an alias, `pipenv run eval`.
+With this command, NsJail uses the same configuration normally used through the web API.
+
+## Managing Dependencies
+
+pip-tools is used to compile required Python packages and all their transitive
+dependencies. All files are located in the `requirements` directory. The `*.in` files are the input files and the `*.pip` files are the compiled outputs.
+
+To add or change a dependency, modify the appropriate `*.in` file(s) and then run `make upgrade` to recompile. The make command compiles the files in a certain order. When adding a new `*.in` file, it should also be included in the make command. Furthermore, the new file should be constrained by the files compiled before it, and the files compiled after it should be constrained by the new file.
-## Updating NsJail
+### Updating NsJail
Updating NsJail mainly involves two steps:
1. Change the version used by the `git clone` command in the [Dockerfile]
-2. Use `pipenv run protoc` to generate new Python code from the config protobuf
+2. Use `python -m scripts.protoc` to generate new Python code from the config protobuf
Other things to look out for are breaking changes to NsJail's config format, its command-line interface, or its logging format. Additionally, dependencies may have to be adjusted in the Dockerfile to get a new version to build or run.
-[pipenv]: https://docs.pipenv.org/en/latest/
-[readme]: README.md
-[Dockerfile]: Dockerfile
+[readme]: ../README.md
+[Dockerfile]: ../Dockerfile
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 8f530ab..d53738b 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -6,21 +6,10 @@ jobs:
name: Lint
runs-on: ubuntu-latest
env:
- PIP_DISABLE_PIP_VERSION_CHECK: 1
- PIP_NO_CACHE_DIR: false
- PIP_USER: 1 # Make dependencies install into PYTHONUSERBASE.
-
- PIPENV_DONT_USE_PYENV: 1
- PIPENV_HIDE_EMOJIS: 1
- PIPENV_NOSPIN: 1
-
- PYTHONUSERBASE: ${{ github.workspace }}/.cache/py-user-base
PRE_COMMIT_HOME: ${{ github.workspace }}/.cache/pre-commit-cache
+ PIP_DISABLE_PIP_VERSION_CHECK: 1
steps:
- - name: Add custom PYTHONUSERBASE to PATH
- run: echo '${{ env.PYTHONUSERBASE }}/bin/' >> $GITHUB_PATH
-
- name: Checkout code
uses: actions/checkout@v2
@@ -29,22 +18,11 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: "3.10"
+ cache: pip
+ cache-dependency-path: requirements/lint.pip
- - name: Python dependency cache
- uses: actions/cache@v2
- id: python_cache
- with:
- path: ${{ env.PYTHONUSERBASE }}
- key: "python-0-${{ runner.os }}-${{ env.PYTHONUSERBASE }}-\
- ${{ steps.python.outputs.python-version }}-\
- ${{ hashFiles('./Pipfile', './Pipfile.lock') }}"
-
- # Install dependencies if there was a cache miss.
- name: Install Python dependencies
- if: steps.python_cache.outputs.cache-hit != 'true'
- run: >-
- pip install pipenv==2021.11.23
- && pipenv install --deploy --system --dev
+ run: pip install -U -r requirements/lint.pip
- name: Pre-commit environment cache
uses: actions/cache@v2
@@ -54,20 +32,18 @@ jobs:
${{ steps.python.outputs.python-version }}-\
${{ hashFiles('./.pre-commit-config.yaml') }}"
- # pre-commit's venv doesn't work with user installs.
- # Skip the flake8 hook because the following step will run it.
+ # Skip the flake8 hook because the following command will run it.
- name: Run pre-commit hooks
id: run-pre-commit-hooks
- run: PIP_USER=0 SKIP=flake8 pre-commit run --all-files
+ run: |
+ SKIP=flake8 pre-commit run --all-files
+ pre-commit run --all-files --hook-stage manual flake8-annotate
# Show the log to debug failures.
- name: Show pre-commit log
if: always() && steps.run-pre-commit-hooks.outcome == 'failure'
- run: cat "${PRE_COMMIT_HOME}/pre-commit.log"
-
- # Output linting errors in the format GitHub Actions recognises for
- # annotations.
- - name: Run flake8
- run: >-
- flake8 --format "::error
- file=%(path)s,line=%(row)d,col=%(col)d::[flake8] %(code)s: %(text)s"
+ run: |
+ set -eu
+ if [ -f "${PRE_COMMIT_HOME}/pre-commit.log" ]; then
+ cat "${PRE_COMMIT_HOME}/pre-commit.log"
+ fi
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 77ef1fc..f7f3635 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -66,7 +66,6 @@ jobs:
needs: test
steps:
- # Needed for the .coveragerc file.
- name: Checkout code
uses: actions/checkout@v2
@@ -74,9 +73,11 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: "3.10"
+ cache: pip
+ cache-dependency-path: requirements/coveralls.pip
- name: Install dependencies
- run: pip install coverage~=6.0 coveralls~=3.3
+ run: pip install -U -r requirements/coveralls.pip
- name: Download coverage data
uses: actions/download-artifact@v2
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b8f3312..6c56f43 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,12 +12,31 @@ repos:
rev: v1.6.0
hooks:
- id: python-check-blanket-noqa
- - repo: local
+ - repo: https://github.com/PyCQA/flake8
+ rev: &flake8_version 3.8.4
hooks:
- - id: flake8
- name: Flake8
- description: This hook runs flake8 within our project's pipenv environment.
- entry: pipenv run flake8
- language: system
- types: [python]
- require_serial: true
+ - &flake8_hook
+ id: flake8
+ additional_dependencies:
+ - flake8-annotations~=2.5.0
+ - flake8-bugbear~=20.11
+ - flake8-docstrings~=1.5
+ - flake8-formatter-junit-xml~=0.0.6
+ - flake8-import-order~=0.18.1
+ - flake8-quotes~=3.2
+ - flake8-string-format~=0.3.0
+ - flake8-tidy-imports~=4.2
+ - flake8-todo~=0.7
+ - pep8-naming~=0.11.1
+ - pydocstyle~=5.1
+ - repo: https://github.com/PyCQA/flake8
+ rev: *flake8_version
+ hooks:
+ - <<: *flake8_hook
+ alias: flake8-annotate
+ name: flake8 (with annotations)
+ stages: [manual]
+ args: [
+ --format,
+ "::error file=%(path)s,line=%(row)d,col=%(col)d::[flake8] %(code)s: %(text)s",
+ ]
diff --git a/Dockerfile b/Dockerfile
index b0d2702..c207a2b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -26,10 +26,7 @@ FROM python:3.10-slim-buster as base
ENV PATH=/root/.local/bin:$PATH \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=false \
- PIP_USER=1 \
- PIPENV_DONT_USE_PYENV=1 \
- PIPENV_HIDE_EMOJIS=1 \
- PIPENV_NOSPIN=1
+ PIP_USER=1
RUN apt-get -y update \
&& apt-get install -y \
@@ -37,7 +34,6 @@ RUN apt-get -y update \
libnl-route-3-200=3.4.* \
libprotobuf17=3.6.* \
&& rm -rf /var/lib/apt/lists/*
-RUN pip install pipenv==2020.11.15
COPY --from=builder /nsjail/nsjail /usr/sbin/
RUN chmod +x /usr/sbin/nsjail
@@ -45,13 +41,13 @@ RUN chmod +x /usr/sbin/nsjail
# ------------------------------------------------------------------------------
FROM base as venv
-COPY Pipfile Pipfile.lock /snekbox/
+COPY requirements/ /snekbox/requirements/
WORKDIR /snekbox
-# Pipenv installs to the default user site since PIP_USER is set.
-RUN pipenv install --deploy --system
+# pip installs to the default user site since PIP_USER is set.
+RUN pip install -U -r requirements/requirements.pip
-# This must come after the first pipenv command! From the docs:
+# This must come after the first pip command! From the docs:
# All RUN instructions following an ARG instruction use the ARG variable
# implicitly (as an environment variable), thus can cause a cache miss.
ARG DEV
@@ -59,13 +55,13 @@ ARG DEV
# Install numpy when in dev mode; one of the unit tests needs it.
RUN if [ -n "${DEV}" ]; \
then \
- pipenv install --deploy --system --dev \
+ pip install -U -r requirements/coverage.pip \
&& PYTHONUSERBASE=/snekbox/user_base pip install numpy~=1.19; \
fi
# At the end to avoid re-installing dependencies when only a config changes.
# It's in the venv image because the final image is not used during development.
-COPY config/ /snekbox/config
+COPY config/ /snekbox/config/
# ------------------------------------------------------------------------------
FROM venv
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8a2705f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+PIP_COMPILE_CMD = pip-compile -q -U
+
+.PHONY: install-piptools
+install-piptools:
+ pip install -U -q -r requirements/pip-tools.pip
+
+.PHONY: setup
+setup: install-piptools
+ pip-sync requirements/coverage.pip \
+ requirements/lint.pip \
+ requirements/requirements.pip
+ pre-commit install
+
+.PHONY: upgrade
+upgrade: install-piptools
+ $(PIP_COMPILE_CMD) -o requirements/requirements.pip requirements/requirements.in
+ $(PIP_COMPILE_CMD) -o requirements/coverage.pip requirements/coverage.in
+ $(PIP_COMPILE_CMD) -o requirements/coveralls.pip requirements/coveralls.in
+ $(PIP_COMPILE_CMD) -o requirements/lint.pip requirements/lint.in
+ $(PIP_COMPILE_CMD) -o requirements/pip-tools.pip requirements/pip-tools.in
+
+.PHONY: lint
+lint: setup
+ pre-commit run --all-files
+
+# Fix ownership of the coverage file even if tests fail & preserve exit code
+# Install numpy because a test checks if it's importable
+.PHONY: test
+test:
+ docker-compose build -q --force-rm
+ docker-compose run --entrypoint /bin/bash --rm snekbox -c \
+ 'coverage run -m unittest; e=$?; chown --reference=. .coverage; exit $e'
+
+.PHONY: report
+report: setup
+ coverage report
+
+.PHONY: build
+build:
+ docker build -t ghcr.io/python-discord/snekbox:latest .
+
+.PHONY: devsh
+devsh:
+ docker-compose run --entrypoint /bin/bash --rm snekbox
diff --git a/Pipfile b/Pipfile
deleted file mode 100644
index c630530..0000000
--- a/Pipfile
+++ /dev/null
@@ -1,54 +0,0 @@
-[[source]]
-url = "https://pypi.org/simple"
-verify_ssl = true
-name = "pypi"
-
-[packages]
-falcon = "~= 2.0.0"
-gunicorn = "~= 20.0"
-jsonschema = "~= 3.2"
-protobuf = "== 3.19.*"
-sentry-sdk = {extras = ["falcon"], version = "~= 0.19.3"}
-
-[dev-packages]
-coverage = "~= 6.0"
-flake8 = "~= 3.8.4"
-flake8-annotations = "~= 2.5.0"
-flake8-bugbear = "~= 20.11"
-flake8-docstrings = "~= 1.5"
-flake8-formatter-junit-xml = "~= 0.0.6"
-flake8-import-order = "~= 0.18.1"
-flake8-quotes = "~= 3.2"
-flake8-string-format = "~= 0.3.0"
-flake8-tidy-imports = "~= 4.2"
-flake8-todo = "~= 0.7"
-pep8-naming = "~= 0.11.1"
-pre-commit = "~= 2.9.3"
-pydocstyle = "~= 5.1"
-
-[requires]
-python_version = "3.10"
-
-[scripts]
-eval = "python -m snekbox"
-webserver = "gunicorn -c config/gunicorn.conf.py snekbox.api.app"
-
-# Linting
-lint = "pre-commit run --all-files"
-precommit = "pre-commit install"
-
-# Testing
-report = "coverage report"
-# Fix ownership of the coverage file even if tests fail & preserve exit code
-# Install numpy because a test checks if it's importable
-test = """
- docker-compose run --entrypoint /bin/bash --rm snekbox -c \
- 'env PYTHONUSERBASE=/snekbox/user_base pip install numpy && coverage run -m unittest; e=$?; chown --reference=. .coverage; exit $e'
-"""
-
-# Docker
-build = "docker build -t ghcr.io/python-discord/snekbox:latest ."
-devsh = "docker-compose run --entrypoint /bin/bash --rm snekbox"
-
-# Other
-protoc = "python -m scripts.protoc"
diff --git a/Pipfile.lock b/Pipfile.lock
deleted file mode 100644
index d56f997..0000000
--- a/Pipfile.lock
+++ /dev/null
@@ -1,480 +0,0 @@
-{
- "_meta": {
- "hash": {
- "sha256": "40333c884fe1b1e44331d0853069db6f84846992c90d4cfa1f69cc24a3c851d0"
- },
- "pipfile-spec": 6,
- "requires": {
- "python_version": "3.10"
- },
- "sources": [
- {
- "name": "pypi",
- "url": "https://pypi.org/simple",
- "verify_ssl": true
- }
- ]
- },
- "default": {
- "attrs": {
- "hashes": [
- "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
- "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==21.2.0"
- },
- "certifi": {
- "hashes": [
- "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
- "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
- ],
- "version": "==2021.10.8"
- },
- "falcon": {
- "hashes": [
- "sha256:18157af2a4fc3feedf2b5dcc6196f448639acf01c68bc33d4d5a04c3ef87f494",
- "sha256:24adcd2b29a8ffa9d552dc79638cd21736a3fb04eda7d102c6cebafdaadb88ad",
- "sha256:54f2cb4b687035b2a03206dbfc538055cc48b59a953187b0458aa1b574d47b53",
- "sha256:59d1e8c993b9a37ea06df9d72cf907a46cc8063b30717cdac2f34d1658b6f936",
- "sha256:733033ec80c896e30a43ab3e776856096836787197a44eb21022320a61311983",
- "sha256:74cf1d18207381c665b9e6292d65100ce146d958707793174b03869dc6e614f4",
- "sha256:95bf6ce986c1119aef12c9b348f4dee9c6dcc58391bdd0bc2b0bf353c2b15986",
- "sha256:9712975adcf8c6e12876239085ad757b8fdeba223d46d23daef82b47658f83a9",
- "sha256:a5ebb22a04c9cc65081938ee7651b4e3b4d2a28522ea8ec04c7bdd2b3e9e8cd8",
- "sha256:aa184895d1ad4573fbfaaf803563d02f019ebdf4790e41cc568a330607eae439",
- "sha256:e3782b7b92fefd46a6ad1fd8fe63fe6c6f1b7740a95ca56957f48d1aee34b357",
- "sha256:e9efa0791b5d9f9dd9689015ea6bce0a27fcd5ecbcd30e6d940bffa4f7f03389",
- "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc",
- "sha256:f93351459f110b4c1ee28556aef9a791832df6f910bea7b3f616109d534df06b"
- ],
- "index": "pypi",
- "version": "==2.0.0"
- },
- "gunicorn": {
- "hashes": [
- "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
- "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
- ],
- "index": "pypi",
- "version": "==20.1.0"
- },
- "jsonschema": {
- "hashes": [
- "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163",
- "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"
- ],
- "index": "pypi",
- "version": "==3.2.0"
- },
- "protobuf": {
- "hashes": [
- "sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942",
- "sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f",
- "sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560",
- "sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089",
- "sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6",
- "sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04",
- "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7",
- "sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e",
- "sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d",
- "sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7",
- "sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c",
- "sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002",
- "sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6",
- "sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853",
- "sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d",
- "sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3",
- "sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8",
- "sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995",
- "sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea",
- "sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6",
- "sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2",
- "sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b",
- "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17",
- "sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d"
- ],
- "index": "pypi",
- "version": "==3.19.1"
- },
- "pyrsistent": {
- "hashes": [
- "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2",
- "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7",
- "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea",
- "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426",
- "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710",
- "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1",
- "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396",
- "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2",
- "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680",
- "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35",
- "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427",
- "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b",
- "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b",
- "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f",
- "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef",
- "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c",
- "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4",
- "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d",
- "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78",
- "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b",
- "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==0.18.0"
- },
- "sentry-sdk": {
- "extras": [
- "falcon"
- ],
- "hashes": [
- "sha256:0a711ec952441c2ec89b8f5d226c33bc697914f46e876b44a4edd3e7864cf4d0",
- "sha256:737a094e49a529dd0fdcaafa9e97cf7c3d5eb964bd229821d640bc77f3502b3f"
- ],
- "index": "pypi",
- "version": "==0.19.5"
- },
- "six": {
- "hashes": [
- "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
- "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.16.0"
- },
- "urllib3": {
- "hashes": [
- "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
- "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==1.26.7"
- }
- },
- "develop": {
- "attrs": {
- "hashes": [
- "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
- "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==21.2.0"
- },
- "backports.entry-points-selectable": {
- "hashes": [
- "sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b",
- "sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386"
- ],
- "markers": "python_version >= '2.7'",
- "version": "==1.1.1"
- },
- "cfgv": {
- "hashes": [
- "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426",
- "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"
- ],
- "markers": "python_full_version >= '3.6.1'",
- "version": "==3.3.1"
- },
- "coverage": {
- "hashes": [
- "sha256:046647b96969fda1ae0605f61288635209dd69dcd27ba3ec0bf5148bc157f954",
- "sha256:06d009e8a29483cbc0520665bc46035ffe9ae0e7484a49f9782c2a716e37d0a0",
- "sha256:0cde7d9fe2fb55ff68ebe7fb319ef188e9b88e0a3d1c9c5db7dd829cd93d2193",
- "sha256:1de9c6f5039ee2b1860b7bad2c7bc3651fbeb9368e4c4d93e98a76358cdcb052",
- "sha256:24ed38ec86754c4d5a706fbd5b52b057c3df87901a8610d7e5642a08ec07087e",
- "sha256:27a3df08a855522dfef8b8635f58bab81341b2fb5f447819bc252da3aa4cf44c",
- "sha256:310c40bed6b626fd1f463e5a83dba19a61c4eb74e1ac0d07d454ebbdf9047e9d",
- "sha256:3348865798c077c695cae00da0924136bb5cc501f236cfd6b6d9f7a3c94e0ec4",
- "sha256:35b246ae3a2c042dc8f410c94bcb9754b18179cdb81ff9477a9089dbc9ecc186",
- "sha256:3f546f48d5d80a90a266769aa613bc0719cb3e9c2ef3529d53f463996dd15a9d",
- "sha256:586d38dfc7da4a87f5816b203ff06dd7c1bb5b16211ccaa0e9788a8da2b93696",
- "sha256:5d3855d5d26292539861f5ced2ed042fc2aa33a12f80e487053aed3bcb6ced13",
- "sha256:610c0ba11da8de3a753dc4b1f71894f9f9debfdde6559599f303286e70aeb0c2",
- "sha256:62646d98cf0381ffda301a816d6ac6c35fc97aa81b09c4c52d66a15c4bef9d7c",
- "sha256:66af99c7f7b64d050d37e795baadf515b4561124f25aae6e1baa482438ecc388",
- "sha256:675adb3b3380967806b3cbb9c5b00ceb29b1c472692100a338730c1d3e59c8b9",
- "sha256:6e5a8c947a2a89c56655ecbb789458a3a8e3b0cbf4c04250331df8f647b3de59",
- "sha256:7a39590d1e6acf6a3c435c5d233f72f5d43b585f5be834cff1f21fec4afda225",
- "sha256:80cb70264e9a1d04b519cdba3cd0dc42847bf8e982a4d55c769b9b0ee7cdce1e",
- "sha256:82fdcb64bf08aa5db881db061d96db102c77397a570fbc112e21c48a4d9cb31b",
- "sha256:8492d37acdc07a6eac6489f6c1954026f2260a85a4c2bb1e343fe3d35f5ee21a",
- "sha256:94f558f8555e79c48c422045f252ef41eb43becdd945e9c775b45ebfc0cbd78f",
- "sha256:958ac66272ff20e63d818627216e3d7412fdf68a2d25787b89a5c6f1eb7fdd93",
- "sha256:95a58336aa111af54baa451c33266a8774780242cab3704b7698d5e514840758",
- "sha256:96129e41405887a53a9cc564f960d7f853cc63d178f3a182fdd302e4cab2745b",
- "sha256:97ef6e9119bd39d60ef7b9cd5deea2b34869c9f0b9777450a7e3759c1ab09b9b",
- "sha256:98d44a8136eebbf544ad91fef5bd2b20ef0c9b459c65a833c923d9aa4546b204",
- "sha256:9d2c2e3ce7b8cc932a2f918186964bd44de8c84e2f9ef72dc616f5bb8be22e71",
- "sha256:a300b39c3d5905686c75a369d2a66e68fd01472ea42e16b38c948bd02b29e5bd",
- "sha256:a34fccb45f7b2d890183a263578d60a392a1a218fdc12f5bce1477a6a68d4373",
- "sha256:a4d48e42e17d3de212f9af44f81ab73b9378a4b2b8413fd708d0d9023f2bbde4",
- "sha256:af45eea024c0e3a25462fade161afab4f0d9d9e0d5a5d53e86149f74f0a35ecc",
- "sha256:ba6125d4e55c0b8e913dad27b22722eac7abdcb1f3eab1bd090eee9105660266",
- "sha256:bc1ee1318f703bc6c971da700d74466e9b86e0c443eb85983fb2a1bd20447263",
- "sha256:c18725f3cffe96732ef96f3de1939d81215fd6d7d64900dcc4acfe514ea4fcbf",
- "sha256:c8e9c4bcaaaa932be581b3d8b88b677489975f845f7714efc8cce77568b6711c",
- "sha256:cc799916b618ec9fd00135e576424165691fec4f70d7dc12cfaef09268a2478c",
- "sha256:cd2d11a59afa5001ff28073ceca24ae4c506da4355aba30d1e7dd2bd0d2206dc",
- "sha256:d0a595a781f8e186580ff8e3352dd4953b1944289bec7705377c80c7e36c4d6c",
- "sha256:d3c5f49ce6af61154060640ad3b3281dbc46e2e0ef2fe78414d7f8a324f0b649",
- "sha256:d9a635114b88c0ab462e0355472d00a180a5fbfd8511e7f18e4ac32652e7d972",
- "sha256:e5432d9c329b11c27be45ee5f62cf20a33065d482c8dec1941d6670622a6fb8f",
- "sha256:eab14fdd410500dae50fd14ccc332e65543e7b39f6fc076fe90603a0e5d2f929",
- "sha256:ebcc03e1acef4ff44f37f3c61df478d6e469a573aa688e5a162f85d7e4c3860d",
- "sha256:fae3fe111670e51f1ebbc475823899524e3459ea2db2cb88279bbfb2a0b8a3de",
- "sha256:fd92ece726055e80d4e3f01fff3b91f54b18c9c357c48fcf6119e87e2461a091",
- "sha256:ffa545230ca2ad921ad066bf8fd627e7be43716b6e0fcf8e32af1b8188ccb0ab"
- ],
- "index": "pypi",
- "version": "==6.1.2"
- },
- "distlib": {
- "hashes": [
- "sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31",
- "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"
- ],
- "version": "==0.3.3"
- },
- "filelock": {
- "hashes": [
- "sha256:7afc856f74fa7006a289fd10fa840e1eebd8bbff6bffb69c26c54a0512ea8cf8",
- "sha256:bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==3.3.2"
- },
- "flake8": {
- "hashes": [
- "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839",
- "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"
- ],
- "index": "pypi",
- "version": "==3.8.4"
- },
- "flake8-annotations": {
- "hashes": [
- "sha256:3a377140556aecf11fa9f3bb18c10db01f5ea56dc79a730e2ec9b4f1f49e2055",
- "sha256:e17947a48a5b9f632fe0c72682fc797c385e451048e7dfb20139f448a074cb3e"
- ],
- "index": "pypi",
- "version": "==2.5.0"
- },
- "flake8-bugbear": {
- "hashes": [
- "sha256:528020129fea2dea33a466b9d64ab650aa3e5f9ffc788b70ea4bc6cf18283538",
- "sha256:f35b8135ece7a014bc0aee5b5d485334ac30a6da48494998cc1fabf7ec70d703"
- ],
- "index": "pypi",
- "version": "==20.11.1"
- },
- "flake8-docstrings": {
- "hashes": [
- "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde",
- "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"
- ],
- "index": "pypi",
- "version": "==1.6.0"
- },
- "flake8-formatter-junit-xml": {
- "hashes": [
- "sha256:1ddd9356bb30ba736c3f14c769c837cfacf4f79c3d383ab963ef9d38eea05a9c",
- "sha256:6358a44ecafdf0f9c8ee5314859b8d6f553dc2e55e946a24c538185e1eba7ce6"
- ],
- "index": "pypi",
- "version": "==0.0.6"
- },
- "flake8-import-order": {
- "hashes": [
- "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543",
- "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"
- ],
- "index": "pypi",
- "version": "==0.18.1"
- },
- "flake8-polyfill": {
- "hashes": [
- "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9",
- "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"
- ],
- "version": "==1.0.2"
- },
- "flake8-quotes": {
- "hashes": [
- "sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a"
- ],
- "index": "pypi",
- "version": "==3.3.1"
- },
- "flake8-string-format": {
- "hashes": [
- "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2",
- "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"
- ],
- "index": "pypi",
- "version": "==0.3.0"
- },
- "flake8-tidy-imports": {
- "hashes": [
- "sha256:87eed94ae6a2fda6a5918d109746feadf1311e0eb8274ab7a7920f6db00a41c9",
- "sha256:ac637961d0f319012d099e49619f8c928e3221f74e00fe6eb89513bc64c40adb"
- ],
- "index": "pypi",
- "version": "==4.5.0"
- },
- "flake8-todo": {
- "hashes": [
- "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915"
- ],
- "index": "pypi",
- "version": "==0.7"
- },
- "identify": {
- "hashes": [
- "sha256:6f0368ba0f21c199645a331beb7425d5374376e71bc149e9cb55e45cb45f832d",
- "sha256:ba945bddb4322394afcf3f703fa68eda08a6acc0f99d9573eb2be940aa7b9bba"
- ],
- "markers": "python_full_version >= '3.6.1'",
- "version": "==2.3.5"
- },
- "junit-xml": {
- "hashes": [
- "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732"
- ],
- "version": "==1.9"
- },
- "mccabe": {
- "hashes": [
- "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
- "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
- ],
- "version": "==0.6.1"
- },
- "nodeenv": {
- "hashes": [
- "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b",
- "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"
- ],
- "version": "==1.6.0"
- },
- "pep8-naming": {
- "hashes": [
- "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724",
- "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"
- ],
- "index": "pypi",
- "version": "==0.11.1"
- },
- "platformdirs": {
- "hashes": [
- "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
- "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==2.4.0"
- },
- "pre-commit": {
- "hashes": [
- "sha256:6c86d977d00ddc8a60d68eec19f51ef212d9462937acf3ea37c7adec32284ac0",
- "sha256:ee784c11953e6d8badb97d19bc46b997a3a9eded849881ec587accd8608d74a4"
- ],
- "index": "pypi",
- "version": "==2.9.3"
- },
- "pycodestyle": {
- "hashes": [
- "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
- "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==2.6.0"
- },
- "pydocstyle": {
- "hashes": [
- "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325",
- "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"
- ],
- "index": "pypi",
- "version": "==5.1.1"
- },
- "pyflakes": {
- "hashes": [
- "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
- "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==2.2.0"
- },
- "pyyaml": {
- "hashes": [
- "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
- "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
- "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
- "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
- "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
- "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
- "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
- "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
- "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
- "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
- "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
- "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
- "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
- "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
- "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
- "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
- "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
- "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
- "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
- "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
- "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
- "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
- "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
- "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
- "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
- "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
- "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
- "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
- "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
- "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
- "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
- "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
- "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
- ],
- "markers": "python_version >= '3.6'",
- "version": "==6.0"
- },
- "six": {
- "hashes": [
- "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
- "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.16.0"
- },
- "snowballstemmer": {
- "hashes": [
- "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
- "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
- ],
- "version": "==2.1.0"
- },
- "toml": {
- "hashes": [
- "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
- "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
- ],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==0.10.2"
- },
- "virtualenv": {
- "hashes": [
- "sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814",
- "sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==20.10.0"
- }
- }
-}
diff --git a/requirements/coverage.in b/requirements/coverage.in
new file mode 100644
index 0000000..7f38b54
--- /dev/null
+++ b/requirements/coverage.in
@@ -0,0 +1,3 @@
+-c requirements.pip
+
+coverage[toml]>=6.3.1
diff --git a/requirements/coverage.pip b/requirements/coverage.pip
new file mode 100644
index 0000000..fcc14da
--- /dev/null
+++ b/requirements/coverage.pip
@@ -0,0 +1,10 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+# pip-compile --output-file=requirements/coverage.pip requirements/coverage.in
+#
+coverage[toml]==6.4
+ # via -r requirements/coverage.in
+tomli==2.0.1
+ # via coverage
diff --git a/requirements/coveralls.in b/requirements/coveralls.in
new file mode 100644
index 0000000..28d4959
--- /dev/null
+++ b/requirements/coveralls.in
@@ -0,0 +1,3 @@
+-c coverage.pip
+
+coveralls>=3.3.1
diff --git a/requirements/coveralls.pip b/requirements/coveralls.pip
new file mode 100644
index 0000000..cd16e7c
--- /dev/null
+++ b/requirements/coveralls.pip
@@ -0,0 +1,28 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+# pip-compile --output-file=requirements/coveralls.pip requirements/coveralls.in
+#
+certifi==2022.5.18.1
+ # via requests
+charset-normalizer==2.0.12
+ # via requests
+coverage[toml]==6.4
+ # via
+ # -c requirements/coverage.pip
+ # coveralls
+coveralls==3.3.1
+ # via -r requirements/coveralls.in
+docopt==0.6.2
+ # via coveralls
+idna==3.3
+ # via requests
+requests==2.27.1
+ # via coveralls
+tomli==2.0.1
+ # via
+ # -c requirements/coverage.pip
+ # coverage
+urllib3==1.26.9
+ # via requests
diff --git a/requirements/lint.in b/requirements/lint.in
new file mode 100644
index 0000000..18bac76
--- /dev/null
+++ b/requirements/lint.in
@@ -0,0 +1,4 @@
+-c coverage.pip
+-c requirements.pip
+
+pre-commit>=2.13
diff --git a/requirements/lint.pip b/requirements/lint.pip
new file mode 100644
index 0000000..a399b6f
--- /dev/null
+++ b/requirements/lint.pip
@@ -0,0 +1,28 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+# pip-compile --output-file=requirements/lint.pip requirements/lint.in
+#
+cfgv==3.3.1
+ # via pre-commit
+distlib==0.3.4
+ # via virtualenv
+filelock==3.7.0
+ # via virtualenv
+identify==2.5.1
+ # via pre-commit
+nodeenv==1.6.0
+ # via pre-commit
+platformdirs==2.5.2
+ # via virtualenv
+pre-commit==2.19.0
+ # via -r requirements/lint.in
+pyyaml==6.0
+ # via pre-commit
+six==1.16.0
+ # via virtualenv
+toml==0.10.2
+ # via pre-commit
+virtualenv==20.14.1
+ # via pre-commit
diff --git a/requirements/pip-tools.in b/requirements/pip-tools.in
new file mode 100644
index 0000000..e459df9
--- /dev/null
+++ b/requirements/pip-tools.in
@@ -0,0 +1,6 @@
+-c coverage.pip
+-c lint.pip
+-c requirements.pip
+
+# Minimum version which supports pip>=22.1
+pip-tools>=6.6.1
diff --git a/requirements/pip-tools.pip b/requirements/pip-tools.pip
new file mode 100644
index 0000000..46d53d0
--- /dev/null
+++ b/requirements/pip-tools.pip
@@ -0,0 +1,22 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+# pip-compile --output-file=requirements/pip-tools.pip requirements/pip-tools.in
+#
+click==8.1.3
+ # via pip-tools
+pep517==0.12.0
+ # via pip-tools
+pip-tools==6.6.2
+ # via -r requirements/pip-tools.in
+tomli==2.0.1
+ # via
+ # -c requirements/coverage.pip
+ # pep517
+wheel==0.37.1
+ # via pip-tools
+
+# The following packages are considered to be unsafe in a requirements file:
+# pip
+# setuptools
diff --git a/requirements/requirements.in b/requirements/requirements.in
new file mode 100644
index 0000000..775ad39
--- /dev/null
+++ b/requirements/requirements.in
@@ -0,0 +1,7 @@
+# Sentry's Falcon integration relies on api_helpers. See falconry/falcon#1902
+falcon>=3.0.1
+
+gunicorn>=20
+jsonschema>=4.0
+protobuf>=3.19
+sentry-sdk[falcon]>=1.5.4
diff --git a/requirements/requirements.pip b/requirements/requirements.pip
new file mode 100644
index 0000000..21b6678
--- /dev/null
+++ b/requirements/requirements.pip
@@ -0,0 +1,29 @@
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+# pip-compile --output-file=requirements/requirements.pip requirements/requirements.in
+#
+attrs==21.4.0
+ # via jsonschema
+certifi==2022.5.18.1
+ # via sentry-sdk
+falcon==3.1.0
+ # via
+ # -r requirements/requirements.in
+ # sentry-sdk
+gunicorn==20.1.0
+ # via -r requirements/requirements.in
+jsonschema==4.5.1
+ # via -r requirements/requirements.in
+protobuf==4.21.1
+ # via -r requirements/requirements.in
+pyrsistent==0.18.1
+ # via jsonschema
+sentry-sdk[falcon]==1.5.12
+ # via -r requirements/requirements.in
+urllib3==1.26.9
+ # via sentry-sdk
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/snekbox/api/snekapi.py b/snekbox/api/snekapi.py
index cb0356a..a1804e6 100644
--- a/snekbox/api/snekapi.py
+++ b/snekbox/api/snekapi.py
@@ -3,7 +3,7 @@ import falcon
from .resources import EvalResource
-class SnekAPI(falcon.API):
+class SnekAPI(falcon.App):
"""
The main entry point to the snekbox JSON API.
@@ -15,7 +15,7 @@ class SnekAPI(falcon.API):
Error response format:
>>> {
- ... "title": "Unsupported media type",
+ ... "title": "415 Unsupported Media Type",
... "description": "application/xml is an unsupported media type."
... }
"""
diff --git a/tests/api/test_eval.py b/tests/api/test_eval.py
index bdeee3e..24e673a 100644
--- a/tests/api/test_eval.py
+++ b/tests/api/test_eval.py
@@ -51,7 +51,7 @@ class TestEvalResource(SnekAPITestCase):
self.assertEqual(result.status_code, 415)
expected = {
- "title": "Unsupported media type",
+ "title": "415 Unsupported Media Type",
"description": "application/xml is an unsupported media type."
}