diff --git a/.coveragerc b/.coveragerc index 562c4d9..c7d68d2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -12,7 +12,6 @@ omit = */.local/* */tests/* conftest.py - */admin.py */urls.py [report] diff --git a/.flake8 b/.flake8 index 0091fb3..27d085d 100644 --- a/.flake8 +++ b/.flake8 @@ -21,6 +21,10 @@ ignore = WPS115, ; f-string missing prefix FS003, + ; Found a too complex `f` string + WPS237, + ; Found wrong metadata variable + WPS410, per-file-ignores = ; all tests @@ -41,6 +45,8 @@ per-file-ignores = WPS211, ; Found too many `assert` statements WPS218, + ; Found too many methods + WPS214, ; all init files __init__.py: diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..73b63ab --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: pip + directory: "/" + schedule: + interval: daily + time: "04:30" + open-pull-requests-limit: 20 + assignees: + - lowitea + labels: + - dependencies diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..66dd4dc --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,37 @@ +name: "CodeQL" + +on: + pull_request: + branches-ignore: + - 'dependabot/**' + + push: + branches-ignore: + - 'dependabot/**' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 0000000..27b618d --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,29 @@ +name: Type checking + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Setup Poetry + uses: Gr1N/setup-poetry@v7 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + poetry install + + - name: Analysing the code with mypy + env: + KESHA_SECRET_KEY: secret + run: | + poetry run mypy kesha --ignore-missing-imports diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ada77c..7dc29c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,57 @@ # Version history We follow [Semantic Versions](https://semver.org/). +## 2.0.0 - New major version + +### Important changes +- Add templating for response body and response headers [#7](https://github.com/NUTtech/Kesha/issues/7) +- Add status saving to the log [#75](https://github.com/NUTtech/Kesha/issues/75) +- Add proxy requests with a two-way log record [#9](https://github.com/NUTtech/Kesha/issues/9) + +### Minor changes +- Add type annotations [#4](https://github.com/NUTtech/Kesha/issues/4) +- Change logo [#60](https://github.com/NUTtech/Kesha/issues/60) +- Update wemake-style-guide [#61](https://github.com/NUTtech/Kesha/issues/61) +- Adds badge for CodeQL [#19](https://github.com/NUTtech/Kesha/pull/19) +- Fixes run CodeQL [#18](https://github.com/NUTtech/Kesha/pull/18) + +### Updating dependencies +- django from 3.1.13 to 3.1.14 [#124](https://github.com/NUTtech/Kesha/pull/124) +- ipython from 7.28.0 to 7.31.0 [#119](https://github.com/NUTtech/Kesha/pull/119) +- uvicorn from 0.15.0 to 0.16.0 [#104](https://github.com/NUTtech/Kesha/pull/104) +- django-debug-toolbar from 3.2.2 to 3.2.4 [#109](https://github.com/NUTtech/Kesha/pull/109) +- django-extensions from 3.1.3 to 3.1.5 [#89](https://github.com/NUTtech/Kesha/pull/89) +- psycopg2-binary from 2.9.1 to 2.9.3 [#116](https://github.com/NUTtech/Kesha/pull/116) +- mypy from 0.910 to 0.931 [#121](https://github.com/NUTtech/Kesha/pull/121) +- requests from 2.26.0 to 2.27.1 [#118](https://github.com/NUTtech/Kesha/pull/118) +- wemake-python-styleguide from 0.15.3 to 0.16.0 [#107](https://github.com/NUTtech/Kesha/pull/107) +- pytest-django from 4.4.0 to 4.5.2 [#102](https://github.com/NUTtech/Kesha/pull/102) +- celery-types from 0.8.2 to 0.10.0 [#117](https://github.com/NUTtech/Kesha/pull/117) +- django-simpleui from 2021.10.15 to 2022.1 [#106](https://github.com/NUTtech/Kesha/pull/106) +- celery from 5.1.2 to 5.2.3 [#115](https://github.com/NUTtech/Kesha/pull/115) +- restrictedpython from 5.1 to 5.2 [#94](https://github.com/NUTtech/Kesha/pull/94) +- types-requests from 2.25.11 to 2.27.5 [#122](https://github.com/NUTtech/Kesha/pull/122) +- lake8-use-fstring from 1.2.1 to 1.3 [#79](https://github.com/NUTtech/Kesha/pull/79) +- django-environ from 0.8.0 to 0.8.1 [#76](https://github.com/NUTtech/Kesha/pull/76) +- flake8-use-fstring from 1.2 to 1.2.1 [#77](https://github.com/NUTtech/Kesha/pull/77) +- pytest-mypy-plugins from 1.9.1 to 1.9.2 [#73](https://github.com/NUTtech/Kesha/pull/73) +- flake8-use-fstring from 1.1 to 1.2 [#74](https://github.com/NUTtech/Kesha/pull/74) +- celery-types from 0.8.1 to 0.8.2 [#72](https://github.com/NUTtech/Kesha/pull/72) +- django-environ from 0.7.0 to 0.8.0 [#69](https://github.com/NUTtech/Kesha/pull/69) +- django-simpleui from 2021.8.17 to 2021.10.15 [#68](https://github.com/NUTtech/Kesha/pull/68) +- pytest-django from 4.3.0 to 4.4.0 [#33](https://github.com/NUTtech/Kesha/pull/33) +- django-environ from 0.4.5 to 0.7.0 [#54](https://github.com/NUTtech/Kesha/pull/54) +- django-simpleui from 2021.3 to 2021.8.17 [#48](https://github.com/NUTtech/Kesha/pull/48) +- celery from 5.1.0 to 5.1.2 [#38](https://github.com/NUTtech/Kesha/pull/38) +- requests from 2.25.1 to 2.26.0 [#41](https://github.com/NUTtech/Kesha/pull/41) +- psycopg2-binary from 2.8.6 to 2.9.1 [#34](https://github.com/NUTtech/Kesha/pull/34) +- pytest from 6.2.4 to 6.2.5 [#51](https://github.com/NUTtech/Kesha/pull/51) +- ipython from 7.23.1 to 7.28.0 [#55](https://github.com/NUTtech/Kesha/pull/55) +- pytest-cov from 2.12.0 to 3.0.0 [#56](https://github.com/NUTtech/Kesha/pull/56) +- celery from 5.0.5 to 5.1.0 [#21](https://github.com/NUTtech/Kesha/pull/21) +- pytest-django from 4.1.0 to 4.3.0 [#13](https://github.com/NUTtech/Kesha/pull/13) +- pytest-cov from 2.11.1 to 2.12.0 [#16](https://github.com/NUTtech/Kesha/pull/16) + ## 1.2.0 - The parrot's name is now Kesha! ## 1.1.0 - Disable logs by default diff --git a/README.md b/README.md index d5f849a..e73fb04 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Build: ![build](https://github.com/NUTtech/Kesha/workflows/build/badge.svg) Code quality: +[![CodeQL](https://github.com/NUTtech/Kesha/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/NUTtech/Kesha/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/NUTtech/Kesha/branch/develop/graph/badge.svg?token=788OM63owZ)](https://codecov.io/gh/NUTtech/Kesha) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/NUTtech/Kesha.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/NUTtech/Kesha/context:python) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=NUTtech_Kesha&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=NUTtech_Kesha) @@ -42,6 +43,3 @@ We welcome all contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) if you want to contribute. You can start with [issues that need some help](https://github.com/NUTtech/Kesha/issues) right now. - -## Note -The project was forked from https://github.com/Uma-Tech/parrot version 1.1.0. And this parrot now lives here. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 7d95665..b4b439d 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -7,6 +7,7 @@ - [Local run](quick-start/local-run.md) - [Usage](usage/index.md) - [First steps](usage/first-steps.md) + - [Proxy stubs](usage/proxy-stubs.md) - [Request scripts](usage/request-scripts.md) - [Changelog](changelog.md) - [Contributing](contributing.md) diff --git a/docs/src/images/01.jpg b/docs/src/images/01.jpg deleted file mode 100644 index 78e368e..0000000 Binary files a/docs/src/images/01.jpg and /dev/null differ diff --git a/docs/src/images/02.jpg b/docs/src/images/02.jpg deleted file mode 100644 index 6646707..0000000 Binary files a/docs/src/images/02.jpg and /dev/null differ diff --git a/docs/src/images/03.jpg b/docs/src/images/03.jpg deleted file mode 100644 index b94b5a3..0000000 Binary files a/docs/src/images/03.jpg and /dev/null differ diff --git a/docs/src/images/04.jpg b/docs/src/images/04.jpg deleted file mode 100644 index c9bffb8..0000000 Binary files a/docs/src/images/04.jpg and /dev/null differ diff --git a/docs/src/images/05.jpg b/docs/src/images/05.jpg deleted file mode 100644 index bfe232b..0000000 Binary files a/docs/src/images/05.jpg and /dev/null differ diff --git a/docs/src/images/06.jpg b/docs/src/images/06.jpg deleted file mode 100644 index 1edfcb4..0000000 Binary files a/docs/src/images/06.jpg and /dev/null differ diff --git a/docs/src/images/home.jpg b/docs/src/images/home.jpg new file mode 100644 index 0000000..868beb5 Binary files /dev/null and b/docs/src/images/home.jpg differ diff --git a/docs/src/images/proxy_logs.jpg b/docs/src/images/proxy_logs.jpg new file mode 100644 index 0000000..839e4cc Binary files /dev/null and b/docs/src/images/proxy_logs.jpg differ diff --git a/docs/src/images/proxy_stub_add.jpg b/docs/src/images/proxy_stub_add.jpg new file mode 100644 index 0000000..74fa98d Binary files /dev/null and b/docs/src/images/proxy_stub_add.jpg differ diff --git a/docs/src/images/proxy_stubs.jpg b/docs/src/images/proxy_stubs.jpg new file mode 100644 index 0000000..2dba872 Binary files /dev/null and b/docs/src/images/proxy_stubs.jpg differ diff --git a/docs/src/images/request_logs.jpg b/docs/src/images/request_logs.jpg new file mode 100644 index 0000000..4183eae Binary files /dev/null and b/docs/src/images/request_logs.jpg differ diff --git a/docs/src/images/request_script.jpg b/docs/src/images/request_script.jpg new file mode 100644 index 0000000..30bc190 Binary files /dev/null and b/docs/src/images/request_script.jpg differ diff --git a/docs/src/images/request_stub_add.jpg b/docs/src/images/request_stub_add.jpg new file mode 100644 index 0000000..c89a6d1 Binary files /dev/null and b/docs/src/images/request_stub_add.jpg differ diff --git a/docs/src/images/request_stubs.jpg b/docs/src/images/request_stubs.jpg new file mode 100644 index 0000000..ff69620 Binary files /dev/null and b/docs/src/images/request_stubs.jpg differ diff --git a/docs/src/index.md b/docs/src/index.md index 7c6ea3b..72cfad8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,10 +1,10 @@ {{#include ../../README.md}} ## Screenshots -![](./images/01.jpg) +![](./images/home.jpg) -![](./images/02.jpg) +![](./images/request_logs.jpg) -![](./images/03.jpg) +![](./images/request_stubs.jpg) -![](./images/04.jpg) +![](./images/request_stub_add.jpg) diff --git a/docs/src/usage/first-steps.md b/docs/src/usage/first-steps.md index fdb56c3..ae30b20 100644 --- a/docs/src/usage/first-steps.md +++ b/docs/src/usage/first-steps.md @@ -2,16 +2,16 @@ ## Create an http-stub -1. Go to `HTTP Stubs` -> `Stubs` - ![](../images/03.jpg) +1. Go to `HTTP Stubs` -> `Request stubs` + ![](../images/request_stubs.jpg) 1. Click the `Add` button 1. Fill in the required fields - ![](../images/04.jpg) + ![](../images/request_stub_add.jpg) 1. Click the `Save` button ## Logs 1. Send a http-request to the created http-stub -1. Go to `HTTP Stubs` -> `Logs` - ![](../images/02.jpg) +1. Go to `HTTP Stubs` -> `Request logs` + ![](../images/request_logs.jpg) 1. Watch incoming requests :) diff --git a/docs/src/usage/index.md b/docs/src/usage/index.md index aa4bd55..c4e6b32 100644 --- a/docs/src/usage/index.md +++ b/docs/src/usage/index.md @@ -1,4 +1,6 @@ # Usage 1. [First steps](first-steps.md) +1. [Proxy stubs](proxy-stubs.md) 1. [Request scripts](request-scripts.md) +1. [Response templating](response-templating.md) diff --git a/docs/src/usage/proxy-stubs.md b/docs/src/usage/proxy-stubs.md new file mode 100644 index 0000000..a400361 --- /dev/null +++ b/docs/src/usage/proxy-stubs.md @@ -0,0 +1,17 @@ +# Proxy stubs + +## Create a proxy http stub + +1. Go to `HTTP Stubs` -> `Proxy stubs` + ![](../images/proxy_stubs.jpg) +1. Click the `Add` button +1. Fill in the required fields + ![](../images/proxy_stub_add.jpg) +1. Click the `Save` button + +## Logs + +1. Send a http-request to the created http-stub +1. Go to `HTTP Stubs` -> `Proxy logs` + ![](../images/proxy_logs.jpg) +1. Watch incoming requests :) diff --git a/docs/src/usage/request-scripts.md b/docs/src/usage/request-scripts.md index 6161331..11fe409 100644 --- a/docs/src/usage/request-scripts.md +++ b/docs/src/usage/request-scripts.md @@ -2,9 +2,9 @@ ## For using it 1. Create an http stub ([first steps](first-steps.html)) - ![](../images/05.jpg) + ![](../images/request_stub_add.jpg) 1. In the "request script" field, add a Python script - ![](../images/06.jpg) + ![](../images/request_script.jpg) ## About scripts Request scripts support a secure subset of Python version 3.8. diff --git a/docs/src/usage/response-templating.md b/docs/src/usage/response-templating.md new file mode 100644 index 0000000..ee9741b --- /dev/null +++ b/docs/src/usage/response-templating.md @@ -0,0 +1,12 @@ +# Response templating + +You can use the GET parameters of your request in the body and headers of the +response. + +To do this, in the body field or in the value of any header, add a string with +parameters in the format `Your body with the GET parameter $arg1` + +Now on the request for example `/stub/?arg1=42` there will be a response with +the body `Your body with GET parameter 42` + + diff --git a/http_stubs/__init__.py b/http_stubs/__init__.py index 6625e70..371377f 100644 --- a/http_stubs/__init__.py +++ b/http_stubs/__init__.py @@ -3,4 +3,7 @@ Allows setting up custom urls returning predefined responses. """ +# importing lookups for registration +from http_stubs.lookups import * # noqa: WPS347 + default_app_config = 'http_stubs.apps.HttpStubsConfig' diff --git a/http_stubs/admin.py b/http_stubs/admin.py index 3d995d2..bc0d183 100644 --- a/http_stubs/admin.py +++ b/http_stubs/admin.py @@ -10,9 +10,10 @@ admin.site.site_header = 'Kesha Admin' -@admin.register(models.LogEntry) -class LogEntryAdmin(admin.ModelAdmin): - """Log entries admin.""" +class LogEntryAdminBase(admin.ModelAdmin): + """Base admin for LogEntry and ProxyLogEntry.""" + + change_form_template = 'admin/http_stubs/logentry/change_form.html' def has_add_permission(self, *args, **kwargs) -> bool: """Forbids adding new entries. @@ -23,42 +24,113 @@ def has_add_permission(self, *args, **kwargs) -> bool: """ return False - def pretty_body(self, instance) -> str: - """Jsonify the request body if possible. + def pretty_str(self, string: str) -> str: + """Jsonify a string if possible. - :param instance: instance of LogEntry - :returns: jsonify body + :param string: a string + :returns: jsonify string """ try: - body = json.loads(instance.body) + body = json.loads(string) except json.JSONDecodeError: - body = None + body = '' else: body = json.dumps(body, indent=2) return body - pretty_body.short_description = 'Jsonify request body' + def pretty_request_body(self, instance) -> str: + """Jsonify the request body if possible. + + :param instance: instance of a log entry + :returns: jsonify body + """ + return self.pretty_str(instance.request_body) + + pretty_request_body.short_description = 'Jsonify request body' - list_filter = ('date', 'method') + list_filter = ('request_date', 'method') search_fields = ('path', 'source_ip') - list_display = ('pk', 'date', 'http_stub', 'source_ip') + list_display = ('pk', 'request_date', 'http_stub', 'source_ip') readonly_fields = ( 'pk', - 'date', + 'request_date', 'http_stub', - 'path', - 'method', + 'resp_status', 'source_ip', - 'headers', - 'body', - 'pretty_body', 'result_script', + 'path', + 'method', + 'request_headers', + 'request_body', + 'pretty_request_body', + ) + + fieldsets = ( + (None, { + 'fields': ( + 'pk', + 'request_date', + 'http_stub', + 'resp_status', + 'source_ip', + 'result_script', + ), + }), + ('Request to Kesha', { + 'fields': ( + 'path', + 'method', + 'request_headers', + 'request_body', + 'pretty_request_body', + ), + }), ) -@admin.register(models.HTTPStub) -class HTTPStubAdmin(admin.ModelAdmin): - """HTTP stub admin.""" +@admin.register(models.LogEntry) +class LogEntryAdmin(LogEntryAdminBase): + """Log entries admin.""" + + +@admin.register(models.ProxyLogEntity) +class ProxyLogEntryAdmin(LogEntryAdminBase): + """Proxy log entries admin.""" + + def pretty_response_body(self, instance) -> str: + """Jsonify the response body if possible. + + :param instance: instance of a log entry + :returns: jsonify body + """ + return self.pretty_str(instance.response_body) + + pretty_response_body.short_description = 'Jsonify response body' + + readonly_fields = LogEntryAdminBase.readonly_fields + ( + 'target_path', + 'response_latency', + 'response_headers', + 'response_body', + 'pretty_response_body', + ) + + fieldsets = ( + *LogEntryAdminBase.fieldsets, + ('Response from target endpoint', { + 'fields': ( + 'target_path', + 'response_latency', + 'response_headers', + 'response_body', + 'pretty_response_body', + ), + }), + ) + + +class HTTPStubAdminBase(admin.ModelAdmin): + """Base admin for HTTPStub and ProxyHTTPStub.""" change_form_template = 'admin/http_stubs/httpstub/change_form.html' extra_buttons_style = 'background-color:#00b0ff;color:white' @@ -91,4 +163,40 @@ def disable_action(self, request: HttpRequest, queryset: QuerySet): list_display = ('pk', 'is_active', 'method', 'path') - list_filter = ('is_active', 'path', 'method', 'resp_status') + list_filter = ('is_active', 'path', 'method') + + +@admin.register(models.HTTPStub) +class HTTPStubAdmin(HTTPStubAdminBase): + """HTTP stub admin.""" + + list_filter = HTTPStubAdminBase.list_filter + ('resp_status',) + + +@admin.register(models.ProxyHTTPStub) +class ProxyHTTPStubAdmin(HTTPStubAdminBase): + """Proxy HTTP stub admin.""" + + fieldsets = ( + (None, { + 'fields': ( + 'is_active', + 'path', + 'regex_path', + 'method', + 'request_script', + 'enable_logging', + ), + }), + ('Target settings', { + 'fields': ( + 'target_url', + 'allow_forward_query', + 'target_ssl_verify', + 'target_timeout', + 'target_method', + 'target_headers', + 'target_body', + ), + }), + ) diff --git a/http_stubs/lookups.py b/http_stubs/lookups.py new file mode 100644 index 0000000..bf7b417 --- /dev/null +++ b/http_stubs/lookups.py @@ -0,0 +1,33 @@ +from typing import Tuple + +from django.db import models + +__all__ = ['RegExpLookup'] + + +@models.CharField.register_lookup +class RegExpLookup(models.Lookup): + """Regular expression field lookup. + + Here's an example of how to use it: + ``` + HTTPStub.objects.filter(path__match='/path/to/target/') + ``` + """ + + lookup_name = 'match' + + # lookup only works with PostgreSQL + def as_sql(self, compiler, connection) -> Tuple[str, list]: # noqa: D102 + raise NotImplementedError + + def as_postgresql(self, compiler, connection) -> Tuple[str, list]: + """Compiles request for postgres. + + :param compiler: sql expression compiler + :param connection: database connection + :return: generated expression with params + """ + lhs, lhs_params = self.process_lhs(compiler, connection) + rhs, rhs_params = self.process_rhs(compiler, connection) + return f'{rhs} ~ {lhs}', lhs_params + rhs_params diff --git a/http_stubs/migrations/0007_add_proxy_stubs.py b/http_stubs/migrations/0007_add_proxy_stubs.py new file mode 100644 index 0000000..539f94c --- /dev/null +++ b/http_stubs/migrations/0007_add_proxy_stubs.py @@ -0,0 +1,106 @@ +# Generated by Django 3.1.13 on 2021-10-18 22:53 + +import django.contrib.postgres.fields.hstore +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('http_stubs', '0006_httpstub_is_logging_enabled'), + ] + + operations = [ + migrations.CreateModel( + name='ProxyHTTPStub', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_active', models.BooleanField(default=True, verbose_name='Enabled')), + ('path', models.CharField(db_index=True, max_length=2000, verbose_name='Request path')), + ('regex_path', models.BooleanField(default=False, help_text='Path is a regular expression', verbose_name='REGEX path')), + ('method', models.CharField(choices=[('GET', 'Get'), ('POST', 'Post'), ('PUT', 'Put'), ('PATCH', 'Patch'), ('DELETE', 'Delete'), ('HEAD', 'Head'), ('OPTIONS', 'Options'), ('TRACE', 'Trace')], db_index=True, max_length=10, verbose_name='Request method')), + ('request_script', models.TextField(blank=True, help_text='Language: python 3.8. The script will run on each request.', verbose_name='Request script')), + ('enable_logging', models.BooleanField(default=False, help_text='Enables logging of requests', verbose_name='Logging')), + ('target_url', models.URLField(verbose_name='Target url')), + ('allow_forward_query', models.BooleanField(default=False, verbose_name='Forward query params')), + ('target_ssl_verify', models.BooleanField(default=True, verbose_name='Target SSL verify')), + ('target_timeout', models.IntegerField(default=15, help_text='In seconds', verbose_name='Target response timeout')), + ('target_method', models.CharField(choices=[('GET', 'Get'), ('POST', 'Post'), ('PUT', 'Put'), ('PATCH', 'Patch'), ('DELETE', 'Delete'), ('HEAD', 'Head'), ('OPTIONS', 'Options'), ('TRACE', 'Trace')], max_length=10, verbose_name='Target request method')), + ('target_headers', django.contrib.postgres.fields.hstore.HStoreField(blank=True, default=dict, help_text='In JSON format', verbose_name='Target headers')), + ('target_body', models.TextField(blank=True, default='', verbose_name='Target body')), + ], + options={ + 'verbose_name': 'proxy http stub', + 'verbose_name_plural': 'proxy stubs', + }, + ), + migrations.CreateModel( + name='ProxyLogEntity', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('path', models.URLField(max_length=2000, verbose_name='Full request path')), + ('method', models.CharField(choices=[('GET', 'Get'), ('POST', 'Post'), ('PUT', 'Put'), ('PATCH', 'Patch'), ('DELETE', 'Delete'), ('HEAD', 'Head'), ('OPTIONS', 'Options'), ('TRACE', 'Trace')], max_length=10, verbose_name='Request method')), + ('source_ip', models.GenericIPAddressField(verbose_name='Source IP')), + ('request_date', models.DateTimeField(auto_now_add=True, verbose_name='Request timestamp')), + ('request_headers', django.contrib.postgres.fields.hstore.HStoreField(verbose_name='Request headers')), + ('request_body', models.TextField(verbose_name='Request body')), + ('result_script', models.CharField(blank=True, max_length=200, verbose_name='Result script')), + ('target_path', models.URLField(max_length=2000, verbose_name='Full target path')), + ('response_latency', models.IntegerField(help_text='In milliseconds', verbose_name='Response latency')), + ('response_body', models.TextField(help_text='From target', verbose_name='Response body')), + ('response_headers', django.contrib.postgres.fields.hstore.HStoreField(help_text='From target', verbose_name='Response headers')), + ], + options={ + 'verbose_name': 'proxy log', + 'verbose_name_plural': 'proxy logs', + }, + ), + migrations.AlterModelOptions( + name='httpstub', + options={'verbose_name': 'request http stub', 'verbose_name_plural': 'request stubs'}, + ), + migrations.AlterModelOptions( + name='logentry', + options={'verbose_name': 'request log', 'verbose_name_plural': 'request logs'}, + ), + migrations.RemoveConstraint( + model_name='httpstub', + name='uniq-path-method', + ), + migrations.RenameField( + model_name='logentry', + old_name='body', + new_name='request_body', + ), + migrations.RenameField( + model_name='logentry', + old_name='date', + new_name='request_date', + ), + migrations.RenameField( + model_name='logentry', + old_name='headers', + new_name='request_headers', + ), + migrations.AlterField( + model_name='httpstub', + name='method', + field=models.CharField(choices=[('GET', 'Get'), ('POST', 'Post'), ('PUT', 'Put'), ('PATCH', 'Patch'), ('DELETE', 'Delete'), ('HEAD', 'Head'), ('OPTIONS', 'Options'), ('TRACE', 'Trace')], db_index=True, max_length=10, verbose_name='Request method'), + ), + migrations.AlterField( + model_name='logentry', + name='http_stub', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='http_stubs.httpstub', verbose_name='Related stub'), + ), + migrations.AlterField( + model_name='logentry', + name='method', + field=models.CharField(choices=[('GET', 'Get'), ('POST', 'Post'), ('PUT', 'Put'), ('PATCH', 'Patch'), ('DELETE', 'Delete'), ('HEAD', 'Head'), ('OPTIONS', 'Options'), ('TRACE', 'Trace')], max_length=10, verbose_name='Request method'), + ), + migrations.AddField( + model_name='proxylogentity', + name='http_stub', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='http_stubs.proxyhttpstub', verbose_name='Related proxy stub'), + ), + ] diff --git a/http_stubs/migrations/0008_log_response_status.py b/http_stubs/migrations/0008_log_response_status.py new file mode 100644 index 0000000..e7e52e2 --- /dev/null +++ b/http_stubs/migrations/0008_log_response_status.py @@ -0,0 +1,26 @@ +# Generated by Django 3.1.13 on 2021-10-21 21:22 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('http_stubs', '0007_add_proxy_stubs'), + ] + + operations = [ + migrations.AddField( + model_name='logentry', + name='resp_status', + field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(600)], verbose_name='Response status'), + preserve_default=False, + ), + migrations.AddField( + model_name='proxylogentity', + name='resp_status', + field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(600)], verbose_name='Response status'), + preserve_default=False, + ), + ] diff --git a/http_stubs/models.py b/http_stubs/models.py index 8372400..29d94b4 100644 --- a/http_stubs/models.py +++ b/http_stubs/models.py @@ -1,13 +1,9 @@ -from enum import Enum -from typing import Iterator, KeysView, Tuple - from django.contrib.postgres.fields import HStoreField from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.db.models import Lookup -class HTTPMethod(Enum): +class HTTPMethod(models.TextChoices): """Enumeration of the available HTTP methods.""" GET = 'GET' @@ -19,25 +15,9 @@ class HTTPMethod(Enum): OPTIONS = 'OPTIONS' TRACE = 'TRACE' - @classmethod - def names(cls) -> KeysView[str]: - """Set of the available HTTP method names. - - :returns: all names in the enumeration. - """ - return cls.__members__.keys() - - @classmethod - def slugs(cls) -> Iterator[Tuple[str, str]]: - """Names of the methods for the model choice fields. - - :returns: iterator of tuples with http method names - """ - return zip(cls.names(), cls.names()) - -class HTTPStub(models.Model): - """HTTP stub.""" +class AbstractHTTPStub(models.Model): + """Abstract HTTP stub.""" is_active = models.BooleanField( verbose_name='Enabled', @@ -53,17 +33,42 @@ class HTTPStub(models.Model): help_text='Path is a regular expression', default=False, ) - enable_logging = models.BooleanField( - verbose_name='Logging', - help_text='Enables logging of requests', - default=False, - ) method = models.CharField( verbose_name='Request method', max_length=10, db_index=True, - choices=HTTPMethod.slugs(), + choices=HTTPMethod.choices, + ) + request_script = models.TextField( + verbose_name='Request script', + help_text='Language: python 3.8. The script will run on each request.', + blank=True, + ) + enable_logging = models.BooleanField( + verbose_name='Logging', + help_text='Enables logging of requests', + default=False, ) + + class Meta: + abstract = True + constraints = [ + models.UniqueConstraint( + fields=('path', 'method'), name='uniq-path-method', + ), + ] + + def __str__(self): + """Return string representation of the model. + + :returns: e.g. `get: /test/`. + """ + return f'{self.method}: {self.path}' + + +class HTTPStub(AbstractHTTPStub): + """HTTP stub.""" + resp_delay = models.PositiveIntegerField( verbose_name='Response delay', help_text='In milliseconds', @@ -90,31 +95,55 @@ class HTTPStub(models.Model): default=dict, blank=True, ) - request_script = models.TextField( - verbose_name='Request script', - help_text='Language: python 3.8. The script will run on each request.', - blank=True, - ) class Meta: - verbose_name = 'http stub' - verbose_name_plural = 'stubs' - constraints = [ - models.UniqueConstraint( - fields=('path', 'method'), name='uniq-path-method', - ), - ] + verbose_name = 'request http stub' + verbose_name_plural = 'request stubs' - def __str__(self): - """Return string representation of the model. - :returns: e.g. `get: /test/`. - """ - return f'{self.method}: {self.path}' +class ProxyHTTPStub(AbstractHTTPStub): + """Proxy HTTP stub.""" + target_url = models.URLField( + verbose_name='Target url', + ) + allow_forward_query = models.BooleanField( + verbose_name='Forward query params', + default=False, + ) + target_ssl_verify = models.BooleanField( + verbose_name='Target SSL verify', + default=True, + ) + target_timeout = models.IntegerField( + verbose_name='Target response timeout', + help_text='In seconds', + default=15, + ) + target_method = models.CharField( + verbose_name='Target request method', + max_length=10, + choices=HTTPMethod.choices, + ) + target_headers = HStoreField( + verbose_name='Target headers', + help_text='In JSON format', + default=dict, + blank=True, + ) + target_body = models.TextField( + verbose_name='Target body', + default='', + blank=True, + ) -class LogEntry(models.Model): - """Log entry.""" + class Meta: + verbose_name = 'proxy http stub' + verbose_name_plural = 'proxy stubs' + + +class AbstractLogEntry(models.Model): + """Abstract log entry.""" path = models.URLField( verbose_name='Full request path', @@ -123,28 +152,25 @@ class LogEntry(models.Model): method = models.CharField( verbose_name='Request method', max_length=10, - choices=HTTPMethod.slugs(), + choices=HTTPMethod.choices, + ) + resp_status = models.IntegerField( + verbose_name='Response status', + validators=(MinValueValidator(0), MaxValueValidator(600)), ) source_ip = models.GenericIPAddressField( verbose_name='Source IP', ) - date = models.DateTimeField( + request_date = models.DateTimeField( verbose_name='Request timestamp', auto_now_add=True, editable=False, ) - body = models.TextField( - verbose_name='Request body', - ) - headers = HStoreField( + request_headers = HStoreField( verbose_name='Request headers', ) - http_stub = models.ForeignKey( - HTTPStub, - verbose_name='Related stub', - related_name='logs', - blank=True, - on_delete=models.CASCADE, + request_body = models.TextField( + verbose_name='Request body', ) result_script = models.CharField( verbose_name='Result script', @@ -153,8 +179,9 @@ class LogEntry(models.Model): ) class Meta: - verbose_name = 'log' - verbose_name_plural = 'logs' + abstract = True + verbose_name = 'MUST BE DEFINED' + verbose_name_plural = 'MUST BE DEFINED' def __str__(self) -> str: """Return string representation of the model. @@ -164,25 +191,47 @@ def __str__(self) -> str: return '' -@models.CharField.register_lookup -class RegExpLookup(Lookup): - """Regular expression field lookup. +class LogEntry(AbstractLogEntry): + """Log entry.""" + + http_stub = models.ForeignKey( + HTTPStub, + verbose_name='Related stub', + related_name='logs', + on_delete=models.CASCADE, + ) - Here's an example of how to use it: - ``` - HTTPStub.objects.filter(path__match='/path/to/target/') - ``` - """ + class Meta: + verbose_name = 'request log' + verbose_name_plural = 'request logs' - lookup_name = 'match' - def as_postgresql(self, compiler, connection) -> Tuple[str, list]: - """Compiles request for postgres. +class ProxyLogEntity(AbstractLogEntry): + """Proxy log entry.""" - :param compiler: sql expression compiler - :param connection: database connection - :return: generated expression with params - """ - lhs, lhs_params = self.process_lhs(compiler, connection) - rhs, rhs_params = self.process_rhs(compiler, connection) - return f'{rhs} ~ {lhs}', lhs_params + rhs_params + http_stub = models.ForeignKey( + ProxyHTTPStub, + verbose_name='Related proxy stub', + related_name='logs', + on_delete=models.CASCADE, + ) + target_path = models.URLField( + verbose_name='Full target path', + max_length=2000, + ) + response_latency = models.IntegerField( + verbose_name='Response latency', + help_text='In milliseconds', + ) + response_body = models.TextField( + verbose_name='Response body', + help_text='From target', + ) + response_headers = HStoreField( + verbose_name='Response headers', + help_text='From target', + ) + + class Meta: + verbose_name = 'proxy log' + verbose_name_plural = 'proxy logs' diff --git a/http_stubs/tasks.py b/http_stubs/tasks.py index 7aafea5..f918bd1 100644 --- a/http_stubs/tasks.py +++ b/http_stubs/tasks.py @@ -42,8 +42,9 @@ def run_request_script( log = LogEntry.objects.get(pk=log_id) loc = {'request_body': request_body, **restricted_builtins} - byte_code = compile_restricted(script) - try: + + try: # noqa: WPS229 + byte_code = compile_restricted(script) exec(byte_code, loc, None) # noqa: S102, WPS421 except SoftTimeLimitExceeded: log_msg = 'Error: Execution time limit exceeded' diff --git a/http_stubs/templatetags/stub_tags.py b/http_stubs/templatetags/stub_tags.py index daf743f..a5711ff 100644 --- a/http_stubs/templatetags/stub_tags.py +++ b/http_stubs/templatetags/stub_tags.py @@ -1,4 +1,4 @@ -import json +import ast from html import unescape from typing import AnyStr, Dict, List, Optional from urllib.parse import urlunparse @@ -46,8 +46,8 @@ def headers_to_list(headers: AnyStr) -> List: :returns: list of individual headers string representation """ try: - headers = json.loads(unescape(headers).replace("'", '"')) - except (TypeError, json.JSONDecodeError): + headers = ast.literal_eval(unescape(headers)) + except (ValueError, SyntaxError): return [] return [ f'{header}: {header_value}' diff --git a/http_stubs/tests/fixtures.py b/http_stubs/tests/fixtures.py index 208c166..6f8bff2 100644 --- a/http_stubs/tests/fixtures.py +++ b/http_stubs/tests/fixtures.py @@ -2,11 +2,11 @@ import pytest -from http_stubs.models import HTTPMethod, HTTPStub, LogEntry +from http_stubs.models import HTTPMethod, HTTPStub, LogEntry, ProxyHTTPStub @pytest.fixture -def http_stub_factory() -> Callable: +def http_stub_factory() -> Callable[..., HTTPStub]: """HTTP stubs factory. :returns: factory closure @@ -27,31 +27,54 @@ def factory(**kwargs) -> HTTPStub: @pytest.fixture -def log_entity_factory(http_stub_factory) -> Callable: # noqa: WPS442 +def proxy_http_stub_factory() -> Callable[..., ProxyHTTPStub]: + """Proxy HTTP stubs factory. + + :returns: factory closure + """ + def factory(**kwargs) -> ProxyHTTPStub: + """Create and return ProxyHTTPStub object. + + :param kwargs: model params + :return: created stub + """ + default_params = { + 'path': '/default_proxy_path/', + 'target_url': '', + 'method': HTTPMethod.GET.name, + 'target_method': HTTPMethod.GET.name, + } + default_params.update(kwargs) + return ProxyHTTPStub.objects.create(**default_params) + return factory + + +@pytest.fixture +def log_entity_factory( + http_stub_factory, # noqa: WPS442 +) -> Callable[..., LogEntry]: """Log Entity factory. :param http_stub_factory: a factory for create a HttpStub :returns: factory closure """ def factory(**kwargs) -> LogEntry: - """Create and return HTTPStub object. + """Create and return LogEntry object. :param kwargs: model params :return: created stub """ - http_stub: HTTPStub = kwargs.get('http_stub') or http_stub_factory( - path='/default_path/', - method=HTTPMethod.GET.name, - ) + http_stub: HTTPStub = kwargs.get('http_stub') or http_stub_factory() default_params = { 'path': http_stub.path, 'method': http_stub.method, 'source_ip': '127.0.0.1', - 'date': 1608757075, - 'body': '', - 'headers': {}, + 'request_date': 1608757075, + 'request_body': '', + 'request_headers': {}, 'http_stub': http_stub, 'result_script': '', + 'resp_status': 418, } default_params.update(kwargs) return LogEntry.objects.create(**default_params) diff --git a/http_stubs/tests/test_admin.py b/http_stubs/tests/test_admin.py new file mode 100644 index 0000000..1a6649c --- /dev/null +++ b/http_stubs/tests/test_admin.py @@ -0,0 +1,81 @@ +import pytest +from django.http import HttpRequest + +from http_stubs.admin import ( + HTTPStubAdminBase, + LogEntryAdminBase, + ProxyLogEntryAdmin, +) +from http_stubs.models import HTTPStub, LogEntry, ProxyLogEntity + + +class TestLogEntryAdminBase: + """Tests for LogEntryAdminBase class.""" + + instance = LogEntryAdminBase(LogEntry, '') + + def test_has_add_permission(self): + """Should always return false.""" + assert self.instance.has_add_permission() is False + + @pytest.mark.parametrize( + 'string, expect', ( + ('', ''), + ('bad json', ''), + ('{"not":"indented"}', '{\n "not": "indented"\n}'), + ('{\n "indent": "json"\n}', '{\n "indent": "json"\n}'), + ), + ) + def test_pretty_str(self, string, expect): + """Test pretty_str method. + + :param string: a string for check + :param expect: expect value for the string + """ + assert self.instance.pretty_str(string) == expect + + def test_pretty_request_body(self): + """Test pretty_request_body method.""" + mock = type('mock', (), {'request_body': '{"valid":"json"}'}) + expect = '{\n "valid": "json"\n}' + assert self.instance.pretty_request_body(mock) == expect + + +class TestProxyLogEntryAdmin: + """Tests for ProxyLogEntryAdmin class.""" + + instance = ProxyLogEntryAdmin(ProxyLogEntity, '') + + def test_pretty_response_body(self): + """Test for pretty_response_body method.""" + mock = type('mock', (), {'response_body': '{"valid":"json"}'}) + expect = '{\n "valid": "json"\n}' + assert self.instance.pretty_response_body(mock) == expect + + +class TestHTTPStubAdminBase: + """Tests for HTTPStubAdminBase class.""" + + instance = HTTPStubAdminBase(HTTPStub, '') + + def test_enable_action(self, http_stub_factory): + """Test for enable_action method. + + :param http_stub_factory: factory for HTTPStubs + """ + stub = http_stub_factory(is_active=False) + self.instance.enable_action(HttpRequest(), HTTPStub.objects.all()) + stub.refresh_from_db() + + assert stub.is_active is True + + def test_disable_action(self, http_stub_factory): + """Test for disable_action method. + + :param http_stub_factory: factory for HTTPStubs + """ + stub = http_stub_factory(is_active=True) + self.instance.disable_action(HttpRequest(), HTTPStub.objects.all()) + stub.refresh_from_db() + + assert stub.is_active is False diff --git a/http_stubs/tests/test_lookups.py b/http_stubs/tests/test_lookups.py new file mode 100644 index 0000000..49882bb --- /dev/null +++ b/http_stubs/tests/test_lookups.py @@ -0,0 +1,9 @@ +import pytest + +from http_stubs import RegExpLookup + + +def test_regexplookup_assql(): + """Smoke test for as_sql method in RegExpLookup.""" + with pytest.raises(NotImplementedError): + RegExpLookup.as_sql('', '', '') diff --git a/http_stubs/tests/test_models.py b/http_stubs/tests/test_models.py new file mode 100644 index 0000000..f7c4af3 --- /dev/null +++ b/http_stubs/tests/test_models.py @@ -0,0 +1,24 @@ +from http_stubs.models import AbstractHTTPStub, AbstractLogEntry + + +class TestAbstractHTTPStub: + """Tests for AbstractHTTPStub class.""" + + model = AbstractHTTPStub( + method='test_method', + path='test_path', + ) + + def test_str(self): + """Test magic method __str__.""" + assert str(self.model) == 'test_method: test_path' + + +class TestAbstractLogEntry: + """Tests for AbstractHTTPStub class.""" + + model = AbstractLogEntry() + + def test_str(self): + """Test magic method __str__.""" + assert str(self.model) == '' diff --git a/http_stubs/tests/test_tasks.py b/http_stubs/tests/test_tasks.py index 78357d8..69772df 100644 --- a/http_stubs/tests/test_tasks.py +++ b/http_stubs/tests/test_tasks.py @@ -1,4 +1,7 @@ +from unittest.mock import patch + import pytest +from billiard.exceptions import SoftTimeLimitExceeded from http_stubs.tasks import run_request_script @@ -7,13 +10,14 @@ 'script, expect', ( # valid scripts ('a = 1', 'Done'), - (""" -def func(*args, **kwargs): - return json.dumps({'fake': 'data'}) - -parsed_json = json.loads(func()) -assert parsed_json['fake'] == 'data' - """, 'Done'), # noqa: WPS319 + ( + 'def func(*args, **kwargs):\n' + " return json.dumps({'fake': 'data'})\n" + '\n' + 'parsed_json = json.loads(func())\n' + "assert parsed_json['fake'] == 'data'\n", + 'Done', + ), # scripts with syntax error ('a = no_def', "Error: name 'no_def' is not defined"), @@ -45,3 +49,17 @@ def test_run_without_log(script): :param script: a request script """ run_request_script.run(script=script, request_body='') + + +@patch('http_stubs.tasks.compile_restricted') +def test_run_request_script_time_limit(compile_restricted, log_entity_factory): + """Test SoftTimeLimitExceeded Exception in run_request_script. + + :param compile_restricted: patched compile_restricted func + :param log_entity_factory: factory log models + """ + compile_restricted.side_effect = SoftTimeLimitExceeded + log = log_entity_factory() + run_request_script.delay(script='', request_body='', log_id=log.id) + log.refresh_from_db() + assert log.result_script == 'Error: Execution time limit exceeded' diff --git a/http_stubs/tests/test_views.py b/http_stubs/tests/test_views.py index 3370e45..a37375a 100644 --- a/http_stubs/tests/test_views.py +++ b/http_stubs/tests/test_views.py @@ -1,15 +1,19 @@ from datetime import datetime from http import HTTPStatus +from unittest.mock import patch import pytest +from django.http import HttpRequest +from requests import Request, Response +from http_stubs import views from http_stubs.models import HTTPMethod, LogEntry class TestHTTPStubView: """Tests representation of the http stubs.""" - @pytest.mark.parametrize('method', HTTPMethod.names()) + @pytest.mark.parametrize('method', HTTPMethod.values) def test_nonexistent_stub(self, method: str, client): """Tests response when stub is not found. @@ -22,7 +26,7 @@ def test_nonexistent_stub(self, method: str, client): assert response.status_code == HTTPStatus.NOT_FOUND - @pytest.mark.parametrize('method', HTTPMethod.names()) + @pytest.mark.parametrize('method', HTTPMethod.values) def test_exist_not_regexp_stub( self, method: str, http_stub_factory, client, ): @@ -54,6 +58,7 @@ def test_write_log(self, http_stub_factory, client): regex_path=True, request_script='a = 1', enable_logging=True, + resp_status=401, ) request_path = f'/regex/?query={"search" * 300}' @@ -72,17 +77,18 @@ def _datefmt(date) -> str: # noqa:WPS430 return date.strftime('%d%m%y') # noqa:WPS323 assert log.source_ip == '127.0.0.1' - assert _datefmt(log.date) == _datefmt(datetime(2020, 5, 25)) - assert log.headers == { + assert _datefmt(log.request_date) == _datefmt(datetime(2020, 5, 25)) + assert log.request_headers == { 'Content-Length': '4', 'Content-Type': content_type, 'Cookie': '', } - assert log.body == 'test' + assert log.request_body == 'test' assert log.http_stub == http_stub assert log.method == HTTPMethod.POST.name assert log.path == f'http://testserver{request_path}' assert log.result_script == 'Done' + assert log.resp_status == http_stub.resp_status def test_empty_log(self, http_stub_factory, client): """Tests http stub without logs. @@ -96,7 +102,7 @@ def test_empty_log(self, http_stub_factory, client): assert response.status_code == HTTPStatus.OK assert LogEntry.objects.last() is None - @pytest.mark.parametrize('method', HTTPMethod.names()) + @pytest.mark.parametrize('method', HTTPMethod.values) def test_exist_regexp_stub(self, method: str, http_stub_factory, client): """Tests response for the regex stub. @@ -166,3 +172,130 @@ def test_regexp_matching( http_stub_factory(path=regexp, regex_path=True) response = client.get(path) assert response.status_code == status_code + + @patch('http_stubs.views.requests.request') + def test_proxy_httpstub_executor( + self, + mock_request_func, + proxy_http_stub_factory, + ): + """Test for _proxy_httpstub_executor fync. + + :param mock_request_func: patched request func + :param proxy_http_stub_factory: factory proxy stubs + """ + request = HttpRequest() + request.method = HTTPMethod.OPTIONS.name + request.path = '127.0.0.1/test_request_path' + request.META['HTTP_TEST_HEADER'] = 'Value' + request.META['SERVER_NAME'] = '127.0.0.1' + request.META['SERVER_PORT'] = '80' + request.META['REMOTE_ADDR'] = '192.168.1.42' + request._body = b'test_body' + request.GET['q'] = 'testParam' + + fake_response = Response() + fake_response.headers['TEST_RESPONSE_HEADER'] = '1' + fake_response.headers['Content-Type'] = 'text/plain' + # not supported wsgi header + fake_response.headers['Trailers'] = 'value' + fake_response._content = b'I am a teapot' + fake_response.status_code = 420 + fake_response.request = Request(url='target_url') + mock_request_func.return_value = fake_response + + # test without additional params + stub = proxy_http_stub_factory(target_url='test_target_url') + views._proxy_httpstub_executor(stub, request) + + call_args = mock_request_func.call_args.kwargs + assert call_args['method'] == HTTPMethod.GET.name + assert call_args['url'] == stub.target_url + assert call_args['data'] == request._body + assert call_args['verify'] is True + assert call_args['timeout'] == 15 + + stub = proxy_http_stub_factory( + target_url='test_target_url', + allow_forward_query=True, + target_body='test target body', + target_method=HTTPMethod.TRACE.name, + target_headers={'test_target_header': 1}, + ) + + response, log = views._proxy_httpstub_executor(stub, request) + + log.refresh_from_db() + + call_args = mock_request_func.call_args.kwargs + assert call_args['method'] == stub.target_method + assert call_args['url'] == stub.target_url + assert call_args['headers']['test_target_header'] == 1 + assert call_args['data'] == stub.target_body + assert call_args['params']['q'] == 'testParam' + + assert response.status_code == fake_response.status_code + assert response.content == fake_response.content + assert response._headers['test_response_header'][1] == '1' + + assert log.http_stub_id == stub.pk + assert log.path == 'http://127.0.0.1/test_request_path' + assert log.method == request.method + assert log.source_ip == request.META['REMOTE_ADDR'] + assert log.request_headers == {'Test-Header': 'Value'} + assert log.request_body == 'test_body' + assert log.result_script == '' + assert log.target_path == fake_response.request.url + assert log.response_latency == 0 + assert log.response_body == 'I am a teapot' + assert log.response_headers == fake_response.headers + assert log.resp_status == fake_response.status_code + + def test_response_templating(self, http_stub_factory, client): + """Tests response templating. + + :param http_stub_factory: HTTPStub factory + :param client: http client fixture + """ + tpl = 'arg1=$arg1 arg2=$arg2 $ $none body=$body' + + http_stub_factory( + path='/test/', + regex_path=True, + method=HTTPMethod.POST, + resp_body=tpl, + resp_headers={'TEST': tpl}, + ) + response = client.post( + '/test/?arg1=Kesha&arg2=42', + 'big body', + 'text/plain', + ) + + exp_string = b'arg1=Kesha arg2=42 $ $none body=big body' + + assert response.status_code == HTTPStatus.OK + assert response.content == exp_string + assert response._headers['test'][1] == exp_string.decode() + + +@pytest.mark.parametrize( + 'body, encoding, expect', ( + (b'test_body', '', 'test_body'), + (b'', '', ''), + ('тестовое тело'.encode('koi8-r'), '', ''), + ('тестовое тело'.encode('koi8-r'), 'koi8-r', 'тестовое тело'), + ), +) +def test_request_body_decode(body, encoding, expect): + """Test for _request_body_decode func. + + :param body: request body + :param encoding: request encoding + :param expect: expecting result + """ + request = HttpRequest() + + request._body = body + request._encoding = encoding + assert views._request_body_decode(request) == expect diff --git a/http_stubs/views.py b/http_stubs/views.py index 9b256e0..4bdf11e 100644 --- a/http_stubs/views.py +++ b/http_stubs/views.py @@ -1,14 +1,132 @@ +from string import Template from time import sleep -from typing import Optional +from typing import Optional, Tuple, Union +from wsgiref.util import is_hop_by_hop +import requests from django.http import HttpRequest, HttpResponse, HttpResponseNotFound from django.views import View from django.views.decorators.csrf import csrf_exempt -from http_stubs.models import HTTPStub, LogEntry +from http_stubs.models import HTTPStub, LogEntry, ProxyHTTPStub, ProxyLogEntity from http_stubs.tasks import run_request_script +def _request_body_decode(request: HttpRequest) -> str: + """Decode a request body. + + :param request: a http request for getting decoded body + :returns: decoded body or empty string + """ + try: + return request.body.decode(request.encoding or 'utf-8') + except ValueError: + return '' + + +def _httpstub_executor( # noqa: WPS210 + stub: HTTPStub, + request: HttpRequest, +) -> Tuple[HttpResponse, Optional[LogEntry]]: + """Execute logic for HTTPStub instance. + + :param stub: a httpstub instance + :param request: received request + :returns: a response instance for return to requester + """ + sleep(stub.resp_delay / 1000) + + request_body = _request_body_decode(request) + + tpl_ctx = {'body': request_body} + + for arg_name, arg_val in dict(request.GET).items(): + tpl_ctx[arg_name] = arg_val[0] + + response = HttpResponse( + content=Template(stub.resp_body).safe_substitute(tpl_ctx), + content_type=stub.resp_content_type, + status=stub.resp_status, + ) + + for header_name, header_value in stub.resp_headers.items(): + response[header_name] = Template(header_value).safe_substitute(tpl_ctx) + + result_script = 'Was launched' if stub.request_script else '' + + log = LogEntry.objects.create( + path=request.build_absolute_uri(), + method=request.method, + source_ip=request.META['REMOTE_ADDR'], + request_headers=dict(request.headers), + request_body=request_body, + http_stub=stub, + result_script=result_script, + resp_status=stub.resp_status, + ) if stub.enable_logging else None + + return response, log + + +def _proxy_httpstub_executor( # noqa: WPS210 + stub: ProxyHTTPStub, + request: HttpRequest, +) -> Tuple[HttpResponse, Optional[ProxyLogEntity]]: + """Execute logic for ProxyHTTPStub instance. + + :param stub: a proxy stub instance + :param request: received request + :returns: a response instance for return to requester + """ + t_request_args = { + 'method': request.method, + 'url': stub.target_url, + 'data': request.body, + 'verify': stub.target_ssl_verify, + 'timeout': stub.target_timeout, + } + if stub.allow_forward_query and (request.GET or request.POST): + t_request_args['params'] = request.GET or request.POST + if stub.target_headers: + t_request_args['headers'] = stub.target_headers + if stub.target_body: + t_request_args['data'] = stub.target_body + + t_request_args['method'] = stub.target_method + + t_response = requests.request(**t_request_args) + + response = HttpResponse( + content=t_response.content, + content_type=t_response.headers['Content-Type'], + status=t_response.status_code, + ) + + for header_name, header_value in t_response.headers.items(): + # skip headers which are meaningful + # only for a single transport-level connection + if is_hop_by_hop(header_name): + continue + response[header_name] = header_value + + log = ProxyLogEntity.objects.create( + http_stub=stub, + path=request.build_absolute_uri(), + method=request.method, + source_ip=request.META['REMOTE_ADDR'], + request_headers=dict(request.headers), + request_body=_request_body_decode(request), + result_script='Was launched' if stub.request_script else '', + target_path=t_response.request.url, + response_latency=t_response.elapsed.microseconds, + response_body=t_response.text, + response_headers=dict(t_response.headers), + resp_status=t_response.status_code, + ) + + return response, log + + class HTTPStubView(View): """HTTP stub view. @@ -17,24 +135,10 @@ class HTTPStubView(View): Returns 404 if stub is not found. """ - def find_stub(self, method: str, path: str) -> Optional[HTTPStub]: - """Search for matching http stub. - - :param method: request method name - :param path: request path - :returns: matching stub or None - """ - method = method.upper() - stub = HTTPStub.objects.filter( - method=method, path=path, - ).exclude(regex_path=True).first() - - if stub: - return stub - - return HTTPStub.objects.filter( - method=method, path__match=path, - ).exclude(regex_path=False).first() + stub_exec_map = { + HTTPStub: _httpstub_executor, + ProxyHTTPStub: _proxy_httpstub_executor, + } @csrf_exempt def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: @@ -45,38 +149,47 @@ def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: :param kwargs: request kwargs :returns: http response """ - stub = self.find_stub(request.method, request.get_full_path()) + stub = self._find_stub(request.method, request.get_full_path()) if not stub: return HttpResponseNotFound() - log = None - - if stub.enable_logging: - log = LogEntry.objects.create( - path=request.build_absolute_uri(), - method=request.method, - source_ip=request.META['REMOTE_ADDR'], - body=request.body.decode('utf-8'), - headers=dict(request.headers), - http_stub=stub, - result_script='Was launched' if stub.request_script else '', - ) + response, log = self.stub_exec_map[stub.__class__](stub, request) if stub.request_script: run_request_script.delay( script=stub.request_script, - request_body=request.body.decode('utf-8'), + request_body=_request_body_decode(request), log_id=log.pk if log else None, ) - sleep(stub.resp_delay / 1000) - response = HttpResponse( - content=stub.resp_body, - content_type=stub.resp_content_type, - status=stub.resp_status, - ) + return response - for header_name, header_value in stub.resp_headers.items(): - response[header_name] = header_value + def _find_stub( + self, + method: str, + path: str, + ) -> Optional[Union[HTTPStub, ProxyHTTPStub]]: + """Search for matching http stub. - return response + :param method: request method name + :param path: request path + :returns: matching stub or None + """ + method = method.upper() + stub_models = (HTTPStub, ProxyHTTPStub) + + for model in stub_models: + stub = model.objects.exclude(regex_path=True).filter( + method=method, path=path, + ).first() + if stub: + return stub + + for s_model in stub_models: + stub = s_model.objects.exclude(regex_path=False).filter( + method=method, path__match=path, + ).first() + if stub: + return stub + + return None diff --git a/kesha/settings.py b/kesha/settings.py index e5e9ece..d0b58e6 100644 --- a/kesha/settings.py +++ b/kesha/settings.py @@ -9,7 +9,7 @@ SECRET_KEY = env('KESHA_SECRET_KEY') -DEBUG = env.bool('DEBUG', False) +DEBUG = env.bool('KESHA_DEBUG', False) ALLOWED_HOSTS = env.list('KESHA_ALLOWED_HOSTS', default=['127.0.0.1']) @@ -105,14 +105,18 @@ # Admin UI settings SIMPLEUI_HOME_INFO = False SIMPLEUI_STATIC_OFFLINE = True -SIMPLEUI_DEFAULT_THEME = 'layui.css' +SIMPLEUI_DEFAULT_THEME = 'dark.green.css' SIMPLEUI_ANALYSIS = False SIMPLEUI_LOGO = STATIC_URL + 'kesha_icon.png' +STUB_ICON = 'fas fa-feather-alt' + SIMPLEUI_ICON = { - 'HTTP Stubs': 'fas fa-feather-alt', - 'Stubs': 'fas fa-feather-alt', - 'Logs': 'fas fa-layer-group', + 'HTTP Stubs': STUB_ICON, + 'Request stubs': STUB_ICON, + 'Request logs': 'fas fa-layer-group', + 'Proxy stubs': STUB_ICON, + 'Proxy logs': 'fas fa-layer-group', } INTERNAL_IPS = env.list('KESHA_INTERNAL_HOSTS', default=['127.0.0.1']) diff --git a/poetry.lock b/poetry.lock index c8ac191..43db30b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,6 +1,6 @@ [[package]] name = "amqp" -version = "5.0.6" +version = "5.0.9" description = "Low-level AMQP client for Python (fork of amqplib)." category = "main" optional = false @@ -19,7 +19,7 @@ python-versions = "*" [[package]] name = "asgiref" -version = "3.3.4" +version = "3.4.1" description = "ASGI specs, helper code, and adapters" category = "main" optional = false @@ -91,91 +91,112 @@ python-versions = "*" [[package]] name = "celery" -version = "5.0.5" +version = "5.2.2" description = "Distributed Task Queue." category = "main" optional = false -python-versions = ">=3.6," +python-versions = ">=3.7," [package.dependencies] -billiard = ">=3.6.3.0,<4.0" -click = ">=7.0,<8.0" +billiard = ">=3.6.4.0,<4.0" +click = ">=8.0,<9.0" click-didyoumean = ">=0.0.3" click-plugins = ">=1.1.1" -click-repl = ">=0.1.6" -kombu = ">=5.0.0,<6.0" -pytz = ">0.0-dev" -redis = {version = ">=3.2.0", optional = true, markers = "extra == \"redis\""} +click-repl = ">=0.2.0" +kombu = ">=5.2.2,<6.0" +pytz = ">0.dev.0" +redis = {version = ">=3.4.1,<4.0.0", optional = true, markers = "extra == \"redis\""} vine = ">=5.0.0,<6.0" [package.extras] arangodb = ["pyArango (>=1.3.2)"] auth = ["cryptography"] -azureblockblob = ["azure-storage (==0.36.0)", "azure-common (==1.1.5)", "azure-storage-common (==1.1.0)"] +azureblockblob = ["azure-storage-blob (==12.9.0)"] brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] cassandra = ["cassandra-driver (<3.21.0)"] -consul = ["python-consul"] +consul = ["python-consul2"] cosmosdbsql = ["pydocumentdb (==2.3.2)"] couchbase = ["couchbase (>=3.0.0)"] couchdb = ["pycouchdb"] django = ["Django (>=1.11)"] dynamodb = ["boto3 (>=1.9.178)"] elasticsearch = ["elasticsearch"] -eventlet = ["eventlet (>=0.26.1)"] -gevent = ["gevent (>=1.0.0)"] +eventlet = ["eventlet (>=0.32.0)"] +gevent = ["gevent (>=1.5.0)"] librabbitmq = ["librabbitmq (>=1.5.0)"] -lzma = ["backports.lzma"] memcache = ["pylibmc"] -mongodb = ["pymongo[srv] (>=3.3.0)"] +mongodb = ["pymongo[srv] (>=3.3.0,<3.12.1)"] msgpack = ["msgpack"] pymemcache = ["python-memcached"] pyro = ["pyro4"] pytest = ["pytest-celery"] -redis = ["redis (>=3.2.0)"] +redis = ["redis (>=3.4.1,<4.0.0)"] s3 = ["boto3 (>=1.9.125)"] slmq = ["softlayer-messaging (>=1.0.3)"] solar = ["ephem"] sqlalchemy = ["sqlalchemy"] -sqs = ["boto3 (>=1.9.125)", "pycurl (==7.43.0.5)"] +sqs = ["kombu"] tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] zstd = ["zstandard"] +[[package]] +name = "celery-types" +version = "0.10.0" +description = "Type stubs for Celery and its related packages" +category = "dev" +optional = false +python-versions = ">=3.7,<4.0" + [[package]] name = "certifi" -version = "2020.12.5" +version = "2021.10.8" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = "*" [[package]] -name = "chardet" -version = "4.0.0" -description = "Universal encoding detector for Python 2 and 3" +name = "charset-normalizer" +version = "2.0.7" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "chevron" +version = "0.14.0" +description = "Mustache templating language renderer" +category = "dev" +optional = false +python-versions = "*" [[package]] name = "click" -version = "7.1.2" +version = "8.0.3" description = "Composable command line interface toolkit" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "click-didyoumean" -version = "0.0.3" -description = "Enable git-like did-you-mean feature in click." +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6.2,<4.0.0" [package.dependencies] -click = "*" +click = ">=7" [[package]] name = "click-plugins" @@ -193,7 +214,7 @@ dev = ["pytest (>=3.6)", "pytest-cov", "wheel", "coveralls"] [[package]] name = "click-repl" -version = "0.1.6" +version = "0.2.0" description = "REPL plugin for Click" category = "main" optional = false @@ -208,24 +229,27 @@ six = "*" name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "5.5" +version = "6.2" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.6" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] -toml = ["toml"] +toml = ["tomli"] [[package]] name = "darglint" -version = "1.8.0" +version = "1.8.1" description = "A utility for ensuring Google-style docstrings stay up to date with the source code." category = "dev" optional = false @@ -233,7 +257,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "decorator" -version = "5.0.9" +version = "5.1.0" description = "Decorators for Humans" category = "dev" optional = false @@ -241,7 +265,7 @@ python-versions = ">=3.5" [[package]] name = "django" -version = "3.1.11" +version = "3.1.14" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." category = "main" optional = false @@ -258,7 +282,7 @@ bcrypt = ["bcrypt"] [[package]] name = "django-debug-toolbar" -version = "3.2.1" +version = "3.2.4" description = "A configurable set of panels that display various debug information about the current request/response." category = "main" optional = false @@ -270,15 +294,20 @@ sqlparse = ">=0.2.0" [[package]] name = "django-environ" -version = "0.4.5" -description = "Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application." +version = "0.8.1" +description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.4,<4" + +[package.extras] +develop = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)", "furo (>=2021.8.17b43,<2021.9.0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +docs = ["furo (>=2021.8.17b43,<2021.9.0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] +testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"] [[package]] name = "django-extensions" -version = "3.1.3" +version = "3.1.5" description = "Extensions for Django" category = "main" optional = false @@ -289,7 +318,7 @@ Django = ">=2.2" [[package]] name = "django-simpleui" -version = "2021.3" +version = "2022.1" description = "django admin theme 后台模板" category = "main" optional = false @@ -298,6 +327,35 @@ python-versions = "*" [package.dependencies] django = "*" +[[package]] +name = "django-stubs" +version = "1.9.0" +description = "Mypy stubs for Django" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +django = "*" +django-stubs-ext = ">=0.3.0" +mypy = ">=0.910" +toml = "*" +types-pytz = "*" +types-PyYAML = "*" +typing-extensions = "*" + +[[package]] +name = "django-stubs-ext" +version = "0.3.1" +description = "Monkey-patching and extensions for django-stubs" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +django = "*" +typing-extensions = "*" + [[package]] name = "docutils" version = "0.17.1" @@ -308,7 +366,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "eradicate" -version = "1.0" +version = "2.0.0" description = "Removes commented-out code." category = "dev" optional = false @@ -351,7 +409,7 @@ pycodestyle = "*" [[package]] name = "flake8-broken-line" -version = "0.2.1" +version = "0.3.0" description = "Flake8 plugin to forbid backslashes for line breaks" category = "dev" optional = false @@ -362,49 +420,53 @@ flake8 = ">=3.5,<4.0" [[package]] name = "flake8-bugbear" -version = "19.8.0" +version = "21.9.2" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] -attrs = "*" +attrs = ">=19.2.0" flake8 = ">=3.0.0" +[package.extras] +dev = ["coverage", "black", "hypothesis", "hypothesmith"] + [[package]] name = "flake8-commas" -version = "2.0.0" +version = "2.1.0" description = "Flake8 lint for trailing commas." category = "dev" optional = false python-versions = "*" [package.dependencies] -flake8 = ">=2,<4.0.0" +flake8 = ">=2" [[package]] name = "flake8-comprehensions" -version = "3.5.0" +version = "3.7.0" description = "A flake8 plugin to help you write better list/set/dict comprehensions." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -flake8 = ">=3.0,<3.2.0 || >3.2.0,<4" +flake8 = ">=3.0,<3.2.0 || >3.2.0,<5" [[package]] name = "flake8-debugger" -version = "3.2.1" +version = "4.0.0" description = "ipdb/pdb statement checker plugin for flake8" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] -flake8 = ">=1.5" +flake8 = ">=3.0" pycodestyle = "*" +six = "*" [[package]] name = "flake8-django" @@ -431,7 +493,7 @@ pydocstyle = ">=2.1" [[package]] name = "flake8-eradicate" -version = "0.3.0" +version = "1.1.0" description = "Flake8 plugin to find commented out code" category = "dev" optional = false @@ -439,24 +501,24 @@ python-versions = ">=3.6,<4.0" [package.dependencies] attrs = "*" -eradicate = ">=1.0,<2.0" +eradicate = ">=2.0,<3.0" flake8 = ">=3.5,<4.0" [[package]] name = "flake8-isort" -version = "3.0.1" +version = "4.1.1" description = "flake8 plugin that integrates isort ." category = "dev" optional = false python-versions = "*" [package.dependencies] -flake8 = ">=3.2.1,<4" -isort = {version = ">=4.3.5,<5", extras = ["pyproject"]} +flake8 = ">=3.2.1,<5" +isort = ">=4.3.5,<6" testfixtures = ">=6.8.0,<7" [package.extras] -test = ["pytest (>=4.0.2,<6)"] +test = ["pytest-cov"] [[package]] name = "flake8-polyfill" @@ -471,7 +533,7 @@ flake8 = "*" [[package]] name = "flake8-quotes" -version = "2.1.2" +version = "3.3.0" description = "Flake8 lint for quotes." category = "dev" optional = false @@ -482,19 +544,20 @@ flake8 = "*" [[package]] name = "flake8-rst-docstrings" -version = "0.0.12" +version = "0.2.3" description = "Python docstring reStructuredText (RST) validator" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.3" [package.dependencies] flake8 = ">=3.0.0" -restructuredtext_lint = "*" +pygments = "*" +restructuredtext-lint = "*" [[package]] name = "flake8-string-format" -version = "0.2.3" +version = "0.3.0" description = "string format checker, plugin for flake8" category = "dev" optional = false @@ -503,30 +566,19 @@ python-versions = "*" [package.dependencies] flake8 = "*" -[[package]] -name = "flake8-type-annotations" -version = "0.1.0" -description = "Flake8 plugin to enforce consistent type annotation styles" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -flake8 = ">=3.5,<4.0" - [[package]] name = "flake8-use-fstring" -version = "1.1" +version = "1.3" description = "Flake8 plugin for string formatting style." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -flake8 = ">=3.0.0,<4.0.0" +flake8 = ">=3" [package.extras] -ci = ["coverage (>=4.0.0,<5.0.0)", "pytest (>=4)", "pytest-cov (>=2)", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo", "python-coveralls"] +ci = ["coverage (>=4.0.0,<5.0.0)", "pytest (>=4)", "pytest-cov (>=2)", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo", "coveralls"] dev = ["coverage (>=4.0.0,<5.0.0)", "pytest (>=4)", "pytest-cov (>=2)", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo"] test = ["coverage (>=4.0.0,<5.0.0)", "pytest (>=4)", "pytest-cov (>=2)", "flake8-builtins", "flake8-commas", "flake8-fixme", "flake8-print", "flake8-quotes", "flake8-todo"] @@ -554,14 +606,15 @@ smmap = ">=3.0.1,<5" [[package]] name = "gitpython" -version = "3.1.17" -description = "Python Git Library" +version = "3.1.24" +description = "GitPython is a python library used to interact with Git repositories" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} [[package]] name = "h11" @@ -573,11 +626,11 @@ python-versions = ">=3.6" [[package]] name = "idna" -version = "2.10" +version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.5" [[package]] name = "iniconfig" @@ -589,7 +642,7 @@ python-versions = "*" [[package]] name = "ipython" -version = "7.23.1" +version = "7.31.0" description = "IPython: Productive Interactive Computing" category = "dev" optional = false @@ -609,7 +662,7 @@ pygments = "*" traitlets = ">=4.2" [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.16)", "pygments", "qtconsole", "requests", "testpath"] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] doc = ["Sphinx (>=1.3)"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] @@ -617,32 +670,21 @@ nbformat = ["nbformat"] notebook = ["notebook", "ipywidgets"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.16)"] - -[[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -category = "dev" -optional = false -python-versions = "*" +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] [[package]] name = "isort" -version = "4.3.21" +version = "5.9.3" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -toml = {version = "*", optional = true, markers = "extra == \"pyproject\""} +python-versions = ">=3.6.1,<4.0" [package.extras] -pipfile = ["pipreqs", "requirementslib"] -pyproject = ["toml"] -requirements = ["pipreqs", "pip-api"] -xdg_home = ["appdirs (>=1.4.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] name = "jedi" @@ -661,34 +703,35 @@ testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] [[package]] name = "kombu" -version = "5.0.2" +version = "5.2.3" description = "Messaging library for Python." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -amqp = ">=5.0.0,<6.0.0" +amqp = ">=5.0.9,<6.0.0" +vine = "*" [package.extras] -azureservicebus = ["azure-servicebus (>=0.21.1)"] +azureservicebus = ["azure-servicebus (>=7.0.0)"] azurestoragequeues = ["azure-storage-queue"] consul = ["python-consul (>=0.6.0)"] -librabbitmq = ["librabbitmq (>=1.5.2)"] -mongodb = ["pymongo (>=3.3.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=3.3.0,<3.12.1)"] msgpack = ["msgpack"] pyro = ["pyro4"] qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=3.3.11)"] +redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] slmq = ["softlayer-messaging (>=1.0.3)"] sqlalchemy = ["sqlalchemy"] -sqs = ["boto3 (>=1.4.4)", "pycurl (==7.43.0.2)"] +sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=1.3.1)"] [[package]] name = "matplotlib-inline" -version = "0.1.2" +version = "0.1.3" description = "Inline Matplotlib backend for Jupyter" category = "dev" optional = false @@ -705,13 +748,38 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mypy" +version = "0.931" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = ">=1.1.0" +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "packaging" -version = "20.9" +version = "21.0" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2" @@ -738,7 +806,7 @@ python-versions = ">=2.6" [[package]] name = "pep8-naming" -version = "0.9.1" +version = "0.11.1" description = "Check PEP-8 naming conventions, plugin for flake8" category = "dev" optional = false @@ -768,33 +836,34 @@ python-versions = "*" [[package]] name = "pluggy" -version = "0.13.1" +version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.extras] dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.18" +version = "3.0.20" description = "Library for building powerful interactive command lines in Python" category = "main" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.6.2" [package.dependencies] wcwidth = "*" [[package]] name = "psycopg2-binary" -version = "2.8.6" +version = "2.9.3" description = "psycopg2 - Python-PostgreSQL Database Adapter" category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" [[package]] name = "ptyprocess" @@ -844,7 +913,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.9.0" +version = "2.10.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -860,7 +929,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.4" +version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -872,7 +941,7 @@ attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0.0a1" +pluggy = ">=0.12,<2.0" py = ">=1.8.2" toml = "*" @@ -881,22 +950,22 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pytest-cov" -version = "2.11.1" +version = "3.0.0" description = "Pytest plugin for measuring coverage." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] -coverage = ">=5.2.1" +coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-django" -version = "4.1.0" +version = "4.5.2" description = "A Django plugin for pytest." category = "dev" optional = false @@ -921,9 +990,25 @@ python-versions = "*" freezegun = ">0.3" pytest = ">=3.0.0" +[[package]] +name = "pytest-mypy-plugins" +version = "1.9.2" +description = "pytest plugin for writing tests for mypy plugins" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +chevron = "*" +decorator = "*" +mypy = ">=0.900" +pytest = ">=6.0.0" +pyyaml = "*" +regex = "*" + [[package]] name = "python-dateutil" -version = "2.8.1" +version = "2.8.2" description = "Extensions to the standard Python datetime module" category = "dev" optional = false @@ -934,7 +1019,7 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2021.1" +version = "2021.3" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -942,11 +1027,11 @@ python-versions = "*" [[package]] name = "pyyaml" -version = "5.4.1" +version = "6.0" description = "YAML parser and emitter for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.6" [[package]] name = "redis" @@ -959,31 +1044,39 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] hiredis = ["hiredis (>=0.1.3)"] +[[package]] +name = "regex" +version = "2021.10.8" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "requests" -version = "2.25.1" +version = "2.27.1" description = "Python HTTP for Humans." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<5" -idna = ">=2.5,<3" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} urllib3 = ">=1.21.1,<1.27" [package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] name = "restrictedpython" -version = "5.1" +version = "5.2" description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.10" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <3.11" [package.extras] docs = ["sphinx", "sphinx-rtd-theme"] @@ -1026,7 +1119,7 @@ python-versions = "*" [[package]] name = "sqlparse" -version = "0.4.1" +version = "0.4.2" description = "A non-validating SQL parser." category = "main" optional = false @@ -1034,7 +1127,7 @@ python-versions = ">=3.5" [[package]] name = "stevedore" -version = "3.3.0" +version = "3.4.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false @@ -1045,7 +1138,7 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "testfixtures" -version = "6.17.1" +version = "6.18.3" description = "A collection of helpers and mock objects for unit tests and doc tests." category = "dev" optional = false @@ -1064,23 +1157,63 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "tomli" +version = "1.2.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "traitlets" -version = "5.0.5" +version = "5.1.0" description = "Traitlets Python configuration system" category = "dev" optional = false python-versions = ">=3.7" -[package.dependencies] -ipython-genutils = "*" - [package.extras] test = ["pytest"] +[[package]] +name = "types-pytz" +version = "2021.3.0" +description = "Typing stubs for pytz" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-pyyaml" +version = "5.4.12" +description = "Typing stubs for PyYAML" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-requests" +version = "2.27.5" +description = "Typing stubs for requests" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +types-urllib3 = "<1.27" + +[[package]] +name = "types-urllib3" +version = "1.26.2" +description = "Typing stubs for urllib3" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" -version = "3.10.0.0" +version = "3.10.0.2" description = "Backported and Experimental Type Hints for Python 3.5+" category = "dev" optional = false @@ -1088,31 +1221,32 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.4" +version = "1.26.7" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] +brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "uvicorn" -version = "0.13.4" +version = "0.16.0" description = "The lightning-fast ASGI server." category = "main" optional = false python-versions = "*" [package.dependencies] -click = ">=7.0.0,<8.0.0" +asgiref = ">=3.4.0" +click = ">=7.0" h11 = ">=0.8" [package.extras] -standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] +standard = ["httptools (>=0.2.0,<0.4.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "websockets (>=9.1)", "websockets (>=10.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] [[package]] name = "vine" @@ -1132,7 +1266,7 @@ python-versions = "*" [[package]] name = "wemake-python-styleguide" -version = "0.14.1" +version = "0.16.0" description = "The strictest and most opinionated python linter ever" category = "dev" optional = false @@ -1142,40 +1276,40 @@ python-versions = ">=3.6,<4.0" astor = ">=0.8,<0.9" attrs = "*" darglint = ">=1.2,<2.0" -flake8 = ">=3.7,<4.0" +flake8 = ">=3.7,<5" flake8-bandit = ">=2.1,<3.0" -flake8-broken-line = ">=0.2,<0.3" -flake8-bugbear = ">=19.3,<20.0" +flake8-broken-line = ">=0.3,<0.5" +flake8-bugbear = ">=20.1,<22.0" flake8-commas = ">=2.0,<3.0" -flake8-comprehensions = ">=3.1.0,<4.0.0" -flake8-debugger = ">=3.1,<4.0" -flake8-docstrings = ">=1.3.1,<2.0.0" -flake8-eradicate = ">=0.3,<0.4" -flake8-isort = ">=3.0.1,<4" -flake8-quotes = ">=2.0.1,<3.0.0" -flake8-rst-docstrings = ">=0.0.12,<0.0.13" -flake8-string-format = ">=0.2,<0.3" -pep8-naming = ">=0.9.1,<0.10.0" +flake8-comprehensions = ">=3.1,<4.0" +flake8-debugger = ">=4.0,<5.0" +flake8-docstrings = ">=1.3,<2.0" +flake8-eradicate = ">=1.0,<2.0" +flake8-isort = ">=4.0,<5.0" +flake8-quotes = ">=3.0,<4.0" +flake8-rst-docstrings = ">=0.2.3,<0.3.0" +flake8-string-format = ">=0.3,<0.4" +pep8-naming = ">=0.11,<0.13" pygments = ">=2.4,<3.0" -typing_extensions = ">=3.6,<4.0" +typing_extensions = ">=3.6,<5.0" [metadata] lock-version = "1.1" python-versions = "~3.8" -content-hash = "032460ab42fcbe800e8e60ea75fc060972346f8faa8c2bb930a725fb693d1a19" +content-hash = "9aaef6129c95c3bc8b1411c68c08171ac2ddf193e8709a1ebd1bbaf7c87bea0d" [metadata.files] amqp = [ - {file = "amqp-5.0.6-py3-none-any.whl", hash = "sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"}, - {file = "amqp-5.0.6.tar.gz", hash = "sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2"}, + {file = "amqp-5.0.9-py3-none-any.whl", hash = "sha256:9cd81f7b023fc04bbb108718fbac674f06901b77bfcdce85b10e2a5d0ee91be5"}, + {file = "amqp-5.0.9.tar.gz", hash = "sha256:1e5f707424e544078ca196e72ae6a14887ce74e02bd126be54b7c03c971bef18"}, ] appnope = [ {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] asgiref = [ - {file = "asgiref-3.3.4-py3-none-any.whl", hash = "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee"}, - {file = "asgiref-3.3.4.tar.gz", hash = "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"}, + {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, + {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, ] astor = [ {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, @@ -1202,123 +1336,135 @@ billiard = [ {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, ] celery = [ - {file = "celery-5.0.5-py3-none-any.whl", hash = "sha256:5e8d364e058554e83bbb116e8377d90c79be254785f357cb2cec026e79febe13"}, - {file = "celery-5.0.5.tar.gz", hash = "sha256:f4efebe6f8629b0da2b8e529424de376494f5b7a743c321c8a2ddc2b1414921c"}, + {file = "celery-5.2.2-py3-none-any.whl", hash = "sha256:5a68a351076cfac4f678fa5ffd898105c28825a2224902da006970005196d061"}, + {file = "celery-5.2.2.tar.gz", hash = "sha256:2844eb040e915398623a43253a8e1016723442ece6b0751a3c416d8a2b34216f"}, +] +celery-types = [ + {file = "celery-types-0.10.0.tar.gz", hash = "sha256:8ddbdc346abdb1d08befb7ebddb673a46a65032d52f1d8b09b94c3713546d387"}, + {file = "celery_types-0.10.0-py3-none-any.whl", hash = "sha256:35616e5ef946dce89853750ec9a94e883248801996f5d91b1fc8d8de0b715bd0"}, ] certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"}, + {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"}, ] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +chevron = [ + {file = "chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443"}, + {file = "chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, ] click-didyoumean = [ - {file = "click-didyoumean-0.0.3.tar.gz", hash = "sha256:112229485c9704ff51362fe34b2d4f0b12fc71cc20f6d2b3afabed4b8bfa6aeb"}, + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, ] click-plugins = [ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, ] click-repl = [ - {file = "click-repl-0.1.6.tar.gz", hash = "sha256:b9f29d52abc4d6059f8e276132a111ab8d94980afe6a5432b9d996544afa95d5"}, - {file = "click_repl-0.1.6-py3-none-any.whl", hash = "sha256:9c4c3d022789cae912aad8a3f5e1d7c2cdd016ee1225b5212ad3e8691563cda5"}, + {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, + {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ - {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, ] darglint = [ - {file = "darglint-1.8.0-py3-none-any.whl", hash = "sha256:ac6797bcc918cd8d8f14c168a4a364f54e1aeb4ced59db58e7e4c6dfec2fe15c"}, - {file = "darglint-1.8.0.tar.gz", hash = "sha256:aa605ef47817a6d14797d32b390466edab621768ea4ca5cc0f3c54f6d8dcaec8"}, + {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, + {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] decorator = [ - {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"}, - {file = "decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"}, + {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"}, + {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"}, ] django = [ - {file = "Django-3.1.11-py3-none-any.whl", hash = "sha256:c79245c488411d1ae300b8f7a08ac18a496380204cf3035aff97ad917a8de999"}, - {file = "Django-3.1.11.tar.gz", hash = "sha256:9a0a2f3d34c53032578b54db7ec55929b87dda6fec27a06cc2587afbea1965e5"}, + {file = "Django-3.1.14-py3-none-any.whl", hash = "sha256:0fabc786489af16ad87a8c170ba9d42bfd23f7b699bd5ef05675864e8d012859"}, + {file = "Django-3.1.14.tar.gz", hash = "sha256:72a4a5a136a214c39cf016ccdd6b69e2aa08c7479c66d93f3a9b5e4bb9d8a347"}, ] django-debug-toolbar = [ - {file = "django-debug-toolbar-3.2.1.tar.gz", hash = "sha256:a5ff2a54f24bf88286f9872836081078f4baa843dc3735ee88524e89f8821e33"}, - {file = "django_debug_toolbar-3.2.1-py3-none-any.whl", hash = "sha256:e759e63e3fe2d3110e0e519639c166816368701eab4a47fed75d7de7018467b9"}, + {file = "django-debug-toolbar-3.2.4.tar.gz", hash = "sha256:644bbd5c428d3283aa9115722471769cac1bec189edf3a0c855fd8ff870375a9"}, + {file = "django_debug_toolbar-3.2.4-py3-none-any.whl", hash = "sha256:6b633b6cfee24f232d73569870f19aa86c819d750e7f3e833f2344a9eb4b4409"}, ] django-environ = [ - {file = "django-environ-0.4.5.tar.gz", hash = "sha256:6c9d87660142608f63ec7d5ce5564c49b603ea8ff25da595fd6098f6dc82afde"}, - {file = "django_environ-0.4.5-py2.py3-none-any.whl", hash = "sha256:c57b3c11ec1f319d9474e3e5a79134f40174b17c7cc024bbb2fad84646b120c4"}, + {file = "django-environ-0.8.1.tar.gz", hash = "sha256:6f0bc902b43891656b20486938cba0861dc62892784a44919170719572a534cb"}, + {file = "django_environ-0.8.1-py2.py3-none-any.whl", hash = "sha256:42593bee519a527602a467c7b682aee1a051c2597f98c45f4f4f44169ecdb6e5"}, ] django-extensions = [ - {file = "django-extensions-3.1.3.tar.gz", hash = "sha256:5f0fea7bf131ca303090352577a9e7f8bfbf5489bd9d9c8aea9401db28db34a0"}, - {file = "django_extensions-3.1.3-py3-none-any.whl", hash = "sha256:50de8977794a66a91575dd40f87d5053608f679561731845edbd325ceeb387e3"}, + {file = "django-extensions-3.1.5.tar.gz", hash = "sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a"}, + {file = "django_extensions-3.1.5-py3-none-any.whl", hash = "sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"}, ] django-simpleui = [ - {file = "django-simpleui-2021.3.tar.gz", hash = "sha256:6daa6717cc1b74bc3f00206bd4b45c51c79afa9e40a7190d198bce990b9cda96"}, + {file = "django-simpleui-2022.1.tar.gz", hash = "sha256:13eb7ee196e8858133f551acd100b00fa1e1e7b5697ea5768b65f1d764c375af"}, +] +django-stubs = [ + {file = "django-stubs-1.9.0.tar.gz", hash = "sha256:664843091636a917faf5256d028476559dc360fdef9050b6df87ab61b21607bf"}, + {file = "django_stubs-1.9.0-py3-none-any.whl", hash = "sha256:59c9f81af64d214b1954eaf90f037778c8d2b9c2de946a3cda177fefcf588fbd"}, +] +django-stubs-ext = [ + {file = "django-stubs-ext-0.3.1.tar.gz", hash = "sha256:783c198d7e39a41be0b90fd843fa2770243a642922af679be4b19e03b82c8c28"}, + {file = "django_stubs_ext-0.3.1-py3-none-any.whl", hash = "sha256:a51a3e9e844d4e1cacaaedbb33bf3def78a3956eed5d9575a640bd97ccd99cec"}, ] docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] eradicate = [ - {file = "eradicate-1.0.tar.gz", hash = "sha256:4ffda82aae6fd49dfffa777a857cb758d77502a1f2e0f54c9ac5155a39d2d01a"}, + {file = "eradicate-2.0.0.tar.gz", hash = "sha256:27434596f2c5314cc9b31410c93d8f7e8885747399773cd088d3adea647a60c8"}, ] flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, @@ -1331,23 +1477,24 @@ flake8-bandit = [ {file = "flake8_bandit-2.1.2.tar.gz", hash = "sha256:687fc8da2e4a239b206af2e54a90093572a60d0954f3054e23690739b0b0de3b"}, ] flake8-broken-line = [ - {file = "flake8-broken-line-0.2.1.tar.gz", hash = "sha256:414477070231a5aa05468d48db2742a594b53fbc1ecba28044646706a11fb861"}, - {file = "flake8_broken_line-0.2.1-py3-none-any.whl", hash = "sha256:75858359e3ccd4f1d92a9e7582aa5c9e4485cbc920dd05954703900cf907667e"}, + {file = "flake8-broken-line-0.3.0.tar.gz", hash = "sha256:f74e052833324a9e5f0055032f7ccc54b23faabafe5a26241c2f977e70b10b50"}, + {file = "flake8_broken_line-0.3.0-py3-none-any.whl", hash = "sha256:611f79c7f27118e7e5d3dc098ef7681c40aeadf23783700c5dbee840d2baf3af"}, ] flake8-bugbear = [ - {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, - {file = "flake8_bugbear-19.8.0-py35.py36.py37-none-any.whl", hash = "sha256:ded4d282778969b5ab5530ceba7aa1a9f1b86fa7618fc96a19a1d512331640f8"}, + {file = "flake8-bugbear-21.9.2.tar.gz", hash = "sha256:db9a09893a6c649a197f5350755100bb1dd84f110e60cf532fdfa07e41808ab2"}, + {file = "flake8_bugbear-21.9.2-py36.py37.py38-none-any.whl", hash = "sha256:4f7eaa6f05b7d7ea4cbbde93f7bcdc5438e79320fa1ec420d860c181af38b769"}, ] flake8-commas = [ - {file = "flake8-commas-2.0.0.tar.gz", hash = "sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7"}, - {file = "flake8_commas-2.0.0-py2.py3-none-any.whl", hash = "sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e"}, + {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, + {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, ] flake8-comprehensions = [ - {file = "flake8-comprehensions-3.5.0.tar.gz", hash = "sha256:f24be9032587127f7a5bc6d066bf755b6e66834f694383adb8a673e229c1f559"}, - {file = "flake8_comprehensions-3.5.0-py3-none-any.whl", hash = "sha256:b07aef3277623db32310aa241a1cec67212b53c1d18e767d7e26d4d83aa05bf7"}, + {file = "flake8-comprehensions-3.7.0.tar.gz", hash = "sha256:6b3218b2dde8ac5959c6476cde8f41a79e823c22feb656be2710cd2a3232cef9"}, + {file = "flake8_comprehensions-3.7.0-py3-none-any.whl", hash = "sha256:a5d7aea6315bbbd6fbcb2b4e80bff6a54d1600155e26236e555d0c6fe1d62522"}, ] flake8-debugger = [ - {file = "flake8-debugger-3.2.1.tar.gz", hash = "sha256:712d7c1ff69ddf3f0130e94cc88c2519e720760bce45e8c330bfdcb61ab4090d"}, + {file = "flake8-debugger-4.0.0.tar.gz", hash = "sha256:e43dc777f7db1481db473210101ec2df2bd39a45b149d7218a618e954177eda6"}, + {file = "flake8_debugger-4.0.0-py3-none-any.whl", hash = "sha256:82e64faa72e18d1bdd0000407502ebb8ecffa7bc027c62b9d4110ce27c091032"}, ] flake8-django = [ {file = "flake8-django-1.1.2.tar.gz", hash = "sha256:b4314abb5bacda450d2eae564a0604447111b1b98188e46bca41682ad2ab59d6"}, @@ -1358,33 +1505,30 @@ flake8-docstrings = [ {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, ] flake8-eradicate = [ - {file = "flake8-eradicate-0.3.0.tar.gz", hash = "sha256:d0b3d283d85079917acbfe39b9d637385cd82cba3ae3d76c1278c07ddcf0d9b9"}, - {file = "flake8_eradicate-0.3.0-py3-none-any.whl", hash = "sha256:e8b32b32300bfb407fe7ef74667c8d2d3a6a81bdf6f09c14a7bcc82b7b870f8b"}, + {file = "flake8-eradicate-1.1.0.tar.gz", hash = "sha256:f5917d6dbca352efcd10c15fdab9c55c48f0f26f6a8d47898b25d39101f170a8"}, + {file = "flake8_eradicate-1.1.0-py3-none-any.whl", hash = "sha256:d8e39b684a37c257a53cda817d86e2d96c9ba3450ddc292742623a5dfee04d9e"}, ] flake8-isort = [ - {file = "flake8-isort-3.0.1.tar.gz", hash = "sha256:5d976da513cc390232ad5a9bb54aee8a092466a15f442d91dfc525834bee727a"}, - {file = "flake8_isort-3.0.1-py2.py3-none-any.whl", hash = "sha256:df1dd6dd73f6a8b128c9c783356627231783cccc82c13c6dc343d1a5a491699b"}, + {file = "flake8-isort-4.1.1.tar.gz", hash = "sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717"}, + {file = "flake8_isort-4.1.1-py3-none-any.whl", hash = "sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949"}, ] flake8-polyfill = [ {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, ] flake8-quotes = [ - {file = "flake8-quotes-2.1.2.tar.gz", hash = "sha256:c844c9592940c8926c60f00bc620808912ff2acd34923ab5338f3a5ca618a331"}, + {file = "flake8-quotes-3.3.0.tar.gz", hash = "sha256:f1dd87830ed77ff2ce47fc0ee0fd87ae20e8f045355354ffbf4dcaa18d528217"}, ] flake8-rst-docstrings = [ - {file = "flake8-rst-docstrings-0.0.12.tar.gz", hash = "sha256:01d38327801781b26c3dfeb71ae37e5a02c5ca1b774a686f63feab8824ca6f9c"}, + {file = "flake8-rst-docstrings-0.2.3.tar.gz", hash = "sha256:3045794e1c8467fba33aaea5c246b8369efc9c44ef8b0b20199bb6df7a4bd47b"}, + {file = "flake8_rst_docstrings-0.2.3-py3-none-any.whl", hash = "sha256:565bbb391d7e4d0042924102221e9857ad72929cdd305b26501736ec22c1451a"}, ] flake8-string-format = [ - {file = "flake8-string-format-0.2.3.tar.gz", hash = "sha256:774d56103d9242ed968897455ef49b7d6de272000cfa83de5814273a868832f1"}, - {file = "flake8_string_format-0.2.3-py2.py3-none-any.whl", hash = "sha256:68ea72a1a5b75e7018cae44d14f32473c798cf73d75cbaed86c6a9a907b770b2"}, -] -flake8-type-annotations = [ - {file = "flake8-type-annotations-0.1.0.tar.gz", hash = "sha256:88775455792ad7bbd63a71bc94e8a077deb5608eacb5add7e5a7a648c7636426"}, - {file = "flake8_type_annotations-0.1.0-py3-none-any.whl", hash = "sha256:de64de5efef3277d7b6012e8618c37d35b21465fb16292e46e6eec5b87e47a8c"}, + {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, + {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, ] flake8-use-fstring = [ - {file = "flake8-use-fstring-1.1.tar.gz", hash = "sha256:a0eea849ffe33fb6903c210c243c0f418da86a530e46cb13e64f1bdb045f22dc"}, + {file = "flake8-use-fstring-1.3.tar.gz", hash = "sha256:1bd4a409adbb93e64e711fdd26b88759c33792e3899f174edc68ddf7307e81b6"}, ] freezegun = [ {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"}, @@ -1395,52 +1539,74 @@ gitdb = [ {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, ] gitpython = [ - {file = "GitPython-3.1.17-py3-none-any.whl", hash = "sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135"}, - {file = "GitPython-3.1.17.tar.gz", hash = "sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"}, + {file = "GitPython-3.1.24-py3-none-any.whl", hash = "sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647"}, + {file = "GitPython-3.1.24.tar.gz", hash = "sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"}, ] h11 = [ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, ] idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] ipython = [ - {file = "ipython-7.23.1-py3-none-any.whl", hash = "sha256:f78c6a3972dde1cc9e4041cbf4de583546314ba52d3c97208e5b6b2221a9cb7d"}, - {file = "ipython-7.23.1.tar.gz", hash = "sha256:714810a5c74f512b69d5f3b944c86e592cee0a5fb9c728e582f074610f6cf038"}, -] -ipython-genutils = [ - {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, - {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, + {file = "ipython-7.31.0-py3-none-any.whl", hash = "sha256:4c4234cdcc6b8f87c5b5c7af9899aa696ac5cfcf0e9f6d0688018bbee5c73bce"}, + {file = "ipython-7.31.0.tar.gz", hash = "sha256:346c74db7312c41fa566d3be45d2e759a528dcc2994fe48aac1a03a70cd668a3"}, ] isort = [ - {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, - {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, + {file = "isort-5.9.3-py3-none-any.whl", hash = "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"}, + {file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"}, ] jedi = [ {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, ] kombu = [ - {file = "kombu-5.0.2-py2.py3-none-any.whl", hash = "sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006"}, - {file = "kombu-5.0.2.tar.gz", hash = "sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c"}, + {file = "kombu-5.2.3-py3-none-any.whl", hash = "sha256:eeaeb8024f3a5cfc71c9250e45cddb8493f269d74ada2f74909a93c59c4b4179"}, + {file = "kombu-5.2.3.tar.gz", hash = "sha256:81a90c1de97e08d3db37dbf163eaaf667445e1068c98bfd89f051a40e9f6dbbd"}, ] matplotlib-inline = [ - {file = "matplotlib-inline-0.1.2.tar.gz", hash = "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"}, - {file = "matplotlib_inline-0.1.2-py3-none-any.whl", hash = "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811"}, + {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, + {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +mypy = [ + {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, + {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, + {file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"}, + {file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"}, + {file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"}, + {file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"}, + {file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"}, + {file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"}, + {file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"}, + {file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"}, + {file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"}, + {file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"}, + {file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"}, + {file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"}, + {file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"}, + {file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"}, + {file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"}, + {file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"}, + {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, + {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] packaging = [ - {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, - {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, + {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, + {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, ] parso = [ {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, @@ -1451,8 +1617,8 @@ pbr = [ {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, ] pep8-naming = [ - {file = "pep8-naming-0.9.1.tar.gz", hash = "sha256:a33d38177056321a167decd6ba70b890856ba5025f0a8eca6a3eda607da93caf"}, - {file = "pep8_naming-0.9.1-py2.py3-none-any.whl", hash = "sha256:45f330db8fcfb0fba57458c77385e288e7a3be1d01e8ea4268263ef677ceea5f"}, + {file = "pep8-naming-0.11.1.tar.gz", hash = "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724"}, + {file = "pep8_naming-0.11.1-py2.py3-none-any.whl", hash = "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"}, ] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, @@ -1463,49 +1629,70 @@ pickleshare = [ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] pluggy = [ - {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, - {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"}, - {file = "prompt_toolkit-3.0.18.tar.gz", hash = "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"}, + {file = "prompt_toolkit-3.0.20-py3-none-any.whl", hash = "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c"}, + {file = "prompt_toolkit-3.0.20.tar.gz", hash = "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c"}, ] psycopg2-binary = [ - {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c"}, - {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1"}, - {file = "psycopg2_binary-2.8.6-cp34-cp34m-win32.whl", hash = "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2"}, - {file = "psycopg2_binary-2.8.6-cp34-cp34m-win_amd64.whl", hash = "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152"}, - {file = "psycopg2_binary-2.8.6-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449"}, - {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859"}, - {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550"}, - {file = "psycopg2_binary-2.8.6-cp35-cp35m-win32.whl", hash = "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd"}, - {file = "psycopg2_binary-2.8.6-cp35-cp35m-win_amd64.whl", hash = "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71"}, - {file = "psycopg2_binary-2.8.6-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4"}, - {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb"}, - {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da"}, - {file = "psycopg2_binary-2.8.6-cp36-cp36m-win32.whl", hash = "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2"}, - {file = "psycopg2_binary-2.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a"}, - {file = "psycopg2_binary-2.8.6-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679"}, - {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf"}, - {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b"}, - {file = "psycopg2_binary-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67"}, - {file = "psycopg2_binary-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66"}, - {file = "psycopg2_binary-2.8.6-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f"}, - {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77"}, - {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94"}, - {file = "psycopg2_binary-2.8.6-cp38-cp38-win32.whl", hash = "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729"}, - {file = "psycopg2_binary-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77"}, - {file = "psycopg2_binary-2.8.6-cp39-cp39-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83"}, - {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52"}, - {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd"}, - {file = "psycopg2_binary-2.8.6-cp39-cp39-win32.whl", hash = "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056"}, - {file = "psycopg2_binary-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6"}, + {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"}, ] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, @@ -1528,79 +1715,136 @@ pyflakes = [ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ - {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, - {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, + {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, + {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, - {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] pytest-cov = [ - {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, - {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, ] pytest-django = [ - {file = "pytest-django-4.1.0.tar.gz", hash = "sha256:26f02c16d36fd4c8672390deebe3413678d89f30720c16efb8b2a6bf63b9041f"}, - {file = "pytest_django-4.1.0-py3-none-any.whl", hash = "sha256:10e384e6b8912ded92db64c58be8139d9ae23fb8361e5fc139d8e4f8fc601bc2"}, + {file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, + {file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, ] pytest-freezegun = [ {file = "pytest-freezegun-0.4.2.zip", hash = "sha256:19c82d5633751bf3ec92caa481fb5cffaac1787bd485f0df6436fd6242176949"}, {file = "pytest_freezegun-0.4.2-py2.py3-none-any.whl", hash = "sha256:5318a6bfb8ba4b709c8471c94d0033113877b3ee02da5bfcd917c1889cde99a7"}, ] +pytest-mypy-plugins = [ + {file = "pytest-mypy-plugins-1.9.2.tar.gz", hash = "sha256:26528ab66549ade53badae2ab396f426379f921bf5ddd91534c669a25b4b7e69"}, + {file = "pytest_mypy_plugins-1.9.2-py3-none-any.whl", hash = "sha256:1985d27d84c92c14b1604eda2adf2d1c9d0c050633fd48183d661eb5deac23ee"}, +] python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] pyyaml = [ - {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, - {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, - {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, - {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, - {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, - {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, - {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, - {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, - {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, - {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, - {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, - {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, - {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] redis = [ {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"}, {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"}, ] +regex = [ + {file = "regex-2021.10.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:094a905e87a4171508c2a0e10217795f83c636ccc05ddf86e7272c26e14056ae"}, + {file = "regex-2021.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981c786293a3115bc14c103086ae54e5ee50ca57f4c02ce7cf1b60318d1e8072"}, + {file = "regex-2021.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b0f2f874c6a157c91708ac352470cb3bef8e8814f5325e3c5c7a0533064c6a24"}, + {file = "regex-2021.10.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51feefd58ac38eb91a21921b047da8644155e5678e9066af7bcb30ee0dca7361"}, + {file = "regex-2021.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea8de658d7db5987b11097445f2b1f134400e2232cb40e614e5f7b6f5428710e"}, + {file = "regex-2021.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1ce02f420a7ec3b2480fe6746d756530f69769292eca363218c2291d0b116a01"}, + {file = "regex-2021.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39079ebf54156be6e6902f5c70c078f453350616cfe7bfd2dd15bdb3eac20ccc"}, + {file = "regex-2021.10.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ff24897f6b2001c38a805d53b6ae72267025878d35ea225aa24675fbff2dba7f"}, + {file = "regex-2021.10.8-cp310-cp310-win32.whl", hash = "sha256:c6569ba7b948c3d61d27f04e2b08ebee24fec9ff8e9ea154d8d1e975b175bfa7"}, + {file = "regex-2021.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:45cb0f7ff782ef51bc79e227a87e4e8f24bc68192f8de4f18aae60b1d60bc152"}, + {file = "regex-2021.10.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fab3ab8aedfb443abb36729410403f0fe7f60ad860c19a979d47fb3eb98ef820"}, + {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74e55f8d66f1b41d44bc44c891bcf2c7fad252f8f323ee86fba99d71fd1ad5e3"}, + {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d52c5e089edbdb6083391faffbe70329b804652a53c2fdca3533e99ab0580d9"}, + {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1abbd95cbe9e2467cac65c77b6abd9223df717c7ae91a628502de67c73bf6838"}, + {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9b5c215f3870aa9b011c00daeb7be7e1ae4ecd628e9beb6d7e6107e07d81287"}, + {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f540f153c4f5617bc4ba6433534f8916d96366a08797cbbe4132c37b70403e92"}, + {file = "regex-2021.10.8-cp36-cp36m-win32.whl", hash = "sha256:1f51926db492440e66c89cd2be042f2396cf91e5b05383acd7372b8cb7da373f"}, + {file = "regex-2021.10.8-cp36-cp36m-win_amd64.whl", hash = "sha256:5f55c4804797ef7381518e683249310f7f9646da271b71cb6b3552416c7894ee"}, + {file = "regex-2021.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb2baff66b7d2267e07ef71e17d01283b55b3cc51a81b54cc385e721ae172ba4"}, + {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e527ab1c4c7cf2643d93406c04e1d289a9d12966529381ce8163c4d2abe4faf"}, + {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c98b013273e9da5790ff6002ab326e3f81072b4616fd95f06c8fa733d2745f"}, + {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55ef044899706c10bc0aa052f2fc2e58551e2510694d6aae13f37c50f3f6ff61"}, + {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0ab3530a279a3b7f50f852f1bab41bc304f098350b03e30a3876b7dd89840e"}, + {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a37305eb3199d8f0d8125ec2fb143ba94ff6d6d92554c4b8d4a8435795a6eccd"}, + {file = "regex-2021.10.8-cp37-cp37m-win32.whl", hash = "sha256:2efd47704bbb016136fe34dfb74c805b1ef5c7313aef3ce6dcb5ff844299f432"}, + {file = "regex-2021.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:924079d5590979c0e961681507eb1773a142553564ccae18d36f1de7324e71ca"}, + {file = "regex-2021.10.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19b8f6d23b2dc93e8e1e7e288d3010e58fafed323474cf7f27ab9451635136d9"}, + {file = "regex-2021.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b09d3904bf312d11308d9a2867427479d277365b1617e48ad09696fa7dfcdf59"}, + {file = "regex-2021.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:951be934dc25d8779d92b530e922de44dda3c82a509cdb5d619f3a0b1491fafa"}, + {file = "regex-2021.10.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f125fce0a0ae4fd5c3388d369d7a7d78f185f904c90dd235f7ecf8fe13fa741"}, + {file = "regex-2021.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f199419a81c1016e0560c39773c12f0bd924c37715bffc64b97140d2c314354"}, + {file = "regex-2021.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:09e1031e2059abd91177c302da392a7b6859ceda038be9e015b522a182c89e4f"}, + {file = "regex-2021.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c070d5895ac6aeb665bd3cd79f673775caf8d33a0b569e98ac434617ecea57d"}, + {file = "regex-2021.10.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:176796cb7f82a7098b0c436d6daac82f57b9101bb17b8e8119c36eecf06a60a3"}, + {file = "regex-2021.10.8-cp38-cp38-win32.whl", hash = "sha256:5e5796d2f36d3c48875514c5cd9e4325a1ca172fc6c78b469faa8ddd3d770593"}, + {file = "regex-2021.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:e4204708fa116dd03436a337e8e84261bc8051d058221ec63535c9403a1582a1"}, + {file = "regex-2021.10.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6dcf53d35850ce938b4f044a43b33015ebde292840cef3af2c8eb4c860730fff"}, + {file = "regex-2021.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8b6ee6555b6fbae578f1468b3f685cdfe7940a65675611365a7ea1f8d724991"}, + {file = "regex-2021.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e2ec1c106d3f754444abf63b31e5c4f9b5d272272a491fa4320475aba9e8157c"}, + {file = "regex-2021.10.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973499dac63625a5ef9dfa4c791aa33a502ddb7615d992bdc89cf2cc2285daa3"}, + {file = "regex-2021.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88dc3c1acd3f0ecfde5f95c32fcb9beda709dbdf5012acdcf66acbc4794468eb"}, + {file = "regex-2021.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4786dae85c1f0624ac77cb3813ed99267c9adb72e59fdc7297e1cf4d6036d493"}, + {file = "regex-2021.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe6ce4f3d3c48f9f402da1ceb571548133d3322003ce01b20d960a82251695d2"}, + {file = "regex-2021.10.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9e3e2cea8f1993f476a6833ef157f5d9e8c75a59a8d8b0395a9a6887a097243b"}, + {file = "regex-2021.10.8-cp39-cp39-win32.whl", hash = "sha256:82cfb97a36b1a53de32b642482c6c46b6ce80803854445e19bc49993655ebf3b"}, + {file = "regex-2021.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:b04e512eb628ea82ed86eb31c0f7fc6842b46bf2601b66b1356a7008327f7700"}, + {file = "regex-2021.10.8.tar.gz", hash = "sha256:26895d7c9bbda5c52b3635ce5991caa90fbb1ddfac9c9ff1c7ce505e2282fb2a"}, +] requests = [ - {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, - {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, ] restrictedpython = [ - {file = "RestrictedPython-5.1-py2.py3-none-any.whl", hash = "sha256:bd0eb3b2188642926570fa7ec725cac5660f69c4f7aa66d6e869525e189a3196"}, - {file = "RestrictedPython-5.1.tar.gz", hash = "sha256:9ae16e500782b41bd1abefd8554ccb26330817bba9ce090d385aa226f1ca83e8"}, + {file = "RestrictedPython-5.2-py2.py3-none-any.whl", hash = "sha256:fdf8621034c5dcb990a2a198f232f66b2d48866dd16d848e00ac7d187ae452ba"}, + {file = "RestrictedPython-5.2.tar.gz", hash = "sha256:634da1f6c5c122a262f433b083ee3d17a9a039f8f1b3778597efb47461cd361b"}, ] restructuredtext-lint = [ {file = "restructuredtext_lint-1.3.2.tar.gz", hash = "sha256:d3b10a1fe2ecac537e51ae6d151b223b78de9fafdd50e5eb6b08c243df173c80"}, @@ -1618,37 +1862,57 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sqlparse = [ - {file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"}, - {file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"}, + {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, + {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, ] stevedore = [ - {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, - {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"}, + {file = "stevedore-3.4.0-py3-none-any.whl", hash = "sha256:920ce6259f0b2498aaa4545989536a27e4e4607b8318802d7ddc3a533d3d069e"}, + {file = "stevedore-3.4.0.tar.gz", hash = "sha256:59b58edb7f57b11897f150475e7bc0c39c5381f0b8e3fa9f5c20ce6c89ec4aa1"}, ] testfixtures = [ - {file = "testfixtures-6.17.1-py2.py3-none-any.whl", hash = "sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137"}, - {file = "testfixtures-6.17.1.tar.gz", hash = "sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda"}, + {file = "testfixtures-6.18.3-py2.py3-none-any.whl", hash = "sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763"}, + {file = "testfixtures-6.18.3.tar.gz", hash = "sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] +tomli = [ + {file = "tomli-1.2.1-py3-none-any.whl", hash = "sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f"}, + {file = "tomli-1.2.1.tar.gz", hash = "sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442"}, +] traitlets = [ - {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, - {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, + {file = "traitlets-5.1.0-py3-none-any.whl", hash = "sha256:03f172516916220b58c9f19d7f854734136dd9528103d04e9bf139a92c9f54c4"}, + {file = "traitlets-5.1.0.tar.gz", hash = "sha256:bd382d7ea181fbbcce157c133db9a829ce06edffe097bcf3ab945b435452b46d"}, +] +types-pytz = [ + {file = "types-pytz-2021.3.0.tar.gz", hash = "sha256:86a61967834dceeaaf98b6902ed8357efdd262bb8afcaf4bc8ccecf748592778"}, + {file = "types_pytz-2021.3.0-py3-none-any.whl", hash = "sha256:b5027e5de50a4c978cd60ca16849d934d44c44ebd7d29cf13ada009efaa9feef"}, +] +types-pyyaml = [ + {file = "types-PyYAML-5.4.12.tar.gz", hash = "sha256:3f4daa754357491625ae8c3a39c9e1b0d7cd5632bc4e1c35e7a7f75a64aa124b"}, + {file = "types_PyYAML-5.4.12-py3-none-any.whl", hash = "sha256:e06083f85375a5678e4c19452ed6467ce2167b71db222313e1792cb8fc76859a"}, +] +types-requests = [ + {file = "types-requests-2.27.5.tar.gz", hash = "sha256:a67dc1a8512312b8cb89f3ba95f9a0e69ef3436ae77c9bd4f328cd88f17adda2"}, + {file = "types_requests-2.27.5-py3-none-any.whl", hash = "sha256:9c9390b18b222956155af6678570f452edafa3bb94bd5a4efe67da1105aa128c"}, +] +types-urllib3 = [ + {file = "types-urllib3-1.26.2.tar.gz", hash = "sha256:fab03aa961be4d43290b13362a9f9801e2025ada33cbfbfaa9146efea6ab9496"}, + {file = "types_urllib3-1.26.2-py3-none-any.whl", hash = "sha256:c21997c160dcdfb3018a7b28e31df88d326d30485833fadd79ad3d00c61e5108"}, ] typing-extensions = [ - {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, - {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, - {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, + {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, + {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, + {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, ] urllib3 = [ - {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, - {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, + {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, + {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, ] uvicorn = [ - {file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"}, - {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, + {file = "uvicorn-0.16.0-py3-none-any.whl", hash = "sha256:d8c839231f270adaa6d338d525e2652a0b4a5f4c2430b5c4ef6ae4d11776b0d2"}, + {file = "uvicorn-0.16.0.tar.gz", hash = "sha256:eacb66afa65e0648fcbce5e746b135d09722231ffffc61883d4fac2b62fbea8d"}, ] vine = [ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, @@ -1659,6 +1923,6 @@ wcwidth = [ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] wemake-python-styleguide = [ - {file = "wemake-python-styleguide-0.14.1.tar.gz", hash = "sha256:e13dc580fa56b7b548de8da170bccb8ddff2d4ab026ca987db8a9893bf8a7b5b"}, - {file = "wemake_python_styleguide-0.14.1-py3-none-any.whl", hash = "sha256:73a501e0547275287a2b926515c000cc25026a8bceb9dcc1bf73ef85a223a3c6"}, + {file = "wemake-python-styleguide-0.16.0.tar.gz", hash = "sha256:3bf0a4962404e6fd6fa479e72e2ba3fb75d5920ea6c44b72b45240c9e519543c"}, + {file = "wemake_python_styleguide-0.16.0-py3-none-any.whl", hash = "sha256:8caa92b4aa77b08a505d718553238812d1b612b1036bc171ca3aa18345efe0b4"}, ] diff --git a/pyproject.toml b/pyproject.toml index 7dd5e1d..a22b272 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "kesha" -version = "1.2.0" +version = "2.0.0" description = "Service for testing http requests and webhooks." authors = ["Evgeniy "] maintainers = ["Evgeniy Mitin "] @@ -19,30 +19,41 @@ classifiers = [ "Topic :: Software Development :: Testing" ] +[tool.mypy] +plugins = ["mypy_django_plugin.main"] + +[tool.django-stubs] +django_settings_module = "kesha.settings" + [tool.poetry.dependencies] python = "~3.8" django = "~3.1" -django-simpleui = "~2021.3" -uvicorn = "~0.13.4" -psycopg2-binary = "~2.8.6" -django-environ = "^0.4.5" -django-extensions = "^3.1.1" -django-debug-toolbar = "^3.1.1" -celery = {extras = ["redis"], version = "^5.0.4"} -RestrictedPython = "~5.1" -requests = "^2.25.1" +django-simpleui = "~2022.1" +uvicorn = "~0.16.0" +psycopg2-binary = "~2.9.3" +django-environ = "^0.8.1" +django-extensions = "^3.1.5" +django-debug-toolbar = "^3.2.4" +celery = {extras = ["redis"], version = "^5.2.2"} +RestrictedPython = "~5.2" +requests = "^2.27.1" [tool.poetry.dev-dependencies] pytest = "~6.2" -pytest-django = "~4.1.0" -pytest-cov = "~2.11.1" +pytest-django = "~4.5.2" +pytest-cov = "~3.0.0" pytest-freezegun = "^0.4.2" -wemake-python-styleguide = "~0.14.1" -flake8-use-fstring = "~1.1" +wemake-python-styleguide = "~0.16.0" +flake8-use-fstring = "~1.3" flake8-django = "~1.1.1" -flake8-type-annotations = "~0.1.0" flake8-annotations-coverage = "~0.0.5" -ipython = "^7.21.0" +ipython = "^7.31.0" +mypy = "^0.931" +django-stubs = "^1.9.0" +celery-types = "^0.10.0" +django-stubs-ext = "^0.3.1" +pytest-mypy-plugins = "^1.9.2" +types-requests = "^2.27.5" [build-system] requires = ["poetry>=1.0.5"] diff --git a/static/kesha_icon.png b/static/kesha_icon.png index 4c4fe26..752ea10 100644 Binary files a/static/kesha_icon.png and b/static/kesha_icon.png differ diff --git a/static/kesha_icon.svg b/static/kesha_icon.svg index 141f270..0b7ba30 100644 --- a/static/kesha_icon.svg +++ b/static/kesha_icon.svg @@ -1,1354 +1,85 @@ - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% elif field.field.name == 'pretty_body' %} + {% elif field.field.name in 'pretty_request_body,pretty_response_body' %}
{{ field.contents }}