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
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 06:03 +0000
1from collections import OrderedDict
2from functools import wraps
4from flask import request, current_app, has_app_context
6from .mask import Mask, apply as apply_mask
7from .utils import unpack
10def make(cls):
11 if isinstance(cls, type):
12 return cls()
13 return cls
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.
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
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 }
35 >>> marshal(data, mfields)
36 {'a': 100, 'c': None, 'd': None}
38 >>> marshal(data, mfields, envelope='data')
39 {'data': {'a': 100, 'c': None, 'd': None}}
41 >>> marshal(data, mfields, skip_none=True)
42 {'a': 100}
44 >>> marshal(data, mfields, ordered=True)
45 OrderedDict([('a', 100), ('c', None), ('d', None)])
47 >>> marshal(data, mfields, envelope='data', ordered=True)
48 OrderedDict([('data', OrderedDict([('a', 100), ('c', None), ('d', None)]))])
50 >>> marshal(data, mfields, skip_none=True, ordered=True)
51 OrderedDict([('a', 100)])
53 """
54 out, has_wildcards = _marshal(data, fields, envelope, skip_none, mask, ordered)
56 if has_wildcards:
57 # ugly local import to avoid dependency loop
58 from .fields import Wildcard
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:
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))
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
95 keys.append(key)
96 if skip_none and (value is None or value == OrderedDict() or value == {}):
97 continue
98 items.append((key, value))
100 items = tuple(items)
102 out = OrderedDict(items) if ordered else dict(items)
104 if envelope:
105 out = OrderedDict([(envelope, out)]) if ordered else {envelope: out}
107 return out
109 return out
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.
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
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 }
131 >>> marshal(data, mfields)
132 {'a': 100, 'c': None, 'd': None}
134 >>> marshal(data, mfields, envelope='data')
135 {'data': {'a': 100, 'c': None, 'd': None}}
137 >>> marshal(data, mfields, skip_none=True)
138 {'a': 100}
140 >>> marshal(data, mfields, ordered=True)
141 OrderedDict([('a', 100), ('c', None), ('d', None)])
143 >>> marshal(data, mfields, envelope='data', ordered=True)
144 OrderedDict([('data', OrderedDict([('a', 100), ('c', None), ('d', None)]))])
146 >>> marshal(data, mfields, skip_none=True, ordered=True)
147 OrderedDict([('a', 100)])
149 """
150 # ugly local import to avoid dependency loop
151 from .fields import Wildcard
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)
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
164 has_wildcards = {"present": False}
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)
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 )
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 )
185 out = OrderedDict(items) if ordered else dict(items)
187 if envelope:
188 out = OrderedDict([(envelope, out)]) if ordered else {envelope: out}
190 return out, has_wildcards["present"]
193class marshal_with(object):
194 """A decorator that apply marshalling to the return values of your methods.
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)])
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)]))])
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)])
223 see :meth:`flask_restx.marshal`
224 """
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)
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 )
268 return wrapper
271class marshal_with_field(object):
272 """
273 A decorator that formats the return values of your methods with a single field.
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]
283 see :meth:`flask_restx.marshal_with`
284 """
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
295 def __call__(self, f):
296 @wraps(f)
297 def wrapper(*args, **kwargs):
298 resp = f(*args, **kwargs)
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)
305 return wrapper