Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hhhrrrttt222111
GitHub Repository: hhhrrrttt222111/Dorkify
Path: blob/master/venv/Lib/site-packages/lxml/html/formfill.py
811 views
1
from lxml.etree import XPath, ElementBase
2
from lxml.html import fromstring, XHTML_NAMESPACE
3
from lxml.html import _forms_xpath, _options_xpath, _nons, _transform_result
4
from lxml.html import defs
5
import copy
6
7
try:
8
basestring
9
except NameError:
10
# Python 3
11
basestring = str
12
13
__all__ = ['FormNotFound', 'fill_form', 'fill_form_html',
14
'insert_errors', 'insert_errors_html',
15
'DefaultErrorCreator']
16
17
class FormNotFound(LookupError):
18
"""
19
Raised when no form can be found
20
"""
21
22
_form_name_xpath = XPath('descendant-or-self::form[name=$name]|descendant-or-self::x:form[name=$name]', namespaces={'x':XHTML_NAMESPACE})
23
_input_xpath = XPath('|'.join(['descendant-or-self::'+_tag for _tag in ('input','select','textarea','x:input','x:select','x:textarea')]),
24
namespaces={'x':XHTML_NAMESPACE})
25
_label_for_xpath = XPath('//label[@for=$for_id]|//x:label[@for=$for_id]',
26
namespaces={'x':XHTML_NAMESPACE})
27
_name_xpath = XPath('descendant-or-self::*[@name=$name]')
28
29
def fill_form(
30
el,
31
values,
32
form_id=None,
33
form_index=None,
34
):
35
el = _find_form(el, form_id=form_id, form_index=form_index)
36
_fill_form(el, values)
37
38
def fill_form_html(html, values, form_id=None, form_index=None):
39
result_type = type(html)
40
if isinstance(html, basestring):
41
doc = fromstring(html)
42
else:
43
doc = copy.deepcopy(html)
44
fill_form(doc, values, form_id=form_id, form_index=form_index)
45
return _transform_result(result_type, doc)
46
47
def _fill_form(el, values):
48
counts = {}
49
if hasattr(values, 'mixed'):
50
# For Paste request parameters
51
values = values.mixed()
52
inputs = _input_xpath(el)
53
for input in inputs:
54
name = input.get('name')
55
if not name:
56
continue
57
if _takes_multiple(input):
58
value = values.get(name, [])
59
if not isinstance(value, (list, tuple)):
60
value = [value]
61
_fill_multiple(input, value)
62
elif name not in values:
63
continue
64
else:
65
index = counts.get(name, 0)
66
counts[name] = index + 1
67
value = values[name]
68
if isinstance(value, (list, tuple)):
69
try:
70
value = value[index]
71
except IndexError:
72
continue
73
elif index > 0:
74
continue
75
_fill_single(input, value)
76
77
def _takes_multiple(input):
78
if _nons(input.tag) == 'select' and input.get('multiple'):
79
# FIXME: multiple="0"?
80
return True
81
type = input.get('type', '').lower()
82
if type in ('radio', 'checkbox'):
83
return True
84
return False
85
86
def _fill_multiple(input, value):
87
type = input.get('type', '').lower()
88
if type == 'checkbox':
89
v = input.get('value')
90
if v is None:
91
if not value:
92
result = False
93
else:
94
result = value[0]
95
if isinstance(value, basestring):
96
# The only valid "on" value for an unnamed checkbox is 'on'
97
result = result == 'on'
98
_check(input, result)
99
else:
100
_check(input, v in value)
101
elif type == 'radio':
102
v = input.get('value')
103
_check(input, v in value)
104
else:
105
assert _nons(input.tag) == 'select'
106
for option in _options_xpath(input):
107
v = option.get('value')
108
if v is None:
109
# This seems to be the default, at least on IE
110
# FIXME: but I'm not sure
111
v = option.text_content()
112
_select(option, v in value)
113
114
def _check(el, check):
115
if check:
116
el.set('checked', '')
117
else:
118
if 'checked' in el.attrib:
119
del el.attrib['checked']
120
121
def _select(el, select):
122
if select:
123
el.set('selected', '')
124
else:
125
if 'selected' in el.attrib:
126
del el.attrib['selected']
127
128
def _fill_single(input, value):
129
if _nons(input.tag) == 'textarea':
130
input.text = value
131
else:
132
input.set('value', value)
133
134
def _find_form(el, form_id=None, form_index=None):
135
if form_id is None and form_index is None:
136
forms = _forms_xpath(el)
137
for form in forms:
138
return form
139
raise FormNotFound(
140
"No forms in page")
141
if form_id is not None:
142
form = el.get_element_by_id(form_id)
143
if form is not None:
144
return form
145
forms = _form_name_xpath(el, name=form_id)
146
if forms:
147
return forms[0]
148
else:
149
raise FormNotFound(
150
"No form with the name or id of %r (forms: %s)"
151
% (id, ', '.join(_find_form_ids(el))))
152
if form_index is not None:
153
forms = _forms_xpath(el)
154
try:
155
return forms[form_index]
156
except IndexError:
157
raise FormNotFound(
158
"There is no form with the index %r (%i forms found)"
159
% (form_index, len(forms)))
160
161
def _find_form_ids(el):
162
forms = _forms_xpath(el)
163
if not forms:
164
yield '(no forms)'
165
return
166
for index, form in enumerate(forms):
167
if form.get('id'):
168
if form.get('name'):
169
yield '%s or %s' % (form.get('id'),
170
form.get('name'))
171
else:
172
yield form.get('id')
173
elif form.get('name'):
174
yield form.get('name')
175
else:
176
yield '(unnamed form %s)' % index
177
178
############################################################
179
## Error filling
180
############################################################
181
182
class DefaultErrorCreator(object):
183
insert_before = True
184
block_inside = True
185
error_container_tag = 'div'
186
error_message_class = 'error-message'
187
error_block_class = 'error-block'
188
default_message = "Invalid"
189
190
def __init__(self, **kw):
191
for name, value in kw.items():
192
if not hasattr(self, name):
193
raise TypeError(
194
"Unexpected keyword argument: %s" % name)
195
setattr(self, name, value)
196
197
def __call__(self, el, is_block, message):
198
error_el = el.makeelement(self.error_container_tag)
199
if self.error_message_class:
200
error_el.set('class', self.error_message_class)
201
if is_block and self.error_block_class:
202
error_el.set('class', error_el.get('class', '')+' '+self.error_block_class)
203
if message is None or message == '':
204
message = self.default_message
205
if isinstance(message, ElementBase):
206
error_el.append(message)
207
else:
208
assert isinstance(message, basestring), (
209
"Bad message; should be a string or element: %r" % message)
210
error_el.text = message or self.default_message
211
if is_block and self.block_inside:
212
if self.insert_before:
213
error_el.tail = el.text
214
el.text = None
215
el.insert(0, error_el)
216
else:
217
el.append(error_el)
218
else:
219
parent = el.getparent()
220
pos = parent.index(el)
221
if self.insert_before:
222
parent.insert(pos, error_el)
223
else:
224
error_el.tail = el.tail
225
el.tail = None
226
parent.insert(pos+1, error_el)
227
228
default_error_creator = DefaultErrorCreator()
229
230
231
def insert_errors(
232
el,
233
errors,
234
form_id=None,
235
form_index=None,
236
error_class="error",
237
error_creator=default_error_creator,
238
):
239
el = _find_form(el, form_id=form_id, form_index=form_index)
240
for name, error in errors.items():
241
if error is None:
242
continue
243
for error_el, message in _find_elements_for_name(el, name, error):
244
assert isinstance(message, (basestring, type(None), ElementBase)), (
245
"Bad message: %r" % message)
246
_insert_error(error_el, message, error_class, error_creator)
247
248
def insert_errors_html(html, values, **kw):
249
result_type = type(html)
250
if isinstance(html, basestring):
251
doc = fromstring(html)
252
else:
253
doc = copy.deepcopy(html)
254
insert_errors(doc, values, **kw)
255
return _transform_result(result_type, doc)
256
257
def _insert_error(el, error, error_class, error_creator):
258
if _nons(el.tag) in defs.empty_tags or _nons(el.tag) == 'textarea':
259
is_block = False
260
else:
261
is_block = True
262
if _nons(el.tag) != 'form' and error_class:
263
_add_class(el, error_class)
264
if el.get('id'):
265
labels = _label_for_xpath(el, for_id=el.get('id'))
266
if labels:
267
for label in labels:
268
_add_class(label, error_class)
269
error_creator(el, is_block, error)
270
271
def _add_class(el, class_name):
272
if el.get('class'):
273
el.set('class', el.get('class')+' '+class_name)
274
else:
275
el.set('class', class_name)
276
277
def _find_elements_for_name(form, name, error):
278
if name is None:
279
# An error for the entire form
280
yield form, error
281
return
282
if name.startswith('#'):
283
# By id
284
el = form.get_element_by_id(name[1:])
285
if el is not None:
286
yield el, error
287
return
288
els = _name_xpath(form, name=name)
289
if not els:
290
# FIXME: should this raise an exception?
291
return
292
if not isinstance(error, (list, tuple)):
293
yield els[0], error
294
return
295
# FIXME: if error is longer than els, should it raise an error?
296
for el, err in zip(els, error):
297
if err is None:
298
continue
299
yield el, err
300
301