comparison env/lib/python3.7/site-packages/bioblend/galaxy/client.py @ 2:6af9afd405e9 draft

"planemo upload commit 0a63dd5f4d38a1f6944587f52a8cd79874177fc1"
author shellac
date Thu, 14 May 2020 14:56:58 -0400
parents 26e78fe6e8c4
children
comparison
equal deleted inserted replaced
1:75ca89e9b81c 2:6af9afd405e9
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 self.url = '/'.join([self.gi.url, self.module])
80
81 def _get(self, id=None, deleted=False, contents=None, url=None,
82 params=None, json=True):
83 """
84 Do a GET request, composing the URL from ``id``, ``deleted`` and
85 ``contents``. Alternatively, an explicit ``url`` can be provided.
86 If ``json`` is set to ``True``, return a decoded JSON object
87 (and treat an empty or undecodable response as an error).
88
89 The request will optionally be retried as configured by
90 ``max_get_retries`` and ``get_retry_delay``: this offers some
91 resilience in the presence of temporary failures.
92
93 :return: The decoded response if ``json`` is set to ``True``, otherwise
94 the response object
95 """
96 if not url:
97 url = self.gi._make_url(self, module_id=id, deleted=deleted,
98 contents=contents)
99 attempts_left = self.max_get_retries()
100 retry_delay = self.get_retry_delay()
101 bioblend.log.debug("GET - attempts left: %s; retry delay: %s",
102 attempts_left, retry_delay)
103 msg = ''
104 while attempts_left > 0:
105 attempts_left -= 1
106 try:
107 r = self.gi.make_get_request(url, params=params)
108 except (requests.exceptions.ConnectionError, ProtocolError) as e:
109 msg = str(e)
110 r = requests.Response() # empty Response object used when raising ConnectionError
111 else:
112 if r.status_code == 200:
113 if not json:
114 return r
115 elif not r.content:
116 msg = "GET: empty response"
117 else:
118 try:
119 return r.json()
120 except ValueError:
121 msg = "GET: invalid JSON : %r" % (r.content,)
122 else:
123 msg = "GET: error %s: %r" % (r.status_code, r.content)
124 msg = "%s, %d attempts left" % (msg, attempts_left)
125 if attempts_left <= 0:
126 bioblend.log.error(msg)
127 raise ConnectionError(msg, body=r.text,
128 status_code=r.status_code)
129 else:
130 bioblend.log.warning(msg)
131 time.sleep(retry_delay)
132
133 def _post(self, payload, id=None, deleted=False, contents=None, url=None,
134 files_attached=False):
135 """
136 Do a generic POST request, composing the url from the contents of the
137 arguments. Alternatively, an explicit ``url`` can be provided to use
138 for the request. ``payload`` must be a dict that contains additional
139 request arguments which will be sent along with the request body.
140 The payload dict may contain file handles (in which case the
141 ``files_attached`` flag must be set to true).
142
143 If ``files_attached`` is set to ``False``, the request body will be
144 JSON-encoded; otherwise, it will be encoded as multipart/form-data.
145
146 :return: The decoded response.
147 """
148 if not url:
149 url = self.gi._make_url(self, module_id=id, deleted=deleted,
150 contents=contents)
151 return self.gi.make_post_request(url, payload=payload,
152 files_attached=files_attached)
153
154 def _put(self, payload, id=None, url=None, params=None):
155 """
156 Do a generic PUT request, composing the url from the contents of the
157 arguments. Alternatively, an explicit ``url`` can be provided to use
158 for the request. ``payload`` must be a dict that contains additional
159 request arguments which will be sent along with the request body.
160
161 :return: The decoded response.
162 """
163 if not url:
164 url = self.gi._make_url(self, module_id=id)
165 return self.gi.make_put_request(url, payload=payload, params=params)
166
167 def _patch(self, payload, id=None, url=None, params=None):
168 """
169 Do a generic PATCH request, composing the url from the contents of the
170 arguments. Alternatively, an explicit ``url`` can be provided to use
171 for the request. ``payload`` must be a dict that contains additional
172 request arguments which will be sent along with the request body.
173
174 :return: The decoded response.
175 """
176 if not url:
177 url = self.gi._make_url(self, module_id=id)
178 return self.gi.make_patch_request(url, payload=payload, params=params)
179
180 def _delete(self, payload=None, id=None, deleted=False, contents=None, url=None, params=None):
181 """
182 Do a generic DELETE request, composing the url from the contents of the
183 arguments. Alternatively, an explicit ``url`` can be provided to use
184 for the request. ``payload`` must be a dict that contains additional
185 request arguments which will be sent along with the request body.
186
187 :return: The decoded response.
188 """
189 if not url:
190 url = self.gi._make_url(self, module_id=id, deleted=deleted,
191 contents=contents)
192 r = self.gi.make_delete_request(url, payload=payload, params=params)
193 if r.status_code == 200:
194 return r.json()
195 # @see self.body for HTTP response body
196 raise ConnectionError("Unexpected HTTP status code: %s" % r.status_code,
197 body=r.text, status_code=r.status_code)