It has been awhile, but I finally made the patch.
Advice and feedback is welcome.

What it does:
- Simplifies the creation of StringField objects. This was already
marked as a TODO in the 3.2.5b code.
- Does not create a file object (cStringIO) for each and every field.
- FieldStorage.get() will always return the same instance(s) for any
given name.
- FieldStorage.get() is very cheap now (does not create new objects,
which is expensive in Python)

It may break:
- Code that creates an instance of Field or StringField directly. This
can be fixed by extending the __init__() routine, but I don't think it's
needed.

It works:
- Perfect on all of my projects.

Attached:
- Patch created by SVN

--
Mike Looijmans
Philips Natlab / Topic Automation
Index: util.py
===================================================================
--- util.py     (revision 348746)
+++ util.py     (working copy)
@@ -48,19 +48,8 @@
 """
 
 class Field:
-   def __init__(self, name, file, ctype, type_options,
-                disp, disp_options, headers = {}):
+   def __init__(self, name):
        self.name = name
-       self.file = file
-       self.type = ctype
-       self.type_options = type_options
-       self.disposition = disp
-       self.disposition_options = disp_options
-       if disp_options.has_key("filename"):
-           self.filename = disp_options["filename"]
-       else:
-           self.filename = None
-       self.headers = headers
 
    def __repr__(self):
        """Return printable representation."""
@@ -81,13 +70,30 @@
        self.file.close()
 
 class StringField(str):
-   """ This class is basically a string with
-   a value attribute for compatibility with std lib cgi.py
-   """
+    """ This class is basically a string with
+    added attributes for compatibility with std lib cgi.py. Basically, this
+    works the opposite of Field, as it stores its data in a string, but creates
+    a file on demand. Field creates a value on demand and stores data in a 
file.
+    """
+    filename = None
+    headers = {}
+    ctype = "text/plain"
+    type_options = {}
+    disposition = None
+    disp_options = None
+    
+    # I wanted __init__(name, value) but that does not work (apparently, you
+    # cannot subclass str with a constructor that takes >1 argument)
+    def __init__(self, value):
+        '''Create StringField instance. You'll have to set name yourself.'''
+        str.__init__(self, value)
+        self.value = value
 
-   def __init__(self, str=""):
-       str.__init__(self, str)
-       self.value = self.__str__()
+    def __getattr__(self, name):
+        if name != 'file':
+            raise AttributeError, name
+        self.file = cStringIO.StringIO(self.value)
+        return self.file
 
 class FieldStorage:
 
@@ -103,8 +109,7 @@
        if req.args:
            pairs = parse_qsl(req.args, keep_blank_values)
            for pair in pairs:
-               file = cStringIO.StringIO(pair[1])
-               self.list.append(Field(pair[0], file, "text/plain", {}, None, 
{}))
+               self.add_field(pair[0], pair[1])
 
        if req.method != "POST":
            return
@@ -123,9 +128,7 @@
        if ctype.startswith("application/x-www-form-urlencoded"):
            pairs = parse_qsl(req.read(clen), keep_blank_values)
            for pair in pairs:
-               # TODO : isn't this a bit heavyweight just for form fields ?
-               file = cStringIO.StringIO(pair[1])
-               self.list.append(Field(pair[0], file, "text/plain", {}, None, 
{}))
+               self.add_field(pair[0], pair[1])
            return
 
        if not ctype.startswith("multipart/"):
@@ -205,33 +208,44 @@
            else:
                name = None
 
+           # create a file object
            # is this a file?
            if disp_options.has_key("filename"):
                if file_callback and callable(file_callback):
                    file = file_callback(disp_options["filename"])
                else:
-                   file = self.make_file()
+                   file = tempfile.TemporaryFile("w+b")
            else:
                if field_callback and callable(field_callback):
                    file = field_callback()
                else:
-                   file = self.make_field()
+                   file = cStringIO.StringIO()
 
            # read it in
-           end_of_stream = self.read_to_boundary(req, boundary, file)
+           self.read_to_boundary(req, boundary, file)
            file.seek(0)
-
+ 
            # make a Field
-           field = Field(name, file, ctype, type_options, disp, disp_options, 
headers)
-
+           if disp_options.has_key("filename"):
+               field = Field(name)
+               field.filename = disp_options["filename"]
+           else:
+               field = StringField(file.read())
+               field.name = name
+           field.file = file
+           field.type = ctype
+           field.type_options = type_options
+           field.disposition = disp
+           field.disposition_options = disp_options
+           field.headers = headers
            self.list.append(field)
 
-   def make_file(self):
-       return tempfile.TemporaryFile("w+b")
+   def add_field(self, key, value):
+       """Insert a field as key/value pair"""
+       item = StringField(value)
+       item.name = key
+       self.list.append(item)
 
-   def make_field(self):
-       return cStringIO.StringIO()
-
    def read_to_boundary(self, req, boundary, file):
         previous_delimiter = None
         while True:
@@ -288,11 +302,7 @@
        found = []
        for item in self.list:
            if item.name == key:
-               if isinstance(item.file, FileType) or \
-                      isinstance(getattr(item.file, 'file', None), FileType):
-                   found.append(item)
-               else:
-                   found.append(StringField(item.value))
+               found.append(item)
        if not found:
            raise KeyError, key
        if len(found) == 1:
@@ -333,11 +343,7 @@
        """ return the first value received """
        for item in self.list:
            if item.name == key:
-               if isinstance(item.file, FileType) or \
-                      isinstance(getattr(item.file, 'file', None), FileType):
-                   return item
-               else:
-                   return StringField(item.value)
+               return item
        return default
 
    def getlist(self, key):
@@ -347,11 +353,7 @@
        found = []
        for item in self.list:
            if item.name == key:
-               if isinstance(item.file, FileType) or \
-                      isinstance(getattr(item.file, 'file', None), FileType):
-                   found.append(item)
-               else:
-                   found.append(StringField(item.value))
+               found.append(item)
        return found
 
 def parse_header(line):

Reply via email to