lxml 5.2.0__cp310-cp310-win32.whl → 5.2.2__cp310-cp310-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. lxml/ElementInclude.py +244 -244
  2. lxml/__init__.py +22 -22
  3. lxml/_elementpath.cp310-win32.pyd +0 -0
  4. lxml/_elementpath.py +341 -341
  5. lxml/apihelpers.pxi +1793 -1793
  6. lxml/builder.cp310-win32.pyd +0 -0
  7. lxml/builder.py +232 -232
  8. lxml/classlookup.pxi +580 -580
  9. lxml/cleanup.pxi +215 -215
  10. lxml/cssselect.py +101 -101
  11. lxml/debug.pxi +90 -90
  12. lxml/docloader.pxi +178 -178
  13. lxml/doctestcompare.py +488 -488
  14. lxml/dtd.pxi +478 -478
  15. lxml/etree.cp310-win32.pyd +0 -0
  16. lxml/etree.h +6 -6
  17. lxml/etree.pyx +3732 -3711
  18. lxml/extensions.pxi +833 -833
  19. lxml/html/ElementSoup.py +10 -10
  20. lxml/html/__init__.py +1923 -1923
  21. lxml/html/_diffcommand.py +86 -86
  22. lxml/html/_html5builder.py +100 -100
  23. lxml/html/_setmixin.py +56 -56
  24. lxml/html/builder.py +133 -133
  25. lxml/html/clean.py +21 -21
  26. lxml/html/defs.py +135 -135
  27. lxml/html/diff.cp310-win32.pyd +0 -0
  28. lxml/html/diff.py +878 -878
  29. lxml/html/formfill.py +299 -299
  30. lxml/html/html5parser.py +260 -260
  31. lxml/html/soupparser.py +314 -314
  32. lxml/html/usedoctest.py +13 -13
  33. lxml/includes/c14n.pxd +25 -25
  34. lxml/includes/config.pxd +3 -3
  35. lxml/includes/dtdvalid.pxd +18 -18
  36. lxml/includes/etree_defs.h +379 -379
  37. lxml/includes/etreepublic.pxd +237 -237
  38. lxml/includes/htmlparser.pxd +56 -56
  39. lxml/includes/lxml-version.h +1 -1
  40. lxml/includes/relaxng.pxd +64 -64
  41. lxml/includes/schematron.pxd +34 -34
  42. lxml/includes/tree.pxd +494 -494
  43. lxml/includes/uri.pxd +5 -5
  44. lxml/includes/xinclude.pxd +22 -22
  45. lxml/includes/xmlerror.pxd +852 -852
  46. lxml/includes/xmlparser.pxd +265 -265
  47. lxml/includes/xmlschema.pxd +35 -35
  48. lxml/includes/xpath.pxd +136 -136
  49. lxml/includes/xslt.pxd +190 -190
  50. lxml/isoschematron/__init__.py +348 -348
  51. lxml/isoschematron/resources/rng/iso-schematron.rng +709 -709
  52. lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -75
  53. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +312 -312
  54. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1159 -1159
  55. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +54 -54
  56. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -1796
  57. lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -588
  58. lxml/iterparse.pxi +438 -438
  59. lxml/lxml.etree.h +6 -6
  60. lxml/nsclasses.pxi +281 -281
  61. lxml/objectify.cp310-win32.pyd +0 -0
  62. lxml/objectify.pyx +2145 -2145
  63. lxml/objectpath.pxi +332 -332
  64. lxml/parser.pxi +1994 -1994
  65. lxml/parsertarget.pxi +180 -180
  66. lxml/proxy.pxi +619 -619
  67. lxml/public-api.pxi +178 -178
  68. lxml/pyclasslookup.py +3 -3
  69. lxml/readonlytree.pxi +565 -565
  70. lxml/relaxng.pxi +165 -165
  71. lxml/sax.cp310-win32.pyd +0 -0
  72. lxml/sax.py +275 -275
  73. lxml/saxparser.pxi +875 -875
  74. lxml/schematron.pxi +168 -168
  75. lxml/serializer.pxi +1871 -1871
  76. lxml/usedoctest.py +13 -13
  77. lxml/xinclude.pxi +67 -67
  78. lxml/xmlerror.pxi +1654 -1654
  79. lxml/xmlid.pxi +179 -179
  80. lxml/xmlschema.pxi +215 -215
  81. lxml/xpath.pxi +487 -487
  82. lxml/xslt.pxi +950 -950
  83. lxml/xsltext.pxi +242 -242
  84. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/LICENSE.txt +29 -29
  85. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/LICENSES.txt +29 -29
  86. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/METADATA +9 -17
  87. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/RECORD +89 -89
  88. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/WHEEL +0 -0
  89. {lxml-5.2.0.dist-info → lxml-5.2.2.dist-info}/top_level.txt +0 -0
lxml/html/formfill.py CHANGED
@@ -1,299 +1,299 @@
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:
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
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:
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