Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/connexion/frameworks/flask.py: 59%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

59 statements  

1""" 

2This module defines functionality specific to the Flask framework. 

3""" 

4 

5import functools 

6import random 

7import re 

8import string 

9import typing as t 

10 

11import flask 

12import werkzeug.routing 

13 

14from connexion import jsonifier 

15from connexion.frameworks.abstract import Framework 

16from connexion.lifecycle import WSGIRequest 

17from connexion.uri_parsing import AbstractURIParser 

18 

19 

20class Flask(Framework): 

21 @staticmethod 

22 def is_framework_response(response: t.Any) -> bool: 

23 return isinstance(response, flask.Response) or isinstance( 

24 response, werkzeug.wrappers.Response 

25 ) 

26 

27 @classmethod 

28 def connexion_to_framework_response(cls, response): 

29 return cls.build_response( 

30 content_type=response.content_type, 

31 headers=response.headers, 

32 status_code=response.status_code, 

33 data=response.body, 

34 ) 

35 

36 @classmethod 

37 def build_response( 

38 cls, 

39 data: t.Any, 

40 *, 

41 content_type: t.Optional[str] = None, 

42 headers: t.Optional[dict] = None, 

43 status_code: t.Optional[int] = None, 

44 ): 

45 if cls.is_framework_response(data): 

46 return flask.current_app.make_response((data, status_code, headers)) 

47 

48 kwargs = { 

49 "mimetype": content_type, 

50 "headers": headers, 

51 "response": data, 

52 "status": status_code, 

53 } 

54 kwargs = {k: v for k, v in kwargs.items() if v is not None} 

55 return flask.current_app.response_class(**kwargs) 

56 

57 @staticmethod 

58 def get_request(*, uri_parser: AbstractURIParser, **kwargs) -> WSGIRequest: # type: ignore 

59 return WSGIRequest( 

60 flask.request, uri_parser=uri_parser, view_args=flask.request.view_args 

61 ) 

62 

63 

64PATH_PARAMETER = re.compile(r"\{([^}]*)\}") 

65 

66# map Swagger type to flask path converter 

67# see http://flask.pocoo.org/docs/0.10/api/#url-route-registrations 

68PATH_PARAMETER_CONVERTERS = {"integer": "int", "number": "float", "path": "path"} 

69 

70 

71def flaskify_endpoint(identifier, randomize=None): 

72 """ 

73 Converts the provided identifier in a valid flask endpoint name 

74 

75 :type identifier: str 

76 :param randomize: If specified, add this many random characters (upper case 

77 and digits) to the endpoint name, separated by a pipe character. 

78 :type randomize: int | None 

79 :rtype: str 

80 

81 """ 

82 result = identifier.replace(".", "_") 

83 if randomize is None: 

84 return result 

85 

86 chars = string.ascii_uppercase + string.digits 

87 return "{result}|{random_string}".format( 

88 result=result, 

89 random_string="".join( 

90 random.SystemRandom().choice(chars) for _ in range(randomize) 

91 ), 

92 ) 

93 

94 

95def convert_path_parameter(match, types): 

96 name = match.group(1) 

97 swagger_type = types.get(name) 

98 converter = PATH_PARAMETER_CONVERTERS.get(swagger_type) 

99 return "<{}{}{}>".format( 

100 converter or "", ":" if converter else "", name.replace("-", "_") 

101 ) 

102 

103 

104def flaskify_path(swagger_path, types=None): 

105 """ 

106 Convert swagger path templates to flask path templates 

107 

108 :type swagger_path: str 

109 :type types: dict 

110 :rtype: str 

111 

112 >>> flaskify_path('/foo-bar/{my-param}') 

113 '/foo-bar/<my_param>' 

114 

115 >>> flaskify_path('/foo/{someint}', {'someint': 'int'}) 

116 '/foo/<int:someint>' 

117 """ 

118 if types is None: 

119 types = {} 

120 convert_match = functools.partial(convert_path_parameter, types=types) 

121 return PATH_PARAMETER.sub(convert_match, swagger_path) 

122 

123 

124class FlaskJSONProvider(flask.json.provider.DefaultJSONProvider): 

125 """Custom JSONProvider which adds connexion defaults on top of Flask's""" 

126 

127 @jsonifier.wrap_default 

128 def default(self, o): 

129 return super().default(o) 

130 

131 

132class NumberConverter(werkzeug.routing.BaseConverter): 

133 """Flask converter for OpenAPI number type""" 

134 

135 regex = r"[+-]?[0-9]*(?:\.[0-9]*)?" 

136 

137 def to_python(self, value): 

138 return float(value) 

139 

140 

141class IntegerConverter(werkzeug.routing.BaseConverter): 

142 """Flask converter for OpenAPI integer type""" 

143 

144 regex = r"[+-]?[0-9]+" 

145 

146 def to_python(self, value): 

147 return int(value)