Merge lp:~jml/piston-mini-client/split-request-1041825 into lp:piston-mini-client

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
Reviewer Review Type Date Requested Status
software-store-developers Pending
Review via email: mp+121335@code.launchpad.net
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)

Subscribers

People subscribed via source and target branches