python-aiohttp-apispec_3.0.0~b2+ds1.orig/LICENSE 0000644 0000000 0000000 00000002063 14231014705 016202 0 ustar 00 MIT License Copyright (c) 2017 Maksim Danilchenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-aiohttp-apispec_3.0.0~b2+ds1.orig/MANIFEST.in 0000644 0000000 0000000 00000000467 14231014705 016741 0 ustar 00 include LICENSE include README.md include requirements.txt recursive-include aiohttp_apispec/static * graft aiohttp_apispec graft docs global-exclude *.pyc global-exclude *.pyd global-exclude *.so global-exclude *.lib global-exclude *.dll global-exclude *.a global-exclude *.obj prune docs/_build prune example python-aiohttp-apispec_3.0.0~b2+ds1.orig/PKG-INFO 0000644 0000000 0000000 00000025127 14231014705 016300 0 ustar 00 Metadata-Version: 2.1 Name: aiohttp-apispec Version: 3.0.0b2 Summary: Build and document REST APIs with aiohttp and apispec Home-page: https://github.com/maximdanilchenko/aiohttp-apispec Author: Danilchenko Maksim Author-email: dmax.dev@gmail.com License: MIT Keywords: aiohttp marshmallow apispec swagger Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Requires-Python: >=3.6 Description-Content-Type: text/markdown License-File: LICENSE
Build and document REST APIs with aiohttp and apispec
```aiohttp-apispec``` key features: - ```docs``` and ```request_schema``` decorators to add swagger spec support out of the box; - ```validation_middleware``` middleware to enable validating with marshmallow schemas from those decorators; - **SwaggerUI** support. - *New from version 2.0* - ```match_info_schema```, ```querystring_schema```, ```form_schema```, ```json_schema```, ```headers_schema``` and ```cookies_schema``` decorators for specific request parts validation. Look [here](#more-decorators) for more info. ```aiohttp-apispec``` api is fully inspired by ```flask-apispec``` library **Version 3.0.0b1 with apispec>=5.0 webargs>=8.0 is in beta now** (`pip install aiohttp-apispec==3.0.0b1`). ## Contents - [Install](#install) - [Quickstart](#quickstart) - [Adding validation middleware](#adding-validation-middleware) - [More decorators](#more-decorators) - [Custom error handling](#custom-error-handling) - [Build swagger web client](#build-swagger-web-client) - [Versioning](#versioning) ## Install ``` pip install aiohttp-apispec ``` ## Quickstart *Also you can read [blog post](https://dmax.blog/how_to_easily_build_modern_web_apis_with_python_and_aiohttp) about quickstart with aiohttp-apispec* ```Python from aiohttp_apispec import ( docs, request_schema, setup_aiohttp_apispec, ) from aiohttp import web from marshmallow import Schema, fields class RequestSchema(Schema): id = fields.Int() name = fields.Str(description="name") @docs( tags=["mytag"], summary="Test method summary", description="Test method description", ) @request_schema(RequestSchema(strict=True)) async def index(request): return web.json_response({"msg": "done", "data": {}}) app = web.Application() app.router.add_post("/v1/test", index) # init docs with all parameters, usual for ApiSpec setup_aiohttp_apispec( app=app, title="My Documentation", version="v1", url="/api/docs/swagger.json", swagger_path="/api/docs", ) # Now we can find spec on 'http://localhost:8080/api/docs/swagger.json' # and docs on 'http://localhost:8080/api/docs' web.run_app(app) ``` Class based views are also supported: ```python class TheView(web.View): @docs( tags=["mytag"], summary="View method summary", description="View method description", ) @request_schema(RequestSchema(strict=True)) @response_schema(ResponseSchema(), 200) def delete(self): return web.json_response( {"msg": "done", "data": {"name": self.request["data"]["name"]}} ) app.router.add_view("/v1/view", TheView) ``` As alternative you can add responses info to `docs` decorator, which is more compact way. And it allows you not to use schemas for responses documentation: ```python @docs( tags=["mytag"], summary="Test method summary", description="Test method description", responses={ 200: { "schema": ResponseSchema, "description": "Success response", }, # regular response 404: {"description": "Not found"}, # responses without schema 422: {"description": "Validation error"}, }, ) @request_schema(RequestSchema(strict=True)) async def index(request): return web.json_response({"msg": "done", "data": {}}) ``` ## Adding validation middleware ```Python from aiohttp_apispec import validation_middleware ... app.middlewares.append(validation_middleware) ``` Now you can access all validated data in route from ```request['data']``` like so: ```Python @docs( tags=["mytag"], summary="Test method summary", description="Test method description", ) @request_schema(RequestSchema(strict=True)) @response_schema(ResponseSchema, 200) async def index(request): uid = request["data"]["id"] name = request["data"]["name"] return web.json_response( {"msg": "done", "data": {"info": f"name - {name}, id - {uid}"}} ) ``` You can change ``Request``'s ``'data'`` param to another with ``request_data_name`` argument of ``setup_aiohttp_apispec`` function: ```python setup_aiohttp_apispec( app=app, request_data_name="validated_data", ) ... @request_schema(RequestSchema(strict=True)) async def index(request): uid = request["validated_data"]["id"] ... ``` Also you can do it for specific view using ```put_into``` parameter (beginning from version 2.0): ```python @request_schema(RequestSchema(strict=True), put_into="validated_data") async def index(request): uid = request["validated_data"]["id"] ... ``` ## More decorators Starting from version 2.0 you can use shortenings for documenting and validating specific request parts like cookies, headers etc using those decorators: | Decorator name | Default put_into param | |:----------|:-----------------| | match_info_schema | match_info | | querystring_schema | querystring | | form_schema | form | | json_schema | json | | headers_schema | headers | | cookies_schema | cookies | And example: ```python @docs( tags=["users"], summary="Create new user", description="Add new user to our toy database", responses={ 200: {"description": "Ok. User created", "schema": OkResponse}, 401: {"description": "Unauthorized"}, 422: {"description": "Validation error"}, 500: {"description": "Server error"}, }, ) @headers_schema(AuthHeaders) # <- schema for headers validation @json_schema(UserMeta) # <- schema for json body validation @querystring_schema(UserParams) # <- schema for querystring params validation async def create_user(request: web.Request): headers = request["headers"] # <- validated headers! json_data = request["json"] # <- validated json! query_params = request["querystring"] # <- validated querystring! ... ``` ## Custom error handling If you want to catch validation errors by yourself you could use `error_callback` parameter and create your custom error handler. Note that it can be one of coroutine or callable and it should have interface exactly like in examples below: ```python from marshmallow import ValidationError, Schema from aiohttp import web from typing import Optional, Mapping, NoReturn def my_error_handler( error: ValidationError, req: web.Request, schema: Schema, error_status_code: Optional[int] = None, error_headers: Optional[Mapping[str, str]] = None, ) -> NoReturn: raise web.HTTPBadRequest( body=json.dumps(error.messages), headers=error_headers, content_type="application/json", ) setup_aiohttp_apispec(app, error_callback=my_error_handler) ``` Also you can create your own exceptions and create regular Request in middleware like so: ```python class MyException(Exception): def __init__(self, message): self.message = message # It can be coroutine as well: async def my_error_handler( error, req, schema, error_status_code, error_headers ): await req.app["db"].do_smth() # So you can use some async stuff raise MyException({"errors": error.messages, "text": "Oops"}) # This middleware will handle your own exceptions: @web.middleware async def intercept_error(request, handler): try: return await handler(request) except MyException as e: return web.json_response(e.message, status=400) setup_aiohttp_apispec(app, error_callback=my_error_handler) # Do not forget to add your own middleware before validation_middleware app.middlewares.extend([intercept_error, validation_middleware]) ``` ## Build swagger web client #### 3.X SwaggerUI version Just add `swagger_path` parameter to `setup_aiohttp_apispec` function. For example: ```python setup_aiohttp_apispec(app, swagger_path="/docs") ``` Then go to `/docs` and see awesome SwaggerUI #### 2.X SwaggerUI version If you prefer older version you can use [aiohttp_swagger](https://github.com/cr0hn/aiohttp-swagger) library. `aiohttp-apispec` adds `swagger_dict` parameter to aiohttp web application after initialization (with `setup_aiohttp_apispec` function). So you can use it easily like: ```Python from aiohttp_apispec import setup_aiohttp_apispec from aiohttp_swagger import setup_swagger def create_app(app): setup_aiohttp_apispec(app) async def swagger(app): setup_swagger( app=app, swagger_url="/api/doc", swagger_info=app["swagger_dict"] ) app.on_startup.append(swagger) # now we can access swagger client on '/api/doc' url ... return app ``` ## Versioning This software follows [Semantic Versioning](http://semver.org/). ------ Please star this repository if this project helped you! python-aiohttp-apispec_3.0.0~b2+ds1.orig/README.md 0000644 0000000 0000000 00000023546 14231014705 016465 0 ustar 00
Build and document REST APIs with aiohttp and apispec
```aiohttp-apispec``` key features: - ```docs``` and ```request_schema``` decorators to add swagger spec support out of the box; - ```validation_middleware``` middleware to enable validating with marshmallow schemas from those decorators; - **SwaggerUI** support. - *New from version 2.0* - ```match_info_schema```, ```querystring_schema```, ```form_schema```, ```json_schema```, ```headers_schema``` and ```cookies_schema``` decorators for specific request parts validation. Look [here](#more-decorators) for more info. ```aiohttp-apispec``` api is fully inspired by ```flask-apispec``` library **Version 3.0.0b1 with apispec>=5.0 webargs>=8.0 is in beta now** (`pip install aiohttp-apispec==3.0.0b1`). ## Contents - [Install](#install) - [Quickstart](#quickstart) - [Adding validation middleware](#adding-validation-middleware) - [More decorators](#more-decorators) - [Custom error handling](#custom-error-handling) - [Build swagger web client](#build-swagger-web-client) - [Versioning](#versioning) ## Install ``` pip install aiohttp-apispec ``` ## Quickstart *Also you can read [blog post](https://dmax.blog/how_to_easily_build_modern_web_apis_with_python_and_aiohttp) about quickstart with aiohttp-apispec* ```Python from aiohttp_apispec import ( docs, request_schema, setup_aiohttp_apispec, ) from aiohttp import web from marshmallow import Schema, fields class RequestSchema(Schema): id = fields.Int() name = fields.Str(description="name") @docs( tags=["mytag"], summary="Test method summary", description="Test method description", ) @request_schema(RequestSchema(strict=True)) async def index(request): return web.json_response({"msg": "done", "data": {}}) app = web.Application() app.router.add_post("/v1/test", index) # init docs with all parameters, usual for ApiSpec setup_aiohttp_apispec( app=app, title="My Documentation", version="v1", url="/api/docs/swagger.json", swagger_path="/api/docs", ) # Now we can find spec on 'http://localhost:8080/api/docs/swagger.json' # and docs on 'http://localhost:8080/api/docs' web.run_app(app) ``` Class based views are also supported: ```python class TheView(web.View): @docs( tags=["mytag"], summary="View method summary", description="View method description", ) @request_schema(RequestSchema(strict=True)) @response_schema(ResponseSchema(), 200) def delete(self): return web.json_response( {"msg": "done", "data": {"name": self.request["data"]["name"]}} ) app.router.add_view("/v1/view", TheView) ``` As alternative you can add responses info to `docs` decorator, which is more compact way. And it allows you not to use schemas for responses documentation: ```python @docs( tags=["mytag"], summary="Test method summary", description="Test method description", responses={ 200: { "schema": ResponseSchema, "description": "Success response", }, # regular response 404: {"description": "Not found"}, # responses without schema 422: {"description": "Validation error"}, }, ) @request_schema(RequestSchema(strict=True)) async def index(request): return web.json_response({"msg": "done", "data": {}}) ``` ## Adding validation middleware ```Python from aiohttp_apispec import validation_middleware ... app.middlewares.append(validation_middleware) ``` Now you can access all validated data in route from ```request['data']``` like so: ```Python @docs( tags=["mytag"], summary="Test method summary", description="Test method description", ) @request_schema(RequestSchema(strict=True)) @response_schema(ResponseSchema, 200) async def index(request): uid = request["data"]["id"] name = request["data"]["name"] return web.json_response( {"msg": "done", "data": {"info": f"name - {name}, id - {uid}"}} ) ``` You can change ``Request``'s ``'data'`` param to another with ``request_data_name`` argument of ``setup_aiohttp_apispec`` function: ```python setup_aiohttp_apispec( app=app, request_data_name="validated_data", ) ... @request_schema(RequestSchema(strict=True)) async def index(request): uid = request["validated_data"]["id"] ... ``` Also you can do it for specific view using ```put_into``` parameter (beginning from version 2.0): ```python @request_schema(RequestSchema(strict=True), put_into="validated_data") async def index(request): uid = request["validated_data"]["id"] ... ``` ## More decorators Starting from version 2.0 you can use shortenings for documenting and validating specific request parts like cookies, headers etc using those decorators: | Decorator name | Default put_into param | |:----------|:-----------------| | match_info_schema | match_info | | querystring_schema | querystring | | form_schema | form | | json_schema | json | | headers_schema | headers | | cookies_schema | cookies | And example: ```python @docs( tags=["users"], summary="Create new user", description="Add new user to our toy database", responses={ 200: {"description": "Ok. User created", "schema": OkResponse}, 401: {"description": "Unauthorized"}, 422: {"description": "Validation error"}, 500: {"description": "Server error"}, }, ) @headers_schema(AuthHeaders) # <- schema for headers validation @json_schema(UserMeta) # <- schema for json body validation @querystring_schema(UserParams) # <- schema for querystring params validation async def create_user(request: web.Request): headers = request["headers"] # <- validated headers! json_data = request["json"] # <- validated json! query_params = request["querystring"] # <- validated querystring! ... ``` ## Custom error handling If you want to catch validation errors by yourself you could use `error_callback` parameter and create your custom error handler. Note that it can be one of coroutine or callable and it should have interface exactly like in examples below: ```python from marshmallow import ValidationError, Schema from aiohttp import web from typing import Optional, Mapping, NoReturn def my_error_handler( error: ValidationError, req: web.Request, schema: Schema, error_status_code: Optional[int] = None, error_headers: Optional[Mapping[str, str]] = None, ) -> NoReturn: raise web.HTTPBadRequest( body=json.dumps(error.messages), headers=error_headers, content_type="application/json", ) setup_aiohttp_apispec(app, error_callback=my_error_handler) ``` Also you can create your own exceptions and create regular Request in middleware like so: ```python class MyException(Exception): def __init__(self, message): self.message = message # It can be coroutine as well: async def my_error_handler( error, req, schema, error_status_code, error_headers ): await req.app["db"].do_smth() # So you can use some async stuff raise MyException({"errors": error.messages, "text": "Oops"}) # This middleware will handle your own exceptions: @web.middleware async def intercept_error(request, handler): try: return await handler(request) except MyException as e: return web.json_response(e.message, status=400) setup_aiohttp_apispec(app, error_callback=my_error_handler) # Do not forget to add your own middleware before validation_middleware app.middlewares.extend([intercept_error, validation_middleware]) ``` ## Build swagger web client #### 3.X SwaggerUI version Just add `swagger_path` parameter to `setup_aiohttp_apispec` function. For example: ```python setup_aiohttp_apispec(app, swagger_path="/docs") ``` Then go to `/docs` and see awesome SwaggerUI #### 2.X SwaggerUI version If you prefer older version you can use [aiohttp_swagger](https://github.com/cr0hn/aiohttp-swagger) library. `aiohttp-apispec` adds `swagger_dict` parameter to aiohttp web application after initialization (with `setup_aiohttp_apispec` function). So you can use it easily like: ```Python from aiohttp_apispec import setup_aiohttp_apispec from aiohttp_swagger import setup_swagger def create_app(app): setup_aiohttp_apispec(app) async def swagger(app): setup_swagger( app=app, swagger_url="/api/doc", swagger_info=app["swagger_dict"] ) app.on_startup.append(swagger) # now we can access swagger client on '/api/doc' url ... return app ``` ## Versioning This software follows [Semantic Versioning](http://semver.org/). ------ Please star this repository if this project helped you! python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/ 0000755 0000000 0000000 00000000000 14231014705 022042 5 ustar 00 python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/ 0000755 0000000 0000000 00000000000 14231014705 020350 5 ustar 00 python-aiohttp-apispec_3.0.0~b2+ds1.orig/docs/ 0000755 0000000 0000000 00000000000 14231014705 016124 5 ustar 00 python-aiohttp-apispec_3.0.0~b2+ds1.orig/pyproject.toml 0000644 0000000 0000000 00000000202 14231014705 020102 0 ustar 00 [tool.black] include = '\.py$' skip-string-normalization = true exclude = ''' ( migrations/ | protobuffers/ | venv/ ) ''' python-aiohttp-apispec_3.0.0~b2+ds1.orig/requirements.txt 0000644 0000000 0000000 00000000071 14231014705 020456 0 ustar 00 aiohttp>=3.0.1,<4.0 apispec>=5.1.1 webargs>=8.0.1 jinja2 python-aiohttp-apispec_3.0.0~b2+ds1.orig/setup.cfg 0000644 0000000 0000000 00000000046 14231014705 017015 0 ustar 00 [egg_info] tag_build = tag_date = 0 python-aiohttp-apispec_3.0.0~b2+ds1.orig/setup.py 0000644 0000000 0000000 00000002302 14231014705 016703 0 ustar 00 from setuptools import find_packages, setup def read(file_name): with open(file_name, encoding="utf-8") as fp: content = fp.read() return content setup( name='aiohttp-apispec', version='3.0.0b2', description='Build and document REST APIs with aiohttp and apispec', long_description=read('README.md'), long_description_content_type="text/markdown", author='Danilchenko Maksim', author_email='dmax.dev@gmail.com', packages=find_packages(exclude=('test*',)), package_dir={'aiohttp_apispec': 'aiohttp_apispec'}, include_package_data=True, install_requires=read('requirements.txt').split(), license='MIT', url='https://github.com/maximdanilchenko/aiohttp-apispec', zip_safe=False, keywords='aiohttp marshmallow apispec swagger', python_requires='>=3.6', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', ], test_suite='tests', ) python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/PKG-INFO 0000644 0000000 0000000 00000025127 14231014705 023146 0 ustar 00 Metadata-Version: 2.1 Name: aiohttp-apispec Version: 3.0.0b2 Summary: Build and document REST APIs with aiohttp and apispec Home-page: https://github.com/maximdanilchenko/aiohttp-apispec Author: Danilchenko Maksim Author-email: dmax.dev@gmail.com License: MIT Keywords: aiohttp marshmallow apispec swagger Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Requires-Python: >=3.6 Description-Content-Type: text/markdown License-File: LICENSE
Build and document REST APIs with aiohttp and apispec
```aiohttp-apispec``` key features:
- ```docs``` and ```request_schema``` decorators
to add swagger spec support out of the box;
- ```validation_middleware``` middleware to enable validating
with marshmallow schemas from those decorators;
- **SwaggerUI** support.
- *New from version 2.0* - ```match_info_schema```, ```querystring_schema```,
```form_schema```, ```json_schema```, ```headers_schema``` and ```cookies_schema```
decorators for specific request parts validation.
Look [here](#more-decorators) for more info.
```aiohttp-apispec``` api is fully inspired by ```flask-apispec``` library
**Version 3.0.0b1 with apispec>=5.0 webargs>=8.0 is in beta now** (`pip install aiohttp-apispec==3.0.0b1`).
## Contents
- [Install](#install)
- [Quickstart](#quickstart)
- [Adding validation middleware](#adding-validation-middleware)
- [More decorators](#more-decorators)
- [Custom error handling](#custom-error-handling)
- [Build swagger web client](#build-swagger-web-client)
- [Versioning](#versioning)
## Install
```
pip install aiohttp-apispec
```
## Quickstart
*Also you can read [blog post](https://dmax.blog/how_to_easily_build_modern_web_apis_with_python_and_aiohttp) about quickstart with aiohttp-apispec*
```Python
from aiohttp_apispec import (
docs,
request_schema,
setup_aiohttp_apispec,
)
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description="name")
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
app = web.Application()
app.router.add_post("/v1/test", index)
# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(
app=app,
title="My Documentation",
version="v1",
url="/api/docs/swagger.json",
swagger_path="/api/docs",
)
# Now we can find spec on 'http://localhost:8080/api/docs/swagger.json'
# and docs on 'http://localhost:8080/api/docs'
web.run_app(app)
```
Class based views are also supported:
```python
class TheView(web.View):
@docs(
tags=["mytag"],
summary="View method summary",
description="View method description",
)
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema(), 200)
def delete(self):
return web.json_response(
{"msg": "done", "data": {"name": self.request["data"]["name"]}}
)
app.router.add_view("/v1/view", TheView)
```
As alternative you can add responses info to `docs` decorator, which is more compact way.
And it allows you not to use schemas for responses documentation:
```python
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
responses={
200: {
"schema": ResponseSchema,
"description": "Success response",
}, # regular response
404: {"description": "Not found"}, # responses without schema
422: {"description": "Validation error"},
},
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
```
## Adding validation middleware
```Python
from aiohttp_apispec import validation_middleware
...
app.middlewares.append(validation_middleware)
```
Now you can access all validated data in route from ```request['data']``` like so:
```Python
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
)
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema, 200)
async def index(request):
uid = request["data"]["id"]
name = request["data"]["name"]
return web.json_response(
{"msg": "done", "data": {"info": f"name - {name}, id - {uid}"}}
)
```
You can change ``Request``'s ``'data'`` param to another with ``request_data_name`` argument of
``setup_aiohttp_apispec`` function:
```python
setup_aiohttp_apispec(
app=app,
request_data_name="validated_data",
)
...
@request_schema(RequestSchema(strict=True))
async def index(request):
uid = request["validated_data"]["id"]
...
```
Also you can do it for specific view using ```put_into```
parameter (beginning from version 2.0):
```python
@request_schema(RequestSchema(strict=True), put_into="validated_data")
async def index(request):
uid = request["validated_data"]["id"]
...
```
## More decorators
Starting from version 2.0 you can use shortenings for documenting and validating
specific request parts like cookies, headers etc using those decorators:
| Decorator name | Default put_into param |
|:----------|:-----------------|
| match_info_schema | match_info |
| querystring_schema | querystring |
| form_schema | form |
| json_schema | json |
| headers_schema | headers |
| cookies_schema | cookies |
And example:
```python
@docs(
tags=["users"],
summary="Create new user",
description="Add new user to our toy database",
responses={
200: {"description": "Ok. User created", "schema": OkResponse},
401: {"description": "Unauthorized"},
422: {"description": "Validation error"},
500: {"description": "Server error"},
},
)
@headers_schema(AuthHeaders) # <- schema for headers validation
@json_schema(UserMeta) # <- schema for json body validation
@querystring_schema(UserParams) # <- schema for querystring params validation
async def create_user(request: web.Request):
headers = request["headers"] # <- validated headers!
json_data = request["json"] # <- validated json!
query_params = request["querystring"] # <- validated querystring!
...
```
## Custom error handling
If you want to catch validation errors by yourself you
could use `error_callback` parameter and create your custom error handler. Note that
it can be one of coroutine or callable and it should
have interface exactly like in examples below:
```python
from marshmallow import ValidationError, Schema
from aiohttp import web
from typing import Optional, Mapping, NoReturn
def my_error_handler(
error: ValidationError,
req: web.Request,
schema: Schema,
error_status_code: Optional[int] = None,
error_headers: Optional[Mapping[str, str]] = None,
) -> NoReturn:
raise web.HTTPBadRequest(
body=json.dumps(error.messages),
headers=error_headers,
content_type="application/json",
)
setup_aiohttp_apispec(app, error_callback=my_error_handler)
```
Also you can create your own exceptions and create
regular Request in middleware like so:
```python
class MyException(Exception):
def __init__(self, message):
self.message = message
# It can be coroutine as well:
async def my_error_handler(
error, req, schema, error_status_code, error_headers
):
await req.app["db"].do_smth() # So you can use some async stuff
raise MyException({"errors": error.messages, "text": "Oops"})
# This middleware will handle your own exceptions:
@web.middleware
async def intercept_error(request, handler):
try:
return await handler(request)
except MyException as e:
return web.json_response(e.message, status=400)
setup_aiohttp_apispec(app, error_callback=my_error_handler)
# Do not forget to add your own middleware before validation_middleware
app.middlewares.extend([intercept_error, validation_middleware])
```
## Build swagger web client
#### 3.X SwaggerUI version
Just add `swagger_path` parameter to `setup_aiohttp_apispec` function.
For example:
```python
setup_aiohttp_apispec(app, swagger_path="/docs")
```
Then go to `/docs` and see awesome SwaggerUI
#### 2.X SwaggerUI version
If you prefer older version you can use
[aiohttp_swagger](https://github.com/cr0hn/aiohttp-swagger) library.
`aiohttp-apispec` adds `swagger_dict` parameter to aiohttp web application
after initialization (with `setup_aiohttp_apispec` function).
So you can use it easily like:
```Python
from aiohttp_apispec import setup_aiohttp_apispec
from aiohttp_swagger import setup_swagger
def create_app(app):
setup_aiohttp_apispec(app)
async def swagger(app):
setup_swagger(
app=app, swagger_url="/api/doc", swagger_info=app["swagger_dict"]
)
app.on_startup.append(swagger)
# now we can access swagger client on '/api/doc' url
...
return app
```
## Versioning
This software follows [Semantic Versioning](http://semver.org/).
------
Please star this repository if this project helped you!
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/SOURCES.txt 0000644 0000000 0000000 00000002221 14231014705 023723 0 ustar 00 LICENSE
MANIFEST.in
README.md
pyproject.toml
requirements.txt
setup.py
aiohttp_apispec/__init__.py
aiohttp_apispec/aiohttp_apispec.py
aiohttp_apispec/middlewares.py
aiohttp_apispec/utils.py
aiohttp_apispec.egg-info/PKG-INFO
aiohttp_apispec.egg-info/SOURCES.txt
aiohttp_apispec.egg-info/dependency_links.txt
aiohttp_apispec.egg-info/not-zip-safe
aiohttp_apispec.egg-info/requires.txt
aiohttp_apispec.egg-info/top_level.txt
aiohttp_apispec/decorators/__init__.py
aiohttp_apispec/decorators/docs.py
aiohttp_apispec/decorators/request.py
aiohttp_apispec/decorators/response.py
aiohttp_apispec/static/favicon-16x16.png
aiohttp_apispec/static/favicon-32x32.png
aiohttp_apispec/static/index.html
aiohttp_apispec/static/oauth2-redirect.html
aiohttp_apispec/static/swagger-ui-bundle.js
aiohttp_apispec/static/swagger-ui-bundle.js.map
aiohttp_apispec/static/swagger-ui-standalone-preset.js
aiohttp_apispec/static/swagger-ui-standalone-preset.js.map
aiohttp_apispec/static/swagger-ui.css
aiohttp_apispec/static/swagger-ui.css.map
aiohttp_apispec/static/swagger-ui.js
aiohttp_apispec/static/swagger-ui.js.map
docs/api.rst
docs/conf.py
docs/index.rst
docs/install.rst
docs/usage.rst python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/dependency_links.txt 0000644 0000000 0000000 00000000001 14231014705 026110 0 ustar 00
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/not-zip-safe 0000644 0000000 0000000 00000000001 14231014705 024270 0 ustar 00
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/requires.txt 0000644 0000000 0000000 00000000071 14231014705 024440 0 ustar 00 aiohttp<4.0,>=3.0.1
apispec>=5.1.1
webargs>=8.0.1
jinja2
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec.egg-info/top_level.txt 0000644 0000000 0000000 00000000030 14231014705 024565 0 ustar 00 aiohttp_apispec
example
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/__init__.py 0000644 0000000 0000000 00000001315 14231014705 022461 0 ustar 00 from .aiohttp_apispec import AiohttpApiSpec, setup_aiohttp_apispec
from .decorators import (
cookies_schema,
docs,
form_schema,
headers_schema,
json_schema,
marshal_with,
match_info_schema,
querystring_schema,
request_schema,
response_schema,
use_kwargs,
)
from .middlewares import validation_middleware
__all__ = [
# setup
"AiohttpApiSpec",
"setup_aiohttp_apispec",
# decorators
"docs",
"request_schema",
"match_info_schema",
"querystring_schema",
"form_schema",
"json_schema",
"headers_schema",
"cookies_schema",
"response_schema",
"use_kwargs",
"marshal_with",
# middleware
"validation_middleware",
]
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/aiohttp_apispec.py 0000644 0000000 0000000 00000026223 14231014705 024103 0 ustar 00 import copy
import os
from pathlib import Path
from typing import Awaitable, Callable
from aiohttp import web
from aiohttp.hdrs import METH_ALL, METH_ANY
from apispec import APISpec
from apispec.core import VALID_METHODS_OPENAPI_V2
from apispec.ext.marshmallow import MarshmallowPlugin, common
from jinja2 import Template
from webargs.aiohttpparser import parser
from .utils import get_path, get_path_keys, issubclass_py37fix
_AiohttpView = Callable[[web.Request], Awaitable[web.StreamResponse]]
VALID_RESPONSE_FIELDS = {"description", "headers", "examples"}
DEFAULT_RESPONSE_LOCATION = "json"
NAME_SWAGGER_SPEC = "swagger.spec"
NAME_SWAGGER_DOCS = "swagger.docs"
NAME_SWAGGER_STATIC = "swagger.static"
INDEX_PAGE = "index.html"
def resolver(schema):
schema_instance = common.resolve_schema_instance(schema)
prefix = "Partial-" if schema_instance.partial else ""
schema_cls = common.resolve_schema_cls(schema)
name = prefix + schema_cls.__name__
if name.endswith("Schema"):
return name[:-6] or name
return name
class AiohttpApiSpec:
def __init__(
self,
url="/api/docs/swagger.json",
app=None,
request_data_name="data",
swagger_path=None,
static_path='/static/swagger',
error_callback=None,
in_place=False,
prefix='',
schema_name_resolver=resolver,
**kwargs,
):
self.plugin = MarshmallowPlugin(schema_name_resolver=schema_name_resolver)
self.spec = APISpec(plugins=(self.plugin,), openapi_version="2.0", **kwargs)
self.url = url
self.swagger_path = swagger_path
self.static_path = static_path
self._registered = False
self._request_data_name = request_data_name
self.error_callback = error_callback
self.prefix = prefix
self._index_page = None
if app is not None:
self.register(app, in_place)
def swagger_dict(self):
"""Returns swagger spec representation in JSON format"""
return self.spec.to_dict()
def register(self, app: web.Application, in_place: bool = False):
"""Creates spec based on registered app routes and registers needed view"""
if self._registered is True:
return None
app["_apispec_request_data_name"] = self._request_data_name
if self.error_callback:
parser.error_callback = self.error_callback
app["_apispec_parser"] = parser
if in_place:
self._register(app)
else:
async def doc_routes(app_):
self._register(app_)
app.on_startup.append(doc_routes)
self._registered = True
if self.url is not None:
async def swagger_handler(request):
return web.json_response(request.app["swagger_dict"])
route_url = self.url
if not self.url.startswith("/"):
route_url = "/{}".format(self.url)
app.router.add_route(
"GET", route_url, swagger_handler, name=NAME_SWAGGER_SPEC
)
if self.swagger_path is not None:
self._add_swagger_web_page(app, self.static_path, self.swagger_path)
def _get_index_page(self, app, static_files, static_path):
if self._index_page is not None:
return self._index_page
with open(str(static_files / INDEX_PAGE)) as swg_tmp:
url = self.url if app is None else app.router[NAME_SWAGGER_SPEC].url_for()
if app is not None:
static_path = app.router[NAME_SWAGGER_STATIC].url_for(
filename=INDEX_PAGE
)
static_path = os.path.dirname(str(static_path))
self._index_page = Template(swg_tmp.read()).render(
path=url, static=static_path
)
return self._index_page
def _add_swagger_web_page(
self, app: web.Application, static_path: str, view_path: str
):
static_files = Path(__file__).parent / "static"
app.router.add_static(static_path, static_files, name=NAME_SWAGGER_STATIC)
async def swagger_view(_):
index_page = self._get_index_page(app, static_files, static_path)
return web.Response(text=index_page, content_type="text/html")
app.router.add_route("GET", view_path, swagger_view, name=NAME_SWAGGER_DOCS)
def _register(self, app: web.Application):
for route in app.router.routes():
if issubclass_py37fix(route.handler, web.View) and route.method == METH_ANY:
for attr in dir(route.handler):
if attr.upper() in METH_ALL:
view = getattr(route.handler, attr)
method = attr
self._register_route(route, method, view)
else:
method = route.method.lower()
view = route.handler
self._register_route(route, method, view)
app["swagger_dict"] = self.swagger_dict()
def _register_route(
self, route: web.AbstractRoute, method: str, view: _AiohttpView
):
if not hasattr(view, "__apispec__"):
return None
url_path = get_path(route)
if not url_path:
return None
self._update_paths(view.__apispec__, method, self.prefix + url_path)
def _update_paths(self, data: dict, method: str, url_path: str):
if method not in VALID_METHODS_OPENAPI_V2:
return None
for schema in data.pop("schemas", []):
parameters = self.plugin.converter.schema2parameters(
schema["schema"], location=schema["location"], **schema["options"]
)
self._add_examples(schema["schema"], parameters, schema["example"])
data["parameters"].extend(parameters)
existing = [p["name"] for p in data["parameters"] if p["in"] == "path"]
data["parameters"].extend(
{"in": "path", "name": path_key, "required": True, "type": "string"}
for path_key in get_path_keys(url_path)
if path_key not in existing
)
if "responses" in data:
responses = {}
for code, actual_params in data["responses"].items():
if "schema" in actual_params:
raw_parameters = self.plugin.converter.schema2parameters(
actual_params["schema"],
location=DEFAULT_RESPONSE_LOCATION,
required=actual_params.get("required", False),
)[0]
updated_params = {
k: v
for k, v in raw_parameters.items()
if k in VALID_RESPONSE_FIELDS
}
updated_params['schema'] = actual_params["schema"]
for extra_info in ("description", "headers", "examples"):
if extra_info in actual_params:
updated_params[extra_info] = actual_params[extra_info]
responses[code] = updated_params
else:
responses[code] = actual_params
data["responses"] = responses
operations = copy.deepcopy(data)
self.spec.path(path=url_path, operations={method: operations})
def _add_examples(self, ref_schema, endpoint_schema, example):
def add_to_endpoint_or_ref():
if add_to_refs:
self.spec.components.schemas[name]["example"] = example
else:
endpoint_schema[0]['schema']['allOf'] = [
endpoint_schema[0]['schema'].pop('$ref')
]
endpoint_schema[0]['schema']["example"] = example
if not example:
return
schema_instance = common.resolve_schema_instance(ref_schema)
name = self.plugin.converter.schema_name_resolver(schema_instance)
add_to_refs = example.pop('add_to_refs')
if self.spec.components.openapi_version.major < 3:
if name and name in self.spec.components.schemas:
add_to_endpoint_or_ref()
else:
add_to_endpoint_or_ref()
def setup_aiohttp_apispec(
app: web.Application,
*,
title: str = "API documentation",
version: str = "0.0.1",
url: str = "/api/docs/swagger.json",
request_data_name: str = "data",
swagger_path: str = None,
static_path: str = '/static/swagger',
error_callback=None,
in_place: bool = False,
prefix: str = '',
schema_name_resolver: Callable = resolver,
**kwargs,
) -> None:
"""
aiohttp-apispec extension.
Usage:
.. code-block:: python
from aiohttp_apispec import docs, request_schema, setup_aiohttp_apispec
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description='name')
bool_field = fields.Bool()
@docs(tags=['mytag'],
summary='Test method summary',
description='Test method description')
@request_schema(RequestSchema)
async def index(request):
return web.json_response({'msg': 'done', 'data': {}})
app = web.Application()
app.router.add_post('/v1/test', index)
# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(app=app,
title='My Documentation',
version='v1',
url='/api/docs/api-docs')
# now we can find it on 'http://localhost:8080/api/docs/api-docs'
web.run_app(app)
:param Application app: aiohttp web app
:param str title: API title
:param str version: API version
:param str url: url for swagger spec in JSON format
:param str request_data_name: name of the key in Request object
where validated data will be placed by
validation_middleware (``'data'`` by default)
:param str swagger_path: experimental SwaggerUI support (starting from v1.1.0).
By default it is None (disabled)
:param str static_path: path for static files used by SwaggerUI
(if it is enabled with ``swagger_path``)
:param error_callback: custom error handler
:param in_place: register all routes at the moment of calling this function
instead of the moment of the on_startup signal.
If True, be sure all routes are added to router
:param prefix: prefix to add to all registered routes
:param schema_name_resolver: custom schema_name_resolver for MarshmallowPlugin.
:param kwargs: any apispec.APISpec kwargs
:return: return instance of AiohttpApiSpec class
:rtype: AiohttpApiSpec
"""
return AiohttpApiSpec(
url,
app,
request_data_name,
title=title,
version=version,
swagger_path=swagger_path,
static_path=static_path,
error_callback=error_callback,
in_place=in_place,
prefix=prefix,
schema_name_resolver=schema_name_resolver,
**kwargs,
)
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/decorators/ 0000755 0000000 0000000 00000000000 14231014705 022515 5 ustar 00 python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/middlewares.py 0000644 0000000 0000000 00000002707 14231014705 023230 0 ustar 00 from aiohttp import web
from .utils import issubclass_py37fix
@web.middleware
async def validation_middleware(request: web.Request, handler) -> web.Response:
"""
Validation middleware for aiohttp web app
Usage:
.. code-block:: python
app.middlewares.append(validation_middleware)
"""
orig_handler = request.match_info.handler
if not hasattr(orig_handler, "__schemas__"):
if not issubclass_py37fix(orig_handler, web.View):
return await handler(request)
sub_handler = getattr(orig_handler, request.method.lower(), None)
if sub_handler is None:
return await handler(request)
if not hasattr(sub_handler, "__schemas__"):
return await handler(request)
schemas = sub_handler.__schemas__
else:
schemas = orig_handler.__schemas__
result = {}
for schema in schemas:
data = await request.app["_apispec_parser"].parse(
schema["schema"],
request,
location=schema["location"],
unknown=None, # Pass None to use the schema’s setting instead.
)
if schema["put_into"]:
request[schema["put_into"]] = data
elif data:
try:
result.update(data)
except (ValueError, TypeError):
result = data
break
request[request.app["_apispec_request_data_name"]] = result
return await handler(request)
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/static/ 0000755 0000000 0000000 00000000000 14231014705 021637 5 ustar 00 python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/utils.py 0000644 0000000 0000000 00000000571 14231014705 022065 0 ustar 00 from string import Formatter
def get_path(route):
path_info = route.resource.get_info()
return path_info.get("path") or path_info.get("formatter")
def get_path_keys(path):
return [i[1] for i in Formatter().parse(path) if i[1]]
def issubclass_py37fix(cls, cls_info):
try:
return issubclass(cls, cls_info)
except TypeError:
return False
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/decorators/__init__.py 0000644 0000000 0000000 00000000401 14231014705 024621 0 ustar 00 from .docs import docs
from .request import (
cookies_schema,
form_schema,
headers_schema,
json_schema,
match_info_schema,
querystring_schema,
request_schema,
use_kwargs,
)
from .response import marshal_with, response_schema
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/decorators/docs.py 0000644 0000000 0000000 00000002376 14231014705 024027 0 ustar 00 def docs(**kwargs):
"""
Annotate the decorated view function with the specified Swagger
attributes.
Usage:
.. code-block:: python
from aiohttp import web
@docs(tags=['my_tag'],
summary='Test method summary',
description='Test method description',
parameters=[{
'in': 'header',
'name': 'X-Request-ID',
'schema': {'type': 'string', 'format': 'uuid'},
'required': 'true'
}]
)
async def index(request):
return web.json_response({'msg': 'done', 'data': {}})
"""
def wrapper(func):
if not kwargs.get("produces"):
kwargs["produces"] = ["application/json"]
if not hasattr(func, "__apispec__"):
func.__apispec__ = {"schemas": [], "responses": {}, "parameters": []}
func.__schemas__ = []
extra_parameters = kwargs.pop("parameters", [])
extra_responses = kwargs.pop("responses", {})
func.__apispec__["parameters"].extend(extra_parameters)
func.__apispec__["responses"].update(extra_responses)
func.__apispec__.update(kwargs)
return func
return wrapper
python-aiohttp-apispec_3.0.0~b2+ds1.orig/aiohttp_apispec/decorators/request.py 0000644 0000000 0000000 00000007034 14231014705 024563 0 ustar 00 import copy
from functools import partial
# locations supported by both openapi and webargs.aiohttpparser
VALID_SCHEMA_LOCATIONS = (
"cookies",
"files",
"form",
"headers",
"json",
"match_info",
"path",
"query",
"querystring",
)
def request_schema(
schema, location="json", put_into=None, example=None, add_to_refs=False, **kwargs
):
"""
Add request info into the swagger spec and
prepare injection keyword arguments from the specified
webargs arguments into the decorated view function in
request['data'] for validation_middleware validation middleware.
Usage:
.. code-block:: python
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description='name')
@request_schema(RequestSchema(strict=True))
async def index(request):
# aiohttp_apispec_middleware should be used for it
data = request['data']
return web.json_response({'name': data['name'],
'id': data['id']})
:param schema: :class:`Schema