Skip to content

Commit 3defd49

Browse files
author
vvcheremushkin
committed
version 0.12.0
1 parent 1694188 commit 3defd49

30 files changed

+611
-418
lines changed

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
ujson
22
typing_extensions
33
marshmallow>=3.0.0rc8,<4
4-
starlette<=1
5-
apispec<3
4+
starlette<1
5+
apispec<4
66

77
# Testing
88
pytest

setup.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,47 @@
77

88

99
def get_long_description():
10-
with open("README.md", encoding="utf8") as f:
10+
with open('README.md', encoding='utf8') as f:
1111
return f.read()
1212

1313

1414
def get_packages(package):
1515
return [
1616
dirpath
1717
for dirpath, dirnames, filenames in os.walk(package)
18-
if os.path.exists(os.path.join(dirpath, "__init__.py"))
18+
if os.path.exists(os.path.join(dirpath, '__init__.py'))
1919
]
2020

2121

2222
setup(
23-
name="star_resty",
24-
python_requires=">=3.7",
23+
name='star_resty',
24+
python_requires='>=3.7',
2525
install_requires=[
26-
"ujson",
27-
"typing_extensions",
28-
"marshmallow>=3.0.0rc8,<4",
29-
"starlette<=1",
30-
"apispec<3",
26+
'ujson',
27+
'typing_extensions',
28+
'marshmallow>=3.0.0rc8,<4',
29+
'starlette<1',
30+
'apispec<4',
3131
],
32-
version="0.0.11",
33-
url="https://github.com/slv0/start_resty",
34-
license="BSD",
35-
description="The web framework",
32+
version='0.0.12',
33+
url='https://github.com/slv0/start_resty',
34+
license='BSD',
35+
description='The web framework',
3636
long_description=get_long_description(),
37-
long_description_content_type="text/markdown",
38-
author="Slava Cheremushkin",
39-
author_email="slv0.chr@gmail.com",
40-
packages=get_packages("star_resty"),
41-
package_data={"star_resty": ["py.typed"]},
42-
data_files=[("", ["LICENSE"])],
37+
long_description_content_type='text/markdown',
38+
author='Slava Cheremushkin',
39+
author_email='slv0.chr@gmail.com',
40+
packages=get_packages('star_resty'),
41+
package_data={'star_resty': ['py.typed']},
42+
data_files=[('', ['LICENSE'])],
4343
classifiers=[
44-
"Development Status :: 3 - Alpha",
45-
"Environment :: Web Environment",
46-
"Intended Audience :: Developers",
47-
"License :: OSI Approved :: BSD License",
48-
"Operating System :: OS Independent",
49-
"Topic :: Internet :: WWW/HTTP",
50-
"Programming Language :: Python :: 3.7",
44+
'Development Status :: 3 - Alpha',
45+
'Environment :: Web Environment',
46+
'Intended Audience :: Developers',
47+
'License :: OSI Approved :: BSD License',
48+
'Operating System :: OS Independent',
49+
'Topic :: Internet :: WWW/HTTP',
50+
'Programming Language :: Python :: 3.7',
5151
],
5252
zip_safe=False,
5353
)

star_resty/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .apidocs import setup_spec
22
from .method import *
33
from .operation import Operation
4-
from .types import *
4+
from .payload import *

star_resty/apidocs/operation.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from starlette.routing import Route
2+
3+
from star_resty.method import Method
4+
from .request import resolve_parameters, resolve_request_body, resolve_request_body_params
5+
from .response import resolve_responses
6+
7+
__all__ = ('setup_route_operations',)
8+
9+
10+
def setup_route_operations(route: Route, endpoint: Method, version: int = 2,
11+
add_head_methods: bool = False):
12+
operation = setup_operation(endpoint, version=version)
13+
operation = {key: val for key, val in operation.items() if val is not None}
14+
return {method.lower(): operation for method in route.methods
15+
if (method != 'HEAD' or add_head_methods)}
16+
17+
18+
def setup_operation(endpoint: Method, version=2):
19+
options = endpoint.meta
20+
res = {
21+
'tags': [options.tag],
22+
'description': options.description,
23+
'summary': options.summary,
24+
'produces': [endpoint.serializer.media_type],
25+
'parameters': resolve_parameters(endpoint),
26+
'responses': resolve_responses(endpoint),
27+
}
28+
29+
if options.security is not None:
30+
res['security'] = options.security
31+
32+
if version > 2:
33+
res['requestBody'] = resolve_request_body(endpoint)
34+
else:
35+
res['parameters'].extend(resolve_request_body_params(endpoint))
36+
37+
if options.meta:
38+
res.update(options.meta)
39+
40+
return res

star_resty/apidocs/request.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from star_resty.method import Method
2+
from star_resty.method.parser import RequestParser
3+
4+
__all__ = ('resolve_parameters', 'resolve_request_body', 'resolve_request_body_params')
5+
6+
7+
def resolve_parameters(endpoint: Method):
8+
parameters = []
9+
parser = getattr(endpoint, '__parser__', None)
10+
if parser is None:
11+
return parameters
12+
13+
for p in parser:
14+
if p.schema is not None and p.location != 'body':
15+
parameters.append({'in': p.location, 'schema': p.schema})
16+
17+
return parameters
18+
19+
20+
def resolve_request_body(endpoint: Method):
21+
parser = getattr(endpoint, '__parser__', None)
22+
if parser is None:
23+
return None
24+
25+
content = resolve_request_body_content(parser)
26+
if content:
27+
return {'content': content}
28+
29+
30+
def resolve_request_body_content(parser: RequestParser):
31+
content = {}
32+
for p in parser:
33+
if p.schema is not None and p.location == 'body' and p.media_type:
34+
content[p.media_type] = {'schema': p.schema}
35+
36+
return content
37+
38+
39+
def resolve_request_body_params(endpoint: Method):
40+
params = []
41+
parser = getattr(endpoint, '__parser__', None)
42+
if parser is None:
43+
return params
44+
45+
for p in parser:
46+
if p.schema is not None and p.location == 'body' and p.media_type:
47+
params.append({
48+
'name': 'body',
49+
'in': 'body',
50+
'schema': p.schema
51+
})
52+
53+
return params

star_resty/apidocs/response.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import inspect
2+
from typing import Union, Dict, Type
3+
4+
from star_resty.method import Method
5+
6+
__all__ = ('resolve_responses',)
7+
8+
9+
def resolve_responses(endpoint: Method):
10+
responses = {}
11+
if endpoint.response_schema:
12+
responses[str(endpoint.status_code)] = {
13+
'schema': endpoint.response_schema
14+
}
15+
16+
errors = endpoint.meta.errors or ()
17+
for e in errors:
18+
if isinstance(e, dict) and e.get('status_code'):
19+
responses[str(e['status_code'])] = {key: val for key, val in e.items() if key != 'status_code'}
20+
elif isinstance(e, Exception) and getattr(e, 'status_code', None) is not None:
21+
responses[str(getattr(e, 'status_code'))] = create_error_schema_by_exc(e)
22+
elif inspect.isclass(e) and issubclass(e, Exception) and getattr(e, 'status_code', None) is not None:
23+
responses[str(getattr(e, 'status_code'))] = create_error_schema_by_exc(e)
24+
25+
parser = getattr(endpoint, '__parser__', None)
26+
if parser and '404' not in responses:
27+
responses['400'] = {'description': 'Bad request'}
28+
29+
return responses
30+
31+
32+
def create_error_schema_by_exc(e: Union[Exception, Type[Exception]]) -> Dict:
33+
schema = {'description': (getattr(e, 'detail', None)
34+
or getattr(e, 'description', None)
35+
or str(e))}
36+
error_schema = getattr(e, 'schema', None)
37+
if error_schema is not None:
38+
schema['schema'] = error_schema
39+
40+
return schema

star_resty/apidocs/route.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Sequence, Union
2+
3+
from apispec import APISpec
4+
from starlette.routing import Route, Mount
5+
6+
from .operation import setup_route_operations
7+
from .utils import convert_path
8+
9+
__all__ = ('setup_routes',)
10+
11+
12+
def setup_routes(routes: Sequence[Union[Route, Mount]],
13+
spec: APISpec, version: int = 2,
14+
add_head_methods: bool = False,
15+
path: str = ''):
16+
for route in routes:
17+
if isinstance(route, Mount):
18+
setup_routes(route.routes, spec, version=version, add_head_methods=add_head_methods,
19+
path=f'{path}{route.path}')
20+
continue
21+
elif isinstance(route, Route) and not route.include_in_schema:
22+
continue
23+
24+
endpoint = getattr(route.endpoint, '__endpoint__', None)
25+
if endpoint is None:
26+
continue
27+
28+
operations = setup_route_operations(route, endpoint, version=version,
29+
add_head_methods=add_head_methods)
30+
route_path = f'{path}{route.path}'
31+
spec.path(convert_path(route_path), operations=operations)

0 commit comments

Comments
 (0)