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)