quixote/form widget.py,1.37,1.38

Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]>
Newsgroups gmane.comp.web.quixote.cvs
Message-ID <[email protected]>
Update of /home/cvs/quixote/form
In directory hewson:/tmp/cvs-serv1873

Modified Files:
	widget.py 
Log Message:
Add "options" keyword to select widgets that can be used instead of
"allowed_values" and "descriptions".  If the "options" list contains
tuples of length three, then the last element denotes the ``key'' of
each option.  The key is used when rendering the select widget and
parsing it (useful if the options list can change between rendering and
parsing).

Change format of "options" so that value comes first.

Don't HTML escape data anywhere except while rendering.


Index: widget.py
===================================================================
RCS file: /home/cvs/quixote/form/widget.py,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -d -r1.37 -r1.38
--- widget.py	1 Nov 2002 18:48:25 -0000	1.37
+++ widget.py	1 Nov 2002 21:43:01 -0000	1.38
@@ -227,7 +227,7 @@
     </select>
 
     Instance attributes:
-      options : [ (description:htmltext : value:any) ]
+      options : [ (value:any, description:any, key:any) ]
       value : any
         The value is None or an element of dict(options.values()).
       size : int
@@ -240,79 +240,96 @@
     def __init__ (self, name, value=None,
                   allowed_values=None,
                   descriptions=None,
+                  options=None,
                   size=None,
                   sort=0):
         assert self.__class__ is not SelectWidget, "abstract class"
+        # if options passed, cannot pass allowed_values or descriptions
+        self.options = []
+        if options:
+            assert not (allowed_values or descriptions), \
+                                'cannot pass both allowed_values and options'
+            self.set_options(options, sort)
+        elif allowed_values:
+            assert not options, 'cannot pass both allowed_values and options'
+            self.set_allowed_values(allowed_values, descriptions, sort)
         self.set_name(name)
-        self.set_allowed_values(allowed_values, descriptions, sort=sort)
         self.set_value(value)
         self.size = size
 
     def get_allowed_values (self):
-        return [ object for description, object in self.options ]
+        return [ item[0] for item in self.options ]
 
     def get_descriptions (self):
-        return [ description for description, object in self.options ]
+        return [ item[1] for item in self.options ]
 
     def set_value (self, value):
         self.value = None
-        for description, object in self.options:
+        for object, description, key in self.options:
             if value == object:
                 self.value = value
                 break
 
     def set_options (self, options, sort=0):
-        """(options: [(description:any, object:any)])
-        Set the options list, applying htmlescape() to each description.
-        Make sure that no descriptions are duplicated.
-        If the sort keyword argument is true, sort
-        the options by case-insensitive lexicographic order of descriptiosn,
-        except that options with value None appear before others.
+        """(options: [objects:any], sort=0)
+         or 
+           (options: [(object:any, description:any)], sort=0)
+         or
+           (options: [(object:any, description:any, key:any)], sort=0)
         """
-        self.options = [ (htmlescape(description), object)
-                         for description, object in options ]
-        found = {}
-        for description, object in self.options:
-            assert description not in found, (
-                "description repeated: %r" % description)
-            found[description] = 1
+
+        """
+        Set the options list.  The list of options can be a list of objects, in
+        which case the descriptions default to map(htmlescape, objects) 
+        applying htmlescape() to each description and
+        key.
+        If keys are provided they must be distinct.  If the sort keyword
+        argument is true, sort the options by case-insensitive lexicographic
+        order of descriptions, except that options with value None appear
+        before others.
+        """
+        if options:
+            # provide default values for descriptions and keys
+            first = options[0]
+            if type(first) is TupleType:
+                if len(first) == 3:
+                    pass
+                elif len(first) == 2:
+                    options = [options[i] + (i,)
+                               for i in range(len(options))]
+                else:
+                    raise ValueError, 'invalid options %r' % options
+            else:
+                options = zip(options, options, range(len(options)))
+
         if sort:
             def compare(a, b):
-                a_description, a_value = a
-                b_description, b_value = b
+                a_value, a_description, a_key = a
+                b_value, b_description, b_key = b
                 if a_value is None:
                     if b_value is not None:
                         return -1
                 elif b_value is None:
                     return 1
-                return cmp(a_description.lower(), b_description.lower())
-            self.options.sort(compare)
+                return cmp(str(a_description).lower(),
+                           str(b_description).lower())
+            options.sort(compare)
+        self.options = options
 
-    def set_allowed_values (self, allowed_values, descriptions, sort=0):
+    def set_allowed_values (self, allowed_values, descriptions=None, sort=0):
         """(allowed_values:[any], descriptions:[any], sort:boolean=0)
 
         Set the options for this widget.  The allowed_values and descriptions
-        parameters must be lists or tuples of the same length.
-        The sort option causes the options to be sorted using case-insensitive
-        lexicographic order of descriptions, except that options with value
-        None appear before others.
+        parameters must be sequences of the same length.  The sort option
+        causes the options to be sorted using case-insensitive lexicographic
+        order of descriptions, except that options with value None appear
+        before others.
         """
-        assert type(allowed_values) in (ListType, TupleType), (
-            "allowed_values for '%s' not a list or tuple: got %r" % (
-            self.name, allowed_values))
         if descriptions is None:
-            descriptions = []
-            for value in allowed_values:
-                if value is None:
-                    descriptions.append(htmltext(""))
-                else:
-                    descriptions.append(htmlescape(value))
+            self.set_options(allowed_values, sort)
         else:
-            assert type(descriptions) in (ListType, TupleType), (
-                "descriptions for '%s' not a list or tuple: got %r" % (
-                self.name, descriptions))
             assert len(descriptions) == len(allowed_values)
-        self.set_options(zip(descriptions, allowed_values), sort=sort)
+            self.set_options(zip(allowed_values, descriptions), sort)
 
     def is_selected (self, value):
         return value == self.value
@@ -329,15 +346,15 @@
         tags = [htmltag("select", name=self.name,
                         multiple=multiple, onchange=onchange,
                         size=self.size)]
-        for description, object in self.options:
+        for object, description, key in self.options:
             if self.is_selected(object):
                 selected = ValuelessAttr
             else:
                 selected = None
-            r = htmltag("option",
-                        value=description,
-                        selected=selected)
-            tags.append(r + description + htmltext('</option>'))
+            if description is None:
+                description = ""
+            r = htmltag("option", value=key, selected=selected)
+            tags.append(r + htmlescape(description) + htmltext('</option>'))
         tags.append(htmltext("</select>"))
         return htmltext("\n").join(tags)
 
@@ -354,8 +371,8 @@
         if value:
             if type(value) is ListType:
                 raise FormValueError, "cannot select multiple values"
-            for description, object in self.options:
-                if value == description:
+            for object, description, key in self.options:
+                if value == str(key): # str() not htmlescape()!
                     self.value = object
                     break
         return self.value
@@ -377,10 +394,10 @@
     def __init__ (self, name, value=None,
                   allowed_values=None,
                   descriptions=None,
+                  options=None,
                   delim=None):
-        self.set_name(name)
-        self.set_allowed_values(allowed_values, descriptions)
-        self.set_value(value)
+        SingleSelectWidget.__init__(self, name, value, allowed_values,
+                                    descriptions, options)
         if delim is None:
             self.delim = "\n"
         else:
@@ -388,7 +405,7 @@
 
     def render (self, request):
         tags = []
-        for description, object in self.options:
+        for object, description, key in self.options:
             if self.is_selected(object):
                 checked = ValuelessAttr
             else:
@@ -398,7 +415,7 @@
                         name=self.name,
                         value=description,
                         checked=checked)
-            tags.append(r + description + htmltext('</input>'))
+            tags.append(r + htmlescape(description) + htmltext('</input>'))
         return htmlescape(self.delim).join(tags)
 
 
@@ -435,12 +452,12 @@
         value = request.form.get(self.name)
         if value and type(value) is ListType:
             self.value =  [ object
-                            for description, object in self.options
-                            if description in value ] or None
+                            for object, description, key in self.options
+                            if str(key) in value ] or None
         else:
             self.value =  [ object
-                            for description, object in self.options
-                            if description == value ] or None
+                            for object, description, key in self.options
+                            if str(key) == value ] or None
         return self.value
 
 
@@ -572,6 +589,7 @@
     widget_type = "option_select"
 
     def __init__(self, *args, **kwargs):
+        # XXX this garbage should be removed but if changes the API
         if kwargs.has_key("options"):
             kwargs["allowed_values"] = kwargs["options"]
             del kwargs["options"]
@@ -581,7 +599,8 @@
         if request.form:
             SingleSelectWidget.parse(self, request)
         if self.value is None:
-            self.value = self.options[0][1]
+            self.value = self.options[0][0]
+
 
     def render (self, request):
         return (SingleSelectWidget.render(self, request) +
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.