Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/lxml/html/formfill.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
| author | shellac |
|---|---|
| date | Sat, 02 May 2020 07:14:21 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:26e78fe6e8c4 |
|---|---|
| 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 |
