Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/bioblend/galaxy/client.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:18:57 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d30785e31577 |
---|---|
1 """ | |
2 An interface the clients should implement. | |
3 | |
4 This class is primarily a helper for the library and user code | |
5 should not use it directly. | |
6 """ | |
7 | |
8 import time | |
9 | |
10 import requests | |
11 from requests.packages.urllib3.exceptions import ProtocolError | |
12 | |
13 import bioblend | |
14 # The following import must be preserved for compatibility because | |
15 # ConnectionError class was originally defined here | |
16 from bioblend import ConnectionError # noqa: I202 | |
17 | |
18 | |
19 class Client(object): | |
20 | |
21 # Class variables that configure GET request retries. Note that since these | |
22 # are class variables their values are shared by all Client instances -- | |
23 # i.e., HistoryClient, WorkflowClient, etc. | |
24 # | |
25 # Number of attempts before giving up on a GET request. | |
26 _max_get_retries = 1 | |
27 # Delay in seconds between subsequent retries. | |
28 _get_retry_delay = 10 | |
29 | |
30 @classmethod | |
31 def max_get_retries(cls): | |
32 """ | |
33 The maximum number of attempts for a GET request. | |
34 """ | |
35 return cls._max_get_retries | |
36 | |
37 @classmethod | |
38 def set_max_get_retries(cls, value): | |
39 """ | |
40 Set the maximum number of attempts for GET requests. A value greater | |
41 than one causes failed GET requests to be retried `value` - 1 times. | |
42 | |
43 Default: 1 | |
44 """ | |
45 if value < 1: | |
46 raise ValueError("Number of retries must be >= 1 (got: %s)" % value) | |
47 cls._max_get_retries = value | |
48 return cls | |
49 | |
50 @classmethod | |
51 def get_retry_delay(cls): | |
52 """ | |
53 The delay (in seconds) to wait before retrying a failed GET | |
54 request. | |
55 """ | |
56 return cls._get_retry_delay | |
57 | |
58 @classmethod | |
59 def set_get_retry_delay(cls, value): | |
60 """ | |
61 Set the delay (in seconds) to wait before retrying a failed GET | |
62 request. Default: 10 | |
63 """ | |
64 if value < 0: | |
65 raise ValueError("Retry delay must be >= 0 (got: %s)" % value) | |
66 cls._get_retry_delay = value | |
67 return cls | |
68 | |
69 def __init__(self, galaxy_instance): | |
70 """ | |
71 A generic Client interface defining the common fields. | |
72 | |
73 All clients *must* define the following field (which will be | |
74 used as part of the URL composition (e.g., | |
75 ``http://<galaxy_instance>/api/libraries``): ``self.module = | |
76 'workflows' | 'libraries' | 'histories' | ...`` | |
77 """ | |
78 self.gi = galaxy_instance | |
79 | |
80 def _make_url(self, module_id=None, deleted=False, contents=False): | |
81 """ | |
82 Compose a URL based on the provided arguments. | |
83 | |
84 :type module_id: str | |
85 :param module_id: The encoded ID for a specific module (eg, library ID) | |
86 | |
87 :type deleted: bool | |
88 :param deleted: If ``True``, include ``deleted`` in the URL, after the module | |
89 name (eg, ``<base_url>/api/libraries/deleted``) | |
90 | |
91 :type contents: bool | |
92 :param contents: If ``True``, include 'contents' in the URL, after the module ID: | |
93 ``<base_url>/api/libraries/<encoded_library_id>/contents`` | |
94 """ | |
95 c_url = '/'.join((self.gi.url, self.module)) | |
96 if deleted is True: | |
97 c_url = c_url + '/deleted' | |
98 if module_id is not None: | |
99 c_url = '/'.join((c_url, module_id)) | |
100 if contents is True: | |
101 c_url = c_url + '/contents' | |
102 return c_url | |
103 | |
104 def _get(self, id=None, deleted=False, contents=None, url=None, | |
105 params=None, json=True): | |
106 """ | |
107 Do a GET request, composing the URL from ``id``, ``deleted`` and | |
108 ``contents``. Alternatively, an explicit ``url`` can be provided. | |
109 If ``json`` is set to ``True``, return a decoded JSON object | |
110 (and treat an empty or undecodable response as an error). | |
111 | |
112 The request will optionally be retried as configured by | |
113 ``max_get_retries`` and ``get_retry_delay``: this offers some | |
114 resilience in the presence of temporary failures. | |
115 | |
116 :return: The decoded response if ``json`` is set to ``True``, otherwise | |
117 the response object | |
118 """ | |
119 if not url: | |
120 url = self._make_url(module_id=id, deleted=deleted, contents=contents) | |
121 attempts_left = self.max_get_retries() | |
122 retry_delay = self.get_retry_delay() | |
123 bioblend.log.debug("GET - attempts left: %s; retry delay: %s", | |
124 attempts_left, retry_delay) | |
125 msg = '' | |
126 while attempts_left > 0: | |
127 attempts_left -= 1 | |
128 try: | |
129 r = self.gi.make_get_request(url, params=params) | |
130 except (requests.exceptions.ConnectionError, ProtocolError) as e: | |
131 msg = str(e) | |
132 r = requests.Response() # empty Response object used when raising ConnectionError | |
133 else: | |
134 if r.status_code == 200: | |
135 if not json: | |
136 return r | |
137 elif not r.content: | |
138 msg = "GET: empty response" | |
139 else: | |
140 try: | |
141 return r.json() | |
142 except ValueError: | |
143 msg = "GET: invalid JSON : %r" % (r.content,) | |
144 else: | |
145 msg = "GET: error %s: %r" % (r.status_code, r.content) | |
146 msg = "%s, %d attempts left" % (msg, attempts_left) | |
147 if attempts_left <= 0: | |
148 bioblend.log.error(msg) | |
149 raise ConnectionError(msg, body=r.text, | |
150 status_code=r.status_code) | |
151 else: | |
152 bioblend.log.warning(msg) | |
153 time.sleep(retry_delay) | |
154 | |
155 def _post(self, payload, id=None, deleted=False, contents=None, url=None, | |
156 files_attached=False): | |
157 """ | |
158 Do a generic POST request, composing the url from the contents of the | |
159 arguments. Alternatively, an explicit ``url`` can be provided to use | |
160 for the request. ``payload`` must be a dict that contains additional | |
161 request arguments which will be sent along with the request body. | |
162 The payload dict may contain file handles (in which case the | |
163 ``files_attached`` flag must be set to true). | |
164 | |
165 If ``files_attached`` is set to ``False``, the request body will be | |
166 JSON-encoded; otherwise, it will be encoded as multipart/form-data. | |
167 | |
168 :return: The decoded response. | |
169 """ | |
170 if not url: | |
171 url = self._make_url(module_id=id, deleted=deleted, contents=contents) | |
172 return self.gi.make_post_request(url, payload=payload, | |
173 files_attached=files_attached) | |
174 | |
175 def _put(self, payload, id=None, url=None, params=None): | |
176 """ | |
177 Do a generic PUT request, composing the url from the contents of the | |
178 arguments. Alternatively, an explicit ``url`` can be provided to use | |
179 for the request. ``payload`` must be a dict that contains additional | |
180 request arguments which will be sent along with the request body. | |
181 | |
182 :return: The decoded response. | |
183 """ | |
184 if not url: | |
185 url = self._make_url(module_id=id) | |
186 return self.gi.make_put_request(url, payload=payload, params=params) | |
187 | |
188 def _patch(self, payload, id=None, url=None, params=None): | |
189 """ | |
190 Do a generic PATCH request, composing the url from the contents of the | |
191 arguments. Alternatively, an explicit ``url`` can be provided to use | |
192 for the request. ``payload`` must be a dict that contains additional | |
193 request arguments which will be sent along with the request body. | |
194 | |
195 :return: The decoded response. | |
196 """ | |
197 if not url: | |
198 url = self._make_url(module_id=id) | |
199 return self.gi.make_patch_request(url, payload=payload, params=params) | |
200 | |
201 def _delete(self, payload=None, id=None, deleted=False, contents=None, url=None, params=None): | |
202 """ | |
203 Do a generic DELETE request, composing the url from the contents of the | |
204 arguments. Alternatively, an explicit ``url`` can be provided to use | |
205 for the request. ``payload`` must be a dict that contains additional | |
206 request arguments which will be sent along with the request body. | |
207 | |
208 :return: The decoded response. | |
209 """ | |
210 if not url: | |
211 url = self._make_url(module_id=id, deleted=deleted, contents=contents) | |
212 r = self.gi.make_delete_request(url, payload=payload, params=params) | |
213 if r.status_code == 200: | |
214 return r.json() | |
215 # @see self.body for HTTP response body | |
216 raise ConnectionError("Unexpected HTTP status code: %s" % r.status_code, | |
217 body=r.text, status_code=r.status_code) |