[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>&nbsp;|
-      <label><input type="checkbox" name="form.grocery" value="bread"/>
+      <label><input type="checkbox" name="form.grocery" value="bread" id="2"/>
         Loaf of Bread</label>&nbsp;|
-      <label><input type="checkbox" name="form.grocery" value="bread"/>
+      <label><input type="checkbox" name="form.grocery" value="bread" id="3"/>
         Loaf of Bread</label>&nbsp;|
       <label><input type="checkbox" name="form.grocery" value="challah"/>
         Loaf of Challah</label>&nbsp;|
@@ -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