Compare commits

...

85 Commits

Author SHA1 Message Date
Taoufik d820f0277f Merge pull request #387 from taoufik07/v-1.3.2
Bump version and update CHANGELOG
2019-08-15 22:57:21 +02:00
taoufik 7219856177 Bump version and update CHANGELOG 2019-08-15 22:51:28 +02:00
Taoufik e6d302aabb Merge pull request #385 from FirstKlaas/state-for-request
Added a state property to the request object.
2019-08-07 19:28:13 +02:00
FirstKlaas d73243ab60 Linting with black 2019-08-07 18:44:43 +02:00
FirstKlaas 8101e7d7b0 Added a state property to the request opbject. 2019-08-07 18:29:03 +02:00
Taoufik 784c7e72ae Merge pull request #381 from taoufik07/ci-py-3.8-dev
Travis-ci add python 3.8-dev
2019-08-05 20:08:44 +02:00
taoufik 93156fd2f1 Travis-ci add python 3.8-dev 2019-08-05 20:00:14 +02:00
Taoufik e4f6898498 Merge pull request #380 from taoufik07/refactor-changelog
Refactor CHANGELOG following the keepachangelog format
2019-08-05 19:59:00 +02:00
taoufik ef330135f9 Refactor CHANGELOG following the keepachangelog format
- Format following https://keepachangelog.com/en/1.0.0/
- Refactor CHANGELOGS
- Add missing changes
2019-08-05 19:56:01 +02:00
Taoufik 54cbbdface Merge pull request #379 from taoufik07/asgi3
ASGI 3 support
2019-08-05 19:38:46 +02:00
taoufik f3c9320837 ASGI 3 support 2019-08-05 15:33:56 +02:00
Taoufik 0529629ac8 Merge pull request #378 from ucpr/add_formats_test
add test for #367
2019-07-29 23:00:50 +02:00
Taoufik 22af42ead6 Merge pull request #377 from ucpr/fix_readme
fix links
2019-07-29 22:59:40 +02:00
ucpr bd2efb68e1 test: add test for #367 2019-07-30 00:26:01 +09:00
ucpr 37ba3d2efc fix: fix links 2019-07-29 23:24:57 +09:00
kennethreitz ed8afeaa87 Merge pull request #367 from ucpr/fix_format_form
format data when mimetype is form-data
2019-07-16 11:05:57 -04:00
kennethreitz ee6efe5aa4 Merge pull request #372 from mlschneid/master
Encouraging use of stable
2019-07-16 11:05:07 -04:00
Mike Schneider 3a8113d8b0 Found more --pre flags 2019-07-16 07:43:55 -07:00
Mike Schneider 7afce42943 Encouraging use of stable 2019-07-16 07:34:35 -07:00
Frost Ming 67a6c25256 Merge pull request #370 from waghanza/remove_pipenv_requires
Specify constraint for python version in pipenv
2019-07-09 17:15:53 +08:00
Marwan Rabbâa 38dea8311c refactor(pip): Remove pipenv restriction on python version 2019-07-09 11:09:29 +02:00
Marwan Rabbâa 555e1f7924 refactor(pip): Specify constraint for python version in pipenv 2019-07-08 18:35:42 +02:00
Taoufik 8b87f63609 Merge pull request #369 from waghanza/py_37
Add python3.7 on CI
2019-07-08 13:56:46 +02:00
Marwan Rabbâa f01b1d493f feat(ci): Test under python 3.7 2019-07-08 09:26:34 +02:00
ucpr a802245bf0 format data when mimetype is form-data 2019-07-01 05:58:43 +09:00
kennethreitz 6dbbad158a Merge pull request #357 from amikrop/master
Remove extra `static_url` function in `API.docs`
2019-06-25 09:17:14 -07:00
kennethreitz 877fe144b4 Merge pull request #365 from tedder/ted/add_links_2
add some links to the main page
2019-06-25 09:16:47 -07:00
ted 70e6bc0466 add some links to the main page
Basically linked things I had to google/look up on my own. There are a million things that _could_ be linked, but using myself as a median Python coder, it seems about right. Removed italics because I can't figure out how to use them with a link.
2019-06-25 08:21:07 -07:00
Taoufik 63f2e833eb Merge pull request #362 from mtcronin99/patch-1
Update tour.rst to add quotes around hostnames
2019-06-04 17:29:35 +02:00
mtcronin99 fb71abe534 Update tour.rst to add quotes around hostnames
The API call in the Trusted Hosts section needs quotes around the hostnames in the example.
2019-06-04 11:21:27 -04:00
Taoufik 8ccb39560e Merge pull request #359 from EdwardBetts/patch-1
Correct a spelling mistake
2019-05-11 22:24:16 +02:00
Edward Betts e6b880be62 Correct a spelling mistake 2019-05-11 20:17:10 +01:00
kennethreitz d0016ac7c9 Update README.md 2019-05-06 07:42:31 -04:00
Aristotelis Mikropoulos 05035e0171 Remove extra static_url function in API.docs 2019-05-02 17:09:34 +03:00
Taoufik 78b5bef879 Merge pull request #354 from kennethreitz/starlette-new-lifespan
Starlette 0.11.* and cleanup
2019-04-29 23:14:27 +02:00
taoufik a6955b5db5 Fix lifespan and bump starlette to 0.11.* 2019-04-29 23:09:48 +02:00
taoufik a1a0a1b71e Remove duplicated code and rename _dispatch_request 2019-04-29 22:44:44 +02:00
Taoufik 0bdde6d5fe Merge pull request #353 from kennethreitz/taoufik07-patch-1
Typo schema docs
2019-04-28 16:02:29 +02:00
Taoufik cf5447d5bd Typo schema docs 2019-04-28 15:59:15 +02:00
taoufik b2dd2c205d Fix tests 2019-04-28 15:48:36 +02:00
kennethreitz e52c9277c8 fix lockfile 2019-04-28 09:43:29 -04:00
kennethreitz 712ec2410d changes 2019-04-28 09:39:12 -04:00
kennethreitz dea2ca41d2 Merge pull request #351 from kennethreitz/route-params-docs
Add docs for route params convertors
2019-04-28 09:19:42 -04:00
Taoufik ca0f32c02b Black check before tests 2019-04-27 20:44:02 +02:00
Taoufik f21b296fba Add docs for route params convertors 2019-04-27 20:10:31 +02:00
Taoufik 3224479b99 Merge pull request #350 from kennethreitz/cleanup
Cleanup
2019-04-27 13:52:48 +02:00
Taoufik f95950eedc Cleanup 2019-04-27 13:49:54 +02:00
kennethreitz 4467376d0a Merge pull request #333 from taoufik07/custom_specifiers
Route params convertors
2019-04-27 07:32:13 -04:00
kennethreitz ac65dc5361 Merge pull request #347 from s-pace/doc/add_search
[doc website] Add a nice search experience
2019-04-27 07:25:38 -04:00
s-pace 4957793c80 feat: add search to every documentation pages 2019-04-22 22:23:07 +02:00
s-pace ff7f4b502d feat: add search to the main introduction page 2019-04-22 22:22:46 +02:00
Timo Furrer 816cb7188b Merge pull request #339 from jonbeebe/master
Removed asgiref dependency
2019-03-29 18:34:14 +01:00
Jonathan Beebe 6456d435eb Revert Pipfile.lock (with asgiref removed) 2019-03-28 20:36:11 -07:00
Jonathan Beebe 63e338ed6f Removed asgiref dependency
Replaced WsgiToAsgi (asgiref.wsgi) usage with WSGIMiddleware (starlette.middleware.wsgi) to fix breaking changes with asgiref 3.0.0.
2019-03-28 20:12:22 -07:00
Timo Furrer 00211c8f03 Merge pull request #335 from kobayashi/master
add sample of uploading a file
2019-03-21 21:30:14 +01:00
kobayashi ebed9fe3aa fix typos 2019-03-17 19:05:14 -04:00
kobayashi 734b5e7303 add sample of uploading a file 2019-03-17 15:21:27 -04:00
Taoufik 1696d501e2 Merge pull request #334 from MerleLiuKun/fix_test_responder_variable
fix variable error at test responder
2019-03-14 08:19:53 +00:00
ikronskun e65d2f8c50 fix variable error at test responder 2019-03-14 14:25:45 +08:00
taoufik07 9ea705b2ea Specifiers test 2019-03-12 17:39:53 +01:00
taoufik07 5a5a811dca Add routes specifiers 2019-03-12 16:58:36 +01:00
kennethreitz df7b9419c2 Merge pull request #332 from jlewis91/patch-1
Update tour.rst to be openapi compliant
2019-03-10 10:55:38 -04:00
Jeremiah 37318f1106 Update tour.rst 2019-03-10 14:53:44 +01:00
kennethreitz 19e9f6ac5d Merge pull request #320 from taoufik07/static_serve
Fix #303
2019-03-05 08:33:39 -05:00
kennethreitz 658b51a449 Merge pull request #321 from taoufik07/revert_before_requests
Revert before_requests
2019-03-05 08:33:25 -05:00
Parth Shandilya 485303c0f2 Merge pull request #314 from vlcinsky/fix_uvloop_env_marker
Fix #313 incomplete environment marker for uvloop
2019-03-03 23:22:53 +05:30
taoufik07 885d902b7d Revert 2019-02-26 23:46:07 +01:00
taoufik07 a35f02fb64 Add tests 2019-02-26 22:23:43 +01:00
taoufik07 28d1f16ad5 Disable serving when static_dir is None and handle templates_dir 2019-02-26 22:10:13 +01:00
Taoufik a04d7c3a9a Merge pull request #319 from taoufik07/refactor_before_ws
Refactor before_requests and websockets
2019-02-26 19:27:26 +00:00
taoufik07 b876f8484c Update docs 2019-02-26 17:01:13 +01:00
taoufik07 854c6d3d65 Add @before_request 2019-02-26 16:44:12 +01:00
taoufik07 f9a850a8fe Add before_requets for ws and refactor 2019-02-26 15:50:55 +01:00
taoufik07 e808662fe7 Refactor websockets 2019-02-26 15:27:26 +01:00
Taoufik 7bbb02126e Merge pull request #316 from tkamenoko/patch-4
fix typo
2019-02-23 15:15:31 +00:00
Taoufik aa101059a7 Merge pull request #315 from taoufik07/websockets_tests
Websockets tests
2019-02-23 15:11:23 +00:00
T.Kameyama d1f7fe02e4 fix typo 2019-02-24 00:10:47 +09:00
taoufik07 3e26dc1373 Add websockets tests 2019-02-23 16:00:22 +01:00
Jan Vlčinský 0a9d819555 Merge branch 'master' into fix_uvloop_env_marker 2019-02-22 20:47:25 +01:00
Jan Vlcinsky b31dfeefb7 Fix #313 incomplete environment marker for uvloop 2019-02-22 20:44:32 +01:00
Taoufik fc640ec331 Merge pull request #312 from iancleary/bug/242_docs_consistency
fixed flow between OpenAPI and Interactive Docs sections
2019-02-22 13:33:37 +00:00
iancleary 3382723457 fixed flow between OpenAPI and Interactive Docs sections 2019-02-22 06:16:45 -07:00
Taoufik 1fc0722ad6 Merge pull request #310 from taoufik07/fix/req_text
DEFAULT_ENCODING if none is detected
2019-02-22 11:57:11 +00:00
taoufik07 b21e308357 Add tests 2019-02-22 12:44:32 +01:00
taoufik07 738105314b Return DEFAULT_ENCODING if none and remove redundant code 2019-02-22 12:34:22 +01:00
19 changed files with 945 additions and 492 deletions
+10 -5
View File
@@ -1,13 +1,18 @@
# travis use trusty by default
dist: xenial
language: python
python:
- "3.6"
- 3.6
- 3.7
- "3.8-dev"
# command to install dependencies
install:
- "pip install pipenv --upgrade-strategy=only-if-needed"
- "pipenv install --dev"
- pip install pipenv --upgrade-strategy=only-if-needed
- pipenv install --dev
# command to run the dependencies
script:
- "pytest"
- "black responder tests setup.py --check"
- black responder tests setup.py --check
- pytest
+181 -46
View File
@@ -1,115 +1,250 @@
# v1.3.0
# Changelog
All notable changes to this project will be documented in this file.
## Fixed
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [v1.3.2] - 2019-08-15
### Added
- ASGI 3 support
- CI tests for python 3.8-dev
- Now requests have `state` a mapping object
### Deprecated
- ASGI 2
## [v1.3.1] - 2019-04-28
### Added
- Route params Converters
- Add search for documentation pages
### Changed
- Bump dependencies
## [v1.3.0] - 2019-02-22
### Fixed
- Versioning issue
- Multiple cookies.
- Whitenoise returns not found.
- Other bugfixes.
## Added
### Added
- Stream support via `resp.stream`.
- Cookie directives via `resp.set_cookie`.
- Add `resp.html` to send HTML.
- Other improvements.
# v1.1.1
## [v1.1.3] - 2019-01-12
### Changed
- Refactor `_route_for`
### Fixed
- Resolve startup/shutdwown events
## [v1.2.0] - 2018-12-29
### Added
- Documentations
### Changed
- Use Starlette's LifeSpan middleware
- Update denpendencies
### Fixed
- Fix route.is_class_based
- Fix test_500
- Typos
## [v1.1.2] - 2018-11-11
### Fixed
- Minor fixes for Open API
- Typos
## [v1.1.1] - 2018-10-29
### Changed
- Run sync views in a threadpoolexecutor.
# v1.1.0
## [v1.1.0] - 2018-10-27
### Added
- Support for `before_request`.
# v1.0.4
## [v1.0.5]- 2018-10-27
### Fixed
- Fix sessions.
## [v1.0.4] - 2018-10-27
### Fixed
- Potential bufix for cookies.
# v1.0.3
## [v1.0.3] - 2018-10-27
### Fixed
- Bugfix for redirects.
# v1.0.2
## [v1.0.2] - 2018-10-27
### Changed
- Improvement for static file hosting.
# v1.0.1
## [v1.0.1] - 2018-10-26
### Changed
- Improve cors configuration settings.
# v1.0.0
## [v1.0.0] - 2018-10-26
### Changed
- Move GraphQL support into a built-in plugin.
# v0.3.3
- Improved exceptions.
- CORS support.
## [v0.3.3] - 2018-10-25
### Added
- CORS support
# v0.3.2
### Changed
- Improved exceptions.
## [v0.3.2] - 2018-10-25
### Changed
- Subtle improvements.
# v0.3.1
## [v0.3.1] - 2018-10-24
### Fixed
- Packaging fix.
# v0.3.0
## [v0.3.0] - 2018-10-24
### Changed
- Interactive Documentation endpoint.
- Minor improvements.
# v0.2.3
## [v0.2.3] - 2018-10-24
### Changed
- Overall improvements.
# v0.2.2
## [v0.2.2] - 2018-10-23
### Added
- Show traceback info when background tasks raise exceptions.
# v0.2.1
## [v0.2.1] - 2018-10-23
### Added
- api.requests.
# v0.2.0
## [v0.2.0] - 2018-10-22
### Added
- WebSocket support.
# v0.1.6
## [v0.1.6] - 2018-10-20
### Added
- 500 support.
# v0.1.5
- Improvements to sequential media reading.
- File upload support.
## [v0.1.5] - 2018-10-20
### Added
- File upload support
# v0.1.4
### Changed
- Improvements to sequential media reading.
## [v0.1.4] - 2018-10-19
### Fixed
- Stability.
# v0.1.3
## [v0.1.3] - 2018-10-18
### Added
- Sessions support.
# v0.1.2
## [v0.1.2] - 2018-10-18
### Added
- Cookies support.
# v0.1.1
## [v0.1.1] - 2018-10-17
### Changed
- Default routes.
# v0.1.0
## [v0.1.0] - 2018-10-17
### Added
- Prototype of static application support.
# v0.0.10
## [v0.0.10] - 2018-10-17
### Fixed
- Bugfix for async class-based views.
# v0.0.9
## [v0.0.9] - 2018-10-17
### Fixed
- Bugfix for async class-based views.
# v0.0.8
## [v0.0.8] - 2018-10-17
### Added
- GraphiQL Support.
### Changed
- Improvement to route selection.
# v0.0.7
- Immutable Request object.
## [v0.0.7] - 2018-10-16
### Changed
- Immutable Request object.
# v0.0.6:
- Ability to mount WSGI apps.
- Supply content-type when serving up the schema.
## [v0.0.6] - 2018-10-16
### Added
- Ability to mount WSGI apps.
- Supply content-type when serving up the schema.
# v0.0.5:
- OpenAPI Schema support.
- Safe load/dump yaml.
## [v0.0.5] - 2018-10-15
### Added
- OpenAPI Schema support.
- Safe load/dump yaml.
# v0.0.4:
- Asynchronous support for data uploads.
- Bug fixes.
## [v0.0.4] - 2018-10-15
### Added
- Asynchronous support for data uploads.
# v0.0.3:
### Fixed
- Bug fixes.
# v0.0.2
## [v0.0.3] - 2018-10-13
### Fixed
- Bug fixes.
## [v0.0.2] - 2018-10-13
### Changed
- Switch to ASGI/Starlette.
# v0.0.1
## [v0.0.1] - 2018-10-12
### Added
- Conception!
[Unreleased]: https://github.com/taoufik07/responder/compare/v1.3.2..HEAD
[v1.3.2]: https://github.com/taoufik07/responder/compare/v1.3.1..v1.3.2
[v1.3.1]: https://github.com/taoufik07/responder/compare/v1.3.0..v1.3.1
[v1.3.0]: https://github.com/taoufik07/responder/compare/v1.2.0..v1.3.0
[v1.2.0]: https://github.com/taoufik07/responder/compare/v1.1.3..v1.2.0
[v1.1.3]: https://github.com/taoufik07/responder/compare/v1.1.2..v1.1.3
[v1.1.2]: https://github.com/taoufik07/responder/compare/v1.1.1..v1.1.2
[v1.1.1]: https://github.com/taoufik07/responder/compare/v1.1.0..v1.1.1
[v1.1.0]: https://github.com/taoufik07/responder/compare/v1.0.5..v1.1.0
[v1.0.5]: https://github.com/taoufik07/responder/compare/v1.0.4..v1.0.5
[v1.0.4]: https://github.com/taoufik07/responder/compare/v1.0.3..v1.0.4
[v1.0.3]: https://github.com/taoufik07/responder/compare/v1.0.2..v1.0.3
[v1.0.2]: https://github.com/taoufik07/responder/compare/v1.0.1..v1.0.2
[v1.0.1]: https://github.com/taoufik07/responder/compare/v1.0.0..v1.0.1
[v1.0.0]: https://github.com/taoufik07/responder/compare/v0.3.3..v1.0.0
[v0.3.3]: https://github.com/taoufik07/responder/compare/v0.3.2..v0.3.3
[v0.3.2]: https://github.com/taoufik07/responder/compare/v0.3.1..v0.3.2
[v0.3.1]: https://github.com/taoufik07/responder/compare/v0.3.0..v0.3.1
[v0.3.0]: https://github.com/taoufik07/responder/compare/v0.2.3..v0.3.0
[v0.2.3]: https://github.com/taoufik07/responder/compare/v0.2.2..v0.2.3
[v0.2.2]: https://github.com/taoufik07/responder/compare/v0.2.1..v0.2.2
[v0.2.1]: https://github.com/taoufik07/responder/compare/v0.2.0..v0.2.1
[v0.2.0]: https://github.com/taoufik07/responder/compare/v0.1.6..v0.2.0
[v0.1.6]: https://github.com/taoufik07/responder/compare/v0.1.5..v0.1.6
[v0.1.5]: https://github.com/taoufik07/responder/compare/v0.1.4..v0.1.5
[v0.1.4]: https://github.com/taoufik07/responder/compare/v0.1.3..v0.1.4
[v0.1.3]: https://github.com/taoufik07/responder/compare/v0.1.2..v0.1.3
[v0.1.2]: https://github.com/taoufik07/responder/compare/v0.1.1..v0.1.2
[v0.1.1]: https://github.com/taoufik07/responder/compare/v0.1.0..v0.1.1
[v0.1.0]: https://github.com/taoufik07/responder/compare/v0.0.10..v0.1.0
[v0.0.10]: https://github.com/taoufik07/responder/compare/v0.0.9..v0.0.10
[v0.0.9]: https://github.com/taoufik07/responder/compare/v0.0.8..v0.0.9
[v0.0.8]: https://github.com/taoufik07/responder/compare/v0.0.7..v0.0.8
[v0.0.7]: https://github.com/taoufik07/responder/compare/v0.0.6..v0.0.7
[v0.0.6]: https://github.com/taoufik07/responder/compare/v0.0.5..v0.0.6
[v0.0.5]: https://github.com/taoufik07/responder/compare/v0.0.4..v0.0.5
[v0.0.4]: https://github.com/taoufik07/responder/compare/v0.0.3..v0.0.4
[v0.0.3]: https://github.com/taoufik07/responder/compare/v0.0.2..v0.0.3
[v0.0.2]: https://github.com/taoufik07/responder/compare/v0.0.1..v0.0.2
[v0.0.1]: https://github.com/taoufik07/responder/compare/v0.0.0..v0.0.1
-3
View File
@@ -16,8 +16,5 @@ sphinx = "*"
marshmallow = "*"
pytest-cov = "*"
[requires]
python_version = "3.7"
[pipenv]
allow_prereleases = true
Generated
+274 -257
View File
@@ -1,12 +1,10 @@
{
"_meta": {
"hash": {
"sha256": "7bbe1f0addd73250027de73d6fb749aa2be3149af9744b107820c5e10498428e"
"sha256": "ea12c0d556a3ca0848b0eba291a11a5ea98a701f0885c2d030b2aeb1e5b9c15f"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"requires": {},
"sources": [
{
"name": "pypi",
@@ -25,44 +23,30 @@
},
"aniso8601": {
"hashes": [
"sha256:7849749cf00ae0680ad2bdfe4419c7a662bef19c03691a19e008c8b9a5267802",
"sha256:94f90871fcd314a458a3d4eca1c84448efbd200e86f55fe4c733c7a40149ef50"
"sha256:b8a6a9b24611fc50cf2d9b45d371bfdc4fd0581d1cc52254f5502130a776d4af",
"sha256:bb167645c79f7a438f9dfab6161af9bed75508c645b1f07d1158240841d22673"
],
"version": "==3.0.2"
"version": "==6.0.0"
},
"apispec": {
"hashes": [
"sha256:57a7b81fd19fff0663a7e5ffd196eaea79b5364151ed2b65533be36d55e0229c",
"sha256:b45def53903516e67e8584ee41f34bc60c3e4acace6892b69340293ea20f3caa"
"sha256:11d1aaf620a80f67ded7688fcaf14fa4fd975d566876b5db69b067ffbfe4d1d9",
"sha256:2f4734203e53f6ae6499d1f2168d19e62f04f071d6e4b86c6d6014ebf2ebdf6b"
],
"version": "==1.0.0"
"version": "==2.0.2"
},
"apistar": {
"hashes": [
"sha256:4338b24468b49526ceac4a8f84046056081ee747f373ca8d0647bd6b2344c895"
"sha256:8da0d3f15748c8ed6e68914ba5b8f6dd5dff5afbe137950d07103575df0bce73"
],
"version": "==0.6.0"
},
"asgiref": {
"hashes": [
"sha256:9b05dcd41a6a89ca8c6e7f7e4089c3f3e76b5af60aebb81ae6d455ad81989c97",
"sha256:b21dc4c43d7aba5a844f4c48b8f49d56277bc34937fd9f9cb93ec97fde7e3082"
],
"version": "==2.3.2"
},
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
"version": "==3.0.1"
"version": "==0.7.2"
},
"certifi": {
"hashes": [
"sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
"sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
],
"version": "==2018.11.29"
"version": "==2019.6.16"
},
"chardet": {
"hashes": [
@@ -86,23 +70,25 @@
},
"graphene": {
"hashes": [
"sha256:b8ec446d17fa68721636eaad3d6adc1a378cb6323e219814c8f98c9928fc9642",
"sha256:faa26573b598b22ffd274e2fd7a4c52efa405dcca96e01a62239482246248aa3"
"sha256:77d61618132ccd084c343e64c22d806cee18dce73cc86e0f427378dbdeeac287",
"sha256:acf808d50d053b94f7958414d511489a9e490a7f9563b9be80f6875fc5723d2a"
],
"version": "==2.1.3"
"version": "==2.1.7"
},
"graphql-core": {
"hashes": [
"sha256:889e869be5574d02af77baf1f30b5db9ca2959f1c9f5be7b2863ead5a3ec6181",
"sha256:9462e22e32c7f03b667373ec0a84d95fba10e8ce2ead08f29fbddc63b671b0c1"
"sha256:1488f2a5c2272dc9ba66e3042a6d1c30cea0db4c80bd1e911c6791ad6187d91b",
"sha256:da64c472d720da4537a2e8de8ba859210b62841bd47a9be65ca35177f62fe0e4"
],
"version": "==2.1"
"version": "==2.2.1"
},
"graphql-relay": {
"hashes": [
"sha256:2716b7245d97091af21abf096fabafac576905096d21ba7118fba722596f65db"
"sha256:0e94201af4089e1f81f07d7bd8f84799768e39d70fa1ea16d1df505b46cc6335",
"sha256:75aa0758971e252964cb94068a4decd472d2a8295229f02189e3cbca1f10dbb5",
"sha256:7fa74661246e826ef939ee92e768f698df167a7617361ab399901eaebf80dce6"
],
"version": "==0.4.5"
"version": "==2.0.0"
},
"graphql-server-core": {
"hashes": [
@@ -119,9 +105,9 @@
},
"httptools": {
"hashes": [
"sha256:04c7703bbef0e8ca28b09811547352b8c7c20549eab70dc24e536bb24fd2b7c5"
"sha256:e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc"
],
"version": "==0.0.11"
"version": "==0.0.13"
},
"idna": {
"hashes": [
@@ -139,56 +125,56 @@
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
],
"version": "==2.10"
"version": "==2.10.1"
},
"markupsafe": {
"hashes": [
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
"version": "==1.1.0"
"version": "==1.1.1"
},
"marshmallow": {
"hashes": [
"sha256:7f9aba737a59dd3c6c6c79846f1df2fbfe036c17f038bbc2c83911b7304a90e1",
"sha256:b41cc52fe0491bdb8aa3e2186ca57d478d9ef69dba87fe37d309aa8a08fd30dd"
"sha256:7ea8540fc7e35be3b0af8b017313944b984d5acdb118b4ba3c270ac9611765c7",
"sha256:bc91e3f90e86133241ac62ea0dd35217d631a207e8628430bc66c347dbe12f7d"
],
"version": "==3.0.0rc4"
"version": "==3.0.0rc9"
},
"parse": {
"hashes": [
"sha256:870dd675c1ee8951db3e29b81ebe44fd131e3eb8c03a79483a58ea574f3145c2"
"sha256:1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142"
],
"version": "==1.11.1"
"version": "==1.12.0"
},
"promise": {
"hashes": [
@@ -205,20 +191,28 @@
},
"pyyaml": {
"hashes": [
"sha256:254bf6fda2b7c651837acb2c718e213df29d531eebf00edb54743d10bcb694eb",
"sha256:3108529b78577327d15eec243f0ff348a0640b0c3478d67ad7f5648f93bac3e2",
"sha256:3c17fb92c8ba2f525e4b5f7941d850e7a48c3a59b32d331e2502a3cdc6648e76",
"sha256:8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b",
"sha256:c8a88edd93ee29ede719080b2be6cb2333dfee1dccba213b422a9c8e97f2967b"
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
],
"version": "==4.2b4"
"version": "==5.1.2"
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"version": "==2.21.0"
"version": "==2.22.0"
},
"requests-toolbelt": {
"hashes": [
@@ -233,17 +227,17 @@
},
"rfc3986": {
"hashes": [
"sha256:5ad82677b02b88c8d24f6511b4ee9baa5e7da675599b479fbbc5c9c578b5b737",
"sha256:bc3ae4b7cd88a99eff2d3900fcb858d44562fd7f273fc07aeef568b9bb6fc4e1"
"sha256:0344d0bd428126ce554e7ca2b61787b6a28d2bbd19fc70ed2dd85efe31176405",
"sha256:df4eba676077cefb86450c8f60121b9ae04b94f65f85b69f3f731af0516b7b18"
],
"version": "==1.2.0"
"version": "==1.3.2"
},
"rx": {
"hashes": [
"sha256:7e6919a3159d6c6cee266fdeeb48783118bab20177e38fa5d6a8ebd24a132f72",
"sha256:e7ccf18bb8e76f8a44557febd5c149c2ad36df19442f678be529c710b0553d85"
"sha256:13a1d8d9e252625c173dc795471e614eadfe1cf40ffc684e08b8fff0d9748c23",
"sha256:7357592bc7e881a95e0c2013b73326f704953301ab551fbc8133a6fadab84105"
],
"version": "==3.0.0a2"
"version": "==1.6.1"
},
"six": {
"hashes": [
@@ -254,37 +248,43 @@
},
"starlette": {
"hashes": [
"sha256:8bc2e41f7638290379ae91450413796f92d6c97b88a6b754f3c1a7f8bc7a07d6"
"sha256:849553e6dc8dd4faac5d4383219bdf49b199f3f09d60ce374c2a00a42ab7c77d"
],
"version": "==0.10.7"
"version": "==0.12.7"
},
"typesystem": {
"hashes": [
"sha256:ba2bd10f1c5844d08dd8841e777bdee55bfca569bf21cb96cd0f91e0a4f66cd8"
],
"version": "==0.2.4"
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
],
"version": "==1.24.1"
"version": "==1.25.3"
},
"uvicorn": {
"hashes": [
"sha256:f27889a332ee5c55b4841b11b2392d00dac079f39063fabc1e13e18ada3eb7ba"
"sha256:8aa44f9d9c3082ef693950387ea25d376e32944df6d4071dbd8edc3c25a40c74"
],
"version": "==0.4.5"
"version": "==0.8.6"
},
"uvloop": {
"hashes": [
"sha256:198fe0c196056930ec6c4a0a878e531a66d15467ca7c74a875aa90271f0c6e3f",
"sha256:1c175f47d34b84e33c0e312f4987c927ea004afc3a5f05d2f0f610d71d0e4c89",
"sha256:1c47f197be8f0a3c651dd20be1e1bd43268186246f246d4e86c91e95a89e4865",
"sha256:3fd4943570d20e8cd4d9f0a3190ebd5cf040e5610b685e05c878128a11f7ad14",
"sha256:435e232869923fd2248e4ca0ad73e24a5b4debf40bed9dcde133cfe1bef98a7a",
"sha256:9cfdb966ae804c46b96c92207dfd2174935ffc70e706e42e1c94c60d16dbe860",
"sha256:a585781443eeb2edb858f8c08c503aac237a5f1bebf0c84ea8340cc337afa408",
"sha256:b296493e033846e46488a6aa227a75c790091f5ee5456ec637bb0badad1e8851",
"sha256:c684047c6cf6d697ba37872fb1b4489012ea91f3f802c8fbb9c367c4902e88dc",
"sha256:da5a59d8812188b57b5783c7fb78891d14dd1050b6259680e0dbd4253d7d0f64"
"sha256:0fcd894f6fc3226a962ee7ad895c4f52e3f5c3c55098e21efb17c071849a0573",
"sha256:2f31de1742c059c96cb76b91c5275b22b22b965c886ee1fced093fa27dde9e64",
"sha256:459e4649fcd5ff719523de33964aa284898e55df62761e7773d088823ccbd3e0",
"sha256:67867aafd6e0bc2c30a079603a85d83b94f23c5593b3cc08ec7e58ac18bf48e5",
"sha256:8c200457e6847f28d8bb91c5e5039d301716f5f2fce25646f5fb3fd65eda4a26",
"sha256:958906b9ca39eb158414fbb7d6b8ef1b7aee4db5c8e8e5d00fcbb69a1ce9dca7",
"sha256:ac1dca3d8f3ef52806059e81042ee397ac939e5a86c8a3cea55d6b087db66115",
"sha256:b284c22d8938866318e3b9d178142b8be316c52d16fcfe1560685a686718a021",
"sha256:c48692bf4587ce281d641087658eca275a5ad3b63c78297bbded96570ae9ce8f",
"sha256:fefc3b2b947c99737c348887db2c32e539160dcbeb7af9aa6b53db7a283538fe"
],
"version": "==0.12.1"
"version": "==0.12.2"
},
"websockets": {
"hashes": [
@@ -314,10 +314,10 @@
},
"whitenoise": {
"hashes": [
"sha256:118ab3e5f815d380171b100b05b76de2a07612f422368a201a9ffdeefb2251c1",
"sha256:42133ddd5229eeb6a0c9899496bdbe56c292394bf8666da77deeb27454c0456a"
"sha256:59d880d25d0e90bcc6554fe0504a11195bd2e59b3d690b6fb42a8040d4e67ef5",
"sha256:c9b7c47fdc1dba4d37bf2787a01a844dc7a521e174fcd22a2d429e0be65e1782"
],
"version": "==4.1.2"
"version": "==4.1.3"
}
},
"develop": {
@@ -344,25 +344,25 @@
},
"attrs": {
"hashes": [
"sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
"sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
],
"version": "==18.2.0"
"version": "==19.1.0"
},
"babel": {
"hashes": [
"sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669",
"sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23"
"sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab",
"sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"
],
"version": "==2.6.0"
"version": "==2.7.0"
},
"black": {
"hashes": [
"sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739",
"sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
],
"index": "pypi",
"version": "==18.9b0"
"version": "==19.3b0"
},
"bleach": {
"hashes": [
@@ -373,10 +373,10 @@
},
"certifi": {
"hashes": [
"sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
"sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
],
"version": "==2018.11.29"
"version": "==2019.6.16"
},
"chardet": {
"hashes": [
@@ -394,45 +394,43 @@
},
"coverage": {
"hashes": [
"sha256:029c69deaeeeae1b15bc6c59f0ffa28aa8473721c614a23f2c2976dec245cd12",
"sha256:02abbbebc6e9d5abe13cd28b5e963dedb6ffb51c146c916d17b18f141acd9947",
"sha256:1bbfe5b82a3921d285e999c6d256c1e16b31c554c29da62d326f86c173d30337",
"sha256:210c02f923df33a8d0e461c86fdcbbb17228ff4f6d92609fc06370a98d283c2d",
"sha256:2d0807ba935f540d20b49d5bf1c0237b90ce81e133402feda906e540003f2f7a",
"sha256:35d7a013874a7c927ce997350d314144ffc5465faf787bb4e46e6c4f381ef562",
"sha256:3636f9d0dcb01aed4180ef2e57a4e34bb4cac3ecd203c2a23db8526d86ab2fb4",
"sha256:42f4be770af2455a75e4640f033a82c62f3fb0d7a074123266e143269d7010ef",
"sha256:48440b25ba6cda72d4c638f3a9efa827b5b87b489c96ab5f4ff597d976413156",
"sha256:4dac8dfd1acf6a3ac657475dfdc66c621f291b1b7422a939cc33c13ac5356473",
"sha256:4e8474771c69c2991d5eab65764289a7dd450bbea050bc0ebb42b678d8222b42",
"sha256:551f10ddfeff56a1325e5a34eff304c5892aa981fd810babb98bfee77ee2fb17",
"sha256:5b104982f1809c1577912519eb249f17d9d7e66304ad026666cb60a5ef73309c",
"sha256:5c62aef73dfc87bfcca32cee149a1a7a602bc74bac72223236b0023543511c88",
"sha256:633151f8d1ad9467b9f7e90854a7f46ed8f2919e8bc7d98d737833e8938fc081",
"sha256:772207b9e2d5bf3f9d283b88915723e4e92d9a62c83f44ec92b9bd0cd685541b",
"sha256:7d5e02f647cd727afc2659ec14d4d1cc0508c47e6cfb07aea33d7aa9ca94d288",
"sha256:a9798a4111abb0f94584000ba2a2c74841f2cfe5f9254709756367aabbae0541",
"sha256:b38ea741ab9e35bfa7015c93c93bbd6a1623428f97a67083fc8ebd366238b91f",
"sha256:b6a5478c904236543c0347db8a05fac6fc0bd574c870e7970faa88e1d9890044",
"sha256:c6248bfc1de36a3844685a2e10ba17c18119ba6252547f921062a323fb31bff1",
"sha256:c705ab445936457359b1424ef25ccc0098b0491b26064677c39f1d14a539f056",
"sha256:d95a363d663ceee647291131dbd213af258df24f41350246842481ec3709bd33",
"sha256:e27265eb80cdc5dab55a40ef6f890e04ecc618649ad3da5265f128b141f93f78",
"sha256:ebc276c9cb5d917bd2ae959f84ffc279acafa9c9b50b0fa436ebb70bbe2166ea",
"sha256:f4d229866d030863d0fe3bf297d6d11e6133ca15bbb41ed2534a8b9a3d6bd061",
"sha256:f95675bd88b51474d4fe5165f3266f419ce754ffadfb97f10323931fa9ac95e5",
"sha256:f95bc54fb6d61b9f9ff09c4ae8ff6a3f5edc937cda3ca36fc937302a7c152bf1",
"sha256:fd0f6be53de40683584e5331c341e65a679dbe5ec489a0697cec7c2ef1a48cda"
"sha256:108efa19b676e62590a7a13084098e35183479c0d9608131c20b0921c5a72dc0",
"sha256:16fe3ef881eff27bab287f91dadb4ff0ce4388b9e928d84cbf148a83cc70b3a1",
"sha256:1d0bbc11421827d1100da82ac8dc929532b97ad464038475a0f6505cbf83d6ea",
"sha256:23a8ca5b3c9673f775cc151e85a737f1a967df2ec02b09e8c5a3b606ff2050bf",
"sha256:24b890e51455276762b55cb06fa1c922066e8fc18d1deb1a6399b4d24dfa8ea2",
"sha256:2f0041757ca4801f3c6a74d1660862fdb18a25aea302dd0ce9b067ddbb06b667",
"sha256:3169aba03baddfccdab7cc04cf0878dbf76fc06d300bc35639129a6b794d6484",
"sha256:364fb1bf0f999af2e7f4b1a1e614b2af8c3e0017d11af716aad25f911b7cd0c7",
"sha256:5256856d23f3e45959e7e3a8f9d4cbad3d1613e5660cb8117cd1417798efc395",
"sha256:5b26daa1e1a1147455bf62cd682e504e68f1d1e04235374d50a5248a3c792b1c",
"sha256:60247c8f0c756732e2cfe21f03e6847b923b9a9eaff61f04dc64d3047ec1b669",
"sha256:6463d51507308eb3973340d903537f17ece2ee1e6513aa0c27548fc3a09b0471",
"sha256:64cbadf7a884b299794238bc4391752130e74f71e919993b50c1c431786ef2a2",
"sha256:6de85748ea39ce819ad6d90e660da43964457a1f5cd25262e962a7c7c87945b3",
"sha256:6f95b4794bd84f64aeca25087d8e3abc416aad76842afcac34fa6c3a6f61c62e",
"sha256:778fa184aa3079fa3cbd240e2f5b36771c3382db26bc7bf78aea9d06212c6c66",
"sha256:790a9c5e2dbdf6c41eec9776ed663e99bd36c1604e3bf2e8ae3b123181bfee9f",
"sha256:7d97c1aec0b68b4ea5e3c9edb9fc3f951e8a52360f4bad3aacab9a77defe5b17",
"sha256:93cefddcc0b541d3c52981a232947bf085a38092b0812317f1adb56f02869bcb",
"sha256:95e49867ac616ec63ecd69ea005e65e4b896a48b8db7f9f3ad69f37be29324b7",
"sha256:aca423563eafba66a7c15125391b267befd1e45238de5e1a119ae1fb4ea83b5c",
"sha256:baef7c35e7fce738d9637e9c7a6aa79cb79085e4de49c2ec517ce19239a660f6",
"sha256:c10ccf0797ffce85e93a40aff3a96a3adb63c734f95b59384a7c9522ed25c9e2",
"sha256:ca39704a05bba1886c384a4d7944fda72c53fe5e61979cd933d22084678ad4c1",
"sha256:f6e96d5eee578187f5b7e9266bf646b73de29e2dd7adca8bd83e383680ce1f4c",
"sha256:fc6524511fa664cb4e91401229eedd0dad4ba6ded9c4423fee2f698d78908d9c",
"sha256:fdf2e7e5f074495ad6ea796ca0d245aa6a8b9e4c546ffbf8d30aaaee6601af0f"
],
"version": "==5.0a4"
"version": "==5.0a6"
},
"docutils": {
"hashes": [
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
"sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
"sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827",
"sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"
],
"version": "==0.14"
"version": "==0.15.2"
},
"entrypoints": {
"hashes": [
@@ -443,19 +441,19 @@
},
"flake8": {
"hashes": [
"sha256:6d8c66a65635d46d54de59b027a1dda40abbe2275b3164b634835ac9c13fd048",
"sha256:6eab21c6e34df2c05416faa40d0c59963008fff29b6f0ccfe8fa28152ab3e383"
"sha256:19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548",
"sha256:8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"
],
"index": "pypi",
"version": "==3.7.6"
"version": "==3.7.8"
},
"flask": {
"hashes": [
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
"sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52",
"sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6"
],
"index": "pypi",
"version": "==1.0.2"
"version": "==1.1.1"
},
"idna": {
"hashes": [
@@ -471,6 +469,13 @@
],
"version": "==1.1.0"
},
"importlib-metadata": {
"hashes": [
"sha256:23d3d873e008a513952355379d93cbcab874c58f4f034ff657c7a87422fa64e8",
"sha256:80d2de76188eabfbfcf27e6a37342c2827801e59c4cc14b0371c56fed43820e3"
],
"version": "==0.19"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
@@ -480,50 +485,50 @@
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
],
"version": "==2.10"
"version": "==2.10.1"
},
"markupsafe": {
"hashes": [
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
"version": "==1.1.0"
"version": "==1.1.1"
},
"marshmallow": {
"hashes": [
"sha256:7f9aba737a59dd3c6c6c79846f1df2fbfe036c17f038bbc2c83911b7304a90e1",
"sha256:b41cc52fe0491bdb8aa3e2186ca57d478d9ef69dba87fe37d309aa8a08fd30dd"
"sha256:7ea8540fc7e35be3b0af8b017313944b984d5acdb118b4ba3c270ac9611765c7",
"sha256:bc91e3f90e86133241ac62ea0dd35217d631a207e8628430bc66c347dbe12f7d"
],
"version": "==3.0.0rc4"
"version": "==3.0.0rc9"
},
"mccabe": {
"hashes": [
@@ -534,18 +539,17 @@
},
"more-itertools": {
"hashes": [
"sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40",
"sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
],
"markers": "python_version > '2.7'",
"version": "==6.0.0"
"version": "==7.2.0"
},
"packaging": {
"hashes": [
"sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af",
"sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"
"sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
"sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
],
"version": "==19.0"
"version": "==19.1"
},
"pkginfo": {
"hashes": [
@@ -556,17 +560,17 @@
},
"pluggy": {
"hashes": [
"sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616",
"sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"
"sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc",
"sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"
],
"version": "==0.8.1"
"version": "==0.12.0"
},
"py": {
"hashes": [
"sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
"sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
],
"version": "==1.7.0"
"version": "==1.8.0"
},
"pycodestyle": {
"hashes": [
@@ -577,47 +581,47 @@
},
"pyflakes": {
"hashes": [
"sha256:5e8c00e30c464c99e0b501dc160b13a14af7f27d4dffb529c556e30a159e231d",
"sha256:f277f9ca3e55de669fba45b7393a1449009cff5a37d1af10ebb76c52765269cd"
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.0"
"version": "==2.1.1"
},
"pygments": {
"hashes": [
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
"sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127",
"sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"
],
"version": "==2.3.1"
"version": "==2.4.2"
},
"pyparsing": {
"hashes": [
"sha256:66c9268862641abcac4a96ba74506e594c884e3f57690a696d21ad8210ed667a",
"sha256:f6c5ef0d7480ad048c054c37632c67fca55299990fff127850181659eea33fc3"
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
],
"version": "==2.3.1"
"version": "==2.4.2"
},
"pytest": {
"hashes": [
"sha256:067a1d4bf827ffdd56ad21bd46674703fce77c5957f6c1eef731f6146bfcef1c",
"sha256:9687049d53695ad45cf5fdc7bbd51f0c49f1ea3ecfc4b7f3fde7501b541f17f4"
"sha256:6ef6d06de77ce2961156013e9dff62f1b2688aa04d0dc244299fe7d67e09370d",
"sha256:a736fed91c12681a7b34617c8fcefe39ea04599ca72c608751c31d89579a3f77"
],
"index": "pypi",
"version": "==4.3.0"
"version": "==5.0.1"
},
"pytest-cov": {
"hashes": [
"sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33",
"sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"
"sha256:2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6",
"sha256:e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"
],
"index": "pypi",
"version": "==2.6.1"
"version": "==2.7.1"
},
"pytz": {
"hashes": [
"sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9",
"sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c"
"sha256:26c0b32e437e54a18161324a2fca3c4b9846b74a8dccddd843113109e1116b32",
"sha256:c894d57500a4cd2d5c71114aaab77dbab5eabd9022308ce5ac9bb93a60a6f0c7"
],
"version": "==2018.9"
"version": "==2019.2"
},
"readme-renderer": {
"hashes": [
@@ -628,10 +632,10 @@
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"version": "==2.21.0"
"version": "==2.22.0"
},
"requests-toolbelt": {
"hashes": [
@@ -649,18 +653,17 @@
},
"snowballstemmer": {
"hashes": [
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
"sha256:9f3b9ffe0809d174f7047e121431acf99c89a7040f0ca84f94ba53a498e6d0c9"
],
"version": "==1.2.1"
"version": "==1.9.0"
},
"sphinx": {
"hashes": [
"sha256:230af939a2f678ab4f2a0a948c3b24a822a0d280821859caaefb750ef7413003",
"sha256:835c701420102a0a71ba2ed54a5bada2da6fd01263bf6dc8c5c80c798e27709c"
"sha256:22538e1bbe62b407cf5a8aabe1bb15848aa66bb79559f42f5202bbce6b757a69",
"sha256:f9a79e746b87921cabc3baa375199c6076d1270cee53915dbd24fdbeaaacc427"
],
"index": "pypi",
"version": "==2.0.0b1"
"version": "==2.1.2"
},
"sphinxcontrib-applehelp": {
"hashes": [
@@ -678,10 +681,10 @@
},
"sphinxcontrib-htmlhelp": {
"hashes": [
"sha256:0d691ca8edf5995fbacfe69b191914256071a94cbad03c3688dca47385c9206c",
"sha256:e31c8271f5a8f04b620a500c0442a7d5cfc1a732fa5c10ec363f90fe72af0cb8"
"sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422",
"sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"
],
"version": "==1.0.1"
"version": "==1.0.2"
},
"sphinxcontrib-jsmath": {
"hashes": [
@@ -692,17 +695,17 @@
},
"sphinxcontrib-qthelp": {
"hashes": [
"sha256:18ec9f74ea2c92fd512d5f3b532d6ab4ac2be76b12cc2490f7729842ba2a60c9",
"sha256:f39159b45de6d3d86c30874a3220be4f8e75ed12c71aff50cb8b2cac46e240f0"
"sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20",
"sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"
],
"version": "==1.0.1"
"version": "==1.0.2"
},
"sphinxcontrib-serializinghtml": {
"hashes": [
"sha256:01d9b2617d7e8ddf7a00cae091f08f9fa4db587cc160b493141ee56710810932",
"sha256:392187ac558863b8aff0d76dc78e0731fed58f3b06e2b00e22995dcdb630f213"
"sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227",
"sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"
],
"version": "==1.1.1"
"version": "==1.1.3"
},
"toml": {
"hashes": [
@@ -713,10 +716,10 @@
},
"tqdm": {
"hashes": [
"sha256:d385c95361699e5cf7622485d9b9eae2d4864b21cd5a2374a9c381ffed701021",
"sha256:e22977e3ebe961f72362f6ddfb9197cc531c9737aaf5f607ef09740c849ecd05"
"sha256:14a285392c32b6f8222ecfbcd217838f88e11630affe9006cd0e94c7eff3cb61",
"sha256:25d4c0ea02a305a688e7e9c2cdc8f862f989ef2a4701ab28ee963295f5b109ab"
],
"version": "==4.31.1"
"version": "==4.32.2"
},
"twine": {
"hashes": [
@@ -728,10 +731,17 @@
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
],
"version": "==1.24.1"
"version": "==1.25.3"
},
"wcwidth": {
"hashes": [
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
],
"version": "==0.1.7"
},
"webencodings": {
"hashes": [
@@ -742,10 +752,17 @@
},
"werkzeug": {
"hashes": [
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
"sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4",
"sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6"
],
"version": "==0.14.1"
"version": "==0.15.5"
},
"zipp": {
"hashes": [
"sha256:4970c3758f4e89a7857a973b1e2a5d75bcdc47794442f2e2dd4fe8e0466e809a",
"sha256:8a5712cfd3bb4248015eb3b0b3c54a5f6ee3f2425963ef2a0125b8bc40aafaec"
],
"version": "==0.5.2"
}
}
}
+5 -10
View File
@@ -1,11 +1,11 @@
# Responder: a familiar HTTP Service Framework for Python
[![Build Status](https://travis-ci.org/kennethreitz/responder.svg?branch=master)](https://travis-ci.org/kennethreitz/responder)
[![Build Status](https://travis-ci.org/taoufik07/responder.svg?branch=master)](https://travis-ci.org/taoufik07/responder)
[![Documentation Status](https://readthedocs.org/projects/mybinder/badge/?version=latest)](https://responder.readthedocs.io/en/latest/)
[![image](https://img.shields.io/pypi/v/responder.svg)](https://pypi.org/project/responder/)
[![image](https://img.shields.io/pypi/l/responder.svg)](https://pypi.org/project/responder/)
[![image](https://img.shields.io/pypi/pyversions/responder.svg)](https://pypi.org/project/responder/)
[![image](https://img.shields.io/github/contributors/kennethreitz/responder.svg)](https://github.com/kennethreitz/responder/graphs/contributors)
[![image](https://img.shields.io/github/contributors/taoufik07/responder.svg)](https://github.com/taoufik07/responder/graphs/contributors)
[![](https://farm2.staticflickr.com/1959/43750081370_a4e20752de_o_d.png)](https://python-responder.org/)
@@ -31,16 +31,16 @@ See [the documentation's feature tour](https://python-responder.org/en/latest/to
# Installing Responder
Install the latest release:
Install the stable release:
$ pipenv install responder --pre
$ pipenv install responder
✨🍰✨
Or, install from the development branch:
$ pipenv install -e git+https://github.com/kennethreitz/responder.git#egg=responder
$ pipenv install -e git+https://github.com/taoufik07/responder.git#egg=responder
Only **Python 3.6+** is supported.
@@ -68,8 +68,3 @@ The primary concept here is to bring the niceties that are brought forth from bo
- Uvicorn built-in as a production web server. I would have chosen Gunicorn, but it doesn't run on Windows. Plus, Uvicorn serves well to protect against slowloris attacks, making nginx unnecessary in production.
- GraphQL support, via Graphene. The goal here is to have any GraphQL query exposable at any route, magically.
- Provide an official way to run webpack.
----------
[![hacktoberfest](https://hacktoberfest.digitalocean.com/assets/hacktoberfest-2018-social-card-c8d2e1489f647f2e0a26e6f598adeb760872818905b34cd437afc7ac2857ceab.png)](https://hacktoberfest.digitalocean.com/)
+21
View File
@@ -8,6 +8,27 @@
<iframe src="https://ghbtns.com/github-btn.html?user=kennethreitz&repo=responder&type=watch&count=true&size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
</p>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
<style>
.algolia-autocomplete{
width: 100%;
height: 1.5em
}
.algolia-autocomplete a{
border-bottom: none !important;
}
#doc_search{
width: 100%;
height: 100%;
}
</style>
<input id="doc_search" placeholder="Search the doc" autofocus/>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js" onload="docsearch({
apiKey: 'ac965312db252e0496283c75c6f76f0b',
indexName: 'python-responder',
inputSelector: '#doc_search',
debug: false // Set debug to true if you want to inspect the dropdown
})" async></script>
<p>
<strong>Responder</strong> is a web service framework, written for human beings.
+21
View File
@@ -8,6 +8,27 @@
<iframe src="https://ghbtns.com/github-btn.html?user=kennethreitz&repo=responder&type=watch&count=true&size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
</p>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
<style>
.algolia-autocomplete{
width: 100%;
height: 1.5em
}
.algolia-autocomplete a{
border-bottom: none !important;
}
#doc_search{
width: 100%;
height: 100%;
}
</style>
<input id="doc_search" placeholder="Search the doc" autofocus/>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js" onload="docsearch({
apiKey: 'ac965312db252e0496283c75c6f76f0b',
indexName: 'python-responder',
inputSelector: '#doc_search',
debug: false // Set debug to true if you want to inspect the dropdown
})" async></script>
<p>
<strong>Responder</strong> is a web service framework, written for human beings.
+1 -1
View File
@@ -31,7 +31,7 @@ The basics::
Install Responder::
$ pipenv install responder --pre
$ pipenv install responder
...
Write out an ``api.py``::
+8 -7
View File
@@ -36,20 +36,21 @@ A familiar HTTP Service Framework
Powered by `Starlette <https://www.starlette.io/>`_. That ``async`` declaration is optional.
This gets you a ASGI app, with a production static files server (WhiteNoise)
This gets you a ASGI app, with a production static files server
(`WhiteNoise <http://whitenoise.evans.io/en/stable/>`_)
pre-installed, jinja2 templating (without additional imports), and a
production webserver based on uvloop, serving up requests with gzip
compression automatically.
production webserver based on uvloop, serving up requests with
automatic gzip compression.
Features
--------
- A pleasant API, with a single import statement.
- Class-based views without inheritance.
- ASGI framework, the future of Python web services.
- `ASGI <https://asgi.readthedocs.io>`_ framework, the future of Python web services.
- WebSocket support!
- The ability to mount any ASGI / WSGI app at a subroute.
- *f-string syntax* route declaration.
- `f-string syntax <https://docs.python.org/3/whatsnew/3.6.html#pep-498-formatted-string-literals>`_ route declaration.
- Mutable response object, passed into each view. No need to return anything.
- Background tasks, spawned off in a ``ThreadPoolExecutor``.
- GraphQL (with *GraphiQL*) support!
@@ -102,7 +103,7 @@ Installing Responder
.. code-block:: shell
$ pipenv install responder --pre
$ pipenv install responder
✨🍰✨
Only **Python 3.6+** is supported.
@@ -129,7 +130,7 @@ Ideas
- Automatic gzipped-responses.
- In addition to Falcon's ``on_get``, ``on_post``, etc methods, Responder features an ``on_request`` method, which gets called on every type of request, much like Requests.
- A production static files server is built-in.
- Uvicorn built-in as a production web server. I would have chosen Gunicorn, but it doesn't run on Windows. Plus, Uvicorn serves well to protect against slowloris attacks, making nginx unnecessary in production.
- `Uvicorn <https://www.uvicorn.org/>`_ is built-in as a production web server. I would have chosen Gunicorn, but it doesn't run on Windows. Plus, Uvicorn serves well to protect against `slowloris <https://en.wikipedia.org/wiki/Slowloris_(computer_security)>`_ attacks, making nginx unnecessary in production.
- GraphQL support, via Graphene. The goal here is to have any GraphQL query exposable at any route, magically.
+34
View File
@@ -48,6 +48,14 @@ If you want dynamic URLs, you can use Python's familiar *f-string syntax* to dec
A ``GET`` request to ``/hello/brettcannon`` will result in a response of ``hello, brettcannon!``.
Type convertors are also available::
@api.route("/add/{a:int}/{b:int}")
async def add(req, resp, *, a, b):
resp.text = f"{a} + {b} = {a + b}"
Supported types: ``str``, ``int`` and ``float``.
Returning JSON / YAML
---------------------
@@ -124,3 +132,29 @@ Here, we'll process our data in the background, while responding immediately to
resp.media = {'success': True}
A ``POST`` request to ``/incoming`` will result in an immediate response of ``{'success': true}``.
Here's a sample code to post a file with background::
@api.route("/")
async def upload_file(req, resp):
@api.background.task
def process_data(data):
f = open('./{}'.format(data['file']['filename']), 'w')
f.write(data['file']['content'].decode('utf-8'))
f.close()
data = await req.media(format='files')
process_data(data)
resp.media = {'success': 'ok'}
You can send a file easily with requests::
import requests
data = {'file': ('hello.txt', 'hello, world!', "text/plain")}
r = requests.post('http://127.0.0.1:8210/file', files=data)
print(r.text)
+38 -13
View File
@@ -79,7 +79,6 @@ Responder comes with built-in support for OpenAPI / marshmallow::
title="Web Service",
version="1.0",
openapi="3.0.2",
docs_route='/docs',
description=description,
terms_of_service=terms_of_service,
contact=contact,
@@ -101,8 +100,10 @@ Responder comes with built-in support for OpenAPI / marshmallow::
responses:
200:
description: A pet to be returned
schema:
$ref = "#/components/schemas/Pet"
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
"""
resp.media = PetSchema().dump({"name": "little orange"})
@@ -113,20 +114,28 @@ Responder comes with built-in support for OpenAPI / marshmallow::
>>> print(r.text)
components:
parameters: {}
schemas:
parameters: {}
responses: {}
schemas:
Pet:
properties:
properties:
name: {type: string}
type: object
info: {title: Web Service, version: 1.0}
openapi: '3.0'
type: object
securitySchemes: {}
info:
contact: {email: support@example.com, name: API Support, url: 'http://www.example.com/support'}
description: This is a sample server for a pet store.
license: {name: Apache 2.0, url: 'https://www.apache.org/licenses/LICENSE-2.0.html'}
termsOfService: http://example.com/terms/
title: Web Service
version: 1.0
openapi: 3.0.2
paths:
/:
get:
description: Get a random pet
responses:
200: {description: A pet to be returned, schema: $ref = "#/components/schemas/Pet"}
200: {description: A pet to be returned, schema: $ref: "#/components/schemas/Pet"}
tags: []
@@ -135,7 +144,16 @@ Interactive Documentation
Responder can automatically supply API Documentation for you. Using the example above::
api = responder.API(title="Web Service", version="1.0", openapi="3.0.0", docs_route="/docs")
api = responder.API(
title="Web Service",
version="1.0",
openapi="3.0.2",
docs_route='/docs',
description=description,
terms_of_service=terms_of_service,
contact=contact,
license=license,
)
This will make ``/docs`` render interactive documentation for your API.
@@ -184,7 +202,7 @@ To set cookies directives, you should use `resp.set_cookie`::
Supported directives:
* ``key`` - **Reduired**
* ``key`` - **Required**
* ``value`` - [OPTIONAL] - Defaults to ``""``.
* ``expires`` - Defaults to ``None``.
* ``max_age`` - Defaults to ``None``.
@@ -225,6 +243,13 @@ If you'd like a view to be executed before every request, simply do the followin
Now all requests to your HTTP Service will include an ``X-Pizza`` header.
For ``websockets``::
@api.route(before_request=True, websocket=True)
def prepare_response(ws):
await ws.accept()
WebSocket Support
-----------------
@@ -319,7 +344,7 @@ A 400 response will be raised, if a request does not match any of the provided p
::
api = responder.API(allowed_hosts=[example.com, tenant.example.com])
api = responder.API(allowed_hosts=['example.com', 'tenant.example.com'])
* ``allowed_hosts`` - A list of allowed hostnames.
+1 -1
View File
@@ -1 +1 @@
__version__ = "1.3.0"
__version__ = "1.3.2"
+122 -103
View File
@@ -12,14 +12,13 @@ import uvicorn
import yaml
from apispec import APISpec, yaml_utils
from apispec.ext.marshmallow import MarshmallowPlugin
from asgiref.wsgi import WsgiToAsgi
from starlette.middleware.wsgi import WSGIMiddleware
from starlette.middleware.errors import ServerErrorMiddleware
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.gzip import GZipMiddleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.lifespan import LifespanMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
from starlette.routing import Router, LifespanHandler
from starlette.routing import Lifespan
from starlette.staticfiles import StaticFiles
from starlette.testclient import TestClient
from starlette.websockets import WebSocket
@@ -89,13 +88,27 @@ class API:
self.contact = contact
self.license = license
self.openapi_version = openapi
self.static_dir = Path(os.path.abspath(static_dir))
self.static_route = f"/{static_route.strip('/')}"
self.templates_dir = Path(os.path.abspath(templates_dir))
if static_dir is not None:
if static_route is None:
static_route = static_dir
static_dir = Path(os.path.abspath(static_dir))
self.static_dir = static_dir
self.static_route = static_route
self.built_in_templates_dir = Path(
os.path.abspath(os.path.dirname(__file__) + "/templates")
)
if templates_dir is not None:
templates_dir = Path(os.path.abspath(templates_dir))
self.templates_dir = templates_dir or self.built_in_templates_dir
self.apps = {}
self.routes = {}
self.before_requests = {"http": [], "ws": []}
self.docs_theme = DEFAULT_API_THEME
self.docs_route = docs_route
self.schemas = {}
@@ -116,19 +129,23 @@ class API:
# Make the static/templates directory if they don't exist.
for _dir in (self.static_dir, self.templates_dir):
os.makedirs(_dir, exist_ok=True)
if _dir is not None:
os.makedirs(_dir, exist_ok=True)
self.whitenoise = WhiteNoise(application=self._notfound_wsgi_app)
self.whitenoise.add_files(str(self.static_dir))
if self.static_dir is not None:
self.whitenoise = WhiteNoise(application=self._notfound_wsgi_app)
self.whitenoise.add_files(str(self.static_dir))
self.whitenoise.add_files(
(
Path(apistar.__file__).parent / "themes" / self.docs_theme / "static"
).resolve()
)
self.whitenoise.add_files(
(
Path(apistar.__file__).parent
/ "themes"
/ self.docs_theme
/ "static"
).resolve()
)
self.apps = {}
self.mount(self.static_route, self.whitenoise)
self.mount(self.static_route, self.whitenoise)
self.formats = get_formats()
@@ -142,7 +159,7 @@ class API:
self.add_route(self.docs_route, self.docs_response)
self.default_endpoint = None
self.app = self.dispatch
self.app = self.asgi
self.add_middleware(GZipMiddleware)
if self.hsts_enabled:
@@ -150,13 +167,13 @@ class API:
self.add_middleware(TrustedHostMiddleware, allowed_hosts=self.allowed_hosts)
self.lifespan_handler = LifespanMiddleware(LifespanHandler)
self.lifespan_handler = Lifespan()
if self.cors:
self.add_middleware(CORSMiddleware, **self.cors_params)
self.add_middleware(ServerErrorMiddleware, debug=debug)
# Jinja enviroment
# Jinja environment
self.jinja_env = jinja2.Environment(
loader=jinja2.FileSystemLoader(
[str(self.templates_dir), str(self.built_in_templates_dir)],
@@ -178,14 +195,23 @@ class API:
start_response("404 NOT FOUND", [("Content-Type", "text/plain")])
return [b"Not Found."]
@property
def before_requests(self):
def gen():
for route in self.routes:
if self.routes[route].before_request:
yield self.routes[route]
def before_request(self, websocket=False):
def decorator(f):
if websocket:
self.before_requests.setdefault("ws", []).append(f)
else:
self.before_requests.setdefault("http", []).append(f)
return f
return [g for g in gen()]
return decorator
@property
def before_http_requests(self):
return self.before_requests.get("http", [])
@property
def before_ws_requests(self):
return self.before_requests.get("ws", [])
@property
def _apispec(self):
@@ -227,10 +253,10 @@ class API:
def add_middleware(self, middleware_cls, **middleware_config):
self.app = middleware_cls(self.app, **middleware_config)
def __call__(self, scope):
async def __call__(self, scope, receive, send):
if scope["type"] == "lifespan":
return self.lifespan_handler(scope)
await self.lifespan_handler(scope, receive, send)
return
path = scope["path"]
root_path = scope.get("root_path", "")
@@ -241,50 +267,62 @@ class API:
scope["path"] = path[len(path_prefix) :]
scope["root_path"] = root_path + path_prefix
try:
return app(scope)
await app(scope, receive, send)
return
except TypeError:
app = WsgiToAsgi(app)
return app(scope)
app = WSGIMiddleware(app)
await app(scope, receive, send)
return
return self.app(scope)
await self.app(scope, receive, send)
def dispatch(self, scope):
# Call the main dispatcher.
async def asgi(receive, send):
nonlocal scope, self
async def asgi(self, scope, receive, send):
assert scope["type"] in ("http", "websocket")
if scope["type"] == "lifespan":
return self.lifespan_handler(scope)
elif scope["type"] == "websocket":
ws = WebSocket(scope=scope, receive=receive, send=send)
await self._dispatch_ws(ws)
else:
req = models.Request(scope, receive=receive, api=self)
resp = await self._dispatch_request(
req, scope=scope, send=send, receive=receive
)
await resp(receive, send)
if scope["type"] == "websocket":
await self._dispatch_ws(scope=scope, receive=receive, send=send)
else:
req = models.Request(scope, receive=receive, api=self)
resp = await self._dispatch_http(
req, scope=scope, send=send, receive=receive
)
await resp(scope, receive, send)
return asgi
async def _dispatch_http(self, req, **options):
# Set formats on Request object.
req.formats = self.formats
# Get the route.
route = self.path_matches_route(req.url.path)
route = self.routes.get(route)
if route:
resp = models.Response(req=req, formats=self.formats)
for before_request in self.before_http_requests:
await self.background(before_request, req=req, resp=resp)
await self._execute_route(route=route, req=req, resp=resp, **options)
else:
resp = models.Response(req=req, formats=self.formats)
self.default_response(req=req, resp=resp, notfound=True)
self.default_response(req=req, resp=resp)
self._prepare_session(resp)
return resp
async def _dispatch_ws(self, scope, receive, send):
ws = WebSocket(scope=scope, receive=receive, send=send)
async def _dispatch_ws(self, ws):
route = self.path_matches_route(ws.url.path)
route = self.routes.get(route)
try:
try:
# Run the view.
r = self.background(route.endpoint, ws)
# If it's async, await it.
if hasattr(r, "cr_running"):
await r
except TypeError as e:
cont = True
except Exception:
await self.background(
self.default_response, websocket=route.uses_websocket, error=True
)
raise
if route:
for before_request in self.before_ws_requests:
await self.background(before_request, ws=ws)
await self.background(route.endpoint, ws)
else:
await send({"type": "websocket.close", "code": 1000})
def add_schema(self, name, schema, check_existing=True):
"""Adds a mashmallow schema to the API specification."""
@@ -337,29 +375,6 @@ class API:
def no_response(req, resp, **params):
pass
async def _dispatch_request(self, req, **options):
# Set formats on Request object.
req.formats = self.formats
# Get the route.
route = self.path_matches_route(req.url.path)
route = self.routes.get(route)
if route:
resp = models.Response(req=req, formats=self.formats)
for before_request in self.before_requests:
await self._execute_route(route=before_request, req=req, resp=resp)
await self._execute_route(route=route, req=req, resp=resp, **options)
else:
resp = models.Response(req=req, formats=self.formats)
self.default_response(req=req, resp=resp, notfound=True)
self.default_response(req=req, resp=resp)
self._prepare_session(resp)
return resp
async def _execute_route(self, *, route, req, resp, **options):
params = route.incoming_matches(req.url.path)
@@ -443,22 +458,29 @@ class API:
:param static: If ``True``, and no endpoint was passed, render "static/index.html", and it will become a default route.
:param check_existing: If ``True``, an AssertionError will be raised, if the route is already defined.
"""
if before_request:
if websocket:
self.before_requests.setdefault("ws", []).append(endpoint)
else:
self.before_requests.setdefault("http", []).append(endpoint)
return
if route is None:
route = f"/{uuid4().hex}"
if check_existing:
assert route not in self.routes
if not endpoint and static:
endpoint = self.static_response
default = True
if static:
assert self.static_dir is not None
if not endpoint:
endpoint = self.static_response
default = True
if default:
self.default_endpoint = endpoint
self.routes[route] = Route(
route, endpoint, websocket=websocket, before_request=before_request
)
self.routes[route] = Route(route, endpoint, websocket=websocket)
# TODO: A better data structure or sort it once the app is loaded
self.routes = dict(
sorted(self.routes.items(), key=lambda item: item[1]._weight())
@@ -487,6 +509,9 @@ class API:
resp.html = self.docs
def static_response(self, req, resp):
assert self.static_dir is not None
index = (self.static_dir / "index.html").resolve()
if os.path.exists(index):
with open(index, "r") as f:
@@ -520,7 +545,7 @@ class API:
def on_event(self, event_type: str, **args):
"""Decorator for registering functions or coroutines to run at certain events
Supported events: startup, cleanup, shutdown, tick
Supported events: startup, shutdown
Usage::
@@ -528,11 +553,7 @@ class API:
async def open_database_connection_pool():
...
@api.on_event('tick', seconds=10)
async def do_stuff():
...
@api.on_event('cleanup')
@api.on_event('shutdown')
async def close_database_connection_pool():
...
@@ -598,6 +619,7 @@ class API:
def static_url(self, asset):
"""Given a static asset, return its URL path."""
assert None not in (self.static_dir, self.static_route)
return f"{self.static_route}/{str(asset)}"
@property
@@ -616,14 +638,11 @@ class API:
template = env.get_template("/".join([self.docs_theme, "index.html"]))
def static_url(asset):
return f"{self.static_route}/{asset}"
return template.render(
document=document,
langs=["javascript", "python"],
code_style=None,
static_url=static_url,
static_url=self.static_url,
schema_url="/schema.yml",
)
+17
View File
@@ -1,4 +1,5 @@
import json
from urllib.parse import urlencode
import yaml
from requests_toolbelt.multipart import decoder
@@ -9,6 +10,22 @@ from .models import QueryDict
async def format_form(r, encode=False):
if encode:
pass
elif "multipart/form-data" in r.headers.get("Content-Type"):
decode = decoder.MultipartDecoder(await r.content, r.mimetype)
querys = list()
for part in decode.parts:
header = part.headers.get(b"Content-Disposition").decode("utf-8")
text = part.text
for section in [h.strip() for h in header.split(";")]:
split = section.split("=")
if len(split) > 1:
key = split[1]
key = key[1:-1]
querys.append((key, text))
content = urlencode(querys)
return QueryDict(content)
else:
return QueryDict(await r.text)
+19 -12
View File
@@ -14,7 +14,7 @@ import yaml
from requests.structures import CaseInsensitiveDict
from requests.cookies import RequestsCookieJar
from starlette.datastructures import MutableHeaders
from starlette.requests import Request as StarletteRequest
from starlette.requests import Request as StarletteRequest, State
from starlette.responses import (
Response as StarletteResponse,
StreamingResponse as StarletteStreamingResponse,
@@ -178,6 +178,19 @@ class Request:
except AttributeError:
return QueryDict({})
@property
def state(self) -> State:
"""
Use the state to store additional information.
This can be a very helpful feature, if you want to hand over
information from a middelware or a route decorator to the
actual route handler.
For example: ``request.state.time_started = time.time()``
"""
return self._starlette.state
@property
async def encoding(self):
"""The encoding of the Request's body. Can be set, manually. Must be awaited."""
@@ -185,13 +198,7 @@ class Request:
if self._encoding:
return self._encoding
# Then try what's defined by the Request.
elif await self.declared_encoding:
return self.declared_encoding
# Then, automatically detect the encoding.
else:
return await self.apparent_encoding
return await self.apparent_encoding
@encoding.setter
def encoding(self, value):
@@ -221,8 +228,8 @@ class Request:
if declared_encoding:
return declared_encoding
else:
return chardet.detect(await self.content)["encoding"]
return chardet.detect(await self.content)["encoding"] or DEFAULT_ENCODING
@property
def is_secure(self):
@@ -360,7 +367,7 @@ class Response:
)
starlette_response.raw_headers.extend(cookie_header)
async def __call__(self, receive, send):
async def __call__(self, scope, receive, send):
body, headers = await self.body
if self.headers:
headers.update(self.headers)
@@ -373,4 +380,4 @@ class Response:
response = response_cls(body, status_code=self.status_code, headers=headers)
self._prepare_cookies(response)
await response(receive, send)
await response(scope, receive, send)
+17 -2
View File
@@ -1,7 +1,22 @@
import re
import functools
import inspect
from parse import parse
from parse import parse, with_pattern
def _make_convertor(type, pattern):
@with_pattern(pattern)
def inner(value):
return type(value)
return inner
_convertors = {
"int": _make_convertor(int, r"\d+"),
"str": _make_convertor(str, r"[^/]+"),
"float": _make_convertor(float, r"\d+(.\d+)?"),
}
class Route:
@@ -46,7 +61,7 @@ class Route:
@functools.lru_cache(maxsize=None)
def incoming_matches(self, s):
results = parse(self.route, s)
results = parse(self.route, s, _convertors)
return results.named if results else {}
def url(self, **params):
+4 -19
View File
@@ -22,8 +22,8 @@ if sys.argv[-1] == "publish":
sys.exit()
required = [
"starlette==0.10.*",
"uvicorn",
"starlette==0.12.*",
"uvicorn>=0.7, <0.9",
"aiofiles",
"pyyaml",
"requests",
@@ -31,13 +31,12 @@ required = [
"graphql-server-core>=1.1",
"jinja2",
"parse",
"uvloop; sys_platform != 'win32'",
"uvloop; sys_platform != 'win32' and sys_platform != 'cygwin' and sys_platform != 'cli'",
"rfc3986",
"python-multipart",
"chardet",
"apispec>=1.0.0b1",
"marshmallow",
"asgiref",
"whitenoise",
"docopt",
"itsdangerous",
@@ -123,21 +122,7 @@ setup(
url="https://github.com/kennethreitz/responder",
packages=find_packages(exclude=["tests"]),
entry_points={"console_scripts": ["responder=responder.cli:cli"]},
package_data={
# "": ["LICENSE", "NOTICES"],
# "pipenv.vendor.requests": ["*.pem"],
# "pipenv.vendor.certifi": ["*.pem"],
# "pipenv.vendor.click_completion": ["*.j2"],
# "pipenv.patched.notpip._vendor.certifi": ["*.pem"],
# "pipenv.patched.notpip._vendor.requests": ["*.pem"],
# "pipenv.patched.notpip._vendor.distlib._backport": ["sysconfig.cfg"],
# "pipenv.patched.notpip._vendor.distlib": [
# "t32.exe",
# "t64.exe",
# "w32.exe",
# "w64.exe",
# ],
},
package_data={},
python_requires=">=3.6",
setup_requires=[],
install_requires=required,
+146 -13
View File
@@ -8,7 +8,9 @@ import requests
import string
import io
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import PlainTextResponse
from starlette.testclient import TestClient as StarletteTestClient
def test_api_basic_route(api):
@@ -125,7 +127,7 @@ def test_yaml_media(api):
r = api.requests.get("http://;/", headers={"Accept": "yaml"})
assert "yaml" in r.headers["Content-Type"]
assert yaml.load(r.content) == dump
assert yaml.load(r.content, Loader=yaml.FullLoader) == dump
def test_graphql_schema_query_querying(api, schema):
@@ -221,7 +223,7 @@ def test_media_parsing(api):
assert r.json() == dump
r = api.requests.get(api.url_for(route), headers={"Accept": "application/x-yaml"})
assert r.text == "{hello: sam}\n"
assert r.text == "hello: sam\n"
def test_background(api):
@@ -304,6 +306,11 @@ def test_form_uploads(api):
r = api.requests.post(api.url_for(route), data=dump)
assert r.json() == dump
# requests with boundary
files = {"complicated": (None, "times")}
r = api.requests.post(api.url_for(route), files=files)
assert r.json() == {"complicated": "times"}
def test_json_downloads(api):
dump = {"testing": "123"}
@@ -353,7 +360,7 @@ def test_schema_generation():
200:
description: A pet to be returned
schema:
$ref = "#/components/schemas/Pet"
$ref: "#/components/schemas/Pet"
"""
resp.media = PetSchema().dump({"name": "little orange"})
@@ -406,7 +413,7 @@ def test_documentation():
200:
description: A pet to be returned
schema:
$ref = "#/components/schemas/Pet"
$ref: "#/components/schemas/Pet"
"""
resp.media = PetSchema().dump({"name": "little orange"})
@@ -522,13 +529,71 @@ def test_404(api):
assert r.status_code == responder.status_codes.HTTP_404
def test_kinda_websockets(api):
def test_websockets_text(api):
payload = "Hello via websocket!"
@api.route("/ws", websocket=True)
async def websocket(ws):
await ws.accept()
await ws.send_text("Hello via websocket!")
await ws.send_text(payload)
await ws.close()
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_text()
assert data == payload
def test_websockets_bytes(api):
payload = b"Hello via websocket!"
@api.route("/ws", websocket=True)
async def websocket(ws):
await ws.accept()
await ws.send_bytes(payload)
await ws.close()
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_bytes()
assert data == payload
def test_websockets_json(api):
payload = {"Hello": "via websocket!"}
@api.route("/ws", websocket=True)
async def websocket(ws):
await ws.accept()
await ws.send_json(payload)
await ws.close()
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_json()
assert data == payload
def test_before_websockets(api):
payload = {"Hello": "via websocket!"}
@api.route("/ws", websocket=True)
async def websocket(ws):
await ws.send_json(payload)
await ws.close()
@api.route(before_request=True, websocket=True)
async def before_request(ws):
await ws.accept()
await ws.send_json({"before": "request"})
client = StarletteTestClient(api)
with client.websocket_connect("ws://;/ws") as websocket:
data = websocket.receive_json()
assert data == {"before": "request"}
data = websocket.receive_json()
assert data == payload
def test_startup(api):
who = [None]
@@ -684,16 +749,13 @@ def test_staticfiles(tmpdir):
def test_staticfiles_custom_route(tmpdir):
static_dir = tmpdir.mkdir("static")
static_route = "custom/static/route/"
static_route = "/custom/static/route"
asset = create_asset(static_dir)
api = responder.API(static_dir=str(static_dir), static_route=static_route)
session = api.session()
# Check
assert api.static_route == "/custom/static/route"
static_route = api.static_route
# ok
@@ -709,6 +771,48 @@ def test_staticfiles_custom_route(tmpdir):
assert r.status_code == api.status_codes.HTTP_404
def test_staticfiles_none_dir(tmpdir):
api = responder.API(static_dir=None)
session = api.session()
static_dir = tmpdir.mkdir("static")
asset = create_asset(static_dir)
static_route = api.static_route
# ok
r = session.get(f"{static_route}/{asset.basename}")
assert r.status_code == api.status_codes.HTTP_404
# dir listing
r = session.get(f"{static_route}")
assert r.status_code == api.status_codes.HTTP_404
# SPA
with pytest.raises(Exception) as excinfo:
api.add_route("/spa", static=True)
def test_staticfiles_none_dir_route(tmpdir):
api = responder.API(static_dir=None, static_route=None)
session = api.session()
static_dir = tmpdir.mkdir("static")
asset = create_asset(static_dir)
static_route = api.static_route
# ok
r = session.get(f"{static_route}/{asset.basename}")
assert r.status_code == api.status_codes.HTTP_404
# dir listing
r = session.get(f"{static_route}")
assert r.status_code == api.status_codes.HTTP_404
def test_response_html_property(api):
@api.route("/")
def view(req, resp):
@@ -756,18 +860,47 @@ def test_stream(api, session):
def foo():
pass
res.stream(foo)
resp.stream(foo)
with pytest.raises(AssertionError):
async def foo():
pass
res.stream(foo)
resp.stream(foo)
with pytest.raises(AssertionError):
def foo():
yield "oopsie"
res.stream(foo)
resp.stream(foo)
def test_empty_req_text(api):
content = "It's working"
@api.route("/")
async def home(req, resp):
await req.text
resp.text = content
r = api.requests.post("/")
assert r.text == content
def test_api_request_state(api, url):
class StateMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
request.state.test1 = 42
request.state.test2 = "Foo"
response = await call_next(request)
return response
api.add_middleware(StateMiddleware)
@api.route("/")
def home(req, resp):
resp.text = "{}_{}".format(req.state.test2, req.state.test1)
assert api.requests.get(url("/")).text == "Foo_42"
+26
View File
@@ -131,3 +131,29 @@ def test_does_match_with_route(route, match, expected):
def test_weight(path_param, expected_weight):
r = routes.Route(path_param, "test_endpoint")
assert r._weight() == expected_weight
@pytest.mark.parametrize(
"route, path, expected_result",
[
pytest.param("/{greetings:str}", "/hello", {"greetings": "hello"}),
pytest.param(
"/{greetings:str}/{who}",
"/hello/Laidia",
{"greetings": "hello", "who": "Laidia"},
),
pytest.param("/{birth_date:int}", "/1937", {"birth_date": 1937}),
pytest.param(
"/{name:str}/{age:int}", "/Fatna/80", {"name": "Fatna", "age": 80}
),
pytest.param(
"/{x:float}/{y:float}", "/10.20/75", {"x": float(10.20), "y": float(75)}
),
pytest.param("/{name:str}/{age:int}", "/Fatna/eighty", {}),
pytest.param("/{greetings:int}", "/hello", {}),
pytest.param("/{name:float}", "/Fatna", {}),
],
)
def test_custom_specifiers(route, path, expected_result):
r = routes.Route(route, "test_endpoint")
assert r.incoming_matches(path) == expected_result