1
2
3
4
5
6
7 """
8 restkit.resource
9 ~~~~~~~~~~~~~~~~
10
11 This module provide a common interface for all HTTP request.
12 """
13 from copy import copy
14 import urlparse
15
16 from restkit.errors import ResourceNotFound, Unauthorized, RequestFailed,\
17 ParserError, RequestError
18 from restkit.client import HttpRequest, HttpResponse
19 from restkit.filters import BasicAuth
20 from restkit import util
21 from restkit.pool.simple import SimplePool
22
23
24 DEFAULT_TIMEOUT = 300
25 DEFAULT_KEEPALIVE = 10
26
27 _default_pool = None
33
35 """A class that can be instantiated for access to a RESTful resource,
36 including authentication.
37 """
38
39 charset = 'utf-8'
40 encode_keys = True
41 safe = "/:"
42 keepalive = True
43 basic_auth_url = True
44 response_class = HttpResponse
45
47 """Constructor for a `Resource` object.
48
49 Resource represent an HTTP resource.
50
51 :param uri: str, full uri to the server.
52 :param client_opts: `restkit.client.HttpRequest` Options
53 """
54 client_opts = client_opts or {}
55
56 self.initial = dict(
57 uri = uri,
58 client_opts = client_opts.copy()
59 )
60
61
62 if self.response_class is not None and \
63 not 'response_class' in client_opts:
64 client_opts['response_class'] = self.response_class
65
66
67 if not 'pool_instance' in client_opts and self.keepalive:
68 timeout = client_opts.get('timeout') or DEFAULT_TIMEOUT
69 keepalive = client_opts.get('keepalive') or DEFAULT_KEEPALIVE
70 client_opts['pool_instance'] = default_pool(keepalive, timeout)
71
72 self.filters = client_opts.get('filters') or []
73 if self.basic_auth_url:
74
75 u = urlparse.urlparse(uri)
76 if u.username:
77 password = u.password or ""
78
79
80 filters = copy(self.filters)
81 filters.append(BasicAuth(u.username, password))
82 client_opts['filters'] = filters
83
84
85 uri = urlparse.urlunparse((u.scheme, u.netloc.split("@")[-1],
86 u.path, u.params, u.query, u.fragment))
87
88 self.uri = uri
89 self.client_opts = client_opts
90
92 return '<%s %s>' % (self.__class__.__name__, self.uri)
93
95 """if you want to add a path to resource uri, you can do:
96
97 .. code-block:: python
98
99 resr2 = res.clone()
100
101 """
102 obj = self.__class__(self.initial['uri'],
103 **self.initial['client_opts'])
104 return obj
105
107 """if you want to add a path to resource uri, you can do:
108
109 .. code-block:: python
110
111 Resource("/path").get()
112 """
113
114 uri = self.initial['uri']
115
116 new_uri = util.make_uri(uri, path, charset=self.charset,
117 safe=self.safe, encode_keys=self.encode_keys)
118
119 obj = type(self)(new_uri, **self.initial['client_opts'])
120 return obj
121
123 """ Close all the connections related to the resource """
124 pool = self.client_opts.get('pool_instance')
125 if not pool:
126 return
127
128 parsed_url = urlparse.urlparse(self.uri)
129 pool.clear_host(util.parse_netloc(parsed_url))
130
131 - def get(self, path=None, headers=None, params_dict=None, **params):
132 """ HTTP GET
133
134 :param path: string additionnal path to the uri
135 :param headers: dict, optionnal headers that will
136 be added to HTTP request.
137 :param params: Optionnal parameterss added to the request.
138 """
139 return self.request("GET", path=path, headers=headers,
140 params_dict=params_dict, **params)
141
142 - def head(self, path=None, headers=None, params_dict=None, **params):
143 """ HTTP HEAD
144
145 see GET for params description.
146 """
147 return self.request("HEAD", path=path, headers=headers,
148 params_dict=params_dict, **params)
149
150 - def delete(self, path=None, headers=None, params_dict=None, **params):
151 """ HTTP DELETE
152
153 see GET for params description.
154 """
155 return self.request("DELETE", path=path, headers=headers,
156 params_dict=params_dict, **params)
157
158 - def post(self, path=None, payload=None, headers=None,
159 params_dict=None, **params):
160 """ HTTP POST
161
162 :param payload: string passed to the body of the request
163 :param path: string additionnal path to the uri
164 :param headers: dict, optionnal headers that will
165 be added to HTTP request.
166 :param params: Optionnal parameterss added to the request
167 """
168
169 return self.request("POST", path=path, payload=payload,
170 headers=headers, params_dict=params_dict, **params)
171
172 - def put(self, path=None, payload=None, headers=None,
173 params_dict=None, **params):
174 """ HTTP PUT
175
176 see POST for params description.
177 """
178 return self.request("PUT", path=path, payload=payload,
179 headers=headers, params_dict=params_dict, **params)
180
183
186
189
190 - def request(self, method, path=None, payload=None, headers=None,
191 params_dict=None, **params):
192 """ HTTP request
193
194 This method may be the only one you want to override when
195 subclassing `restkit.rest.Resource`.
196
197 :param payload: string or File object passed to the body of the request
198 :param path: string additionnal path to the uri
199 :param headers: dict, optionnal headers that will
200 be added to HTTP request.
201 :params_dict: Options parameters added to the request as a dict
202 :param params: Optionnal parameterss added to the request
203 """
204
205 params = params or {}
206 params.update(params_dict or {})
207
208 while True:
209 uri = util.make_uri(self.uri, path, charset=self.charset,
210 safe=self.safe, encode_keys=self.encode_keys,
211 **self.make_params(params))
212
213
214 http = HttpRequest(**self.client_opts)
215 resp = http.request(uri, method=method, body=payload,
216 headers=self.make_headers(headers))
217
218 if resp is None:
219
220 raise ValueError("Unkown error: response object is None")
221
222 if resp.status_int >= 400:
223 if resp.status_int == 404:
224 raise ResourceNotFound(resp.body_string(),
225 response=resp)
226 elif resp.status_int in (401, 403):
227 if self.unauthorized(resp):
228 raise Unauthorized(resp.body_string(),
229 http_code=resp.status_int,
230 response=resp)
231 else:
232 raise RequestFailed(resp.body_string(),
233 http_code=resp.status_int,
234 response=resp)
235 else:
236 break
237
238 return resp
239
250