Skip to content

Commit 32db691

Browse files
authored
Merge pull request #177 from reportportal/no_failures_fix
No failures fix
2 parents c4064d5 + 7dedb11 commit 32db691

File tree

10 files changed

+286
-134
lines changed

10 files changed

+286
-134
lines changed

reportportal_client/client.py

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import logging
1818

1919
import requests
20-
from requests.adapters import HTTPAdapter
20+
from requests.adapters import HTTPAdapter, Retry
2121

2222
from ._local import set_current
2323
from .core.log_manager import LogManager
@@ -96,10 +96,15 @@ def __init__(self,
9696
self.step_reporter = StepReporter(self)
9797
self._item_stack = []
9898
if retries:
99+
retry_strategy = Retry(
100+
total=retries,
101+
backoff_factor=0.1,
102+
status_forcelist=[429, 500, 502, 503, 504]
103+
)
99104
self.session.mount('https://', HTTPAdapter(
100-
max_retries=retries, pool_maxsize=max_pool_size))
105+
max_retries=retry_strategy, pool_maxsize=max_pool_size))
101106
self.session.mount('http://', HTTPAdapter(
102-
max_retries=retries, pool_maxsize=max_pool_size))
107+
max_retries=retry_strategy, pool_maxsize=max_pool_size))
103108
self.session.headers['Authorization'] = 'bearer {0}'.format(self.token)
104109

105110
self._log_manager = LogManager(
@@ -119,6 +124,9 @@ def finish_launch(self,
119124
CANCELLED
120125
:param attributes: Launch attributes
121126
"""
127+
if self.launch_id is NOT_FOUND or not self.launch_id:
128+
logger.warning("Attempt to finish non-existent launch")
129+
return
122130
url = uri_join(self.base_url_v2, 'launch', self.launch_id, 'finish')
123131
request_payload = LaunchFinishRequest(
124132
end_time,
@@ -127,7 +135,10 @@ def finish_launch(self,
127135
description=kwargs.get('description')
128136
).payload
129137
response = HttpRequest(self.session.put, url=url, json=request_payload,
130-
verify_ssl=self.verify_ssl).make()
138+
verify_ssl=self.verify_ssl,
139+
name='Finish Launch').make()
140+
if not response:
141+
return
131142
logger.debug('finish_launch - ID: %s', self.launch_id)
132143
logger.debug('response message: %s', response.message)
133144
return response.message
@@ -156,9 +167,9 @@ def finish_test_item(self,
156167
:param retry: Used to report retry of the test. Allowable values:
157168
"True" or "False"
158169
"""
159-
if item_id is NOT_FOUND:
160-
logger.warning("Uttempt to finish non-existent item")
161-
return None
170+
if item_id is NOT_FOUND or not item_id:
171+
logger.warning("Attempt to finish non-existent item")
172+
return
162173
url = uri_join(self.base_url_v2, 'item', item_id)
163174
request_payload = ItemFinishRequest(
164175
end_time,
@@ -172,7 +183,10 @@ def finish_test_item(self,
172183
).payload
173184
response = HttpRequest(self.session.put, url=url, json=request_payload,
174185
verify_ssl=self.verify_ssl).make()
175-
self._item_stack.pop()
186+
if not response:
187+
return
188+
# noinspection PyUnresolvedReferences
189+
self._item_stack.pop() if len(self._item_stack) > 0 else None
176190
logger.debug('finish_test_item - ID: %s', item_id)
177191
logger.debug('response message: %s', response.message)
178192
return response.message
@@ -186,7 +200,7 @@ def get_item_id_by_uuid(self, uuid):
186200
url = uri_join(self.base_url_v1, 'item', 'uuid', uuid)
187201
response = HttpRequest(self.session.get, url=url,
188202
verify_ssl=self.verify_ssl).make()
189-
return response.id
203+
return response.id if response else None
190204

191205
def get_launch_info(self):
192206
"""Get the current launch information.
@@ -199,6 +213,8 @@ def get_launch_info(self):
199213
logger.debug('get_launch_info - ID: %s', self.launch_id)
200214
response = HttpRequest(self.session.get, url=url,
201215
verify_ssl=self.verify_ssl).make()
216+
if not response:
217+
return
202218
if response.is_success:
203219
launch_info = response.json
204220
logger.debug(
@@ -214,14 +230,17 @@ def get_launch_ui_id(self):
214230
215231
:return: UI ID of the given launch. None if UI ID has not been found.
216232
"""
217-
return self.get_launch_info().get('id')
233+
launch_info = self.get_launch_info()
234+
return launch_info.get('id') if launch_info else None
218235

219236
def get_launch_ui_url(self):
220237
"""Get UI URL of the current launch.
221238
222239
:return: launch URL or all launches URL.
223240
"""
224-
ui_id = self.get_launch_ui_id() or ''
241+
ui_id = self.get_launch_ui_id()
242+
if not ui_id:
243+
return
225244
path = 'ui/#{0}/launches/all/{1}'.format(self.project, ui_id)
226245
url = uri_join(self.endpoint, path)
227246
logger.debug('get_launch_ui_url - ID: %s', self.launch_id)
@@ -235,7 +254,7 @@ def get_project_settings(self):
235254
url = uri_join(self.base_url_v1, 'settings')
236255
response = HttpRequest(self.session.get, url=url, json={},
237256
verify_ssl=self.verify_ssl).make()
238-
return response.json
257+
return response.json if response else None
239258

240259
def log(self, time, message, level=None, attachment=None, item_id=None):
241260
"""Send log message to the Report Portal.
@@ -287,6 +306,8 @@ def start_launch(self,
287306
url=url,
288307
json=request_payload,
289308
verify_ssl=self.verify_ssl).make()
309+
if not response:
310+
return
290311
self._log_manager.launch_id = self.launch_id = response.id
291312
logger.debug('start_launch - ID: %s', self.launch_id)
292313
return self.launch_id
@@ -324,6 +345,10 @@ def start_test_item(self,
324345
values: "True" or "False"
325346
:param test_case_id: A unique ID of the current step
326347
"""
348+
if parent_item_id is NOT_FOUND:
349+
logger.warning("Attempt to start item for non-existent parent "
350+
"item")
351+
return
327352
if parent_item_id:
328353
url = uri_join(self.base_url_v2, 'item', parent_item_id)
329354
else:
@@ -346,9 +371,12 @@ def start_test_item(self,
346371
url=url,
347372
json=request_payload,
348373
verify_ssl=self.verify_ssl).make()
374+
if not response:
375+
return
349376
item_id = response.id
350377
if item_id is not NOT_FOUND:
351378
logger.debug('start_test_item - ID: %s', item_id)
379+
# noinspection PyUnresolvedReferences
352380
self._item_stack.append(item_id)
353381
else:
354382
logger.warning('start_test_item - invalid response: %s',
@@ -375,9 +403,12 @@ def update_test_item(self, item_uuid, attributes=None, description=None):
375403
url = uri_join(self.base_url_v1, 'item', item_id, 'update')
376404
response = HttpRequest(self.session.put, url=url, json=data,
377405
verify_ssl=self.verify_ssl).make()
406+
if not response:
407+
return
378408
logger.debug('update_test_item - Item: %s', item_id)
379409
return response.message
380410

381411
def current_item(self):
382412
"""Retrieve the last item reported by the client."""
413+
# noinspection PyUnresolvedReferences
383414
return self._item_stack[-1] if len(self._item_stack) > 0 else None

reportportal_client/core/log_manager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
RPRequestLog
2525
)
2626
from reportportal_client.core.worker import APIWorker
27+
from reportportal_client.static.defines import NOT_FOUND
2728

2829
logger = logging.getLogger(__name__)
2930

@@ -91,6 +92,9 @@ def log(self, time, message=None, level=None, attachment=None,
9192
:param attachment: Attachments(images,files,etc.)
9293
:param item_id: parent item UUID
9394
"""
95+
if item_id is NOT_FOUND:
96+
logger.warning("Attempt to log to non-existent item")
97+
return
9498
rp_file = RPFile(**attachment) if attachment else None
9599
rp_log = RPRequestLog(self.launch_id, time, rp_file, item_id,
96100
level, message)

reportportal_client/core/rp_requests.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121

2222
import json
23+
import logging
2324
import uuid
2425

2526
from reportportal_client.core.rp_file import RPFile
@@ -32,17 +33,19 @@
3233
from reportportal_client.static.defines import (
3334
DEFAULT_PRIORITY,
3435
LOW_PRIORITY,
35-
RP_LOG_LEVELS,
36-
SEND_RETRY_COUNT
36+
RP_LOG_LEVELS
3737
)
3838
from .rp_responses import RPResponse
3939

40+
logger = logging.getLogger(__name__)
41+
4042

4143
class HttpRequest:
4244
"""This model stores attributes related to RP HTTP requests."""
4345

4446
def __init__(self, session_method, url, data=None, json=None,
45-
files=None, verify_ssl=True, http_timeout=(10, 10)):
47+
files=None, verify_ssl=True, http_timeout=(10, 10),
48+
name=None):
4649
"""Initialize instance attributes.
4750
4851
:param session_method: Method of the requests.Session instance
@@ -54,6 +57,7 @@ def __init__(self, session_method, url, data=None, json=None,
5457
:param http_timeout: a float in seconds for connect and read
5558
timeout. Use a Tuple to specific connect and
5659
read separately.
60+
:param name: request name
5761
"""
5862
self.data = data
5963
self.files = files
@@ -62,20 +66,23 @@ def __init__(self, session_method, url, data=None, json=None,
6266
self.url = url
6367
self.verify_ssl = verify_ssl
6468
self.http_timeout = http_timeout
69+
self.name = name
6570

6671
def make(self):
6772
"""Make HTTP request to the Report Portal API."""
68-
for attempt in range(SEND_RETRY_COUNT):
69-
try:
70-
return RPResponse(self.session_method(
71-
self.url, data=self.data, json=self.json,
72-
files=self.files, verify=self.verify_ssl,
73-
timeout=self.http_timeout)
74-
)
73+
try:
74+
return RPResponse(self.session_method(
75+
self.url, data=self.data, json=self.json,
76+
files=self.files, verify=self.verify_ssl,
77+
timeout=self.http_timeout)
78+
)
7579
# https://github.com/reportportal/client-Python/issues/39
76-
except KeyError:
77-
if attempt + 1 == SEND_RETRY_COUNT:
78-
raise
80+
except (KeyError, IOError, ValueError) as exc:
81+
logger.warning(
82+
"Report Portal %s request failed",
83+
self.name,
84+
exc_info=exc
85+
)
7986

8087

8188
class RPRequestBase(object):

reportportal_client/core/rp_requests.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ class HttpRequest:
1515
json: Optional[Dict] = ...
1616
verify_ssl: Optional[bool] = ...
1717
http_timeout: Union[float, Tuple[float, float]] = ...
18+
name: Optional[Text] = ...
1819
def __init__(self,
1920
session_method: Callable,
2021
url: Text,
2122
data = Optional[Union[Dict, List[Union[tuple, ByteString, IO]]]],
2223
json = Optional[Dict],
2324
files = Optional[Dict],
24-
verify_ssl = Optional[bool]) -> None: ...
25-
def make(self) -> RPResponse: ...
25+
verify_ssl = Optional[bool],
26+
name = Optional[Text]) -> None: ...
27+
def make(self) -> Optional[RPResponse]: ...
2628

2729

2830
class RPRequestBase(metaclass=AbstractBaseClass):

reportportal_client/core/rp_responses.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
See the License for the specific language governing permissions and
1919
limitations under the License.
2020
"""
21+
import logging
2122

2223
from reportportal_client.static.defines import NOT_FOUND
23-
from reportportal_client.static.errors import ResponseError
24+
25+
logger = logging.getLogger(__name__)
2426

2527

2628
class RPMessage(object):
@@ -74,8 +76,10 @@ def _get_json(data):
7476
try:
7577
return data.json()
7678
except ValueError as error:
77-
raise ResponseError('Invalid response: {0}: {1}'
78-
.format(error, data.text))
79+
logger.warning('Invalid response: {0}: {1}'
80+
.format(error, data.text),
81+
exc_info=error)
82+
return {}
7983

8084
@property
8185
def id(self):

0 commit comments

Comments
 (0)