Package restkit :: Module forms
[hide private]

Source Code for Module restkit.forms

  1  # -*- coding: utf-8 - 
  2  # 
  3  # This file is part of restkit released under the MIT license.  
  4  # See the NOTICE for more information. 
  5   
  6   
  7  import mimetypes 
  8  import os 
  9  import re 
 10  import urllib 
 11   
 12   
 13  from restkit.util import to_bytestring, url_quote, url_encode 
 14   
 15  MIME_BOUNDARY = 'END_OF_PART' 
 16  CRLF = '\r\n' 
 17   
18 -def form_encode(obj, charset="utf8"):
19 encoded = url_encode(obj, charset=charset) 20 return to_bytestring(encoded)
21 22
23 -class BoundaryItem(object):
24 - def __init__(self, name, value, fname=None, filetype=None, filesize=None, 25 quote=url_quote):
26 self.quote = quote 27 self.name = quote(name) 28 if value is not None and not hasattr(value, 'read'): 29 value = self.encode_unreadable_value(value) 30 self.size = len(value) 31 self.value = value 32 if fname is not None: 33 if isinstance(fname, unicode): 34 fname = fname.encode("utf-8").encode("string_escape").replace('"', '\\"') 35 else: 36 fname = fname.encode("string_escape").replace('"', '\\"') 37 self.fname = fname 38 if filetype is not None: 39 filetype = to_bytestring(filetype) 40 self.filetype = filetype 41 42 if isinstance(value, file) and filesize is None: 43 try: 44 value.flush() 45 except IOError: 46 pass 47 self.size = int(os.fstat(value.fileno())[6]) 48 49 self._encoded_hdr = None 50 self._encoded_bdr = None
51
52 - def encode_hdr(self, boundary):
53 """Returns the header of the encoding of this parameter""" 54 if not self._encoded_hdr or self._encoded_bdr != boundary: 55 boundary = self.quote(boundary) 56 self._encoded_bdr = boundary 57 headers = ["--%s" % boundary] 58 if self.fname: 59 disposition = 'form-data; name="%s"; filename="%s"' % (self.name, 60 self.fname) 61 else: 62 disposition = 'form-data; name="%s"' % self.name 63 headers.append("Content-Disposition: %s" % disposition) 64 if self.filetype: 65 filetype = self.filetype 66 else: 67 filetype = "text/plain; charset=utf-8" 68 headers.append("Content-Type: %s" % filetype) 69 headers.append("Content-Length: %i" % self.size) 70 headers.append("") 71 headers.append("") 72 self._encoded_hdr = CRLF.join(headers) 73 return self._encoded_hdr
74
75 - def encode(self, boundary):
76 """Returns the string encoding of this parameter""" 77 value = self.value 78 if re.search("^--%s$" % re.escape(boundary), value, re.M): 79 raise ValueError("boundary found in encoded string") 80 81 return "%s%s%s" % (self.encode_hdr(boundary), value, CRLF)
82
83 - def iter_encode(self, boundary, blocksize=16384):
84 if not hasattr(self.value, "read"): 85 yield self.encode(boundary) 86 else: 87 yield self.encode_hdr(boundary) 88 while True: 89 block = self.value.read(blocksize) 90 if not block: 91 yield CRLF 92 return 93 yield block
94
95 - def encode_unreadable_value(self, value):
96 return value
97 98
99 -class MultipartForm(object):
100 - def __init__(self, params, boundary, headers, bitem_cls=BoundaryItem, 101 quote=url_quote):
102 self.boundary = boundary 103 self.tboundary = "--%s--%s" % (boundary, CRLF) 104 self.boundaries = [] 105 self._clen = headers.get('Content-Length') 106 107 if hasattr(params, 'items'): 108 params = params.items() 109 110 for param in params: 111 name, value = param 112 if hasattr(value, "read"): 113 fname = getattr(value, 'name') 114 if fname is not None: 115 filetype = ';'.join(filter(None, mimetypes.guess_type(fname))) 116 else: 117 filetype = None 118 if not isinstance(value, file) and self._clen is None: 119 value = value.read() 120 121 boundary = bitem_cls(name, value, fname, filetype, quote=quote) 122 self.boundaries.append(boundary) 123 elif isinstance(value, list): 124 for v in value: 125 boundary = bitem_cls(name, v, quote=quote) 126 self.boundaries.append(boundary) 127 else: 128 boundary = bitem_cls(name, value, quote=quote) 129 self.boundaries.append(boundary)
130
131 - def get_size(self, recalc=False):
132 if self._clen is None or recalc: 133 self._clen = 0 134 for boundary in self.boundaries: 135 self._clen += boundary.size 136 self._clen += len(boundary.encode_hdr(self.boundary)) 137 self._clen += len(CRLF) 138 self._clen += len(self.tboundary) 139 return int(self._clen)
140
141 - def __iter__(self):
142 for boundary in self.boundaries: 143 for block in boundary.iter_encode(self.boundary): 144 yield block 145 yield self.tboundary
146 147
148 -def multipart_form_encode(params, headers, boundary, quote=url_quote):
149 """Creates a tuple with MultipartForm instance as body and dict as headers 150 151 params 152 dict with fields for the body 153 154 headers 155 dict with fields for the header 156 157 boundary 158 string to use as boundary 159 160 quote (default: url_quote) 161 some callable expecting a string an returning a string. Use for quoting of 162 boundary and form-data keys (names). 163 """ 164 headers = headers or {} 165 boundary = quote(boundary) 166 body = MultipartForm(params, boundary, headers, quote=quote) 167 headers['Content-Type'] = "multipart/form-data; boundary=%s" % boundary 168 headers['Content-Length'] = str(body.get_size()) 169 return body, headers
170