|
23 | 23 | from configparser import SafeConfigParser |
24 | 24 | from http.cookiejar import LoadError, LWPCookieJar, MozillaCookieJar |
25 | 25 | from urllib.parse import urlparse, parse_qsl |
26 | | - from xmlrpc.client import ( |
27 | | - Binary, Fault, ProtocolError, ServerProxy, Transport) |
| 26 | + from xmlrpc.client import Binary, Fault |
28 | 27 | else: |
29 | 28 | from ConfigParser import SafeConfigParser |
30 | 29 | from cookielib import LoadError, LWPCookieJar, MozillaCookieJar |
31 | 30 | from urlparse import urlparse, parse_qsl |
32 | | - from xmlrpclib import ( |
33 | | - Binary, Fault, ProtocolError, ServerProxy, Transport) |
| 31 | + from xmlrpclib import Binary, Fault |
34 | 32 |
|
35 | | -import requests |
36 | 33 |
|
37 | 34 | from .apiversion import __version__ |
38 | 35 | from .bug import _Bug, _User |
| 36 | +from .transport import BugzillaError, _BugzillaServerProxy, _RequestsTransport |
| 37 | + |
39 | 38 |
|
40 | 39 | log = getLogger(__name__) |
41 | 40 |
|
@@ -110,166 +109,6 @@ def _build_cookiejar(cookiefile): |
110 | 109 | return retcj |
111 | 110 |
|
112 | 111 |
|
113 | | -class _BugzillaToken(object): |
114 | | - def __init__(self, uri, tokenfilename): |
115 | | - self.tokenfilename = tokenfilename |
116 | | - self.tokenfile = SafeConfigParser() |
117 | | - self.domain = urlparse(uri)[1] |
118 | | - |
119 | | - if self.tokenfilename: |
120 | | - self.tokenfile.read(self.tokenfilename) |
121 | | - |
122 | | - if self.domain not in self.tokenfile.sections(): |
123 | | - self.tokenfile.add_section(self.domain) |
124 | | - |
125 | | - @property |
126 | | - def value(self): |
127 | | - if self.tokenfile.has_option(self.domain, 'token'): |
128 | | - return self.tokenfile.get(self.domain, 'token') |
129 | | - else: |
130 | | - return None |
131 | | - |
132 | | - @value.setter |
133 | | - def value(self, value): |
134 | | - if self.value == value: |
135 | | - return |
136 | | - |
137 | | - if value is None: |
138 | | - self.tokenfile.remove_option(self.domain, 'token') |
139 | | - else: |
140 | | - self.tokenfile.set(self.domain, 'token', value) |
141 | | - |
142 | | - if self.tokenfilename: |
143 | | - with open(self.tokenfilename, 'w') as tokenfile: |
144 | | - log.debug("Saving to tokenfile") |
145 | | - self.tokenfile.write(tokenfile) |
146 | | - |
147 | | - def __repr__(self): |
148 | | - return '<Bugzilla Token :: %s>' % (self.value) |
149 | | - |
150 | | - |
151 | | -class _BugzillaServerProxy(ServerProxy): |
152 | | - def __init__(self, uri, tokenfile, *args, **kwargs): |
153 | | - # pylint: disable=super-init-not-called |
154 | | - # No idea why pylint complains here, must be a bug |
155 | | - ServerProxy.__init__(self, uri, *args, **kwargs) |
156 | | - self.token = _BugzillaToken(uri, tokenfile) |
157 | | - |
158 | | - def clear_token(self): |
159 | | - self.token.value = None |
160 | | - |
161 | | - def _ServerProxy__request(self, methodname, params): |
162 | | - if self.token.value is not None: |
163 | | - if len(params) == 0: |
164 | | - params = ({}, ) |
165 | | - |
166 | | - if 'Bugzilla_token' not in params[0]: |
167 | | - params[0]['Bugzilla_token'] = self.token.value |
168 | | - |
169 | | - # pylint: disable=maybe-no-member |
170 | | - ret = ServerProxy._ServerProxy__request(self, methodname, params) |
171 | | - # pylint: enable=maybe-no-member |
172 | | - |
173 | | - if isinstance(ret, dict) and 'token' in ret.keys(): |
174 | | - self.token.value = ret.get('token') |
175 | | - return ret |
176 | | - |
177 | | - |
178 | | -class RequestsTransport(Transport): |
179 | | - user_agent = 'Python/Bugzilla' |
180 | | - |
181 | | - def __init__(self, url, cookiejar=None, |
182 | | - sslverify=True, sslcafile=None, debug=0): |
183 | | - # pylint: disable=W0231 |
184 | | - # pylint does not handle multiple import of Transport well |
185 | | - if hasattr(Transport, "__init__"): |
186 | | - Transport.__init__(self, use_datetime=False) |
187 | | - |
188 | | - self.verbose = debug |
189 | | - self._cookiejar = cookiejar |
190 | | - |
191 | | - # transport constructor needs full url too, as xmlrpc does not pass |
192 | | - # scheme to request |
193 | | - self.scheme = urlparse(url)[0] |
194 | | - if self.scheme not in ["http", "https"]: |
195 | | - raise Exception("Invalid URL scheme: %s (%s)" % (self.scheme, url)) |
196 | | - |
197 | | - self.use_https = self.scheme == 'https' |
198 | | - |
199 | | - self.request_defaults = { |
200 | | - 'cert': sslcafile if self.use_https else None, |
201 | | - 'cookies': cookiejar, |
202 | | - 'verify': sslverify, |
203 | | - 'headers': { |
204 | | - 'Content-Type': 'text/xml', |
205 | | - 'User-Agent': self.user_agent, |
206 | | - } |
207 | | - } |
208 | | - |
209 | | - # Using an explicit Session, rather than requests.get, will use |
210 | | - # HTTP KeepAlive if the server supports it. |
211 | | - self.session = requests.Session() |
212 | | - |
213 | | - def parse_response(self, response): |
214 | | - """ Parse XMLRPC response """ |
215 | | - parser, unmarshaller = self.getparser() |
216 | | - parser.feed(response.text.encode('utf-8')) |
217 | | - parser.close() |
218 | | - return unmarshaller.close() |
219 | | - |
220 | | - def _request_helper(self, url, request_body): |
221 | | - """ |
222 | | - A helper method to assist in making a request and provide a parsed |
223 | | - response. |
224 | | - """ |
225 | | - response = None |
226 | | - try: |
227 | | - response = self.session.post( |
228 | | - url, data=request_body, **self.request_defaults) |
229 | | - |
230 | | - # We expect utf-8 from the server |
231 | | - response.encoding = 'UTF-8' |
232 | | - |
233 | | - # update/set any cookies |
234 | | - if self._cookiejar is not None: |
235 | | - for cookie in response.cookies: |
236 | | - self._cookiejar.set_cookie(cookie) |
237 | | - |
238 | | - if self._cookiejar.filename is not None: |
239 | | - # Save is required only if we have a filename |
240 | | - self._cookiejar.save() |
241 | | - |
242 | | - response.raise_for_status() |
243 | | - return self.parse_response(response) |
244 | | - except requests.RequestException: |
245 | | - e = sys.exc_info()[1] |
246 | | - if not response: |
247 | | - raise |
248 | | - raise ProtocolError( |
249 | | - url, response.status_code, str(e), response.headers) |
250 | | - except Fault: |
251 | | - raise sys.exc_info()[1] |
252 | | - except Exception: |
253 | | - # pylint: disable=W0201 |
254 | | - e = BugzillaError(str(sys.exc_info()[1])) |
255 | | - e.__traceback__ = sys.exc_info()[2] |
256 | | - raise e |
257 | | - |
258 | | - def request(self, host, handler, request_body, verbose=0): |
259 | | - self.verbose = verbose |
260 | | - url = "%s://%s%s" % (self.scheme, host, handler) |
261 | | - |
262 | | - # xmlrpclib fails to escape \r |
263 | | - request_body = request_body.replace(b'\r', b'
') |
264 | | - |
265 | | - return self._request_helper(url, request_body) |
266 | | - |
267 | | - |
268 | | -class BugzillaError(Exception): |
269 | | - '''Error raised in the Bugzilla client code.''' |
270 | | - pass |
271 | | - |
272 | | - |
273 | 112 | class _FieldAlias(object): |
274 | 113 | """ |
275 | 114 | Track API attribute names that differ from what we expose in users. |
@@ -616,7 +455,7 @@ def connect(self, url=None): |
616 | 455 | url = self.url |
617 | 456 | url = self.fix_url(url) |
618 | 457 |
|
619 | | - self._transport = RequestsTransport( |
| 458 | + self._transport = _RequestsTransport( |
620 | 459 | url, self._cookiejar, sslverify=self._sslverify) |
621 | 460 | self._transport.user_agent = self.user_agent |
622 | 461 | self._proxy = _BugzillaServerProxy(url, self.tokenfile, |
|
0 commit comments