Skip to content

Commit

Permalink
Merge pull request #3 from dapper91/dev
Browse files Browse the repository at this point in the history
- refactoring done.
- README fixed.
  • Loading branch information
dapper91 authored Jan 29, 2022
2 parents 5150d2b + 8675bd0 commit 8f956de
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 48 deletions.
73 changes: 41 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pip install aiohttp-validator
## A Simple Example

```py
import datetime as dt
from uuid import UUID
from typing import Any, Dict, List

import pydantic
Expand All @@ -35,52 +37,59 @@ import aiohttp_validator as validator
routes = web.RouteTableDef()


class RequestHeaders(pydantic.BaseModel):
requestId: str
timestamp: float = 0.0


@routes.get('/')
@routes.get('/posts')
@validator.validated()
async def simple_get(request: web.Request, headers: RequestHeaders, offset: int = 0):
assert isinstance(headers, RequestHeaders)
async def get_posts(request: web.Request, tags: List[str], limit: pydantic.conint(gt=0, le=100), offset: int = 0):
assert isinstance(tags, list)
assert isinstance(limit, int)
assert isinstance(offset, int)
# your code here ...

return web.Response()
return web.Response(status=200)


@routes.post('/{path}')
@validator.validated()
async def simple_post(request: web.Request, body: Dict[str, Any], path: str, offset: int, limit: int = 10):
assert isinstance(body, dict)
assert isinstance(path, str)
assert isinstance(offset, int)
assert isinstance(limit, int)

return web.Response()
class RequestHeaders(pydantic.BaseModel):
requestId: int


class SubModel(pydantic.BaseModel):
l: List[str]
i: int
class User(pydantic.BaseModel):
name: str
surname: str


class Body(pydantic.BaseModel):
i: int
f: float
sub: SubModel
class Post(pydantic.BaseModel):
title: str
text: str
timestamp: float
author: User
tags: List[str] = pydantic.Field(default_factory=list)


@routes.post('/{path1}/{path2}')
@routes.post('/posts/{section}/{date}')
@validator.validated()
async def pydantic_body(request: web.Request, body: Body, path1: str, path2: int, pages: List[int]):
assert isinstance(body, Body)
assert isinstance(path1, str)
assert isinstance(path2, int)
assert isinstance(pages, list)
async def create_post(request: web.Request, body: Post, headers: RequestHeaders, section: str, date: dt.date):
assert isinstance(body, Post)
assert isinstance(headers, RequestHeaders)
assert isinstance(date, dt.date)
assert isinstance(section, str)
# your code here ...

return web.Response(status=201)


class AuthCookies(pydantic.BaseModel):
tokenId: UUID

return web.Response()

@routes.post('/users')
@validator.validated()
async def create_user(request: web.Request, body: Dict[str, Any], headers: RequestHeaders, cookies: AuthCookies):
assert isinstance(body, dict)
assert isinstance(headers, RequestHeaders)
assert isinstance(cookies, AuthCookies)
# your code here ...

return web.Response(status=201)

app = web.Application()
app.add_routes(routes)
Expand Down
32 changes: 20 additions & 12 deletions aiohttp_validator/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
import json
import typing
from collections import defaultdict
from types import SimpleNamespace
from typing import Any, Callable, Collection, Dict, Type

import multidict
import pydantic
from aiohttp import web


def extract_annotations(func: Callable, body_argname: str, headers_argname: str, cookies_argname: str):
def extract_annotations(
func: Callable,
body_argname: str,
headers_argname: str,
cookies_argname: str,
) -> SimpleNamespace:
body_annotation, headers_annotation, cookies_annotation, params_annotations = None, None, None, {}

signature = inspect.signature(func)
Expand All @@ -30,7 +36,12 @@ def extract_annotations(func: Callable, body_argname: str, headers_argname: str,
param.default if param.default is not inspect.Parameter.empty else ...,
)

return body_annotation, headers_annotation, cookies_annotation, params_annotations
return SimpleNamespace(
body=body_annotation,
headers=headers_annotation,
cookies=cookies_annotation,
params=params_annotations,
)


def multidict_to_dict(mdict: multidict.MultiMapping) -> Dict[str, Any]:
Expand Down Expand Up @@ -123,19 +134,16 @@ def validated(
def decorator(func: Callable) -> Callable:
annotations = extract_annotations(func, body_argname, headers_argname, cookies_argname)

body_annotation, headers_annotation, cookies_annotation, params_annotations = annotations
params_model = pydantic.create_model('Params', **params_annotations)
params_model = pydantic.create_model('Params', **annotations.params)

@ft.wraps(func)
async def wrapper(request: web.Request, *args, **kwargs) -> Any:
if body_annotation is not None:
kwargs[body_argname] = await process_body(request, body_annotation)

if headers_annotation is not None:
kwargs[headers_argname] = await process_headers(request, headers_annotation)

if cookies_annotation is not None:
kwargs[cookies_argname] = await process_cookes(request, cookies_annotation)
if annotations.body is not None:
kwargs[body_argname] = await process_body(request, annotations.body)
if annotations.headers is not None:
kwargs[headers_argname] = await process_headers(request, annotations.headers)
if annotations.cookies is not None:
kwargs[cookies_argname] = await process_cookes(request, annotations.cookies)

fitted_query = fit_multidict_to_model(request.query, params_model)
try:
Expand Down
6 changes: 3 additions & 3 deletions examples/quickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async def get_posts(request: web.Request, tags: List[str], limit: pydantic.conin
assert isinstance(tags, list)
assert isinstance(limit, int)
assert isinstance(offset, int)
# you code here ...
# your code here ...

return web.Response(status=200)

Expand Down Expand Up @@ -45,7 +45,7 @@ async def create_post(request: web.Request, body: Post, headers: RequestHeaders,
assert isinstance(headers, RequestHeaders)
assert isinstance(date, dt.date)
assert isinstance(section, str)
# you code here ...
# your code here ...

return web.Response(status=201)

Expand All @@ -60,7 +60,7 @@ async def create_user(request: web.Request, body: Dict[str, Any], headers: Reque
assert isinstance(body, dict)
assert isinstance(headers, RequestHeaders)
assert isinstance(cookies, AuthCookies)
# you code here ...
# your code here ...

return web.Response(status=201)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aiohttp-validator"
version = "0.1.1"
version = "0.1.2"
description = "aiohttp simple pydantic validator"
authors = ["Dmitry Pershin <[email protected]>"]
license = "Unlicense"
Expand Down

0 comments on commit 8f956de

Please sign in to comment.