Compare commits

...

32 Commits

Author SHA1 Message Date
taoufik07 f1b2f46a10 v2.0.6 2021-01-06 09:02:42 +01:00
Taoufik a0913e3f63 Merge pull request #447 from timgates42/bugfix_typo_marshmallow
docs: fix simple typo, mashmallow -> marshmallow
2020-12-24 08:44:43 +01:00
Tim Gates f90955a9b9 docs: fix simple typo, mashmallow -> marshmallow
There is a small typo in responder/ext/schema/__init__.py.

Should read `marshmallow` rather than `mashmallow`.
2020-12-24 13:27:01 +11:00
Taoufik 3736c9229d Merge pull request #429 from taoufik07/dependabot/pip/docs/bleach-3.1.4
Bump bleach from 3.1.1 to 3.1.4 in /docs
2020-12-02 09:44:55 +01:00
dependabot[bot] a802853367 Bump bleach from 3.1.1 to 3.1.4 in /docs
Bumps [bleach](https://github.com/mozilla/bleach) from 3.1.1 to 3.1.4.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.1.1...v3.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-01 23:11:49 +00:00
Taoufik 96ca88fe88 Merge pull request #446 from taoufik07/github_actions
Switch to github actions
2020-12-02 00:11:05 +01:00
taoufik07 a57570210a Add prettier to pre-commit 2020-12-01 23:36:42 +01:00
taoufik07 7682e94b35 Disable tests in CI for windows 2020-12-01 23:10:24 +01:00
taoufik07 8bbebe113c Bump black 2020-12-01 23:10:24 +01:00
taoufik07 7c921f827b Switch to github actions 2020-12-01 23:10:24 +01:00
taoufik07 4cc055f93a Add pre-commit 2020-12-01 23:10:21 +01:00
Taoufik e596a8b457 Merge pull request #444 from majiang/patch-1
Call user-provided `default_response`
2020-12-01 21:42:13 +01:00
Taoufik fd2da55880 Merge pull request #445 from majiang/patch-2
test_redirects: access '/2' and redirect to '/1'
2020-12-01 21:37:28 +01:00
majiang 975e9b5643 test_redirects: access '/2' and redirect to '/1' 2020-12-01 16:18:56 +09:00
majiang c0036e0474 Call user-provided default_response
I'm not 100% sure, but it seems that user-provided `default_response`, stored as `Router.default_endpoint`, should be called when no match was found.
2020-12-01 16:15:57 +09:00
Taoufik 103816e27a Merge pull request #439 from ryuuji/master
bump uvicorn 0.11.* to 0.11.7
2020-08-12 11:01:13 +02:00
Ryuuji Yoshimoto b7c1684ab4 bump 0.11.7 2020-08-11 18:57:35 +09:00
Ryuuji Yoshimoto 16bd6ca266 bump uvicorn 2020-08-11 18:53:59 +09:00
Taoufik 20bae4712b Merge pull request #430 from ucpr/fix-lock
Fix hash in pygment from piwheel to pypi.
2020-04-17 05:40:29 +02:00
ucpr a7aa80c690 Fix hash in pygment from piwheel to pypi. 2020-04-15 00:30:34 +09:00
Taoufik df89d1d58b Merge pull request #424 from taoufik07/starlette_0_13
Fixes and bump dependencies
2020-03-09 05:32:30 +01:00
taoufik07 477cddd29c travisci add python 3.8 2020-03-09 05:26:48 +01:00
taoufik07 9b8cf3a1b1 Bump uvicorn version to 0.11 2020-03-09 05:16:34 +01:00
taoufik07 2871a3c07f starlette 0.13 and fix lifespan 2020-03-09 05:13:12 +01:00
Taoufik 13763296dd Merge pull request #421 from taoufik07/dependabot/pip/docs/bleach-3.1.1
Bump bleach from 3.0.2 to 3.1.1 in /docs
2020-02-25 02:24:11 +01:00
dependabot[bot] 783b22ab1c Bump bleach from 3.0.2 to 3.1.1 in /docs
Bumps [bleach](https://github.com/mozilla/bleach) from 3.0.2 to 3.1.1.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.0.2...v3.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-24 18:31:05 +00:00
Taoufik 109937adf4 Merge pull request #414 from taoufik07/v2.0.5
v2.0.5
2019-12-15 16:38:02 +01:00
taoufik 63ea9cc4e0 v2.0.5 2019-12-15 16:32:47 +01:00
Taoufik ec40a0c4c3 Merge pull request #413 from taoufik07/support_python3.8
Update requirements to support python3.8
2019-12-15 16:28:17 +01:00
taoufik 0855d1a378 Update requirements to support python3.8 2019-12-15 16:22:26 +01:00
Taoufik 77fe17d350 Merge pull request #411 from StevenAvelino24/feature/use-openapi-params
Use OpenAPI info params on init of API
2019-12-04 20:07:46 +01:00
Steven Avelino 0b8a031ccb Use openapi info params on init of API 2019-12-04 14:50:30 +01:00
62 changed files with 4893 additions and 676 deletions
+23
View File
@@ -0,0 +1,23 @@
name: Lint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install dependencies
run: |
python -m pip install pipenv
pipenv install --dev --system
- name: Lint
run: pre-commit run --all-files --show-diff-on-failure
+30
View File
@@ -0,0 +1,30 @@
name: Test
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install pipenv
pipenv install --dev --system
- name: Tests
run: |
python --version
pytest
+13
View File
@@ -0,0 +1,13 @@
repos:
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: black
types: [python]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
hooks:
- id: prettier
args: [--prose-wrap=always, --print-width=88]
exclude: ^docs/source/_static/
-18
View File
@@ -1,18 +0,0 @@
# travis use trusty by default
dist: xenial
language: python
python:
- 3.6
- 3.7
- "3.8-dev"
# command to install dependencies
install:
- pip install pipenv --upgrade-strategy=only-if-needed
- pipenv install --dev
# command to run the dependencies
script:
- black responder tests setup.py --check
- pytest
+110 -3
View File
@@ -1,233 +1,340 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
## [v2.0.5] - 2019-12-15
### Added
- Update requirements to support python 3.8
## [v2.0.4] - 2019-11-19 ## [v2.0.4] - 2019-11-19
### Fixed ### Fixed
- Fix static app resolving - Fix static app resolving
## [v2.0.3] - 2019-09-20 ## [v2.0.3] - 2019-09-20
### Fixed ### Fixed
- Fix template conflicts - Fix template conflicts
## [v2.0.2] - 2019-09-20 ## [v2.0.2] - 2019-09-20
### Fixed ### Fixed
- Fix template conflicts - Fix template conflicts
## [v2.0.1] - 2019-09-20 ## [v2.0.1] - 2019-09-20
### Fixed ### Fixed
- Fix template import - Fix template import
## [v2.0.0] - 2019-09-19 ## [v2.0.0] - 2019-09-19
### Changed ### Changed
- Refactor Router and Schema - Refactor Router and Schema
## [v1.3.2] - 2019-08-15 ## [v1.3.2] - 2019-08-15
### Added ### Added
- ASGI 3 support - ASGI 3 support
- CI tests for python 3.8-dev - CI tests for python 3.8-dev
- Now requests have `state` a mapping object - Now requests have `state` a mapping object
### Deprecated ### Deprecated
- ASGI 2 - ASGI 2
## [v1.3.1] - 2019-04-28 ## [v1.3.1] - 2019-04-28
### Added ### Added
- Route params Converters - Route params Converters
- Add search for documentation pages - Add search for documentation pages
### Changed ### Changed
- Bump dependencies - Bump dependencies
## [v1.3.0] - 2019-02-22 ## [v1.3.0] - 2019-02-22
### Fixed ### Fixed
- Versioning issue - Versioning issue
- Multiple cookies. - Multiple cookies.
- Whitenoise returns not found. - Whitenoise returns not found.
- Other bugfixes. - Other bugfixes.
### Added ### Added
- Stream support via `resp.stream`. - Stream support via `resp.stream`.
- Cookie directives via `resp.set_cookie`. - Cookie directives via `resp.set_cookie`.
- Add `resp.html` to send HTML. - Add `resp.html` to send HTML.
- Other improvements. - Other improvements.
## [v1.1.3] - 2019-01-12 ## [v1.1.3] - 2019-01-12
### Changed ### Changed
- Refactor `_route_for` - Refactor `_route_for`
### Fixed ### Fixed
- Resolve startup/shutdwown events - Resolve startup/shutdwown events
## [v1.2.0] - 2018-12-29 ## [v1.2.0] - 2018-12-29
### Added ### Added
- Documentations - Documentations
### Changed ### Changed
- Use Starlette's LifeSpan middleware - Use Starlette's LifeSpan middleware
- Update denpendencies - Update denpendencies
### Fixed ### Fixed
- Fix route.is_class_based - Fix route.is_class_based
- Fix test_500 - Fix test_500
- Typos - Typos
## [v1.1.2] - 2018-11-11 ## [v1.1.2] - 2018-11-11
### Fixed ### Fixed
- Minor fixes for Open API - Minor fixes for Open API
- Typos - Typos
## [v1.1.1] - 2018-10-29 ## [v1.1.1] - 2018-10-29
### Changed ### Changed
- Run sync views in a threadpoolexecutor. - Run sync views in a threadpoolexecutor.
## [v1.1.0] - 2018-10-27 ## [v1.1.0] - 2018-10-27
### Added ### Added
- Support for `before_request`. - Support for `before_request`.
## [v1.0.5]- 2018-10-27 ## [v1.0.5]- 2018-10-27
### Fixed ### Fixed
- Fix sessions. - Fix sessions.
## [v1.0.4] - 2018-10-27 ## [v1.0.4] - 2018-10-27
### Fixed ### Fixed
- Potential bufix for cookies. - Potential bufix for cookies.
## [v1.0.3] - 2018-10-27 ## [v1.0.3] - 2018-10-27
### Fixed ### Fixed
- Bugfix for redirects. - Bugfix for redirects.
## [v1.0.2] - 2018-10-27 ## [v1.0.2] - 2018-10-27
### Changed ### Changed
- Improvement for static file hosting. - Improvement for static file hosting.
## [v1.0.1] - 2018-10-26 ## [v1.0.1] - 2018-10-26
### Changed ### Changed
- Improve cors configuration settings. - Improve cors configuration settings.
## [v1.0.0] - 2018-10-26 ## [v1.0.0] - 2018-10-26
### Changed ### Changed
- Move GraphQL support into a built-in plugin. - Move GraphQL support into a built-in plugin.
## [v0.3.3] - 2018-10-25 ## [v0.3.3] - 2018-10-25
### Added ### Added
- CORS support - CORS support
### Changed ### Changed
- Improved exceptions. - Improved exceptions.
## [v0.3.2] - 2018-10-25 ## [v0.3.2] - 2018-10-25
### Changed ### Changed
- Subtle improvements. - Subtle improvements.
## [v0.3.1] - 2018-10-24 ## [v0.3.1] - 2018-10-24
### Fixed ### Fixed
- Packaging fix. - Packaging fix.
## [v0.3.0] - 2018-10-24 ## [v0.3.0] - 2018-10-24
### Changed ### Changed
- Interactive Documentation endpoint. - Interactive Documentation endpoint.
- Minor improvements. - Minor improvements.
## [v0.2.3] - 2018-10-24 ## [v0.2.3] - 2018-10-24
### Changed ### Changed
- Overall improvements. - Overall improvements.
## [v0.2.2] - 2018-10-23 ## [v0.2.2] - 2018-10-23
### Added ### Added
- Show traceback info when background tasks raise exceptions. - Show traceback info when background tasks raise exceptions.
## [v0.2.1] - 2018-10-23 ## [v0.2.1] - 2018-10-23
### Added ### Added
- api.requests. - api.requests.
## [v0.2.0] - 2018-10-22 ## [v0.2.0] - 2018-10-22
### Added ### Added
- WebSocket support. - WebSocket support.
## [v0.1.6] - 2018-10-20 ## [v0.1.6] - 2018-10-20
### Added ### Added
- 500 support. - 500 support.
## [v0.1.5] - 2018-10-20 ## [v0.1.5] - 2018-10-20
### Added ### Added
- File upload support - File upload support
### Changed ### Changed
- Improvements to sequential media reading. - Improvements to sequential media reading.
## [v0.1.4] - 2018-10-19 ## [v0.1.4] - 2018-10-19
### Fixed ### Fixed
- Stability. - Stability.
## [v0.1.3] - 2018-10-18 ## [v0.1.3] - 2018-10-18
### Added ### Added
- Sessions support. - Sessions support.
## [v0.1.2] - 2018-10-18 ## [v0.1.2] - 2018-10-18
### Added ### Added
- Cookies support. - Cookies support.
## [v0.1.1] - 2018-10-17 ## [v0.1.1] - 2018-10-17
### Changed ### Changed
- Default routes. - Default routes.
## [v0.1.0] - 2018-10-17 ## [v0.1.0] - 2018-10-17
### Added ### Added
- Prototype of static application support. - Prototype of static application support.
## [v0.0.10] - 2018-10-17 ## [v0.0.10] - 2018-10-17
### Fixed ### Fixed
- Bugfix for async class-based views. - Bugfix for async class-based views.
## [v0.0.9] - 2018-10-17 ## [v0.0.9] - 2018-10-17
### Fixed ### Fixed
- Bugfix for async class-based views. - Bugfix for async class-based views.
## [v0.0.8] - 2018-10-17 ## [v0.0.8] - 2018-10-17
### Added ### Added
- GraphiQL Support. - GraphiQL Support.
### Changed ### Changed
- Improvement to route selection. - Improvement to route selection.
## [v0.0.7] - 2018-10-16 ## [v0.0.7] - 2018-10-16
### Changed ### Changed
- Immutable Request object. - Immutable Request object.
## [v0.0.6] - 2018-10-16 ## [v0.0.6] - 2018-10-16
### Added ### Added
- Ability to mount WSGI apps. - Ability to mount WSGI apps.
- Supply content-type when serving up the schema. - Supply content-type when serving up the schema.
## [v0.0.5] - 2018-10-15 ## [v0.0.5] - 2018-10-15
### Added ### Added
- OpenAPI Schema support. - OpenAPI Schema support.
- Safe load/dump yaml. - Safe load/dump yaml.
## [v0.0.4] - 2018-10-15 ## [v0.0.4] - 2018-10-15
### Added ### Added
- Asynchronous support for data uploads. - Asynchronous support for data uploads.
### Fixed ### Fixed
- Bug fixes. - Bug fixes.
## [v0.0.3] - 2018-10-13 ## [v0.0.3] - 2018-10-13
### Fixed ### Fixed
- Bug fixes. - Bug fixes.
## [v0.0.2] - 2018-10-13 ## [v0.0.2] - 2018-10-13
### Changed ### Changed
- Switch to ASGI/Starlette. - Switch to ASGI/Starlette.
## [v0.0.1] - 2018-10-12 ## [v0.0.1] - 2018-10-12
### Added ### Added
- Conception! - Conception!
[Unreleased]: https://github.com/taoufik07/responder/compare/v2.0.4..HEAD [unreleased]: https://github.com/taoufik07/responder/compare/v2.0.5..HEAD
[v2.0.5]: https://github.com/taoufik07/responder/compare/v2.0.4..v2.0.5
[v2.0.4]: https://github.com/taoufik07/responder/compare/v2.0.3..v2.0.4 [v2.0.4]: https://github.com/taoufik07/responder/compare/v2.0.3..v2.0.4
[v2.0.3]: https://github.com/taoufik07/responder/compare/v2.0.2..v2.0.3 [v2.0.3]: https://github.com/taoufik07/responder/compare/v2.0.2..v2.0.3
[v2.0.2]: https://github.com/taoufik07/responder/compare/v2.0.1..v2.0.2 [v2.0.2]: https://github.com/taoufik07/responder/compare/v2.0.1..v2.0.2
+2 -1
View File
@@ -5,11 +5,12 @@ name = "pypi"
[packages] [packages]
responder = {editable = true, path = "."} responder = {editable = true, path = "."}
pre-commit = "*"
[dev-packages] [dev-packages]
pytest = "*" pytest = "*"
"flake8" = "*" "flake8" = "*"
black = "*" black = "==20.8b1"
twine = "*" twine = "*"
flask = "*" flask = "*"
sphinx = "*" sphinx = "*"
Generated
+466 -294
View File
File diff suppressed because it is too large Load Diff
+35 -20
View File
@@ -9,45 +9,52 @@
[![](https://farm2.staticflickr.com/1959/43750081370_a4e20752de_o_d.png)](https://responder.readthedocs.io) [![](https://farm2.staticflickr.com/1959/43750081370_a4e20752de_o_d.png)](https://responder.readthedocs.io)
Powered by [Starlette](https://www.starlette.io/). That `async` declaration is optional.
[View documentation](https://responder.readthedocs.io).
Powered by [Starlette](https://www.starlette.io/). That `async` declaration is optional. [View documentation](https://responder.readthedocs.io). This gets you a ASGI app, with a production static files server pre-installed, jinja2
templating (without additional imports), and a production webserver based on uvloop,
This gets you a ASGI app, with a production static files server pre-installed, jinja2 templating (without additional imports), and a production webserver based on uvloop, serving up requests with gzip compression automatically. serving up requests with gzip compression automatically.
## Testimonials ## Testimonials
> "Pleasantly very taken with python-responder. [@kennethreitz](https://twitter.com/kennethreitz) at his absolute best." —Rudraksh M.K. > "Pleasantly very taken with python-responder.
> [@kennethreitz](https://twitter.com/kennethreitz) at his absolute best." —Rudraksh
> M.K.
> "ASGI is going to enable all sorts of new high-performance web services. It's awesome to see Responder starting to take advantage of that." — Tom Christie author of [Django REST Framework](https://www.django-rest-framework.org/) > "ASGI is going to enable all sorts of new high-performance web services. It's awesome
> to see Responder starting to take advantage of that." — Tom Christie author of
> "I love that you are exploring new patterns. Go go go!" — Danny Greenfield, author of [Two Scoops of Django]() > [Django REST Framework](https://www.django-rest-framework.org/)
> "I love that you are exploring new patterns. Go go go!" — Danny Greenfield, author of
> [Two Scoops of Django]()
## More Examples ## More Examples
See [the documentation's feature tour](https://responder.readthedocs.io/en/latest/tour.html) for more details on features available in Responder. See
[the documentation's feature tour](https://responder.readthedocs.io/en/latest/tour.html)
for more details on features available in Responder.
# Installing Responder # Installing Responder
Install the stable release: Install the stable release:
$ pipenv install responder $ pipenv install responder
✨🍰✨ ✨🍰✨
Or, install from the development branch: Or, install from the development branch:
$ pipenv install -e git+https://github.com/taoufik07/responder.git#egg=responder $ pipenv install -e git+https://github.com/taoufik07/responder.git#egg=responder
Only **Python 3.6+** is supported. Only **Python 3.6+** is supported.
# The Basic Idea # The Basic Idea
The primary concept here is to bring the niceties that are brought forth from both Flask and Falcon and unify them into a single framework, along with some new ideas I have. I also wanted to take some of the API primitives that are instilled in the Requests library and put them into a web framework. So, you'll find a lot of parallels here with Requests. The primary concept here is to bring the niceties that are brought forth from both Flask
and Falcon and unify them into a single framework, along with some new ideas I have. I
also wanted to take some of the API primitives that are instilled in the Requests
library and put them into a web framework. So, you'll find a lot of parallels here with
Requests.
- Setting `resp.content` sends back bytes. - Setting `resp.content` sends back bytes.
- Setting `resp.text` sends back unicode, while setting `resp.html` sends back HTML. - Setting `resp.text` sends back unicode, while setting `resp.html` sends back HTML.
@@ -55,16 +62,24 @@ The primary concept here is to bring the niceties that are brought forth from bo
- Case-insensitive `req.headers` dict (from Requests directly). - Case-insensitive `req.headers` dict (from Requests directly).
- `resp.status_code`, `req.method`, `req.url`, and other familiar friends. - `resp.status_code`, `req.method`, `req.url`, and other familiar friends.
## Ideas ## Ideas
- Flask-style route expression, with new capabilities -- all while using Python 3.6+'s new f-string syntax. - Flask-style route expression, with new capabilities -- all while using Python 3.6+'s
- I love Falcon's "every request and response is passed into to each view and mutated" methodology, especially `response.media`, and have used it here. In addition to supporting JSON, I have decided to support YAML as well, as Kubernetes is slowly taking over the world, and it uses YAML for all the things. Content-negotiation and all that. new f-string syntax.
- I love Falcon's "every request and response is passed into to each view and mutated"
methodology, especially `response.media`, and have used it here. In addition to
supporting JSON, I have decided to support YAML as well, as Kubernetes is slowly
taking over the world, and it uses YAML for all the things. Content-negotiation and
all that.
- **A built in testing client that uses the actual Requests you know and love**. - **A built in testing client that uses the actual Requests you know and love**.
- The ability to mount other WSGI apps easily. - The ability to mount other WSGI apps easily.
- Automatic gzipped-responses. - 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. - 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 file server is built-in. - A production static file 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 built-in as a production web server. I would have chosen Gunicorn, but it
- GraphQL support, via Graphene. The goal here is to have any GraphQL query exposable at any route, magically. 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. - Provide an official way to run webpack.
+1 -1
View File
@@ -4,7 +4,7 @@ atomicwrites==1.2.1
attrs==18.2.0 attrs==18.2.0
babel==2.6.0 babel==2.6.0
black==18.9b0 black==18.9b0
bleach==3.0.2 bleach==3.1.4
certifi==2018.8.24 certifi==2018.8.24
cffi==1.11.5 cffi==1.11.5
chardet==3.0.4 chardet==3.0.4
+4 -4
View File
@@ -1,7 +1,7 @@
/* Hide module name and default value for environment variable section */ /* Hide module name and default value for environment variable section */
div[id$='environment-variables'] code.descclassname { div[id$="environment-variables"] code.descclassname {
display: none; display: none;
} }
div[id$='environment-variables'] em.property { div[id$="environment-variables"] em.property {
display: none; display: none;
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+142 -132
View File
@@ -10,142 +10,152 @@
*/ */
var Konami = function (callback) { var Konami = function (callback) {
var konami = { var konami = {
addEvent: function (obj, type, fn, ref_obj) { addEvent: function (obj, type, fn, ref_obj) {
if (obj.addEventListener) if (obj.addEventListener) obj.addEventListener(type, fn, false);
obj.addEventListener(type, fn, false); else if (obj.attachEvent) {
else if (obj.attachEvent) { // IE
// IE obj["e" + type + fn] = fn;
obj["e" + type + fn] = fn; obj[type + fn] = function () {
obj[type + fn] = function () { obj["e" + type + fn](window.event, ref_obj);
obj["e" + type + fn](window.event, ref_obj); };
} obj.attachEvent("on" + type, obj[type + fn]);
obj.attachEvent("on" + type, obj[type + fn]); }
} },
}, removeEvent: function (obj, eventName, eventCallback) {
removeEvent: function (obj, eventName, eventCallback) { if (obj.removeEventListener) {
if (obj.removeEventListener) { obj.removeEventListener(eventName, eventCallback);
obj.removeEventListener(eventName, eventCallback); } else if (obj.attachEvent) {
} else if (obj.attachEvent) { obj.detachEvent(eventName);
obj.detachEvent(eventName); }
} },
}, input: "",
input: "", pattern: "38384040373937396665",
pattern: "38384040373937396665", keydownHandler: function (e, ref_obj) {
keydownHandler: function (e, ref_obj) { if (ref_obj) {
if (ref_obj) { konami = ref_obj;
konami = ref_obj; } // IE
} // IE konami.input += e ? e.keyCode : event.keyCode;
konami.input += e ? e.keyCode : event.keyCode; if (konami.input.length > konami.pattern.length) {
if (konami.input.length > konami.pattern.length) { konami.input = konami.input.substr(konami.input.length - konami.pattern.length);
konami.input = konami.input.substr((konami.input.length - konami.pattern.length)); }
} if (konami.input === konami.pattern) {
if (konami.input === konami.pattern) { konami.code(konami._currentLink);
konami.code(konami._currentLink); konami.input = "";
konami.input = ''; e.preventDefault();
e.preventDefault(); return false;
return false; }
} },
}, load: function (link) {
load: function (link) { this._currentLink = link;
this._currentLink = link; this.addEvent(document, "keydown", this.keydownHandler, this);
this.addEvent(document, "keydown", this.keydownHandler, this); this.iphone.load(link);
this.iphone.load(link); },
}, unload: function () {
unload: function () { this.removeEvent(document, "keydown", this.keydownHandler);
this.removeEvent(document, 'keydown', this.keydownHandler); this.iphone.unload();
this.iphone.unload(); },
}, code: function (link) {
code: function (link) { window.location = link;
window.location = link },
}, iphone: {
iphone: { start_x: 0,
start_x: 0, start_y: 0,
start_y: 0, stop_x: 0,
stop_x: 0, stop_y: 0,
stop_y: 0, tap: false,
tap: false, capture: false,
capture: false, orig_keys: "",
orig_keys: "", keys: [
keys: ["UP", "UP", "DOWN", "DOWN", "LEFT", "RIGHT", "LEFT", "RIGHT", "TAP", "TAP"], "UP",
input: [], "UP",
code: function (link) { "DOWN",
konami.code(link); "DOWN",
}, "LEFT",
touchmoveHandler: function (e) { "RIGHT",
if (e.touches.length === 1 && konami.iphone.capture === true) { "LEFT",
var touch = e.touches[0]; "RIGHT",
konami.iphone.stop_x = touch.pageX; "TAP",
konami.iphone.stop_y = touch.pageY; "TAP",
konami.iphone.tap = false; ],
konami.iphone.capture = false; input: [],
konami.iphone.check_direction(); code: function (link) {
} konami.code(link);
}, },
touchendHandler: function () { touchmoveHandler: function (e) {
konami.iphone.input.push(konami.iphone.check_direction()); if (e.touches.length === 1 && konami.iphone.capture === true) {
var touch = e.touches[0];
if (konami.iphone.input.length > konami.iphone.keys.length) konami.iphone.input.shift(); konami.iphone.stop_x = touch.pageX;
konami.iphone.stop_y = touch.pageY;
if (konami.iphone.input.length === konami.iphone.keys.length) { konami.iphone.tap = false;
var match = true; konami.iphone.capture = false;
for (var i = 0; i < konami.iphone.keys.length; i++) { konami.iphone.check_direction();
if (konami.iphone.input[i] !== konami.iphone.keys[i]) {
match = false;
}
}
if (match) {
konami.iphone.code(konami._currentLink);
}
}
},
touchstartHandler: function (e) {
konami.iphone.start_x = e.changedTouches[0].pageX;
konami.iphone.start_y = e.changedTouches[0].pageY;
konami.iphone.tap = true;
konami.iphone.capture = true;
},
load: function (link) {
this.orig_keys = this.keys;
konami.addEvent(document, "touchmove", this.touchmoveHandler);
konami.addEvent(document, "touchend", this.touchendHandler, false);
konami.addEvent(document, "touchstart", this.touchstartHandler);
},
unload: function () {
konami.removeEvent(document, 'touchmove', this.touchmoveHandler);
konami.removeEvent(document, 'touchend', this.touchendHandler);
konami.removeEvent(document, 'touchstart', this.touchstartHandler);
},
check_direction: function () {
x_magnitude = Math.abs(this.start_x - this.stop_x);
y_magnitude = Math.abs(this.start_y - this.stop_y);
x = ((this.start_x - this.stop_x) < 0) ? "RIGHT" : "LEFT";
y = ((this.start_y - this.stop_y) < 0) ? "DOWN" : "UP";
result = (x_magnitude > y_magnitude) ? x : y;
result = (this.tap === true) ? "TAP" : result;
return result;
}
} }
} },
touchendHandler: function () {
konami.iphone.input.push(konami.iphone.check_direction());
typeof callback === "string" && konami.load(callback); if (konami.iphone.input.length > konami.iphone.keys.length)
if (typeof callback === "function") { konami.iphone.input.shift();
konami.code = callback;
konami.load();
}
return konami; if (konami.iphone.input.length === konami.iphone.keys.length) {
var match = true;
for (var i = 0; i < konami.iphone.keys.length; i++) {
if (konami.iphone.input[i] !== konami.iphone.keys[i]) {
match = false;
}
}
if (match) {
konami.iphone.code(konami._currentLink);
}
}
},
touchstartHandler: function (e) {
konami.iphone.start_x = e.changedTouches[0].pageX;
konami.iphone.start_y = e.changedTouches[0].pageY;
konami.iphone.tap = true;
konami.iphone.capture = true;
},
load: function (link) {
this.orig_keys = this.keys;
konami.addEvent(document, "touchmove", this.touchmoveHandler);
konami.addEvent(document, "touchend", this.touchendHandler, false);
konami.addEvent(document, "touchstart", this.touchstartHandler);
},
unload: function () {
konami.removeEvent(document, "touchmove", this.touchmoveHandler);
konami.removeEvent(document, "touchend", this.touchendHandler);
konami.removeEvent(document, "touchstart", this.touchstartHandler);
},
check_direction: function () {
x_magnitude = Math.abs(this.start_x - this.stop_x);
y_magnitude = Math.abs(this.start_y - this.stop_y);
x = this.start_x - this.stop_x < 0 ? "RIGHT" : "LEFT";
y = this.start_y - this.stop_y < 0 ? "DOWN" : "UP";
result = x_magnitude > y_magnitude ? x : y;
result = this.tap === true ? "TAP" : result;
return result;
},
},
};
typeof callback === "string" && konami.load(callback);
if (typeof callback === "function") {
konami.code = callback;
konami.load();
}
return konami;
}; };
if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = Konami;
module.exports = Konami;
} else { } else {
if (typeof define === 'function' && define.amd) { if (typeof define === "function" && define.amd) {
define([], function() { define([], function () {
return Konami; return Konami;
}); });
} else { } else {
window.Konami = Konami; window.Konami = Konami;
} }
} }
+71 -37
View File
@@ -1,5 +1,11 @@
<link rel="stylesheet" type="text/css" href="https://cloud.typography.com/7584432/7586812/css/fonts.css" /> <link
<script type="text/javascript">$('#searchbox').hide(0);</script> rel="stylesheet"
type="text/css"
href="https://cloud.typography.com/7584432/7586812/css/fonts.css"
/>
<script type="text/javascript">
$("#searchbox").hide(0);
</script>
<!--Alabaster (krTheme++) Hacks --> <!--Alabaster (krTheme++) Hacks -->
<!-- CSS Adjustments (I'm very picky.) --> <!-- CSS Adjustments (I'm very picky.) -->
@@ -39,9 +45,7 @@
} }
.method { .method {
margin-bottom: 2em; margin-bottom: 2em;
} }
.si, .si,
@@ -80,8 +84,6 @@
margin-top: -1em; margin-top: -1em;
} }
/* "Quick Search" should be not be shown for now. */ /* "Quick Search" should be not be shown for now. */
div#searchbox h3 { div#searchbox h3 {
display: none; display: none;
@@ -118,10 +120,12 @@
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-127383416-1"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-127383416-1"></script>
<script> <script>
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); } function gtag() {
gtag('js', new Date()); dataLayer.push(arguments);
}
gtag("js", new Date());
gtag('config', 'UA-127383416-1'); gtag("config", "UA-127383416-1");
</script> </script>
<!-- There are no more hacks. --> <!-- There are no more hacks. -->
@@ -130,7 +134,10 @@
<script src="{{ pathto('_static/', 1) }}/konami.js"></script> <script src="{{ pathto('_static/', 1) }}/konami.js"></script>
<script> <script>
var easter_egg = new Konami('https://www.myfortunecookie.co.uk/fortunes/' + (Math.floor(Math.random() * 152) + 1)); var easter_egg = new Konami(
"https://www.myfortunecookie.co.uk/fortunes/" +
(Math.floor(Math.random() * 152) + 1)
);
</script> </script>
<style> <style>
@@ -140,67 +147,94 @@
</style> </style>
<!-- GitHub Logo --> <!-- GitHub Logo -->
<a href="https://github.com/kennethreitz/responder" class="github-corner" aria-label="View source on GitHub"> <a
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" href="https://github.com/kennethreitz/responder"
aria-hidden="true"> class="github-corner"
aria-label="View source on GitHub"
>
<svg
width="80"
height="80"
viewBox="0 0 250 250"
style="fill: #151513; color: #fff; position: absolute; top: 0; border: 0; right: 0"
aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path> <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" <path
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path> d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor"
fill="currentColor" class="octo-body"></path> style="transform-origin: 130px 106px"
class="octo-arm"
></path>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"
></path>
</svg> </svg>
</a> </a>
<style> <style>
.github-corner:hover .octo-arm { .github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out animation: octocat-wave 560ms ease-in-out;
} }
@keyframes octocat-wave { @keyframes octocat-wave {
0%, 0%,
100% { 100% {
transform: rotate(0) transform: rotate(0);
} }
20%, 20%,
60% { 60% {
transform: rotate(-25deg) transform: rotate(-25deg);
} }
40%, 40%,
80% { 80% {
transform: rotate(10deg) transform: rotate(10deg);
} }
} }
@media (max-width:500px) { @media (max-width: 500px) {
.github-corner:hover .octo-arm { .github-corner:hover .octo-arm {
animation: none animation: none;
} }
.github-corner .octo-arm { .github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out animation: octocat-wave 560ms ease-in-out;
} }
} }
</style> </style>
<!-- That was not a hack. That was art. <!-- That was not a hack. That was art.
<!-- UserVoice JavaScript SDK (only needed once on a page) --> <!-- UserVoice JavaScript SDK (only needed once on a page) -->
<script>(function () { var uv = document.createElement('script'); uv.type = 'text/javascript'; uv.async = true; uv.src = '//widget.uservoice.com/f4AQraEfwInlMzkexfRLg.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(uv, s) })()</script> <script>
(function () {
var uv = document.createElement("script");
uv.type = "text/javascript";
uv.async = true;
uv.src = "//widget.uservoice.com/f4AQraEfwInlMzkexfRLg.js";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(uv, s);
})();
</script>
<!-- A tab to launch the Classic Widget --> <!-- A tab to launch the Classic Widget -->
<script> <script>
UserVoice = window.UserVoice || []; UserVoice = window.UserVoice || [];
UserVoice.push(['showTab', 'classic_widget', { UserVoice.push([
mode: 'feedback', "showTab",
primary_color: '#fa8c28', "classic_widget",
link_color: '#0a8cc6', {
forum_id: 913660, mode: "feedback",
tab_label: 'Got feedback?', primary_color: "#fa8c28",
tab_color: '#00994f', link_color: "#0a8cc6",
tab_position: 'bottom-left', forum_id: 913660,
tab_inverted: true tab_label: "Got feedback?",
}]); tab_color: "#00994f",
tab_position: "bottom-left",
tab_inverted: true,
},
]);
</script> </script>
+67 -28
View File
@@ -1,54 +1,93 @@
<p class="logo"> <p class="logo">
<a href="{{ pathto(master_doc) }}"> <a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/responder.png', 1) }}" title="https://kennethreitz.org/tattoos" /> <img
class="logo"
src="{{ pathto('_static/responder.png', 1) }}"
title="https://kennethreitz.org/tattoos"
/>
</a> </a>
</p> </p>
<p> <p>
<iframe src="https://ghbtns.com/github-btn.html?user=kennethreitz&repo=responder&type=watch&count=true&size=large" <iframe
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></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> </p>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" /> <link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"
/>
<style> <style>
.algolia-autocomplete{ .algolia-autocomplete {
width: 100%; width: 100%;
height: 1.5em height: 1.5em;
} }
.algolia-autocomplete a{ .algolia-autocomplete a {
border-bottom: none !important; border-bottom: none !important;
} }
#doc_search{ #doc_search {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
<input id="doc_search" placeholder="Search the doc" autofocus/> <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({ <script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"
onload="docsearch({
apiKey: 'ac965312db252e0496283c75c6f76f0b', apiKey: 'ac965312db252e0496283c75c6f76f0b',
indexName: 'python-responder', indexName: 'python-responder',
inputSelector: '#doc_search', inputSelector: '#doc_search',
debug: false // Set debug to true if you want to inspect the dropdown debug: false // Set debug to true if you want to inspect the dropdown
})" async></script> })"
async
></script>
<p> <p><strong>Responder</strong> is a web service framework, written for human beings.</p>
<strong>Responder</strong> is a web service framework, written for human beings.
</p>
<h3>Stay Informed</h3> <h3>Stay Informed</h3>
<p>Receive updates on new releases and upcoming projects.</p> <p>Receive updates on new releases and upcoming projects.</p>
<p><iframe src="https://ghbtns.com/github-btn.html?user=kennethreitz&type=follow&count=true" allowtransparency="true" <p>
frameborder="0" scrolling="0" width="200" height="20"></iframe></p> <iframe
src="https://ghbtns.com/github-btn.html?user=kennethreitz&type=follow&count=true"
<p><a href="https://twitter.com/kennethreitz" class="twitter-follow-button" data-show-count="false">Follow allowtransparency="true"
@kennethreitz</a> frameborder="0"
<script>!function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https'; if (!d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = p + '://platform.twitter.com/widgets.js'; fjs.parentNode.insertBefore(js, fjs); } }(document, 'script', 'twitter-wjs');</script> scrolling="0"
width="200"
height="20"
></iframe>
</p> </p>
<p>
<a
href="https://twitter.com/kennethreitz"
class="twitter-follow-button"
data-show-count="false"
>Follow @kennethreitz</a
>
<script>
!(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0],
p = /^http:/.test(d.location) ? "http" : "https";
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + "://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
})(document, "script", "twitter-wjs");
</script>
</p>
<h3>Useful Links</h3> <h3>Useful Links</h3>
<ul> <ul>
<li><a href="http://github.com/kennethreitz/responder">Responder @ GitHub</a></li> <li><a href="http://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="http://pypi.python.org/pypi/responder">Responder @ PyPI</a></li> <li><a href="http://pypi.python.org/pypi/responder">Responder @ PyPI</a></li>
<li><a href="http://github.com/kennethreitz/responder/issues">Issue Tracker</a></li> <li><a href="http://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
+67 -28
View File
@@ -1,54 +1,93 @@
<p class="logo"> <p class="logo">
<a href="{{ pathto(master_doc) }}"> <a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/responder.png', 1) }}" title="https://kennethreitz.org/tattoos" /> <img
class="logo"
src="{{ pathto('_static/responder.png', 1) }}"
title="https://kennethreitz.org/tattoos"
/>
</a> </a>
</p> </p>
<p> <p>
<iframe src="https://ghbtns.com/github-btn.html?user=kennethreitz&repo=responder&type=watch&count=true&size=large" <iframe
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></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> </p>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" /> <link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"
/>
<style> <style>
.algolia-autocomplete{ .algolia-autocomplete {
width: 100%; width: 100%;
height: 1.5em height: 1.5em;
} }
.algolia-autocomplete a{ .algolia-autocomplete a {
border-bottom: none !important; border-bottom: none !important;
} }
#doc_search{ #doc_search {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
</style> </style>
<input id="doc_search" placeholder="Search the doc" autofocus/> <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({ <script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"
onload="docsearch({
apiKey: 'ac965312db252e0496283c75c6f76f0b', apiKey: 'ac965312db252e0496283c75c6f76f0b',
indexName: 'python-responder', indexName: 'python-responder',
inputSelector: '#doc_search', inputSelector: '#doc_search',
debug: false // Set debug to true if you want to inspect the dropdown debug: false // Set debug to true if you want to inspect the dropdown
})" async></script> })"
async
></script>
<p> <p><strong>Responder</strong> is a web service framework, written for human beings.</p>
<strong>Responder</strong> is a web service framework, written for human beings.
</p>
<h3>Stay Informed</h3> <h3>Stay Informed</h3>
<p>Receive updates on new releases and upcoming projects.</p> <p>Receive updates on new releases and upcoming projects.</p>
<p><iframe src="https://ghbtns.com/github-btn.html?user=kennethreitz&type=follow&count=true" allowtransparency="true" <p>
frameborder="0" scrolling="0" width="200" height="20"></iframe></p> <iframe
src="https://ghbtns.com/github-btn.html?user=kennethreitz&type=follow&count=true"
<p><a href="https://twitter.com/kennethreitz" class="twitter-follow-button" data-show-count="false">Follow allowtransparency="true"
@kennethreitz</a> frameborder="0"
<script>!function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https'; if (!d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = p + '://platform.twitter.com/widgets.js'; fjs.parentNode.insertBefore(js, fjs); } }(document, 'script', 'twitter-wjs');</script> scrolling="0"
width="200"
height="20"
></iframe>
</p> </p>
<p>
<a
href="https://twitter.com/kennethreitz"
class="twitter-follow-button"
data-show-count="false"
>Follow @kennethreitz</a
>
<script>
!(function (d, s, id) {
var js,
fjs = d.getElementsByTagName(s)[0],
p = /^http:/.test(d.location) ? "http" : "https";
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + "://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
})(document, "script", "twitter-wjs");
</script>
</p>
<h3>Useful Links</h3> <h3>Useful Links</h3>
<ul> <ul>
<li><a href="http://github.com/kennethreitz/responder">Responder @ GitHub</a></li> <li><a href="http://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="http://pypi.python.org/pypi/responder">Responder @ PyPI</a></li> <li><a href="http://pypi.python.org/pypi/responder">Responder @ PyPI</a></li>
<li><a href="http://github.com/kennethreitz/responder/issues">Issue Tracker</a></li> <li><a href="http://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
+3
View File
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"
+2 -2
View File
@@ -1,5 +1,5 @@
build: build:
image: latest image: latest
python: python:
version: 3.6 version: 3.6
+1 -1
View File
@@ -1 +1 @@
__version__ = "2.0.4" __version__ = "2.0.6"
+8 -9
View File
@@ -13,7 +13,6 @@ from starlette.middleware.gzip import GZipMiddleware
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware from starlette.middleware.trustedhost import TrustedHostMiddleware
from starlette.middleware.sessions import SessionMiddleware from starlette.middleware.sessions import SessionMiddleware
from starlette.routing import Lifespan
from starlette.staticfiles import StaticFiles from starlette.staticfiles import StaticFiles
from starlette.testclient import TestClient from starlette.testclient import TestClient
from starlette.websockets import WebSocket from starlette.websockets import WebSocket
@@ -31,10 +30,10 @@ from .templates import Templates
class API: class API:
"""The primary web-service class. """The primary web-service class.
:param static_dir: The directory to use for static files. Will be created for you if it doesn't already exist. :param static_dir: The directory to use for static files. Will be created for you if it doesn't already exist.
:param templates_dir: The directory to use for templates. Will be created for you if it doesn't already exist. :param templates_dir: The directory to use for templates. Will be created for you if it doesn't already exist.
:param auto_escape: If ``True``, HTML and XML templates will automatically be escaped. :param auto_escape: If ``True``, HTML and XML templates will automatically be escaped.
:param enable_hsts: If ``True``, send all responses to HTTPS URLs. :param enable_hsts: If ``True``, send all responses to HTTPS URLs.
""" """
status_codes = status_codes status_codes = status_codes
@@ -117,9 +116,9 @@ class API:
if openapi or docs_route: if openapi or docs_route:
self.openapi = OpenAPISchema( self.openapi = OpenAPISchema(
app=self, app=self,
title="Web Service", title=title,
version="1.0", version=version,
openapi="3.0.2", openapi=openapi,
docs_route=docs_route, docs_route=docs_route,
description=description, description=description,
terms_of_service=terms_of_service, terms_of_service=terms_of_service,
@@ -263,7 +262,7 @@ class API:
:param handler: The function to run. Can be either a function or a coroutine. :param handler: The function to run. Can be either a function or a coroutine.
""" """
self.router.lifespan_handler.add_event_handler(event_type, handler) self.router.add_event_handler(event_type, handler)
def route(self, route=None, **options): def route(self, route=None, **options):
"""Decorator for creating new routes around function and class definitions. """Decorator for creating new routes around function and class definitions.
+1 -1
View File
@@ -97,7 +97,7 @@ class Schema:
return self._apispec.to_yaml() return self._apispec.to_yaml()
def add_schema(self, name, schema, check_existing=True): def add_schema(self, name, schema, check_existing=True):
"""Adds a mashmallow schema to the API specification.""" """Adds a marshmallow schema to the API specification."""
if check_existing: if check_existing:
assert name not in self.schemas assert name not in self.schemas
+1 -3
View File
@@ -283,9 +283,7 @@ class Response:
self.content = None #: A bytes representation of the response body. self.content = None #: A bytes representation of the response body.
self.mimetype = None self.mimetype = None
self.encoding = DEFAULT_ENCODING self.encoding = DEFAULT_ENCODING
self.media = ( self.media = None #: A Python object that will be content-negotiated and sent back to the client. Typically, in JSON formatting.
None
) #: A Python object that will be content-negotiated and sent back to the client. Typically, in JSON formatting.
self._stream = None self._stream = None
self.headers = ( self.headers = (
{} {}
+38 -7
View File
@@ -2,8 +2,9 @@ import asyncio
import json import json
import re import re
import inspect import inspect
import traceback
from collections import defaultdict
from starlette.routing import Lifespan
from starlette.middleware.wsgi import WSGIMiddleware from starlette.middleware.wsgi import WSGIMiddleware
from starlette.websockets import WebSocket, WebSocketClose from starlette.websockets import WebSocket, WebSocketClose
from starlette.concurrency import run_in_threadpool from starlette.concurrency import run_in_threadpool
@@ -213,10 +214,10 @@ class Router:
self.default_endpoint = ( self.default_endpoint = (
self.default_response if default_response is None else default_response self.default_response if default_response is None else default_response
) )
self.lifespan_handler = Lifespan()
self.before_requests = ( self.before_requests = (
{"http": [], "ws": []} if before_requests is None else before_requests {"http": [], "ws": []} if before_requests is None else before_requests
) )
self.events = defaultdict(list)
def add_route( def add_route(
self, self,
@@ -228,7 +229,7 @@ class Router:
before_request=False, before_request=False,
check_existing=False, check_existing=False,
): ):
""" Adds a route to the router. """Adds a route to the router.
:param route: A string representation of the route :param route: A string representation of the route
:param endpoint: The endpoint for the route -- can be callable, or class. :param endpoint: The endpoint for the route -- can be callable, or class.
:param default: If ``True``, all unknown requests will route to this view. :param default: If ``True``, all unknown requests will route to this view.
@@ -256,10 +257,23 @@ class Router:
self.routes.append(route) self.routes.append(route)
def mount(self, route, app): def mount(self, route, app):
"""Mounts ASGI / WSGI applications at a given route """Mounts ASGI / WSGI applications at a given route"""
"""
self.apps.update(route, app) self.apps.update(route, app)
def add_event_handler(self, event_type, handler):
assert event_type in (
"startup",
"shutdown",
), f"Only 'startup' and 'shutdown' events are supported, not {event_type}."
self.events[event_type].append(handler)
async def trigger_event(self, event_type):
for handler in self.events.get(event_type, []):
if asyncio.iscoroutinefunction(handler):
await handler()
else:
handler()
def before_request(self, endpoint, websocket=False): def before_request(self, endpoint, websocket=False):
if websocket: if websocket:
self.before_requests.setdefault("ws", []).append(endpoint) self.before_requests.setdefault("ws", []).append(endpoint)
@@ -292,11 +306,28 @@ class Router:
return route return route
return None return None
async def lifespan(self, scope, receive, send):
message = await receive()
assert message["type"] == "lifespan.startup"
try:
await self.trigger_event("startup")
except BaseException:
msg = traceback.format_exc()
await send({"type": "lifespan.startup.failed", "message": msg})
raise
await send({"type": "lifespan.startup.complete"})
message = await receive()
assert message["type"] == "lifespan.shutdown"
await self.trigger_event("shutdown")
await send({"type": "lifespan.shutdown.complete"})
async def __call__(self, scope, receive, send): async def __call__(self, scope, receive, send):
assert scope["type"] in ("http", "websocket", "lifespan") assert scope["type"] in ("http", "websocket", "lifespan")
if scope["type"] == "lifespan": if scope["type"] == "lifespan":
await self.lifespan_handler(scope, receive, send) await self.lifespan(scope, receive, send)
return return
path = scope["path"] path = scope["path"]
@@ -324,4 +355,4 @@ class Router:
await app(scope, receive, send) await app(scope, receive, send)
return return
await self.default_response(scope, receive, send) await self.default_endpoint(scope, receive, send)
+2 -3
View File
@@ -22,15 +22,14 @@ if sys.argv[-1] == "publish":
sys.exit() sys.exit()
required = [ required = [
"starlette==0.12.*", "starlette==0.13.*",
"uvicorn>=0.7, <0.9", "uvicorn>=0.11.7,<0.13",
"aiofiles", "aiofiles",
"pyyaml", "pyyaml",
"requests", "requests",
"graphene<3.0", "graphene<3.0",
"graphql-server-core>=1.1", "graphql-server-core>=1.1",
"jinja2", "jinja2",
"uvloop; sys_platform != 'win32' and sys_platform != 'cygwin' and sys_platform != 'cli'",
"rfc3986", "rfc3986",
"python-multipart", "python-multipart",
"chardet", "chardet",
+3 -5
View File
@@ -23,8 +23,7 @@ def test_api_basic_route(api):
def test_route_repr(): def test_route_repr():
def home(req, resp): def home(req, resp):
"""Home page """Home page"""
"""
resp.text = "Hello !" resp.text = "Hello !"
route = Route("/", home) route = Route("/", home)
@@ -37,8 +36,7 @@ def test_route_repr():
def test_websocket_route_repr(): def test_websocket_route_repr():
def chat_endpoint(ws): def chat_endpoint(ws):
"""Chat """Chat"""
"""
pass pass
route = WebSocketRoute("/", chat_endpoint) route = WebSocketRoute("/", chat_endpoint)
@@ -750,7 +748,7 @@ def test_redirects(api, session):
def one(req, resp): def one(req, resp):
resp.text = "redirected" resp.text = "redirected"
assert session.get("/1").url == "http://;/1" assert session.get("/2").url == "http://;/1"
def test_session_thoroughly(api, session): def test_session_thoroughly(api, session):