Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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

58 statements  

1""" 

2This module defines functionality specific to the Flask framework. 

3""" 

4import functools 

5import random 

6import re 

7import string 

8import typing as t 

9 

10import flask 

11import werkzeug.routing 

12 

13from connexion import jsonifier 

14from connexion.frameworks.abstract import Framework 

15from connexion.lifecycle import WSGIRequest 

16from connexion.uri_parsing import AbstractURIParser 

17 

18 

19class Flask(Framework): 

20 @staticmethod 

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

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

23 response, werkzeug.wrappers.Response 

24 ) 

25 

26 @classmethod 

27 def connexion_to_framework_response(cls, response): 

28 return cls.build_response( 

29 content_type=response.content_type, 

30 headers=response.headers, 

31 status_code=response.status_code, 

32 data=response.body, 

33 ) 

34 

35 @classmethod 

36 def build_response( 

37 cls, 

38 data: t.Any, 

39 *, 

40 content_type: str = None, 

41 headers: dict = None, 

42 status_code: int = None 

43 ): 

44 if cls.is_framework_response(data): 

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

46 

47 kwargs = { 

48 "mimetype": content_type, 

49 "headers": headers, 

50 "response": data, 

51 "status": status_code, 

52 } 

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

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

55 

56 @staticmethod 

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

58 return WSGIRequest( 

59 flask.request, uri_parser=uri_parser, view_args=flask.request.view_args 

60 ) 

61 

62 

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

64 

65# map Swagger type to flask path converter 

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

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

68 

69 

70def flaskify_endpoint(identifier, randomize=None): 

71 """ 

72 Converts the provided identifier in a valid flask endpoint name 

73 

74 :type identifier: str 

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

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

77 :type randomize: int | None 

78 :rtype: str 

79 

80 """ 

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

82 if randomize is None: 

83 return result 

84 

85 chars = string.ascii_uppercase + string.digits 

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

87 result=result, 

88 random_string="".join( 

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

90 ), 

91 ) 

92 

93 

94def convert_path_parameter(match, types): 

95 name = match.group(1) 

96 swagger_type = types.get(name) 

97 converter = PATH_PARAMETER_CONVERTERS.get(swagger_type) 

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

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

100 ) 

101 

102 

103def flaskify_path(swagger_path, types=None): 

104 """ 

105 Convert swagger path templates to flask path templates 

106 

107 :type swagger_path: str 

108 :type types: dict 

109 :rtype: str 

110 

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

112 '/foo-bar/<my_param>' 

113 

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

115 '/foo/<int:someint>' 

116 """ 

117 if types is None: 

118 types = {} 

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

120 return PATH_PARAMETER.sub(convert_match, swagger_path) 

121 

122 

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

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

125 

126 @jsonifier.wrap_default 

127 def default(self, o): 

128 return super().default(o) 

129 

130 

131class NumberConverter(werkzeug.routing.BaseConverter): 

132 """Flask converter for OpenAPI number type""" 

133 

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

135 

136 def to_python(self, value): 

137 return float(value) 

138 

139 

140class IntegerConverter(werkzeug.routing.BaseConverter): 

141 """Flask converter for OpenAPI integer type""" 

142 

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

144 

145 def to_python(self, value): 

146 return int(value)