Merge lp:~jml/piston-mini-client/split-request-1041825 into lp:piston-mini-client
- split-request-1041825
- Merge into trunk
Proposed by
Jonathan Lange
Status: | Work in progress | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~jml/piston-mini-client/split-request-1041825 | ||||
Merge into: | lp:piston-mini-client | ||||
Diff against target: |
347 lines (+209/-47) (has conflicts) 1 file modified
piston_mini_client/__init__.py (+209/-47) Text conflict in piston_mini_client/__init__.py |
||||
To merge this branch: | bzr merge lp:~jml/piston-mini-client/split-request-1041825 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
software-store-developers | Pending | ||
Review via email: mp+121335@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Unmerged revisions
- 60. By Jonathan Lange
-
RED: Roughly hew a PistonRequester out of PistonAPI
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'piston_mini_client/__init__.py' |
2 | --- piston_mini_client/__init__.py 2012-07-30 17:09:26 +0000 |
3 | +++ piston_mini_client/__init__.py 2012-08-26 15:27:21 +0000 |
4 | @@ -225,36 +225,23 @@ |
5 | return self.as_serializable() |
6 | |
7 | |
8 | -class PistonAPI(object): |
9 | +class PistonRequester(object): |
10 | """This class provides methods to make http requests slightly easier. |
11 | |
12 | - It's a wrapper around ``httplib2`` to allow for a bit of state to |
13 | - be stored (like the service root) so that you don't need to repeat |
14 | - yourself as much. |
15 | - |
16 | - It's not intended to be used directly. Children classes should implement |
17 | - methods that actually call out to the api methods. |
18 | - |
19 | - When you define your API's methods you'll |
20 | - want to just call out to the ``_get``, ``_post``, ``_put`` or ``_delete`` |
21 | - methods provided by this class. |
22 | + It's a wrapper around ``httplib2`` to allow for a bit of state to be |
23 | + stored so that you don't need to repeat yourself as much. |
24 | """ |
25 | + |
26 | SUPPORTED_SCHEMAS = ("http", "https") |
27 | - default_service_root = '' |
28 | - default_content_type = 'application/json' |
29 | default_timeout = None |
30 | fail_handler = ExceptionFailHandler |
31 | extra_headers = None |
32 | serializers = None |
33 | |
34 | - def __init__(self, service_root=None, cachedir=None, auth=None, |
35 | - offline_mode=False, disable_ssl_validation=False, |
36 | - log_filename=None, timeout=None): |
37 | - """Initialize a ``PistonAPI``. |
38 | - |
39 | - ``service_root`` is the url to the server's service root. |
40 | - Children classes can provide a ``default_service_root`` class |
41 | - attribute that will be used if ``service_root`` is ``None``. |
42 | + def __init__(self, cachedir=None, auth=None, offline_mode=False, |
43 | + disable_ssl_validation=False, log_filename=None, |
44 | + timeout=None): |
45 | + """Initialize a ``PistonRequest``. |
46 | |
47 | ``cachedir`` will be used as ``httplib2``'s cache directory if |
48 | provided. |
49 | @@ -284,16 +271,6 @@ |
50 | default is also None, Python's default timeout for sockets will be |
51 | used. All these should be in seconds. |
52 | """ |
53 | - if service_root is None: |
54 | - service_root = self.default_service_root |
55 | - if not service_root: |
56 | - raise ValueError("No service_root provided, and no default found") |
57 | - parsed_service_root = urlparse(service_root) |
58 | - scheme = parsed_service_root.scheme |
59 | - if scheme not in self.SUPPORTED_SCHEMAS: |
60 | - raise ValueError("service_root's scheme must be http or https") |
61 | - self._service_root = service_root |
62 | - self._parsed_service_root = list(parsed_service_root) |
63 | if cachedir: |
64 | self._create_dir_if_needed(cachedir) |
65 | self._httplib2_cache = httplib2.FileCache(cachedir, safe=safename) |
66 | @@ -372,7 +349,7 @@ |
67 | return proxy_info |
68 | return None |
69 | |
70 | - def _get(self, path, args=None, scheme=None, extra_headers=None): |
71 | + def get(self, path, args=None, scheme=None, extra_headers=None): |
72 | """Perform an HTTP GET request. |
73 | |
74 | The provided ``path`` is appended to this resource's ``_service_root`` |
75 | @@ -398,8 +375,13 @@ |
76 | return self._request(path, method='GET', scheme=scheme, |
77 | headers=headers) |
78 | |
79 | +<<<<<<< TREE |
80 | def _post(self, path, data=None, content_type=None, scheme=None, |
81 | extra_headers=None): |
82 | +======= |
83 | + def post(self, path, data=None, content_type=None, scheme=None, |
84 | + extra_headers=None): |
85 | +>>>>>>> MERGE-SOURCE |
86 | """Perform an HTTP POST request. |
87 | |
88 | The provided ``path`` is appended to this api's ``_service_root`` |
89 | @@ -428,8 +410,13 @@ |
90 | return self._request(path, method='POST', body=body, |
91 | headers=headers, scheme=scheme) |
92 | |
93 | +<<<<<<< TREE |
94 | def _put(self, path, data=None, content_type=None, scheme=None, |
95 | extra_headers=None): |
96 | +======= |
97 | + def put(self, path, data=None, content_type=None, scheme=None, |
98 | + extra_headers=None): |
99 | +>>>>>>> MERGE-SOURCE |
100 | """Perform an HTTP PUT request. |
101 | |
102 | The provided ``path`` is appended to this api's ``_service_root`` |
103 | @@ -458,7 +445,7 @@ |
104 | return self._request(path, method='PUT', body=body, |
105 | headers=headers, scheme=scheme) |
106 | |
107 | - def _delete(self, path, scheme=None, extra_headers=None): |
108 | + def delete(self, path, scheme=None, extra_headers=None): |
109 | """Perform an HTTP DELETE request. |
110 | |
111 | The provided ``path`` is appended to this resource's ``_service_root`` |
112 | @@ -523,7 +510,7 @@ |
113 | body = serializer.serialize(data) |
114 | return body |
115 | |
116 | - def _request(self, path, method, body='', headers=None, scheme=None): |
117 | + def _request(self, url, method, body='', headers=None, scheme=None): |
118 | """Perform an HTTP request. |
119 | |
120 | You probably want to call one of the ``_get``, ``_post``, ``_put`` |
121 | @@ -531,9 +518,10 @@ |
122 | """ |
123 | if headers is None: |
124 | headers = {} |
125 | - if scheme not in [None, 'http', 'https']: |
126 | + if scheme is None: |
127 | + scheme = urlparse(url).scheme |
128 | + if scheme not in self.SUPPORTED_SCHEMAS: |
129 | raise ValueError('Invalid scheme %s' % scheme) |
130 | - url = self._path2url(path, scheme=scheme) |
131 | |
132 | # in offline mode either get it from the cache or return None |
133 | if self._offline_mode: |
134 | @@ -542,9 +530,6 @@ |
135 | raise OfflineModeException(err) |
136 | return self._get_from_cache(url) |
137 | |
138 | - if scheme is None: |
139 | - scheme = urlparse(url).scheme |
140 | - |
141 | if self._auth: |
142 | self._auth.sign_request(url, method, body, headers) |
143 | if self.log_filename: |
144 | @@ -599,6 +584,191 @@ |
145 | '\r\n\r\n', 1) |
146 | return content |
147 | |
148 | + def _get_serializer(self, content_type=None): |
149 | + # Import here to avoid a circular import |
150 | + from piston_mini_client.serializers import get_serializer |
151 | + if content_type is None: |
152 | + content_type = self.default_content_type |
153 | + default_serializer = get_serializer(content_type) |
154 | + return self.serializers.get(content_type, default_serializer) |
155 | + |
156 | + |
157 | +class PistonAPI(object): |
158 | + """This class provides methods to make http requests slightly easier. |
159 | + |
160 | + It's a wrapper around ``httplib2`` to allow for a bit of state to |
161 | + be stored (like the service root) so that you don't need to repeat |
162 | + yourself as much. |
163 | + |
164 | + It's not intended to be used directly. Children classes should implement |
165 | + methods that actually call out to the api methods. |
166 | + |
167 | + When you define your API's methods you'll |
168 | + want to just call out to the ``_get``, ``_post``, ``_put`` or ``_delete`` |
169 | + methods provided by this class. |
170 | + """ |
171 | + SUPPORTED_SCHEMAS = ("http", "https") |
172 | + default_service_root = '' |
173 | + default_content_type = 'application/json' |
174 | + default_timeout = None |
175 | + fail_handler = ExceptionFailHandler |
176 | + extra_headers = None |
177 | + serializers = None |
178 | + |
179 | + def __init__(self, service_root=None, cachedir=None, auth=None, |
180 | + offline_mode=False, disable_ssl_validation=False, |
181 | + log_filename=None, timeout=None): |
182 | + """Initialize a ``PistonAPI``. |
183 | + |
184 | + ``service_root`` is the url to the server's service root. |
185 | + Children classes can provide a ``default_service_root`` class |
186 | + attribute that will be used if ``service_root`` is ``None``. |
187 | + |
188 | + ``cachedir`` will be used as ``httplib2``'s cache directory if |
189 | + provided. |
190 | + |
191 | + ``auth`` can be an instance of ``BasicAuthorizer`` or |
192 | + ``OAuthAuthorizer`` or any object that provides a ``sign_request`` |
193 | + method. If ``auth`` is ``None`` you'll only be able to make public |
194 | + API calls. See :ref:`authentication` for details. |
195 | + |
196 | + ``disable_ssl_validation`` will skip server SSL certificate |
197 | + validation when using secure connections. ``httplib2`` < 0.7.0 |
198 | + doesn't support certificate validation anyway, so if you're using an |
199 | + older ``httplib2`` this will have no effect. |
200 | + |
201 | + ``offline_mode`` will not touch the network. In this case only cached |
202 | + results will be available. |
203 | + |
204 | + If you pass in a ``log_filename``, all requests and responses |
205 | + including headers will be logged to this file. |
206 | + |
207 | + ``timeout`` will be used as a socket timeout for all calls this |
208 | + instance makes. To explicitly set no timeout, set timeout=0. The |
209 | + default timeout=None will first check for an environment variable |
210 | + ``PISTON_MINI_CLIENT_DEFAULT_TIMEOUT`` and try to use that. If this |
211 | + environment variable is not found or it is an invalid float, the |
212 | + class's ``default_timeout`` will be used. Finally, if the class's |
213 | + default is also None, Python's default timeout for sockets will be |
214 | + used. All these should be in seconds. |
215 | + """ |
216 | + if service_root is None: |
217 | + service_root = self.default_service_root |
218 | + if not service_root: |
219 | + raise ValueError("No service_root provided, and no default found") |
220 | + parsed_service_root = urlparse(service_root) |
221 | + scheme = parsed_service_root.scheme |
222 | + if scheme not in self.SUPPORTED_SCHEMAS: |
223 | + raise ValueError("service_root's scheme must be http or https") |
224 | + self._service_root = service_root |
225 | + self._parsed_service_root = list(parsed_service_root) |
226 | + self._requester = PistonRequester( |
227 | + cachedir, auth, offline_mode, disable_ssl_validation, |
228 | + log_filename, timeout) |
229 | + |
230 | + def _get(self, path, args=None, scheme=None, extra_headers=None): |
231 | + """Perform an HTTP GET request. |
232 | + |
233 | + The provided ``path`` is appended to this resource's ``_service_root`` |
234 | + attribute to obtain the absolute URL that will be requested. |
235 | + |
236 | + If provided, ``args`` should be a dict specifying additional GET |
237 | + arguments that will be encoded on to the end of the url. |
238 | + |
239 | + ``scheme`` must be one of *http* or *https*, and will determine the |
240 | + scheme used for this particular request. If not provided the |
241 | + service_root's scheme will be used. |
242 | + |
243 | + ``extra_headers`` is an optional dictionary of header key/values that |
244 | + will be added to the http request. |
245 | + """ |
246 | + if args is not None: |
247 | + if '?' in path: |
248 | + path += '&' |
249 | + else: |
250 | + path += '?' |
251 | + path += urlencode(args) |
252 | + headers = self._prepare_headers(extra_headers=extra_headers) |
253 | + return self._request(path, method='GET', scheme=scheme, |
254 | + headers=headers) |
255 | + |
256 | + def _post(self, path, data=None, content_type=None, scheme=None, |
257 | + extra_headers=None): |
258 | + """Perform an HTTP POST request. |
259 | + |
260 | + The provided ``path`` is appended to this api's ``_service_root`` |
261 | + attribute to obtain the absolute URL that will be requested. ``data`` |
262 | + should be: |
263 | + |
264 | + - A string, in which case it will be used directly as the request's |
265 | + body, or |
266 | + - A ``list``, ``dict``, ``int``, ``bool`` or ``PistonSerializable`` |
267 | + (something with an ``as_serializable`` method) or even ``None``, |
268 | + in which case it will be serialized into a string according to |
269 | + ``content_type``. |
270 | + |
271 | + If ``content_type`` is ``None``, ``self.default_content_type`` will |
272 | + be used. |
273 | + |
274 | + ``scheme`` must be one of *http* or *https*, and will determine the |
275 | + scheme used for this particular request. If not provided the |
276 | + service_root's scheme will be used. |
277 | + |
278 | + ``extra_headers`` is an optional dictionary of header key/values that |
279 | + will be added to the http request. |
280 | + """ |
281 | + body, headers = self._prepare_request(data, content_type, |
282 | + extra_headers=extra_headers) |
283 | + return self._request(path, method='POST', body=body, |
284 | + headers=headers, scheme=scheme) |
285 | + |
286 | + def _put(self, path, data=None, content_type=None, scheme=None, |
287 | + extra_headers=None): |
288 | + """Perform an HTTP PUT request. |
289 | + |
290 | + The provided ``path`` is appended to this api's ``_service_root`` |
291 | + attribute to obtain the absolute URL that will be requested. ``data`` |
292 | + should be: |
293 | + |
294 | + - A string, in which case it will be used directly as the request's |
295 | + body, or |
296 | + - A ``list``, ``dict``, ``int``, ``bool`` or ``PistonSerializable`` |
297 | + (something with an ``as_serializable`` method) or even ``None``, |
298 | + in which case it will be serialized into a string according to |
299 | + ``content_type``. |
300 | + |
301 | + If ``content_type`` is ``None``, ``self.default_content_type`` will be |
302 | + used. |
303 | + |
304 | + ``scheme`` must be one of *http* or *https*, and will determine the |
305 | + scheme used for this particular request. If not provided the |
306 | + service_root's scheme will be used. |
307 | + |
308 | + ``extra_headers`` is an optional dictionary of header key/values that |
309 | + will be added to the http request. |
310 | + """ |
311 | + body, headers = self._prepare_request(data, content_type, |
312 | + extra_headers=extra_headers) |
313 | + return self._request(path, method='PUT', body=body, |
314 | + headers=headers, scheme=scheme) |
315 | + |
316 | + def _delete(self, path, scheme=None, extra_headers=None): |
317 | + """Perform an HTTP DELETE request. |
318 | + |
319 | + The provided ``path`` is appended to this resource's ``_service_root`` |
320 | + attribute to obtain the absolute URL that will be requested. |
321 | + |
322 | + ``scheme`` must be one of *http* or *https*, and will determine the |
323 | + scheme used for this particular request. If not provided the |
324 | + service_root's scheme will be used. |
325 | + |
326 | + ``extra_headers`` is an optional dictionary of header key/values that |
327 | + will be added to the http request. |
328 | + """ |
329 | + headers = self._prepare_headers(extra_headers=extra_headers) |
330 | + return self._request(path, method='DELETE', scheme=scheme, |
331 | + headers=headers) |
332 | + |
333 | def _path2url(self, path, scheme=None): |
334 | if scheme is None: |
335 | service_root = self._service_root |
336 | @@ -606,11 +776,3 @@ |
337 | parts = [scheme] + self._parsed_service_root[1:] |
338 | service_root = urlunparse(parts) |
339 | return (service_root.strip('/') + '/' + path.lstrip('/')) |
340 | - |
341 | - def _get_serializer(self, content_type=None): |
342 | - # Import here to avoid a circular import |
343 | - from piston_mini_client.serializers import get_serializer |
344 | - if content_type is None: |
345 | - content_type = self.default_content_type |
346 | - default_serializer = get_serializer(content_type) |
347 | - return self.serializers.get(content_type, default_serializer) |