1import functools
2import re
3import typing as t
4
5import starlette.convertors
6from starlette.responses import JSONResponse as StarletteJSONResponse
7from starlette.responses import Response as StarletteResponse
8from starlette.types import Receive, Scope
9
10from connexion.frameworks.abstract import Framework
11from connexion.lifecycle import ConnexionRequest
12from connexion.uri_parsing import AbstractURIParser
13
14
15class Starlette(Framework):
16 @staticmethod
17 def is_framework_response(response: t.Any) -> bool:
18 return isinstance(response, StarletteResponse)
19
20 @classmethod
21 def connexion_to_framework_response(cls, response):
22 return cls.build_response(
23 status_code=response.status_code,
24 content_type=response.content_type,
25 headers=response.headers,
26 data=response.body,
27 )
28
29 @classmethod
30 def build_response(
31 cls,
32 data: t.Any,
33 *,
34 content_type: str = None,
35 headers: dict = None,
36 status_code: int = None,
37 ):
38 if isinstance(data, dict) or isinstance(data, list):
39 response_cls = StarletteJSONResponse
40 else:
41 response_cls = StarletteResponse
42
43 return response_cls(
44 content=data,
45 status_code=status_code,
46 media_type=content_type,
47 headers=headers,
48 )
49
50 @staticmethod
51 def get_request(*, scope: Scope, receive: Receive, uri_parser: AbstractURIParser, **kwargs) -> ConnexionRequest: # type: ignore
52 return ConnexionRequest(scope, receive, uri_parser=uri_parser)
53
54
55PATH_PARAMETER = re.compile(r"\{([^}]*)\}")
56PATH_PARAMETER_CONVERTERS = {"integer": "int", "number": "float", "path": "path"}
57
58
59def convert_path_parameter(match, types):
60 name = match.group(1)
61 swagger_type = types.get(name)
62 converter = PATH_PARAMETER_CONVERTERS.get(swagger_type)
63 return f'{{{name.replace("-", "_")}{":" if converter else ""}{converter or ""}}}'
64
65
66def starlettify_path(swagger_path, types=None):
67 """
68 Convert swagger path templates to flask path templates
69
70 :type swagger_path: str
71 :type types: dict
72 :rtype: str
73
74 >>> starlettify_path('/foo-bar/{my-param}')
75 '/foo-bar/{my_param}'
76
77 >>> starlettify_path('/foo/{someint}', {'someint': 'int'})
78 '/foo/{someint:int}'
79 """
80 if types is None:
81 types = {}
82 convert_match = functools.partial(convert_path_parameter, types=types)
83 return PATH_PARAMETER.sub(convert_match, swagger_path)
84
85
86class FloatConverter(starlette.convertors.FloatConvertor):
87 """Starlette converter for OpenAPI number type"""
88
89 regex = r"[+-]?[0-9]*(\.[0-9]*)?"
90
91
92class IntegerConverter(starlette.convertors.IntegerConvertor):
93 """Starlette converter for OpenAPI integer type"""
94
95 regex = r"[+-]?[0-9]+"