GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — v1 ( b4dcfa...110339 )
by Maksim
01:36
created

NetworkClient.get()   A

Complexity

Conditions 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
c 3
b 0
f 0
dl 0
loc 13
rs 9.4285
ccs 9
cts 9
cp 1
crap 2
1
# -*- coding: utf-8 -*-
2
3 1
"""
4
Internal module, provides HTTP access to API
5
"""
6
7 1
import re
8 1
import requests
9 1
import platform
10 1
import logging
11
12 1
from .errors import Route4MeNetworkError
13 1
from .errors import Route4MeApiError
14
15 1
from .version import VERSION_STRING
16 1
from .version import RELEASE_STRING
17 1
from .version import BUILD
18 1
from .version import COMMIT
19
20 1
log = logging.getLogger(__name__)
21
22
23 1
class FluentRequest(object):
24 1
	def __init__(self):
25 1
		self._r = requests.Request(
26
			method='GET'
27
		)
28 1
		self.__timeout = 0.1
29
30
		# self.method = method
31
		# self.url = url
32
		# self.headers = headers
33
		# self.files = files
34
		# self.data = data
35
		# self.json = json
36
		# self.params = params
37
		# self.auth = auth
38
		# self.cookies = cookies
39
40 1
	@staticmethod
41
	def __cut_qs(url):
42 1
		return re.sub(r'\?.*', '?***', url)
43
44 1
	def timeout(self, timeout):
45 1
		self.__timeout = float(timeout)
46 1
		return self
47
48 1
	def header(self, name, value):
49 1
		n = name.upper()
50 1
		self._r.headers[n] = value
51 1
		return self
52
53 1
	def user_agent(self, value):
54 1
		return self.header('user-agent', value)
55
56 1
	def accept(self, value):
57 1
		return self.header('accept', value)
58
59 1
	def method(self, method):
60 1
		m = method.upper()
61 1
		self._r.method = m
62 1
		return self
63
64 1
	def url(self, url):
65 1
		self._r.url = url
66 1
		return self
67
68 1
	def qs(self, query):
69 1
		if query:
70 1
			self._r.params.update(query)
71 1
		return self
72
73 1
	def form(self, form):
74 1
		self.header('Content-Type', 'application/x-www-form-urlencoded')
75 1
		self._r.data = form
76 1
		return self
77
78 1
	def json(self, json):
79 1
		self.header('Content-Type', 'application/json')
80 1
		self._r.json = json
81 1
		return self
82
83 1
	def send(self):
84 1
		with requests.sessions.Session() as s:
85 1
			s.max_redirects = 1
86 1
			s.verify = True
87
88 1
			preq = s.prepare_request(self._r)
89 1
			log.debug(
90
				'send prepared request [%s] [%s]',
91
				preq.method,
92
				FluentRequest.__cut_qs(preq.url)
93
			)
94
95 1
			return s.send(
96
				preq,
97
				timeout=self.__timeout
98
			)
99
100 1
	def __repr__(self):
101 1
		return '<FluentRequest, [{method}] [{url}]>'.format(
102
			method=self._r.method,
103
			url=self._r.url,
104
		)
105
106
107 1
class NetworkClient(object):
108
	"""
109
	Internal API-client
110
111
	.. versionadded:: 0.1.0
112
	"""
113
114 1
	DEFAULT_TIMEOUT_SEC = 0.1
115
116 1
	def __init__(self, api_key, base_host='route4me.com'):
117
118 1
		user_agent = (
119
			'requests/{requests_version} '
120
			'({platform_name} {platform_version}) '
121
			'Route4Me-Python-SDK/{sdk_version} '
122
			'{python_implementation}/{python_version}'
123
		).format(
124
			requests_version=requests.__version__,
125
			platform_name=platform.system(),
126
			platform_version=platform.release(),
127
			sdk_version=VERSION_STRING,
128
			python_version=platform.python_version(),
129
			python_implementation=platform.python_implementation(),
130
		)
131
132 1
		self._user_agent = user_agent
133 1
		self.base_host = base_host
134 1
		self.api_key = api_key
135
136 1
	@property
137
	def user_agent(self):
138
		"""
139
		The value of `User-Agent` HTTP-header.
140
141
		Example:
142
143
		.. code-block:: python
144
145
		    'requests/2.18.3 (Linux 4.8.0-53-generic) Route4Me-Python-SDK/0.1.0-dev.4 CPython/3.5.2'
146
147
		:rtype: {str}
148
		"""  # noqa: E101
149 1
		return self._user_agent
150
151 1
	def __url(self, path, subdomain):
152 1
		subdomain = subdomain + '.' if subdomain else ''
153
154
		# remove leading slashes from path
155 1
		path = re.sub(r'^/+', '', path)
156
157 1
		url = 'https://{subdomain}{base_host}/{path}'.format(
158
			subdomain=subdomain,
159
			base_host=self.base_host,
160
			path=path,
161
		)
162 1
		return url
163
164 1
	def __read_response(self, req):
165 1
		res = self.__handle_net_exceptions(req)
166
167 1
		if res.status_code >= 300:
168 1
			print(res.status_code)
169
170
			# TODO: need more details!
171 1
			raise Route4MeApiError(
172
				'Error on Route4Me API',
173
				code='route4me.sdk.api_error',
174
				details={
175
					'req': req.__repr__(),
176
					'status_code': res.status_code,
177
				}
178
			)
179 1
		res.encoding = 'utf-8'
180 1
		return res.json()
181
182 1
	def __handle_net_exceptions(self, req):
183 1
		req.user_agent(self.user_agent)
184 1
		req.header('Route4Me-Agent', self.user_agent)
185 1
		req.header('Route4Me-Agent-Release', RELEASE_STRING)
186 1
		req.header('Route4Me-Agent-Commit', COMMIT)
187 1
		req.header('Route4Me-Agent-Build', BUILD)
188 1
		req.accept('application/json')
189 1
		req.header('Route4Me-Api-Key', self.api_key)
190
191 1
		req.qs({
192
			'api_key': self.api_key,    # TODO: security issue
193
			'format': 'json',
194
		})
195
196 1
		try:
197 1
			return req.send()
198
199 1
		except requests.exceptions.SSLError as exc:
200 1
			err = Route4MeNetworkError(
201
				message='SSL check failed',
202
				code='route4me.sdk.security.invalid_certificate',
203
				inner=exc,
204
			)
205 1
			log.error(err, exc_info=True)
206 1
			raise err
207 1
		except requests.exceptions.TooManyRedirects as exc:
208 1
			err = Route4MeNetworkError(
209
				message='Too many redirects',
210
				code='route4me.sdk.network.many_redirects',
211
				inner=exc,
212
				# details={
213
				# 	'max_redirects': req.max_redirects,
214
				# }
215
			)
216 1
			log.error(err, exc_info=True)
217 1
			raise err
218 1
		except requests.exceptions.Timeout as exc:
219 1
			err = Route4MeNetworkError(
220
				message='Network timeout (still no bytes received)',
221
				code='route4me.sdk.network.timeout',
222
				details={
223
					'timeout': req.timeout,
224
					'timeout_unit': 'sec',
225
				},
226
				inner=exc,
227
			)
228 1
			log.error(err, exc_info=True)
229 1
			raise err
230 1
		except requests.exceptions.ConnectionError as exc:
231 1
			err = Route4MeNetworkError(
232
				message='Can not connect, check your connection settings',
233
				code='route4me.sdk.network.no_connection',
234
				inner=exc,
235
			)
236 1
			log.error(err, exc_info=True)
237 1
			raise err
238
239 1
	def get(self, path, query=None, subdomain=None, timeout_sec=None):
240
241 1
		url = self.__url(path, subdomain=subdomain)
242
243 1
		req = FluentRequest()
244 1
		req.method('GET')
245 1
		req.url(url)
246 1
		req.qs(query)
247
248 1
		if timeout_sec is not None:
249 1
			req.timeout(timeout_sec)
250
251 1
		return self.__read_response(req)
252
253 1
	def delete(self, path, query=None, data=None, subdomain=None, timeout_sec=None):
254
255 1
		url = self.__url(path, subdomain=subdomain)
256
257 1
		req = FluentRequest()
258 1
		req.method('DELETE')
259 1
		req.url(url)
260 1
		req.qs(query)
261 1
		req.json(data)
262
263 1
		if timeout_sec is not None:
264
			req.timeout(timeout_sec)
265
266 1
		return self.__read_response(req)
267
268 1
	def post(self, path, query=None, data=None, subdomain=None, timeout_sec=None):
269
270 1
		url = self.__url(path, subdomain=subdomain)
271
272 1
		req = FluentRequest()
273 1
		req.method('POST')
274 1
		req.url(url)
275 1
		req.qs(query)
276 1
		req.json(data)
277
278 1
		if timeout_sec is not None:
279 1
			req.timeout(timeout_sec)
280
281 1
		return self.__read_response(req)
282
283 1
	def form(self, path, query=None, data=None, subdomain=None, timeout_sec=None):
284
		"""
285
		Posts form data as `multipart/form-data`.
286
287
		:param path str:         Path (part of URL) to API method
288
		:param subdomains [str]: Send request to other subdomain of the API
289
		:param **qargs: [description]
290
		:returns: API response, JSON converted to ..
291
		:rtype: {dict}
292
		"""
293 1
		url = self.__url(path, subdomain=subdomain)
294
295 1
		req = FluentRequest()
296 1
		req.method('POST')
297 1
		req.url(url)
298 1
		req.qs(query)
299 1
		req.form(data)
300
301 1
		if timeout_sec is not None:
302 1
			req.timeout(timeout_sec)
303
304
		return self.__read_response(req)
305