]> granicus.if.org Git - python/commitdiff
Added FieldStorage class, which stores parts in files.
authorGuido van Rossum <guido@python.org>
Thu, 7 Mar 1996 06:33:07 +0000 (06:33 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 7 Mar 1996 06:33:07 +0000 (06:33 +0000)
(Not documented yet, and the files are currently StringIO instances.)

Lib/cgi.py

index d4e5bffef9ed7a72015af26a73794d7c9d7945c7..fd4ce32aeed818cf6fedbddcadcbc383ee7ac8e3 100755 (executable)
@@ -32,8 +32,8 @@ by a blank line.  The first section contains a number of headers,
 telling the client what kind of data is following.  Python code to
 generate a minimal header section looks like this:
 
-       print "Content-type: text/html"         # HTML is following
-       print                                   # blank line, end of headers
+       print "Content-type: text/html" # HTML is following
+       print                           # blank line, end of headers
 
 The second section is usually HTML, which allows the client software
 to display nicely formatted text with header, in-line images, etc.
@@ -503,6 +503,272 @@ def parse_header(line):
        return key, pdict
 
 
+# Classes for field storage
+# =========================
+
+class MiniFieldStorage:
+
+       """Internal: dummy FieldStorage, used with query string format."""
+
+       def __init__(self, name, value):
+               """Constructor from field name and value."""
+               self.name = name
+               self.value = value
+               from StringIO import StringIO
+               self.filename = None
+               self.list = None
+               self.file = StringIO(value)
+
+       def __repr__(self):
+               """Return printable representation."""
+               return "MiniFieldStorage(%s, %s)" % (`self.name`,
+                                                    `self.value`)
+
+
+class FieldStorage:
+
+       """Store a sequence of fields, reading multipart/form-data."""
+
+       def __init__(self, fp=None, headers=None, outerboundary=""):
+               """Constructor.  Read multipart/* until last part."""
+               method = None
+               if environ.has_key('REQUEST_METHOD'):
+                       method = string.upper(environ['REQUEST_METHOD'])
+               if not fp and method == 'GET':
+                       qs = None
+                       if environ.has_key('QUERY_STRING'):
+                               qs = environ['QUERY_STRING']
+                       from StringIO import StringIO
+                       fp = StringIO(qs or "")
+                       if headers is None:
+                               headers = {'content-type':
+                                          "application/x-www-form-urlencoded"}
+               if headers is None:
+                       headers = {}
+                       if environ.has_key('CONTENT_TYPE'):
+                               headers['content-type'] = environ['CONTENT_TYPE']
+                       if environ.has_key('CONTENT_LENGTH'):
+                               headers['content-length'] = environ['CONTENT_LENGTH']
+               self.fp = fp or sys.stdin
+               self.headers = headers
+               self.outerboundary = outerboundary
+               
+               # Process content-disposition header
+               cdisp, pdict = "", {}
+               if self.headers.has_key('content-disposition'):
+                       cdisp, pdict = parse_header(self.headers['content-disposition'])
+               self.disposition = cdisp
+               self.disposition_options = pdict
+               self.name = None
+               if pdict.has_key('name'):
+                       self.name = pdict['name']
+               self.filename = None
+               if pdict.has_key('filename'):
+                       self.filename = pdict['filename']
+               
+               # Process content-type header
+               ctype, pdict = "text/plain", {}
+               if self.headers.has_key('content-type'):
+                       ctype, pdict = parse_header(self.headers['content-type'])
+               self.type = ctype
+               self.type_options = pdict
+               self.innerboundary = ""
+               if pdict.has_key('boundary'):
+                       self.innerboundary = pdict['boundary']
+               clen = -1
+               if self.headers.has_key('content-length'):
+                       try:
+                               clen = string.atoi(self.headers['content-length'])
+                       except:
+                               pass
+               self.length = clen
+
+               self.list = self.file = None
+               self.done = 0
+               self.lines = []
+               if ctype == 'application/x-www-form-urlencoded':
+                       self.read_urlencoded()
+               elif ctype[:10] == 'multipart/':
+                       self.read_multi()
+               else:
+                       self.read_single()
+       
+       def __repr__(self):
+               """Return a printable representation."""
+               return "FieldStorage(%s, %s, %s)" % (
+                       `self.name`, `self.filename`, `self.value`)
+
+       def __getattr__(self, name):
+               if name != 'value':
+                       raise AttributeError, name
+               if self.file:
+                       self.file.seek(0)
+                       value = self.file.read()
+                       self.file.seek(0)
+               elif self.list is not None:
+                       value = self.list
+               else:
+                       value = None
+               return value
+       
+       def __getitem__(self, key):
+               """Dictionary style indexing."""
+               if self.list is None:
+                       raise TypeError, "not indexable"
+               found = []
+               for item in self.list:
+                       if item.name == key: found.append(item)
+               if not found:
+                       raise KeyError, key
+               return found
+       
+       def keys(self):
+               """Dictionary style keys() method."""
+               if self.list is None:
+                       raise TypeError, "not indexable"
+               keys = []
+               for item in self.list:
+                       if item.name not in keys: keys.append(item.name)
+               return keys
+
+       def read_urlencoded(self):
+               """Internal: read data in query string format."""
+               qs = self.fp.read(self.length)
+               dict = parse_qs(qs)
+               self.list = []
+               for key, valuelist in dict.items():
+                       for value in valuelist:
+                               self.list.append(MiniFieldStorage(key, value))
+               self.skip_lines()
+       
+       def read_multi(self):
+               """Internal: read a part that is itself multipart."""
+               import rfc822
+               self.list = []
+               part = self.__class__(self.fp, {}, self.innerboundary)
+               # Throw first part away
+               while not part.done:
+                       headers = rfc822.Message(self.fp)
+                       part = self.__class__(self.fp, headers, self.innerboundary)
+                       self.list.append(part)
+               self.skip_lines()
+       
+       def read_single(self):
+               """Internal: read an atomic part."""
+               if self.length >= 0:
+                       self.read_binary()
+                       self.skip_lines()
+               else:
+                       self.read_lines()
+               self.file.seek(0)
+       
+       bufsize = 8*1024                # I/O buffering size for copy to file
+       
+       def read_binary(self):
+               """Internal: read binary data."""
+               self.file = self.make_file('b')
+               todo = self.length
+               if todo >= 0:
+                       while todo > 0:
+                               data = self.fp.read(min(todo, self.bufsize))
+                               if not data:
+                                       self.done = -1
+                                       break
+                               self.file.write(data)
+                               todo = todo - len(data)
+       
+       def read_lines(self):
+               """Internal: read lines until EOF or outerboundary."""
+               self.file = self.make_file('')
+               if self.outerboundary:
+                       self.read_lines_to_outerboundary()
+               else:
+                       self.read_lines_to_eof()
+       
+       def read_lines_to_eof(self):
+               """Internal: read lines until EOF."""
+               while 1:
+                       line = self.fp.readline()
+                       if not line:
+                               self.done = -1
+                               break
+                       self.lines.append(line)
+                       if line[-2:] == '\r\n':
+                               line = line[:-2] + '\n'
+                       self.file.write(line)
+       
+       def read_lines_to_outerboundary(self):
+               """Internal: read lines until outerboundary."""
+               next = "--" + self.outerboundary
+               last = next + "--"
+               delim = ""
+               while 1:
+                       line = self.fp.readline()
+                       if not line:
+                               self.done = -1
+                               break
+                       self.lines.append(line)
+                       if line[:2] == "--":
+                               strippedline = string.strip(line)
+                               if strippedline == next:
+                                       break
+                               if strippedline == last:
+                                       self.done = 1
+                                       break
+                       if line[-2:] == "\r\n":
+                               line = line[:-2]
+                       elif line[-1] == "\n":
+                               line = line[:-1]
+                       self.file.write(delim + line)
+                       delim = "\n"
+       
+       def skip_lines(self):
+               """Internal: skip lines until outer boundary if defined."""
+               if not self.outerboundary or self.done:
+                       return
+               next = "--" + self.outerboundary
+               last = next + "--"
+               while 1:
+                       line = self.fp.readline()
+                       if not line:
+                               self.done = -1
+                               break
+                       self.lines.append(line)
+                       if line[:2] == "--":
+                               strippedline = string.strip(line)
+                               if strippedline == next:
+                                       break
+                               if strippedline == last:
+                                       self.done = 1
+                                       break
+       
+       def make_file(self, binary):
+               """Overridable: return a readable & writable file.
+               
+               The file will be used as follows:
+               - data is written to it
+               - seek(0)
+               - data is read from it
+               
+               The 'binary' argument is 'b' if the file should be created in
+               binary mode (on non-Unix systems), '' otherwise.
+               
+               The intention is that you can override this method to selectively
+               create a real (temporary) file or use a memory file dependent on
+               the perceived size of the file or the presence of a filename, etc.
+               
+               """
+               
+               # Prefer ArrayIO over StringIO, if it's available
+               try:
+                       from ArrayIO import ArrayIO
+                       ioclass = ArrayIO
+               except ImportError:
+                       from StringIO import StringIO
+                       ioclass = StringIO
+               return ioclass()
+
+
 # Main classes
 # ============
 
@@ -636,7 +902,7 @@ def test():
        sys.stderr = sys.stdout
        try:
                print_environ()
-               print_form(FormContentDict())
+               print_form(FieldStorage())
                print
                print "<H3>Current Working Directory:</H3>"
                try:
@@ -671,8 +937,9 @@ def print_form(form):
        print "<DL>"
        for key in keys:
                print "<DT>" + escape(key) + ":",
-               print "<i>" + escape(`type(form[key])`) + "</i>"
-               print "<DD>" + escape(`form[key]`)
+               value = form[key]
+               print "<i>" + escape(`type(value)`) + "</i>"
+               print "<DD>" + escape(`value`)
        print "</DL>"
        print