-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreqparse.py
190 lines (159 loc) · 6.58 KB
/
reqparse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
from flask import request
from werkzeug.datastructures import MultiDict
import flask_restful
import six
class Namespace(dict):
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
self[name] = value
class Argument(object):
def __init__(self, name, default=None, dest=None, required=False,
ignore=False, type=six.text_type, location=('values',),
choices=(), action='store', help=None, operators=('=',),
case_sensitive=True):
"""
:param name: Either a name or a list of option strings, e.g. foo or
-f, --foo.
:param default: The value produced if the argument is absent from the
request.
:param dest: The name of the attribute to be added to the object
returned by parse_args(req).
:param required: Whether or not the argument may be omitted (optionals
only).
:param action: The basic type of action to be taken when this argument
is encountered in the request.
:param ignore: Whether to ignore cases where the argument fails type
conversion
:param type: The type to which the request argument should be
converted. If a type raises a ValidationError, the message in the
error will be returned in the response.
:param location: Where to source the arguments from the Flask request
(ex: headers, args, etc.), can be an iterator
:param choices: A container of the allowable values for the argument.
:param help: A brief description of the argument, returned in the
response when the argument is invalid. This takes precedence over
the message passed to a ValidationError raised by a type converter.
:param case_sensitive: Whether the arguments in the request are case
sensitive or not
"""
self.name = name
self.default = default
self.dest = dest
self.required = required
self.ignore = ignore
self.location = location
self.type = type
self.choices = choices
self.action = action
self.help = help
self.case_sensitive = case_sensitive
self.operators = operators
def source(self, request):
"""Pulls values off the request in the provided location
:param request: The flask request object to parse arguments from
"""
if isinstance(self.location, six.string_types):
value = getattr(request, self.location, MultiDict())
if value is not None:
return value
else:
for l in self.location:
value = getattr(request, l, None)
if value is not None:
return value
return MultiDict()
def convert(self, value, op):
try:
return self.type(value, self.name, op)
except TypeError:
try:
return self.type(value, self.name)
except TypeError:
return self.type(value)
def handle_validation_error(self, error):
"""Called when an error is raised while parsing. Aborts the request
with a 400 status and an error message
:param error: the error that was raised
"""
msg = self.help if self.help is not None else str(error)
flask_restful.abort(400, message=msg)
def parse(self, request):
"""Parses argument value(s) from the request, converting according to
the argument's type.
:param request: The flask request object to parse arguments from
"""
source = self.source(request)
results = []
for operator in self.operators:
name = self.name + operator.replace("=", "", 1)
if name in source:
# Account for MultiDict and regular dict
if hasattr(source, "getlist"):
values = source.getlist(name)
else:
values = [source.get(name)]
for value in values:
if not self.case_sensitive:
value = value.lower()
if self.choices and value not in self.choices:
self.handle_validation_error(ValueError(
u"{0} is not a valid choice".format(value)))
try:
value = self.convert(value, operator)
except Exception as error:
if self.ignore:
continue
self.handle_validation_error(error)
results.append(value)
if not results and self.required:
if isinstance(self.location, six.string_types):
error_msg = u"{0} is required in {1}".format(
self.name,
self.location
)
else:
error_msg = u"{0} is required in {1}".format(
self.name,
' or '.join(self.location)
)
self.handle_validation_error(ValueError(error_msg))
if not results:
return self.default
if self.action == 'append':
return results
if self.action == 'store' or len(results) == 1:
return results[0]
return results
class RequestParser(object):
"""Enables adding and parsing of multiple arguments in the context of a
single request. Ex::
from flask import request
parser = RequestParser()
parser.add_argument('foo')
parser.add_argument('int_bar', type=int)
args = parser.parse_args()
"""
def __init__(self, argument_class=Argument, namespace_class=Namespace):
self.args = []
self.argument_class = argument_class
self.namespace_class = namespace_class
def add_argument(self, *args, **kwargs):
"""Adds an argument to be parsed. See :class:`Argument`'s constructor
for documentation on the available options.
"""
self.args.append(self.argument_class(*args, **kwargs))
return self
def parse_args(self, req=None):
"""Parse all arguments from the provided request and return the results
as a Namespace
"""
if req is None:
req = request
namespace = self.namespace_class()
for arg in self.args:
namespace[arg.dest or arg.name] = arg.parse(req)
return namespace