[Lxml-checkins] r44258 - in lxml/branch/html/src/lxml/html: . tests
ianb at codespeak.net
ianb at codespeak.net
Fri Jun 15 00:53:28 CEST 2007
Author: ianb
Date: Fri Jun 15 00:53:28 2007
New Revision: 44258
Modified:
lxml/branch/html/src/lxml/html/formfill.py
lxml/branch/html/src/lxml/html/tests/test_formfill.txt
Log:
Added error-filling function
Modified: lxml/branch/html/src/lxml/html/formfill.py
==============================================================================
--- lxml/branch/html/src/lxml/html/formfill.py (original)
+++ lxml/branch/html/src/lxml/html/formfill.py Fri Jun 15 00:53:28 2007
@@ -1,5 +1,6 @@
-from lxml.etree import XPath
-from lxml.html import HTML, tostring
+from lxml.etree import XPath, ElementBase
+from lxml.html import parse, tostring
+from lxml.html import defs
__all__ = ['FormNotFound', 'fill_form']
@@ -10,6 +11,8 @@
_form_name_xpath = XPath('descendant-or-self::form[name=$name]')
_input_xpath = XPath('descendant-or-self::input | descendant-or-self::select | descendant-or-self::textarea')
+_label_for_xpath = XPath('//label[@for=$for_id]')
+_name_xpath = XPath('descendant-or-self::*[@name=$name]')
def fill_form(
el,
@@ -22,7 +25,7 @@
def fill_form_html(html, values, form_id=None, form_index=None):
if isinstance(html, basestring):
- doc = HTML(html)
+ doc = parse(html)
return_string = True
else:
doc = copy.deepcopy(html)
@@ -164,3 +167,130 @@
yield form.get('name')
else:
yield '(unnamed form %s)' % index
+
+############################################################
+## Error filling
+############################################################
+
+class DefaultErrorCreator(object):
+ insert_before = True
+ block_inside = True
+ error_container_tag = 'div'
+ error_message_class = 'error-message'
+ error_block_class = 'error-block'
+ default_message = "Invalid"
+
+ def __init__(self, **kw):
+ for name, value in kw.items():
+ if not hasattr(self, name):
+ raise TypeError(
+ "Unexpected keyword argument: %s" % name)
+ setattr(self, name, value)
+
+ def __call__(self, el, is_block, message):
+ error_el = el.makeelement(self.error_container_tag)
+ if self.error_message_class:
+ error_el.set('class', self.error_message_class)
+ if is_block and self.error_block_class:
+ error_el.set('class', error_el.get('class', '')+' '+self.error_block_class)
+ if message is None or message == '':
+ message = self.default_message
+ if isinstance(message, ElementBase):
+ error_el.append(message)
+ else:
+ assert isinstance(message, basestring), (
+ "Bad message; should be a string or element: %r" % message)
+ error_el.text = message or self.default_message
+ if is_block and self.block_inside:
+ if self.insert_before:
+ error_el.tail = el.text
+ el.text = None
+ el.insert(0, error_el)
+ else:
+ el.append(error_el)
+ else:
+ parent = el.getparent()
+ pos = parent.index(el)
+ if self.insert_before:
+ parent.insert(pos, error_el)
+ else:
+ error_el.tail = el.tail
+ el.tail = None
+ parent.insert(pos+1, error_el)
+
+default_error_creator = DefaultErrorCreator()
+
+
+def insert_errors(
+ el,
+ errors,
+ form_id=None,
+ form_index=None,
+ error_class="error",
+ error_creator=default_error_creator,
+ ):
+ el = _find_form(el, form_id=form_id, form_index=form_index)
+ for name, error in errors.iteritems():
+ if error is None:
+ continue
+ for error_el, message in _find_elements_for_name(el, name, error):
+ assert isinstance(message, (basestring, type(None), ElementBase)), (
+ "Bad message: %r" % message)
+ _insert_error(error_el, message, error_class, error_creator)
+
+def insert_errors_html(html, values, **kw):
+ if isinstance(html, basestring):
+ doc = parse(html)
+ return_string = True
+ else:
+ doc = copy.deepcopy(html)
+ return_string = False
+ insert_errors(doc, values, **kw)
+ if return_string:
+ return tostring(doc)
+ else:
+ return doc
+
+def _insert_error(el, error, error_class, error_creator):
+ if el.tag in defs.empty_tags or el.tag == 'textarea':
+ is_block = False
+ else:
+ is_block = True
+ if el.tag != 'form' and error_class:
+ _add_class(el, error_class)
+ if el.get('id'):
+ labels = _label_for_xpath(el, for_id=el.get('id'))
+ if labels:
+ for label in labels:
+ _add_class(label, error_class)
+ error_creator(el, is_block, error)
+
+def _add_class(el, class_name):
+ if el.get('class'):
+ el.set('class', el.get('class')+' '+class_name)
+ else:
+ el.set('class', class_name)
+
+def _find_elements_for_name(form, name, error):
+ if name is None:
+ # An error for the entire form
+ yield form, error
+ return
+ if name.startswith('#'):
+ # By id
+ el = form.get_element_by_id(name[1:])
+ if el is not None:
+ yield el, error
+ return
+ els = _name_xpath(form, name=name)
+ if not els:
+ # FIXME: should this raise an exception?
+ return
+ if not isinstance(error, (list, tuple)):
+ yield els[0], error
+ return
+ # FIXME: if error is longer than els, should it raise an error?
+ for el, err in zip(els, error):
+ if err is None:
+ continue
+ yield el, err
Modified: lxml/branch/html/src/lxml/html/tests/test_formfill.txt
==============================================================================
--- lxml/branch/html/src/lxml/html/tests/test_formfill.txt (original)
+++ lxml/branch/html/src/lxml/html/tests/test_formfill.txt Fri Jun 15 00:53:28 2007
@@ -52,3 +52,49 @@
FIXME: I need to test more of this. But I'm lazy and want to use the
coverage report for some of this.
+
+
+This module also allows you to add error messages to the form. The errors
+add an "error" class to the input fields, and any labels if the field
+has a label. It also inserts an error message into the form, using a
+function you can provide (or the default function).
+
+Example::
+
+ >>> from lxml.html.formfill import insert_errors_html
+ >>> print insert_errors_html('''
+ ... <form>
+ ... <fieldset id="fieldset">
+ ... <input name="v1"><br>
+ ... <label for="v2">label</label>
+ ... <input name="v2" id="v2"><br>
+ ... </fieldset>
+ ... <input name="v3" class="foo">
+ ... <input name="v3" class="foo">
+ ... <input name="v4">
+ ... <input name="v4">
+ ... </form>''', {
+ ... 'v1': "err1",
+ ... 'v2': "err2",
+ ... 'v3': [None, "err3-2"],
+ ... 'v4': "err4",
+ ... None: 'general error',
+ ... '#fieldset': 'area error',
+ ... })
+ <form>
+ <div class="error-message error-block">general error</div>
+ <fieldset id="fieldset" class="error">
+ <div class="error-message error-block">area error</div>
+ <div class="error-message">err1</div>
+ <input name="v1" class="error"><br>
+ <label for="v2" class="error">label</label>
+ <div class="error-message">err2</div>
+ <input name="v2" id="v2" class="error"><br>
+ </fieldset>
+ <input name="v3" class="foo">
+ <div class="error-message">err3-2</div>
+ <input name="v3" class="foo error">
+ <div class="error-message">err4</div>
+ <input name="v4" class="error">
+ <input name="v4">
+ </form>
More information about the lxml-checkins
mailing list