Package restkit :: Module wrappers
[hide private]
[frames] | no frames]

Source Code for Module restkit.wrappers

  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  import cgi 
  7  import copy 
  8  import mimetypes 
  9  import os 
 10  from StringIO import StringIO 
 11  import types 
 12  import urlparse 
 13  import uuid 
 14   
 15  from .datastructures import MultiDict 
 16  from .errors import AlreadyRead, RequestError 
 17  from .forms import multipart_form_encode, form_encode 
 18  from .tee import ResponseTeeInput 
 19  from .util import to_bytestring 
 20   
21 -class Request(object):
22
23 - def __init__(self, url, method='GET', body=None, headers=None):
24 headers = headers or [] 25 self.url = url 26 self.initial_url = url 27 self.method = method 28 29 self._headers = None 30 self._body = None 31 32 # set parsed uri 33 self.headers = headers 34 if body is not None: 35 self.body = body
36
37 - def _headers__get(self):
38 if not isinstance(self._headers, MultiDict): 39 self._headers = MultiDict(self._headers or []) 40 return self._headers
41 - def _headers__set(self, value):
42 self._headers = MultiDict(copy.copy(value))
43 headers = property(_headers__get, _headers__set, doc=_headers__get.__doc__) 44
45 - def _parsed_url(self):
46 if self.url is None: 47 raise ValueError("url isn't set") 48 return urlparse.urlparse(self.url)
49 parsed_url = property(_parsed_url, doc="parsed url") 50
51 - def _path__get(self):
52 parsed_url = self.parsed_url 53 path = parsed_url.path or '/' 54 55 return urlparse.urlunparse(('','', path, parsed_url.params, 56 parsed_url.query, parsed_url.fragment))
57 path = property(_path__get) 58
59 - def _host__get(self):
60 try: 61 h = self.parsed_url.netloc.encode('ascii') 62 except UnicodeEncodeError: 63 h = self.parsed_url.netloc.encode('idna') 64 65 hdr_host = self.headers.iget("host") 66 if not hdr_host: 67 return h 68 return hdr_host
69 host = property(_host__get) 70
71 - def is_chunked(self):
72 te = self.headers.iget("transfer-encoding") 73 return (te is not None and te.lower() == "chunked")
74
75 - def is_ssl(self):
76 return self.parsed_url.scheme == "https"
77
78 - def _set_body(self, body):
79 ctype = self.headers.ipop('content-type', None) 80 clen = self.headers.ipop('content-length', None) 81 82 if isinstance(body, dict): 83 if ctype is not None and \ 84 ctype.startswith("multipart/form-data"): 85 type_, opts = cgi.parse_header(ctype) 86 boundary = opts.get('boundary', uuid.uuid4().hex) 87 self._body, self.headers = multipart_form_encode(body, 88 self.headers, boundary) 89 else: 90 ctype = "application/x-www-form-urlencoded; charset=utf-8" 91 self._body = form_encode(body) 92 elif hasattr(body, "boundary"): 93 ctype = "multipart/form-data; boundary=%s" % self.body.boundary 94 clen = body.get_size() 95 self._body = body 96 else: 97 self._body = body 98 99 if not ctype: 100 ctype = 'application/octet-stream' 101 if hasattr(self.body, 'name'): 102 ctype = mimetypes.guess_type(body.name)[0] 103 104 if not clen: 105 if hasattr(self._body, 'fileno'): 106 try: 107 self._body.flush() 108 except IOError: 109 pass 110 try: 111 fno = self._body.fileno() 112 clen = str(os.fstat(fno)[6]) 113 except IOError: 114 if not self.is_chunked(): 115 clen = len(self._body.read()) 116 elif hasattr(self._body, 'getvalue') and not \ 117 self.is_chunked(): 118 clen = len(self._body.getvalue()) 119 elif isinstance(self._body, types.StringTypes): 120 self._body = to_bytestring(self._body) 121 clen = len(self._body) 122 123 if clen is not None: 124 self.headers['Content-Length'] = clen 125 126 if ctype is not None: 127 self.headers['Content-Type'] = ctype
128
129 - def _get_body(self):
130 return self._body
131 body = property(_get_body, _set_body, doc="request body")
132 133
134 -class BodyWrapper(object):
135
136 - def __init__(self, resp, connection):
137 self.resp = resp 138 self.body = resp._body 139 self.connection = connection
140
141 - def __enter__(self):
142 return self
143
144 - def __exit__(self, exc_type, exc_val, traceback):
145 self.close()
146
147 - def close(self):
148 """ release connection """ 149 self.connection.release(self.resp.should_close)
150
151 - def __iter__(self):
152 return self
153
154 - def next(self):
155 try: 156 return self.body.next() 157 except StopIteration: 158 self.close() 159 raise
160
161 - def read(self, size=None):
162 data = self.body.read(size=size) 163 if not data: 164 self.close() 165 return data
166
167 - def readline(self, size=None):
168 line = self.body.readline(size=size) 169 if not line: 170 self.close() 171 return line
172
173 - def readlines(self, size=None):
174 lines = self.body.readlines(size=size) 175 if self.body.close: 176 self.close() 177 return lines
178 179
180 -class Response(object):
181 182 charset = "utf8" 183 unicode_errors = 'strict' 184
185 - def __init__(self, connection, request, resp):
186 self.request = request 187 self.connection = connection 188 self._body = resp.body 189 190 # response infos 191 self.headers = resp.headers 192 self.status = resp.status 193 self.status_int = resp.status_int 194 self.version = resp.version 195 self.headerslist = resp.headers.items() 196 self.location = resp.headers.iget('location') 197 self.final_url = request.url 198 self.should_close = resp.should_close() 199 200 201 self._closed = False 202 self._already_read = False 203 204 if request.method == "HEAD": 205 """ no body on HEAD, release the connection now """ 206 self.connection.release() 207 self._body = StringIO()
208
209 - def __getitem__(self, key):
210 try: 211 return getattr(self, key) 212 except AttributeError: 213 pass 214 return self.headers.iget(key)
215
216 - def __contains__(self, key):
217 return (self.headers.iget(key) is not None)
218
219 - def __iter__(self):
220 return self.headers.iteritems()
221
222 - def can_read(self):
223 return not self._already_read
224
225 - def body_string(self, charset=None, unicode_errors="strict"):
226 """ return body string, by default in bytestring """ 227 228 if not self.can_read(): 229 raise AlreadyRead() 230 231 body = self._body.read() 232 self._already_read = True 233 234 # release connection 235 self.connection.release(self.should_close) 236 237 if charset is not None: 238 try: 239 body = body.decode(charset, unicode_errors) 240 except UnicodeDecodeError: 241 pass 242 return body
243
244 - def body_stream(self):
245 """ stream body """ 246 if not self.can_read(): 247 raise AlreadyRead() 248 249 self._already_read = True 250 251 return BodyWrapper(self, self.connection)
252
253 - def tee(self):
254 """ copy response input to standard output or a file if length > 255 sock.MAX_BODY. This make possible to reuse it in your 256 appplication. When all the input has been read, connection is 257 released """ 258 if not hasattr(self._body, "reader"): 259 # head case 260 return self._body 261 262 return ResponseTeeInput(self, self.connection, 263 should_close=self.should_close)
264 ClientResponse = Response 265