[wwwsearch-commits] r17749 - wwwsearch/ClientForm/trunk
jjlee at codespeak.net
jjlee at codespeak.net
Thu Sep 22 00:15:54 CEST 2005
Author: jjlee
Date: Thu Sep 22 00:15:53 2005
New Revision: 17749
Modified:
wwwsearch/ClientForm/trunk/ClientForm.py
wwwsearch/ClientForm/trunk/test.py
Log:
Fix two backwards-compat. bugs in set_value_by_label and update some comments; Normalise quotes
Modified: wwwsearch/ClientForm/trunk/ClientForm.py
==============================================================================
--- wwwsearch/ClientForm/trunk/ClientForm.py (original)
+++ wwwsearch/ClientForm/trunk/ClientForm.py Thu Sep 22 00:15:53 2005
@@ -24,6 +24,13 @@
"""
+# XXXX
+# _find_control &c. and ambiguous control labels
+# SelectControl and labels -- backwards compat., multiple labels
+# Get rid of AList &c
+# choose_boundary .-replacement: merge with cut-n-pasted code from
+# mimetools!
+
# XXX
# Add some more functional tests
# Especially single and multiple file upload on the internet.
@@ -55,12 +62,13 @@
# Would be nice, but I'm not going to do it myself:
# -------------------------------------------------
-# Maybe a 0.3.x, cleaned up a bit and with id support for list items?
-# Not sure it's worth it...
+# Maybe a 0.3.x?
+# Replace by_label etc. with moniker / selector concept. Allows, eg.,
+# a choice between selection by value / id / label / element
+# contents. Or choice between matching labels exactly or by
+# substring. Etc.
+# Remove deprecated methods.
# action should probably be an absolute URI, like DOMForm.
-# Replace by_label with choice between value / id / label / element
-# contents. Moniker / selector concept is clearly the right thing
-# here.
# ...what else?
# Work on DOMForm.
# XForms? Don't know if there's a need here.
@@ -102,7 +110,7 @@
CHUNK = 1024 # size of chunks fed to parser, in bytes
_compress_re = re.compile(r"\s+")
-compress_text = lambda text: _compress_re.sub(' ', text.strip())
+def compress_text(text): return _compress_re.sub(" ", text.strip())
# This version of urlencode is from my Python 1.5.2 back-port of the
# Python 2.1 CVS maintenance branch of urllib. It will accept a sequence
@@ -741,9 +749,9 @@
d = {}
for key, val in attrs:
d[key] = val
- taken = bool(d.get('for')) # empty id is invalid
- d['__text'] = ''
- d['__taken'] = taken
+ taken = bool(d.get("for")) # empty id is invalid
+ d["__text"] = ""
+ d["__taken"] = taken
if taken:
self.labels.append(d)
self._current_label = d
@@ -754,16 +762,16 @@
# something is ugly in the HTML, but we're ignoring it
return
self._current_label = None
- label['__text'] = label['__text']
- del label['__taken'] # if it is staying around, it is True in all cases
+ label["__text"] = label["__text"]
+ del label["__taken"] # if it is staying around, it is True in all cases
def _add_label(self, d):
if self._current_label is not None:
- if self._current_label['__taken']:
+ if self._current_label["__taken"]:
self.end_label() # be fuzzy
else:
- self._current_label['__taken'] = True
- d['__label'] = self._current_label
+ self._current_label["__taken"] = True
+ d["__label"] = self._current_label
def handle_data(self, data):
if self._option is not None:
@@ -835,7 +843,7 @@
def handle_entityref(self, name):
table = self._entitydefs
- fullname = '&%s;' % name
+ fullname = "&%s;" % name
if table.has_key(fullname):
self.handle_data(table[fullname])
else:
@@ -857,8 +865,8 @@
escaped_attrs[key] = self.unescape_attrs(val)
return escaped_attrs
- def unknown_entityref(self, ref): self.handle_data('&%s;' % ref)
- def unknown_charref(self, ref): self.handle_data('&#%s;' % ref)
+ def unknown_entityref(self, ref): self.handle_data("&%s;" % ref)
+ def unknown_charref(self, ref): self.handle_data("&#%s;" % ref)
# HTMLParser.HTMLParser is recent, so live without it if it's not available
@@ -885,10 +893,10 @@
def handle_starttag(self, tag, attrs):
try:
- method = getattr(self, 'start_' + tag)
+ method = getattr(self, "start_" + tag)
except AttributeError:
try:
- method = getattr(self, 'do_' + tag)
+ method = getattr(self, "do_" + tag)
except AttributeError:
pass # unknown tag
else:
@@ -898,7 +906,7 @@
def handle_endtag(self, tag):
try:
- method = getattr(self, 'end_' + tag)
+ method = getattr(self, "end_" + tag)
except AttributeError:
pass # unknown tag
else:
@@ -1036,7 +1044,7 @@
for l in fp.labels:
label = Label(l)
labels.append(label)
- for_id = l['for']
+ for_id = l["for"]
coll = id_to_labels.get(for_id)
if coll is None:
id_to_labels[for_id] = [label]
@@ -1066,12 +1074,12 @@
class Label:
def __init__(self, attrs):
- self.id = attrs.get('for')
- self.text = compress_text(attrs.get('__text'))
+ self.id = attrs.get("for")
+ self.text = compress_text(attrs.get("__text"))
self.attrs = attrs
def _getLabel(attrs):
- label = attrs.get('__label')
+ label = attrs.get("__label")
if label is not None:
label = Label(label)
return label
@@ -1337,7 +1345,7 @@
# single file
file_object, content_type, filename = self._upload_data[0]
mw2 = mw.nextpart()
- fn_part = filename and ('; filename="%s"' % filename) or ''
+ fn_part = filename and ('; filename="%s"' % filename) or ""
disp = 'form-data; name="%s"%s' % (self.name, fn_part)
mw2.addheader("Content-disposition", disp, prefix=1)
fh = mw2.startbody(content_type, prefix=0)
@@ -1350,8 +1358,8 @@
fh = mw2.startmultipartbody("mixed", prefix=0)
for file_object, content_type, filename in self._upload_data:
mw3 = mw2.nextpart()
- fn_part = filename and ('; filename="%s"' % filename) or ''
- disp = 'file%s' % fn_part
+ fn_part = filename and ('; filename="%s"' % filename) or ""
+ disp = "file%s" % fn_part
mw3.addheader("Content-disposition", disp, prefix=1)
fh2 = mw3.startbody(content_type, prefix=0)
fh2.write(file_object.read())
@@ -1528,32 +1536,32 @@
return res
def __getattr__(self, name):
- if name=='selected':
+ if name=="selected":
return self._selected
raise AttributeError(name)
def __setattr__(self, name, value):
- if name == 'selected':
+ if name == "selected":
if bool(value) != bool(self._selected):
self._control._set_selected_state(self, value)
- elif name == 'disabled':
- self.__dict__['disabled'] = bool(value)
+ elif name == "disabled":
+ self.__dict__["disabled"] = bool(value)
else:
raise AttributeError(name)
def __str__(self):
res = self.name
if self.selected:
- res = '*' + res
+ res = "*" + res
if self.disabled:
- res = '(%s)' % res
+ res = "(%s)" % res
return res
def __repr__(self):
attrs = [("name", self.name), ("id", self.id)]+self.attrs.items()
return "<%s %s>" % (
self.__class__.__name__,
- " ".join(['%s=%r' % (k, v) for k, v in attrs])
+ " ".join(["%s=%r" % (k, v) for k, v in attrs])
)
# XXX sort this out (and assertions about items and controls being in exactly
@@ -1722,8 +1730,8 @@
nr is an optional 0-based index of the items matching the query.
If nr is the default None value and more than item is found, raises
- AmbiguityError (unless the HTMLForm constructor was passed a true
- ignore_ambiguity argument).
+ AmbiguityError (unless the HTMLForm instance's ignore_ambiguity
+ attribute is true).
If no item is found, raises ItemNotFoundError.
@@ -1785,14 +1793,14 @@
raise AttributeError("item is disabled")
elif action != item.selected:
if self.multiple:
- item.__dict__['_selected'] = action
+ item.__dict__["_selected"] = action
else:
if not action:
- item.__dict__['_selected'] = False
+ item.__dict__["_selected"] = False
else:
for o in self.items:
- o.__dict__['_selected'] = False
- item.__dict__['_selected'] = True
+ o.__dict__["_selected"] = False
+ item.__dict__["_selected"] = True
def toggle_single(self, by_label=None):
"""Deprecated: toggle the selection of the single item in this control.
@@ -1917,7 +1925,7 @@
for o in self.items:
# set items' controls to self, now that we've merged
- o.__dict__['_control'] = self
+ o.__dict__["_control"] = self
def __getattr__(self, name):
if name == "value":
@@ -1963,7 +1971,7 @@
raise ItemNotFoundError(
"insufficient items with name %r" % name)
else:
- raise AttributeError('disabled item with name %s' % name)
+ raise AttributeError("disabled item with name %s" % name)
on = []
off = []
for o in items:
@@ -2015,6 +2023,10 @@
"""
if isstringlike(value):
raise TypeError(value)
+ if not self.multiple and len(value) > 1:
+ raise ItemCountError(
+ "single selection list, must set sequence of "
+ "length 0 or 1")
items = []
for nn in value:
found = self.items_from_label(nn)
@@ -2025,12 +2037,13 @@
opt_name = found[0].name
if [o for o in found[1:] if o.name != opt_name]:
raise AmbiguityError(nn)
- # else: OK, we'll guess :-(
- # XXXX is this guess consistent with 0.1??
+ else:
+ # OK, we'll guess :-( Assume first available item.
+ found = found[:1]
for o in found:
# For the multiple-item case, we could try to be smarter,
# saving them up and trying to resolve, but that's too much.
- if o not in items:
+ if self._form.ignore_ambiguity or o not in items:
items.append(o)
break
else: # all of them are used
@@ -2105,12 +2118,12 @@
"""
def __init__(self, type, name, attrs, select_default=False):
- attrs.setdefault('value', 'on')
+ attrs.setdefault("value", "on")
ListControl.__init__(self, type, name, attrs, select_default,
called_as_base_class=True)
self.__dict__["multiple"] = False
o = Item(self, attrs)
- o.__dict__['_selected'] = attrs.has_key("checked")
+ o.__dict__["_selected"] = attrs.has_key("checked")
self.items.append(o)
def fixup(self):
@@ -2137,12 +2150,12 @@
"""
def __init__(self, type, name, attrs, select_default=False):
- attrs.setdefault('value', 'on')
+ attrs.setdefault("value", "on")
ListControl.__init__(self, type, name, attrs, select_default,
called_as_base_class=True)
self.__dict__["multiple"] = True
o = Item(self, attrs)
- o.__dict__['_selected'] = attrs.has_key("checked")
+ o.__dict__["_selected"] = attrs.has_key("checked")
self.items.append(o)
def get_labels(self):
@@ -2206,11 +2219,11 @@
# fish out the SELECT HTML attributes from the OPTION HTML attributes
# dictionary
self.attrs = attrs["__select"].copy()
- self.__dict__['_label'] = _getLabel(self.attrs)
- self.__dict__['id'] = self.attrs.get('id')
+ self.__dict__["_label"] = _getLabel(self.attrs)
+ self.__dict__["id"] = self.attrs.get("id")
self.__dict__["multiple"] = self.attrs.has_key("multiple")
# the majority of the contents, label, and value dance already happened
- contents = attrs.get('contents')
+ contents = attrs.get("contents")
attrs = attrs.copy()
del attrs["__select"]
@@ -2218,23 +2231,23 @@
called_as_base_class=True)
self.disabled = self.attrs.has_key("disabled")
self.readonly = self.attrs.has_key("readonly")
- if attrs.has_key('value'):
+ if attrs.has_key("value"):
# otherwise it is a marker 'select started' token
o = Item(self, attrs)
- o.__dict__['_selected'] = attrs.has_key("selected")
+ o.__dict__["_selected"] = attrs.has_key("selected")
# add 'label' label and contents label, if different. If both are
# provided, the 'label' label is used for display in HTML
# 4.0-compliant browsers (and any lower spec? not sure) while the
# contents are used for display in older or less-compliant
# browsers. We make label objects for both, if the values are
# different.
- label = attrs.get('label')
+ label = attrs.get("label")
if label:
- o._labels.append(Label({'__text': label}))
+ o._labels.append(Label({"__text": label}))
if contents and contents != label:
- o._labels.append(Label({'__text': contents}))
+ o._labels.append(Label({"__text": contents}))
elif contents:
- o._labels.append(Label({'__text': contents}))
+ o._labels.append(Label({"__text": contents}))
self.items.append(o)
def fixup(self):
@@ -2272,7 +2285,7 @@
def get_labels(self):
res = []
if self.value:
- res.append(Label({'__text': self.value}))
+ res.append(Label({"__text": self.value}))
res.extend(ScalarControl.get_labels(self))
return res
Modified: wwwsearch/ClientForm/trunk/test.py
==============================================================================
--- wwwsearch/ClientForm/trunk/test.py (original)
+++ wwwsearch/ClientForm/trunk/test.py Thu Sep 22 00:15:53 2005
@@ -1451,6 +1451,8 @@
c.value = []
self.assertRaises(TypeError, c.set_value_by_label, "2002")
+ self.assertRaises(ItemCountError, c.set_value_by_label,
+ ["2000", "2001"])
self.assertRaises(ItemNotFoundError, c.set_value_by_label, ["foo"])
c.set_value_by_label(["2002"])
self.assert_(c.value == ["0"])
@@ -1969,6 +1971,46 @@
self.assertRaises(ControlNotFoundError, setitem, "oops", ["blah"])
self.assertRaises(TypeError, setitem, ["Sort"], ["Relevance"])
+ def testSetValueByLabelIgnoringAmbiguity(self):
+ # regression test: follow ClientForm 0.1 behaviour
+ # also test that ignore_ambiguity argument to ParseFile works
+ f = StringIO("""\
+<form>
+ <select multiple name="form.grocery">
+ <option value="bread" id="1">Loaf of Bread</option>
+ <option value="bread" id="2">Loaf of Bread</option>
+ <option value="challah">Loaf of Challah</option>
+ </select>
+ <input type="submit" value="Submit" />
+</form>
+""")
+ for kwds, ignore_ambiguity in [({}, True),
+ ({"ignore_ambiguity": True}, True),
+ ({"ignore_ambiguity": False}, False),
+ ]:
+ form = ClientForm.ParseFile(f, "http://localhost/", **kwds)[0]
+ f.seek(0)
+ c = form.find_control("form.grocery")
+ #for item in c.items:
+ # print [label.text for label in item.get_labels()]
+ c.set_value_by_label(
+ ["Loaf of Bread", "Loaf of Bread", "Loaf of Challah"])
+ if ignore_ambiguity:
+ # select first item of ambiguous set
+ self.assertEqual(
+ c.get_value_by_label(),
+ ["Loaf of Bread", "Loaf of Challah"])
+ self.assertEqual(
+ [item.id for item in c.items if item.selected],
+ ["1", None])
+ else:
+ self.assertEqual(
+ c.get_value_by_label(),
+ ["Loaf of Bread", "Loaf of Bread", "Loaf of Challah"])
+ self.assertEqual(
+ [item.id for item in c.items if item.selected],
+ ["1", "2", None])
+
def testSearchByLabel(self):
f = StringIO("""\
<form>
@@ -2033,11 +2075,11 @@
to purchase:
</td>
<td>
- <label><input type="checkbox" name="form.grocery" value="bread"/>
+ <label><input type="checkbox" name="form.grocery" value="bread" id="1"/>
Loaf of Bread</label> |
- <label><input type="checkbox" name="form.grocery" value="bread"/>
+ <label><input type="checkbox" name="form.grocery" value="bread" id="2"/>
Loaf of Bread</label> |
- <label><input type="checkbox" name="form.grocery" value="bread"/>
+ <label><input type="checkbox" name="form.grocery" value="bread" id="3"/>
Loaf of Bread</label> |
<label><input type="checkbox" name="form.grocery" value="challah"/>
Loaf of Challah</label> |
@@ -2063,20 +2105,20 @@
form = ClientForm.ParseFile(f, "http://localhost/")[0]
# basic tests
- self.assertEqual(form.find_control(label='Title').value,
- 'The Grapes of Wrath')
- self.assertEqual(form.find_control(label='Submit').value,
- 'Submit')
+ self.assertEqual(form.find_control(label="Title").value,
+ "The Grapes of Wrath")
+ self.assertEqual(form.find_control(label="Submit").value,
+ "Submit")
self.assertEqual(
form.find_control(label="Country").get(
- 'Britain', by_label=True).name, 'EU: Great Britain')
+ "Britain", by_label=True).name, "EU: Great Britain")
self.assertEqual(
form.find_control(label="Origin").get(
- 'GB', by_label=True).name, 'EU: Great Britain')
- self.assertEqual(form.find_control(label='Password').value,
- '123')
- self.assertEqual(form.find_control(label='Title').value,
- 'The Grapes of Wrath')
+ "GB", by_label=True).name, "EU: Great Britain")
+ self.assertEqual(form.find_control(label="Password").value,
+ "123")
+ self.assertEqual(form.find_control(label="Title").value,
+ "The Grapes of Wrath")
# test item ambiguity, get, items_from_label, items_from_name, and
# set_value_by_label
@@ -2085,56 +2127,57 @@
# attribute is True, so ambiguity is ignored. For instance, notice
# that the form.grocery checkboxes include some loaves of bread and
# a loaf of challah. The code just guesses what you mean:
- c = form.find_control('form.grocery')
- self.assertEqual(c.get('Loaf', True), c.items[0])
- c.set_value_by_label(['Loaf'])
- self.assertEqual(c.get_value_by_label(), ['Loaf of Bread'])
+ c = form.find_control("form.grocery")
+ self.assertEqual(c.get("Loaf", True), c.items[0])
+ c.set_value_by_label(["Loaf"])
+ self.assertEqual(c.get_value_by_label(), ["Loaf of Bread"])
+ self.assertEqual(c.items[0].id, "1")
# However, if the form's ignore_ambiguity attribute is False, Ambiguity
# Errors may be raised. This is generally a preferred approach, but is
# not backwards compatible.
form.ignore_ambiguity = False
- self.assertRaises(ClientForm.AmbiguityError, c.get, 'Loaf', True)
+ self.assertRaises(ClientForm.AmbiguityError, c.get, "Loaf", True)
self.assertRaises(
- ClientForm.AmbiguityError, c.set_value_by_label, ['Loaf'])
+ ClientForm.AmbiguityError, c.set_value_by_label, ["Loaf"])
# If items have the same name (value), set_value_by_label will
# be happy (since it is just setting the value anyway).
- c.set_value_by_label(['Loaf of Bread'])
- self.assertEqual(c.get_value_by_label(), ['Loaf of Bread'])
+ c.set_value_by_label(["Loaf of Bread"])
+ self.assertEqual(c.get_value_by_label(), ["Loaf of Bread"])
c.set_value_by_label(
- ['Loaf of Bread', 'Loaf of Bread', 'Loaf of Challah'])
+ ["Loaf of Bread", "Loaf of Bread", "Loaf of Challah"])
self.assertEqual(
c.get_value_by_label(),
- ['Loaf of Bread', 'Loaf of Bread', 'Loaf of Challah'])
+ ["Loaf of Bread", "Loaf of Bread", "Loaf of Challah"])
# "get" will still raise an exception, though.
self.assertRaises(
- ClientForm.AmbiguityError, c.get, 'Loaf of Bread', True)
+ ClientForm.AmbiguityError, c.get, "Loaf of Bread", True)
# If you want an item, you need to
# specify which one you want (or use items_from_label or
# items_from_name to explicitly get all of them).
- self.assertEqual(c.get('Loaf of Bread', True, 0).selected, True)
- self.assertEqual(c.get('Loaf of Bread', True, 1).selected, True)
- self.assertEqual(c.get('Loaf of Bread', True, 2).selected, False)
- self.assertEqual(c.get('Loaf of Challah', True).selected, True)
+ self.assertEqual(c.get("Loaf of Bread", True, 0).selected, True)
+ self.assertEqual(c.get("Loaf of Bread", True, 1).selected, True)
+ self.assertEqual(c.get("Loaf of Bread", True, 2).selected, False)
+ self.assertEqual(c.get("Loaf of Challah", True).selected, True)
self.assertEqual(
- [i.selected for i in c.items_from_label('Loaf of Bread')],
+ [i.selected for i in c.items_from_label("Loaf of Bread")],
[True, True, False])
self.assertEqual(
- [i.selected for i in c.items_from_label('Loaf of Challah')],
+ [i.selected for i in c.items_from_label("Loaf of Challah")],
[True])
self.assertEqual(
- [i.name for i in c.items_from_label('Loaf')],
- ['bread', 'bread', 'bread', 'challah'])
+ [i.name for i in c.items_from_label("Loaf")],
+ ["bread", "bread", "bread", "challah"])
self.assertEqual(
- [i.get_labels()[0].text for i in c.items_from_name('bread')],
- ['Loaf of Bread', 'Loaf of Bread', 'Loaf of Bread'])
+ [i.get_labels()[0].text for i in c.items_from_name("bread")],
+ ["Loaf of Bread", "Loaf of Bread", "Loaf of Bread"])
# test deprecation
if warnings_imported:
try:
for c, f in (
- (form.find_control('form.genre'), 'western'),
- (form.find_control('form.country'), 'zimbabwe'),
- (form.find_control('form.quality'), 'good')):
+ (form.find_control("form.genre"), "western"),
+ (form.find_control("form.country"), "zimbabwe"),
+ (form.find_control("form.quality"), "good")):
# warnings are nasty. :-(
raise_deprecations() # clear onceregistry
try:
@@ -2142,43 +2185,43 @@
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
try:
c.toggle_single()
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
try:
c.set_single(True)
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
try:
c.toggle(f)
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
try:
c.get_item_disabled(f)
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
try:
c.set_item_disabled(True, f)
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
try:
c.get_item_attrs(True, f)
except DeprecationWarning:
pass
else:
- self.fail('deprecation failed')
+ self.fail("deprecation failed")
finally:
reset_deprecations()
More information about the wwwsearch-commits
mailing list