changeset 63:af1476a936fc

add a multipart processor so we can post damn files
author Jeff Hammel <jhammel@mozilla.com>
date Thu, 01 Mar 2012 16:57:00 -0800
parents 7c154953acc4
children bb8d993376aa
files tests/multipart.py tests/test.py
diffstat 2 files changed, 83 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/multipart.py	Thu Mar 01 16:57:00 2012 -0800
@@ -0,0 +1,81 @@
+"""
+from
+http://www.doughellmann.com/PyMOTW/urllib2/#uploading-files
+
+Very very sad that python2 stdlib doesn't have this
+"""
+
+import itertools
+import mimetools
+import mimetypes
+from cStringIO import StringIO
+import urllib
+import urllib2
+
+class MultiPartForm(object):
+    """Accumulate the data to be used when posting a form."""
+
+    def __init__(self):
+        self.form_fields = []
+        self.files = []
+        self.boundary = mimetools.choose_boundary()
+
+    def get_content_type(self):
+        return 'multipart/form-data; boundary=%s' % self.boundary
+
+    def add_field(self, name, value):
+        """Add a simple field to the form data."""
+        self.form_fields.append((name, value))
+
+    def add_file(self, fieldname, filename, fileHandle, mimetype=None):
+        """Add a file to be uploaded."""
+        body = fileHandle.read()
+        if mimetype is None:
+            mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+        self.files.append((fieldname, filename, mimetype, body))
+
+    def post(self, url):
+        request = urllib2.Request(url)
+        body = str(self)
+        request.add_header('Content-type', self.get_content_type())
+        request.add_header('Content-length', len(body))
+        request.add_data(body)
+        return urllib2.urlopen(request)
+
+    def __str__(self):
+        """Return a string representing the form data, including attached files."""
+        # Build a list of lists, each containing "lines" of the
+        # request.  Each part is separated by a boundary string.
+        # Once the list is built, return a string where each
+        # line is separated by '\r\n'.
+        parts = []
+        part_boundary = '--' + self.boundary
+
+        # Add the form fields
+        parts.extend(
+            [ part_boundary,
+              'Content-Disposition: form-data; name="%s"' % name,
+              '',
+              value,
+            ]
+            for name, value in self.form_fields
+            )
+
+        # Add the files to upload
+        parts.extend(
+            [ part_boundary,
+              'Content-Disposition: file; name="%s"; filename="%s"' % \
+                 (field_name, filename),
+              'Content-Type: %s' % content_type,
+              '',
+              body,
+            ]
+            for field_name, filename, content_type, body in self.files
+            )
+
+        # Flatten the list and add closing boundary marker,
+        # then return CR+LF separated data
+        flattened = list(itertools.chain(*parts))
+        flattened.append('--' + self.boundary + '--')
+        flattened.append('')
+        return '\r\n'.join(flattened)
--- a/tests/test.py	Thu Mar 01 16:20:44 2012 -0800
+++ b/tests/test.py	Thu Mar 01 16:57:00 2012 -0800
@@ -41,8 +41,8 @@
         doctest_args['optionflags'] = doctest.REPORT_ONLY_FIRST_FAILURE
 
     # gather tests
-    tests =  [ test for test in os.listdir(directory)
-               if test.endswith('.txt') ]
+    tests =  [test for test in os.listdir(directory)
+              if test.endswith('.txt')]
 
     # run the tests
     for test in tests: