Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask_restx/marshalling.py: 13%

109 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:03 +0000

1from collections import OrderedDict 

2from functools import wraps 

3 

4from flask import request, current_app, has_app_context 

5 

6from .mask import Mask, apply as apply_mask 

7from .utils import unpack 

8 

9 

10def make(cls): 

11 if isinstance(cls, type): 

12 return cls() 

13 return cls 

14 

15 

16def marshal(data, fields, envelope=None, skip_none=False, mask=None, ordered=False): 

17 """Takes raw data (in the form of a dict, list, object) and a dict of 

18 fields to output and filters the data based on those fields. 

19 

20 :param data: the actual object(s) from which the fields are taken from 

21 :param fields: a dict of whose keys will make up the final serialized 

22 response output 

23 :param envelope: optional key that will be used to envelop the serialized 

24 response 

25 :param bool skip_none: optional key will be used to eliminate fields 

26 which value is None or the field's key not 

27 exist in data 

28 :param bool ordered: Wether or not to preserve order 

29 

30 

31 >>> from flask_restx import fields, marshal 

32 >>> data = { 'a': 100, 'b': 'foo', 'c': None } 

33 >>> mfields = { 'a': fields.Raw, 'c': fields.Raw, 'd': fields.Raw } 

34 

35 >>> marshal(data, mfields) 

36 {'a': 100, 'c': None, 'd': None} 

37 

38 >>> marshal(data, mfields, envelope='data') 

39 {'data': {'a': 100, 'c': None, 'd': None}} 

40 

41 >>> marshal(data, mfields, skip_none=True) 

42 {'a': 100} 

43 

44 >>> marshal(data, mfields, ordered=True) 

45 OrderedDict([('a', 100), ('c', None), ('d', None)]) 

46 

47 >>> marshal(data, mfields, envelope='data', ordered=True) 

48 OrderedDict([('data', OrderedDict([('a', 100), ('c', None), ('d', None)]))]) 

49 

50 >>> marshal(data, mfields, skip_none=True, ordered=True) 

51 OrderedDict([('a', 100)]) 

52 

53 """ 

54 out, has_wildcards = _marshal(data, fields, envelope, skip_none, mask, ordered) 

55 

56 if has_wildcards: 

57 # ugly local import to avoid dependency loop 

58 from .fields import Wildcard 

59 

60 items = [] 

61 keys = [] 

62 for dkey, val in fields.items(): 

63 key = dkey 

64 if isinstance(val, dict): 

65 value = marshal(data, val, skip_none=skip_none, ordered=ordered) 

66 else: 

67 field = make(val) 

68 is_wildcard = isinstance(field, Wildcard) 

69 # exclude already parsed keys from the wildcard 

70 if is_wildcard: 

71 field.reset() 

72 if keys: 

73 field.exclude |= set(keys) 

74 keys = [] 

75 value = field.output(dkey, data, ordered=ordered) 

76 if is_wildcard: 

77 

78 def _append(k, v): 

79 if skip_none and (v is None or v == OrderedDict() or v == {}): 

80 return 

81 items.append((k, v)) 

82 

83 key = field.key or dkey 

84 _append(key, value) 

85 while True: 

86 value = field.output(dkey, data, ordered=ordered) 

87 if value is None or value == field.container.format( 

88 field.default 

89 ): 

90 break 

91 key = field.key 

92 _append(key, value) 

93 continue 

94 

95 keys.append(key) 

96 if skip_none and (value is None or value == OrderedDict() or value == {}): 

97 continue 

98 items.append((key, value)) 

99 

100 items = tuple(items) 

101 

102 out = OrderedDict(items) if ordered else dict(items) 

103 

104 if envelope: 

105 out = OrderedDict([(envelope, out)]) if ordered else {envelope: out} 

106 

107 return out 

108 

109 return out 

110 

111 

112def _marshal(data, fields, envelope=None, skip_none=False, mask=None, ordered=False): 

113 """Takes raw data (in the form of a dict, list, object) and a dict of 

114 fields to output and filters the data based on those fields. 

115 

116 :param data: the actual object(s) from which the fields are taken from 

117 :param fields: a dict of whose keys will make up the final serialized 

118 response output 

119 :param envelope: optional key that will be used to envelop the serialized 

120 response 

121 :param bool skip_none: optional key will be used to eliminate fields 

122 which value is None or the field's key not 

123 exist in data 

124 :param bool ordered: Wether or not to preserve order 

125 

126 

127 >>> from flask_restx import fields, marshal 

128 >>> data = { 'a': 100, 'b': 'foo', 'c': None } 

129 >>> mfields = { 'a': fields.Raw, 'c': fields.Raw, 'd': fields.Raw } 

130 

131 >>> marshal(data, mfields) 

132 {'a': 100, 'c': None, 'd': None} 

133 

134 >>> marshal(data, mfields, envelope='data') 

135 {'data': {'a': 100, 'c': None, 'd': None}} 

136 

137 >>> marshal(data, mfields, skip_none=True) 

138 {'a': 100} 

139 

140 >>> marshal(data, mfields, ordered=True) 

141 OrderedDict([('a', 100), ('c', None), ('d', None)]) 

142 

143 >>> marshal(data, mfields, envelope='data', ordered=True) 

144 OrderedDict([('data', OrderedDict([('a', 100), ('c', None), ('d', None)]))]) 

145 

146 >>> marshal(data, mfields, skip_none=True, ordered=True) 

147 OrderedDict([('a', 100)]) 

148 

149 """ 

150 # ugly local import to avoid dependency loop 

151 from .fields import Wildcard 

152 

153 mask = mask or getattr(fields, "__mask__", None) 

154 fields = getattr(fields, "resolved", fields) 

155 if mask: 

156 fields = apply_mask(fields, mask, skip=True) 

157 

158 if isinstance(data, (list, tuple)): 

159 out = [marshal(d, fields, skip_none=skip_none, ordered=ordered) for d in data] 

160 if envelope: 

161 out = OrderedDict([(envelope, out)]) if ordered else {envelope: out} 

162 return out, False 

163 

164 has_wildcards = {"present": False} 

165 

166 def __format_field(key, val): 

167 field = make(val) 

168 if isinstance(field, Wildcard): 

169 has_wildcards["present"] = True 

170 value = field.output(key, data, ordered=ordered) 

171 return (key, value) 

172 

173 items = ( 

174 (k, marshal(data, v, skip_none=skip_none, ordered=ordered)) 

175 if isinstance(v, dict) 

176 else __format_field(k, v) 

177 for k, v in fields.items() 

178 ) 

179 

180 if skip_none: 

181 items = ( 

182 (k, v) for k, v in items if v is not None and v != OrderedDict() and v != {} 

183 ) 

184 

185 out = OrderedDict(items) if ordered else dict(items) 

186 

187 if envelope: 

188 out = OrderedDict([(envelope, out)]) if ordered else {envelope: out} 

189 

190 return out, has_wildcards["present"] 

191 

192 

193class marshal_with(object): 

194 """A decorator that apply marshalling to the return values of your methods. 

195 

196 >>> from flask_restx import fields, marshal_with 

197 >>> mfields = { 'a': fields.Raw } 

198 >>> @marshal_with(mfields) 

199 ... def get(): 

200 ... return { 'a': 100, 'b': 'foo' } 

201 ... 

202 ... 

203 >>> get() 

204 OrderedDict([('a', 100)]) 

205 

206 >>> @marshal_with(mfields, envelope='data') 

207 ... def get(): 

208 ... return { 'a': 100, 'b': 'foo' } 

209 ... 

210 ... 

211 >>> get() 

212 OrderedDict([('data', OrderedDict([('a', 100)]))]) 

213 

214 >>> mfields = { 'a': fields.Raw, 'c': fields.Raw, 'd': fields.Raw } 

215 >>> @marshal_with(mfields, skip_none=True) 

216 ... def get(): 

217 ... return { 'a': 100, 'b': 'foo', 'c': None } 

218 ... 

219 ... 

220 >>> get() 

221 OrderedDict([('a', 100)]) 

222 

223 see :meth:`flask_restx.marshal` 

224 """ 

225 

226 def __init__( 

227 self, fields, envelope=None, skip_none=False, mask=None, ordered=False 

228 ): 

229 """ 

230 :param fields: a dict of whose keys will make up the final 

231 serialized response output 

232 :param envelope: optional key that will be used to envelop the serialized 

233 response 

234 """ 

235 self.fields = fields 

236 self.envelope = envelope 

237 self.skip_none = skip_none 

238 self.ordered = ordered 

239 self.mask = Mask(mask, skip=True) 

240 

241 def __call__(self, f): 

242 @wraps(f) 

243 def wrapper(*args, **kwargs): 

244 resp = f(*args, **kwargs) 

245 mask = self.mask 

246 if has_app_context(): 

247 mask_header = current_app.config["RESTX_MASK_HEADER"] 

248 mask = request.headers.get(mask_header) or mask 

249 if isinstance(resp, tuple): 

250 data, code, headers = unpack(resp) 

251 return ( 

252 marshal( 

253 data, 

254 self.fields, 

255 self.envelope, 

256 self.skip_none, 

257 mask, 

258 self.ordered, 

259 ), 

260 code, 

261 headers, 

262 ) 

263 else: 

264 return marshal( 

265 resp, self.fields, self.envelope, self.skip_none, mask, self.ordered 

266 ) 

267 

268 return wrapper 

269 

270 

271class marshal_with_field(object): 

272 """ 

273 A decorator that formats the return values of your methods with a single field. 

274 

275 >>> from flask_restx import marshal_with_field, fields 

276 >>> @marshal_with_field(fields.List(fields.Integer)) 

277 ... def get(): 

278 ... return ['1', 2, 3.0] 

279 ... 

280 >>> get() 

281 [1, 2, 3] 

282 

283 see :meth:`flask_restx.marshal_with` 

284 """ 

285 

286 def __init__(self, field): 

287 """ 

288 :param field: a single field with which to marshal the output. 

289 """ 

290 if isinstance(field, type): 

291 self.field = field() 

292 else: 

293 self.field = field 

294 

295 def __call__(self, f): 

296 @wraps(f) 

297 def wrapper(*args, **kwargs): 

298 resp = f(*args, **kwargs) 

299 

300 if isinstance(resp, tuple): 

301 data, code, headers = unpack(resp) 

302 return self.field.format(data), code, headers 

303 return self.field.format(resp) 

304 

305 return wrapper