Compare commits

..

No commits in common. "v1.120.3" and "main" have entirely different histories.

16 changed files with 131 additions and 138 deletions

View File

@ -1,12 +0,0 @@
stages:
- package
publish-pypi:
stage: package
image: python:3.10
script:
- sed -i -e "s/1.12.1/${CI_COMMIT_TAG:1}/g" aiohttp_pydantic/__init__.py
- pip install -U setuptools wheel pip; pip install invoke
- invoke upload --pypi-user ${PYPI_REPO_USER} --pypi-password ${PYPI_REPO_PASSWORD} --pypi-url ${PYPI_REPO_URL}
only:
- tags

View File

@ -2,4 +2,4 @@ from .view import PydanticView
__version__ = "1.12.1" __version__ = "1.12.1"
__all__ = ("PydanticView", "__version__") __all__ = ("PydanticView", "__version__")

View File

@ -14,7 +14,7 @@ def setup(
url_prefix: str = "/oas", url_prefix: str = "/oas",
enable: bool = True, enable: bool = True,
version_spec: Optional[str] = None, version_spec: Optional[str] = None,
title_spec: Optional[str] = None title_spec: Optional[str] = None,
): ):
if enable: if enable:
oas_app = web.Application() oas_app = web.Application()

View File

@ -1,27 +1,45 @@
{# This updated file is part of swagger_ui_bundle (https://github.com/bartsanchez/swagger_ui_bundle) #} {# This updated file is part of swagger_ui_bundle (https://github.com/dtkav/swagger_ui_bundle) #}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{ title | default('Swagger UI') }}</title> <title>{{ title | default('Swagger UI') }}</title>
<link rel="stylesheet" type="text/css" href="{{ static_url | trim('/') }}/swagger-ui.css" /> <link rel="stylesheet" type="text/css" href="{{ static_url | trim('/') }}/swagger-ui.css" >
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/png" href="{{ static_url | trim('/') }}/favicon-32x32.png" sizes="32x32" /> <link rel="icon" type="image/png" href="{{ static_url | trim('/') }}/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="{{ static_url | trim('/') }}/favicon-16x16.png" sizes="16x16" /> <link rel="icon" type="image/png" href="{{ static_url | trim('/') }}/favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head> </head>
<body> <body>
<div id="swagger-ui"></div> <div id="swagger-ui"></div>
<script src="{{ static_url | trim('/') }}/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="{{ static_url | trim('/') }}/swagger-ui-standalone-preset.js" charset="UTF-8"> </script> <script src="{{ static_url | trim('/') }}/swagger-ui-bundle.js"> </script>
<script src="{{ static_url | trim('/') }}/swagger-ui-standalone-preset.js"> </script>
<script> <script>
window.onload = function() { window.onload = function() {
// Begin Swagger UI call region // Begin Swagger UI call region
const ui = SwaggerUIBundle({ const ui = SwaggerUIBundle({
url: "{{ openapi_spec_url }}", url: "{{ openapi_spec_url }}",
{% if urls is defined %}
urls: {{ urls|tojson|safe }},
{% endif %}
validatorUrl: {{ validatorUrl | default('null') }}, validatorUrl: {{ validatorUrl | default('null') }},
{% if configUrl is defined %} {% if configUrl is defined %}
configUrl: "{{ configUrl }}", configUrl: "{{ configUrl }}",
@ -36,15 +54,16 @@
SwaggerUIBundle.plugins.DownloadUrl SwaggerUIBundle.plugins.DownloadUrl
], ],
layout: "StandaloneLayout" layout: "StandaloneLayout"
}); })
{% if initOAuth is defined %} {% if initOAuth is defined %}
ui.initOAuth( ui.initOAuth(
{{ initOAuth|tojson|safe }} {{ initOAuth|tojson|safe }}
) )
{% endif %} {% endif %}
// End Swagger UI call region // End Swagger UI call region
window.ui = ui;
}; window.ui = ui
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -185,8 +185,6 @@ async def oas_ui(request):
static_url = request.app.router["static"].url_for(filename="") static_url = request.app.router["static"].url_for(filename="")
spec_url = request.app.router["spec"].url_for() spec_url = request.app.router["spec"].url_for()
if request.scheme != request.headers.get('x-forwarded-proto', request.scheme):
request = request.clone(scheme=request.headers['x-forwarded-proto'])
host = request.url.origin() host = request.url.origin()
return Response( return Response(

View File

@ -3,7 +3,4 @@ requires = [
"setuptools >= 46.4.0", "setuptools >= 46.4.0",
"wheel", "wheel",
] ]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
asyncio_mode = "auto"

View File

@ -1,42 +1,42 @@
aiohttp==3.8.1 async-timeout==3.0.1
aiosignal==1.2.0 attrs==21.2.0
async-timeout==4.0.2 bleach==4.0.0
atomicwrites==1.4.1 certifi==2021.5.30
attrs==21.4.0 cffi==1.14.6
bleach==5.0.1 chardet==4.0.0
certifi==2022.6.15 charset-normalizer==2.0.4
charset-normalizer==2.1.0 codecov==2.1.11
codecov==2.1.12 colorama==0.4.4
colorama==0.4.5 coverage==5.5
commonmark==0.9.1 cryptography==3.4.7
coverage==6.4.2 docutils==0.17.1
docutils==0.19 idna==3.2
frozenlist==1.3.0 importlib-metadata==4.6.3
idna==3.3
importlib-metadata==4.12.0
iniconfig==1.1.1 iniconfig==1.1.1
keyring==23.7.0 jeepney==0.7.1
multidict==6.0.2 keyring==23.0.1
packaging==21.3 multidict==5.1.0
pkginfo==1.8.3 packaging==21.0
pluggy==1.0.0 pkginfo==1.7.1
py==1.11.0 pluggy==0.13.1
Pygments==2.12.0 py==1.10.0
pyparsing==3.0.9 pycparser==2.20
pytest==7.1.2 Pygments==2.9.0
pytest-aiohttp==1.0.4 pyparsing==2.4.7
pytest-asyncio==0.19.0 pytest==6.1.2
pytest-cov==3.0.0 pytest-aiohttp==0.3.0
pywin32-ctypes==0.2.0 pytest-cov==2.10.1
readme-renderer==35.0 readme-renderer==29.0
requests==2.28.1 requests==2.26.0
requests-toolbelt==0.9.1 requests-toolbelt==0.9.1
rfc3986==2.0.0 rfc3986==1.5.0
rich==12.5.1 SecretStorage==3.3.1
six==1.16.0 six==1.16.0
tomli==2.0.1 toml==0.10.2
twine==4.0.1 tqdm==4.62.0
urllib3==1.26.11 twine==3.4.2
typing-extensions==3.10.0.0
urllib3==1.26.6
webencodings==0.5.1 webencodings==0.5.1
yarl==1.7.2 yarl==1.6.3
zipp==3.8.1 zipp==3.5.0

View File

@ -1,28 +1,23 @@
aiohttp==3.8.1 async-timeout==3.0.1
aiosignal==1.2.0 attrs==21.2.0
async-timeout==4.0.2 bleach==4.0.0
atomicwrites==1.4.1 chardet==4.0.0
attrs==21.4.0 coverage==5.5
bleach==5.0.1 docutils==0.17.1
charset-normalizer==2.1.0 idna==3.2
colorama==0.4.5
coverage==6.4.2
docutils==0.19
frozenlist==1.3.0
idna==3.3
iniconfig==1.1.1 iniconfig==1.1.1
multidict==6.0.2 multidict==5.1.0
packaging==21.3 packaging==21.0
pluggy==1.0.0 pluggy==0.13.1
py==1.11.0 py==1.10.0
Pygments==2.12.0 Pygments==2.9.0
pyparsing==3.0.9 pyparsing==2.4.7
pytest==7.1.2 pytest==6.1.2
pytest-aiohttp==1.0.4 pytest-aiohttp==0.3.0
pytest-asyncio==0.19.0 pytest-cov==2.10.1
pytest-cov==3.0.0 readme-renderer==29.0
readme-renderer==35.0
six==1.16.0 six==1.16.0
tomli==2.0.1 toml==0.10.2
typing-extensions==3.10.0.0
webencodings==0.5.1 webencodings==0.5.1
yarl==1.7.2 yarl==1.6.3

View File

@ -32,18 +32,18 @@ python_requires = >=3.8
install_requires = install_requires =
aiohttp aiohttp
pydantic>=1.7 pydantic>=1.7
swagger-4-ui-bundle swagger-ui-bundle
[options.extras_require] [options.extras_require]
test = test =
pytest==7.1.2 pytest==6.1.2
pytest-aiohttp==1.0.4 pytest-aiohttp==0.3.0
pytest-cov==3.0.0 pytest-cov==2.10.1
readme-renderer==35.0 readme-renderer==29.0
ci = ci =
%(test)s %(test)s
codecov==2.1.12 codecov==2.1.11
twine==4.0.1 twine==3.4.2
[options.packages.find] [options.packages.find]
exclude = exclude =

View File

@ -102,9 +102,8 @@ def test(c, isolate=False):
""" """
Launch tests Launch tests
""" """
#opt = "I" if isolate else "" opt = "I" if isolate else ""
#c.run(f"python -{opt}m pytest --cov-report=xml --cov=aiohttp_pydantic tests/") c.run(f"python -{opt}m pytest --cov-report=xml --cov=aiohttp_pydantic tests/")
pass
@task() @task()
@ -137,7 +136,6 @@ def prepare_ci_env(c):
title("Installing wheel", "=") title("Installing wheel", "=")
package_version = read_configuration("./setup.cfg")["metadata"]["version"] package_version = read_configuration("./setup.cfg")["metadata"]["version"]
print([x for x in Path("dist").glob('*')])
dist = next(Path("dist").glob(f"aiohttp_pydantic-{package_version}-*.whl")) dist = next(Path("dist").glob(f"aiohttp_pydantic-{package_version}-*.whl"))
c.run(f"dist_venv/bin/python -m pip install {dist}") c.run(f"dist_venv/bin/python -m pip install {dist}")
@ -158,7 +156,7 @@ def prepare_upload(c):
@task(tag_eq_version, prepare_upload) @task(tag_eq_version, prepare_upload)
def upload(c, pypi_user=None, pypi_password=None, pypi_url=None): def upload(c, pypi_user=None, pypi_password=None):
""" """
Upload on pypi Upload on pypi
""" """
@ -167,9 +165,8 @@ def upload(c, pypi_user=None, pypi_password=None, pypi_url=None):
if pypi_user is not None and pypi_password is not None: if pypi_user is not None and pypi_password is not None:
c.run( c.run(
f"dist_venv/bin/twine upload --non-interactive" f"dist_venv/bin/twine upload --non-interactive"
f" -u {pypi_user} -p {pypi_password} {dist}" f" -u {pypi_user} -p {pypi_password} {dist}",
f" --repository-url {pypi_url}",
hide=True, hide=True,
) )
else: else:
c.run(f"dist_venv/bin/twine upload --repository-url {pypi_url} --repository aiohttp-pydantic {dist}") c.run(f"dist_venv/bin/twine upload --repository aiohttp-pydantic {dist}")

View File

@ -37,14 +37,13 @@ class ArticleView(PydanticView):
async def test_post_an_article_with_wrong_type_field_should_return_an_error_message( async def test_post_an_article_with_wrong_type_field_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
client = await aiohttp_client(app) client = await aiohttp_client(app)
resp = await client.post("/article", json={"name": "foo", "nb_page": "foo"}) resp = await client.post("/article", json={"name": "foo", "nb_page": "foo"})
assert resp.status == 400 assert resp.status == 400
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
assert await resp.json() == [ assert await resp.json() == [

View File

@ -98,7 +98,7 @@ async def ensure_content_durability(client):
@pytest.fixture @pytest.fixture
async def generated_oas(aiohttp_client, event_loop) -> web.Application: async def generated_oas(aiohttp_client, loop) -> web.Application:
app = web.Application() app = web.Application()
app.router.add_view("/pets", PetCollectionView) app.router.add_view("/pets", PetCollectionView)
app.router.add_view("/pets/{id}", PetItemView) app.router.add_view("/pets/{id}", PetItemView)

View File

@ -29,7 +29,7 @@ class ArticleView(PydanticView):
async def test_post_an_article_without_required_field_should_return_an_error_message( async def test_post_an_article_without_required_field_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -49,7 +49,7 @@ async def test_post_an_article_without_required_field_should_return_an_error_mes
async def test_post_an_article_with_wrong_type_field_should_return_an_error_message( async def test_post_an_article_with_wrong_type_field_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -68,7 +68,7 @@ async def test_post_an_article_with_wrong_type_field_should_return_an_error_mess
] ]
async def test_post_an_array_json_is_supported(aiohttp_client, event_loop): async def test_post_an_array_json_is_supported(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -81,7 +81,7 @@ async def test_post_an_array_json_is_supported(aiohttp_client, event_loop):
async def test_post_an_array_json_to_an_object_model_should_return_an_error( async def test_post_an_array_json_to_an_object_model_should_return_an_error(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -101,7 +101,7 @@ async def test_post_an_array_json_to_an_object_model_should_return_an_error(
async def test_post_an_object_json_to_a_list_model_should_return_an_error( async def test_post_an_object_json_to_a_list_model_should_return_an_error(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -120,7 +120,7 @@ async def test_post_an_object_json_to_a_list_model_should_return_an_error(
] ]
async def test_post_a_valid_article_should_return_the_parsed_type(aiohttp_client, event_loop): async def test_post_a_valid_article_should_return_the_parsed_type(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)

View File

@ -61,7 +61,7 @@ class ArticleViewWithSignatureGroup(PydanticView):
async def test_get_article_without_required_header_should_return_an_error_message( async def test_get_article_without_required_header_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -81,7 +81,7 @@ async def test_get_article_without_required_header_should_return_an_error_messag
async def test_get_article_with_wrong_header_type_should_return_an_error_message( async def test_get_article_with_wrong_header_type_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -101,7 +101,7 @@ async def test_get_article_with_wrong_header_type_should_return_an_error_message
async def test_get_article_with_valid_header_should_return_the_parsed_type( async def test_get_article_with_valid_header_should_return_the_parsed_type(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -116,7 +116,7 @@ async def test_get_article_with_valid_header_should_return_the_parsed_type(
async def test_get_article_with_valid_header_containing_hyphen_should_be_returned( async def test_get_article_with_valid_header_containing_hyphen_should_be_returned(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -130,7 +130,7 @@ async def test_get_article_with_valid_header_containing_hyphen_should_be_returne
assert await resp.json() == {"signature": "2020-10-04T18:01:00"} assert await resp.json() == {"signature": "2020-10-04T18:01:00"}
async def test_wrong_value_to_header_defined_with_str_enum(aiohttp_client, event_loop): async def test_wrong_value_to_header_defined_with_str_enum(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/coord", ViewWithEnumType) app.router.add_view("/coord", ViewWithEnumType)
@ -153,7 +153,7 @@ async def test_wrong_value_to_header_defined_with_str_enum(aiohttp_client, event
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_correct_value_to_header_defined_with_str_enum(aiohttp_client, event_loop): async def test_correct_value_to_header_defined_with_str_enum(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/coord", ViewWithEnumType) app.router.add_view("/coord", ViewWithEnumType)
@ -164,7 +164,7 @@ async def test_correct_value_to_header_defined_with_str_enum(aiohttp_client, eve
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_with_signature_group(aiohttp_client, event_loop): async def test_with_signature_group(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleViewWithSignatureGroup) app.router.add_view("/article", ArticleViewWithSignatureGroup)

View File

@ -11,7 +11,7 @@ class ArticleView(PydanticView):
async def test_get_article_with_correct_path_parameters_should_return_parameters_in_path( async def test_get_article_with_correct_path_parameters_should_return_parameters_in_path(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article/{author_id}/tag/{tag}/before/{date}", ArticleView) app.router.add_view("/article/{author_id}/tag/{tag}/before/{date}", ArticleView)
@ -24,7 +24,7 @@ async def test_get_article_with_correct_path_parameters_should_return_parameters
async def test_get_article_with_wrong_path_parameters_should_return_error( async def test_get_article_with_wrong_path_parameters_should_return_error(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article/{author_id}/tag/{tag}/before/{date}", ArticleView) app.router.add_view("/article/{author_id}/tag/{tag}/before/{date}", ArticleView)

View File

@ -55,7 +55,7 @@ class ArticleViewWithPaginationGroup(PydanticView):
async def test_get_article_without_required_qs_should_return_an_error_message( async def test_get_article_without_required_qs_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -75,7 +75,7 @@ async def test_get_article_without_required_qs_should_return_an_error_message(
async def test_get_article_with_wrong_qs_type_should_return_an_error_message( async def test_get_article_with_wrong_qs_type_should_return_an_error_message(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -95,7 +95,7 @@ async def test_get_article_with_wrong_qs_type_should_return_an_error_message(
async def test_get_article_with_valid_qs_should_return_the_parsed_type( async def test_get_article_with_valid_qs_should_return_the_parsed_type(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -114,7 +114,7 @@ async def test_get_article_with_valid_qs_should_return_the_parsed_type(
async def test_get_article_with_valid_qs_and_omitted_optional_should_return_default_value( async def test_get_article_with_valid_qs_and_omitted_optional_should_return_default_value(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -133,7 +133,7 @@ async def test_get_article_with_valid_qs_and_omitted_optional_should_return_defa
async def test_get_article_with_multiple_value_for_qs_age_must_failed( async def test_get_article_with_multiple_value_for_qs_age_must_failed(
aiohttp_client, event_loop aiohttp_client, loop
): ):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -153,7 +153,7 @@ async def test_get_article_with_multiple_value_for_qs_age_must_failed(
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_get_article_with_multiple_value_of_tags(aiohttp_client, event_loop): async def test_get_article_with_multiple_value_of_tags(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -172,7 +172,7 @@ async def test_get_article_with_multiple_value_of_tags(aiohttp_client, event_loo
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_get_article_with_one_value_of_tags_must_be_a_list(aiohttp_client, event_loop): async def test_get_article_with_one_value_of_tags_must_be_a_list(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleView) app.router.add_view("/article", ArticleView)
@ -191,7 +191,7 @@ async def test_get_article_with_one_value_of_tags_must_be_a_list(aiohttp_client,
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_get_article_without_required_field_page(aiohttp_client, event_loop): async def test_get_article_without_required_field_page(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleViewWithPaginationGroup) app.router.add_view("/article", ArticleViewWithPaginationGroup)
@ -210,7 +210,7 @@ async def test_get_article_without_required_field_page(aiohttp_client, event_loo
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_get_article_with_page(aiohttp_client, event_loop): async def test_get_article_with_page(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleViewWithPaginationGroup) app.router.add_view("/article", ArticleViewWithPaginationGroup)
@ -222,7 +222,7 @@ async def test_get_article_with_page(aiohttp_client, event_loop):
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_get_article_with_page_and_page_size(aiohttp_client, event_loop): async def test_get_article_with_page_and_page_size(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleViewWithPaginationGroup) app.router.add_view("/article", ArticleViewWithPaginationGroup)
@ -236,7 +236,7 @@ async def test_get_article_with_page_and_page_size(aiohttp_client, event_loop):
assert resp.content_type == "application/json" assert resp.content_type == "application/json"
async def test_get_article_with_page_and_wrong_page_size(aiohttp_client, event_loop): async def test_get_article_with_page_and_wrong_page_size(aiohttp_client, loop):
app = web.Application() app = web.Application()
app.router.add_view("/article", ArticleViewWithPaginationGroup) app.router.add_view("/article", ArticleViewWithPaginationGroup)