Skip to content

Commit d5a37e7

Browse files
author
frizzby
committed
Make error handling callbacks more useful: full exception info is
now available. log() not accepts attachment= argument to be file contents. In this case name and mime are being set automatically. Updated README.md to reflect recent changes.
1 parent 80c91a5 commit d5a37e7

File tree

3 files changed

+83
-69
lines changed

3 files changed

+83
-69
lines changed

README.md

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,11 @@ Basic usage example:
3333
```python
3434
import os
3535
import subprocess
36-
from time import time
36+
import traceback
3737
from mimetypes import guess_type
38+
from time import time
3839

39-
from reportportal_client import (ReportPortalService,
40-
FinishExecutionRQ,
41-
StartLaunchRQ, StartTestItemRQ,
42-
FinishTestItemRQ, SaveLogRQ)
40+
from reportportal_client import ReportPortalServiceAsync
4341

4442

4543
def timestamp():
@@ -53,73 +51,75 @@ token = "1adf271d-505f-44a8-ad71-0afbdf8c83bd"
5351
launch_name = "Test launch"
5452
launch_doc = "Testing logging with attachment."
5553

56-
service = ReportPortalService(endpoint=endpoint, project=project, token=token)
5754

58-
# Create start launch request.
59-
sl_rq = StartLaunchRQ(name=launch_name,
60-
start_time=timestamp(),
61-
description=launch_doc)
55+
def my_error_handler(exc_info):
56+
"""
57+
This callback function will be called by async service client when error occurs.
58+
Return True if error is not critical and you want to continue work.
59+
:param exc_info: result of sys.exc_info() -> (type, value, traceback)
60+
:return:
61+
"""
62+
print("Error occured: {}".format(exc_info[1]))
63+
traceback.print_exception(*exc_info)
6264

63-
# Start launch.
64-
launch = service.start_launch(sl_rq)
6565

66-
# Create start test item request.
67-
sti_rq = StartTestItemRQ(name="Test Case",
68-
description="First Test Case",
69-
tags=["Image", "Smoke"],
70-
start_time=timestamp(),
71-
launch_id=launch.id,
72-
type="TEST")
66+
service = ReportPortalServiceAsync(endpoint=endpoint, project=project,
67+
token=token, error_handler=my_error_handler)
68+
69+
# Start launch.
70+
launch = service.start_launch(name=launch_name,
71+
start_time=timestamp(),
72+
description=launch_doc)
7373

7474
# Start test item.
75-
test = service.start_test_item(parent_item_id=None, start_test_item_rq=sti_rq)
75+
test = service.start_test_item(name="Test Case",
76+
description="First Test Case",
77+
tags=["Image", "Smoke"],
78+
start_time=timestamp(),
79+
item_type="TEST")
7680

7781
# Create text log message with INFO level.
78-
service.log(SaveLogRQ(item_id=test.id,
79-
time=timestamp(),
80-
message="Hello World!",
81-
level="INFO"))
82+
service.log(time=timestamp(),
83+
message="Hello World!",
84+
level="INFO")
8285

8386
# Create log message with attached text output and WARN level.
84-
service.attach(SaveLogRQ(item_id=test.id,
85-
time=timestamp(),
86-
message="Too high memory usage!",
87-
level="WARN"),
88-
name="free_memory.txt",
89-
data=subprocess.check_output("free -h".split()))
90-
91-
# Create log message with piped binary file, INFO level and custom mimetype.
87+
service.log(time=timestamp(),
88+
message="Too high memory usage!",
89+
level="WARN",
90+
attachment={
91+
"name": "free_memory.txt",
92+
"data": subprocess.check_output("ps".split()),
93+
"mime": "text/plain"
94+
})
95+
96+
# Create log message with binary file, INFO level and custom mimetype.
9297
image = "/tmp/image.png"
93-
sl_rq = SaveLogRQ(test.id, timestamp(), "Screen shot of issue.", "INFO")
9498
with open(image, "rb") as fh:
95-
service.attach(save_log_rq=sl_rq,
96-
name=os.path.basename(image),
97-
data=fh,
98-
mime=guess_type(image)[0] or "application/octet-stream")
99-
100-
# Create log message with binary data and INFO level.
101-
filebin = "/tmp/file.bin"
102-
with open(filebin, "rb") as fd:
103-
bindata = fd.read()
104-
# Note here that we pass binary data instead of file handle.
105-
service.attach(SaveLogRQ(item_id=test.id,
106-
time=timestamp(),
107-
message="Binary data file.",
108-
level="INFO"),
109-
name="file.bin",
110-
data=bindata,
111-
mime="application/octet-stream")
112-
113-
# Create finish test item request.
114-
fti_rq = FinishTestItemRQ(end_time=timestamp(), status="PASSED")
99+
attachment = {
100+
"name": os.path.basename(image),
101+
"data": fh.read(),
102+
"mime": guess_type(image)[0] or "application/octet-stream"
103+
}
104+
service.log(timestamp(), "Screen shot of issue.", "INFO", attachment)
105+
106+
# Create log message supplying only contents
107+
service.log(
108+
timestamp(),
109+
"running processes",
110+
"INFO",
111+
attachment=subprocess.check_output("ps aux".split()))
115112

116113
# Finish test item.
117-
service.finish_test_item(item_id=test.id, finish_test_item_rq=fti_rq)
114+
service.finish_test_item(end_time=timestamp(), status="PASSED")
118115

119-
# Create finish launch request.
120-
fl_rq = FinishExecutionRQ(end_time=timestamp(), status="PASSED")
121116
# Finish launch.
122-
service.finish_launch(launch.id, fl_rq)
117+
service.finish_launch(end_time=timestamp())
118+
119+
# Due to async nature of the service we need to call terminate() method which
120+
# ensures all pending requests to server are proccessed.
121+
# Failure to call terminate() may result in lost data.
122+
service.terminate()
123123
```
124124

125125

reportportal_client/service.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import requests
3+
import uuid
34
from logging import getLogger
45

56
from .errors import ResponseError, EntryCreatedError, OperationCompletionError
@@ -212,19 +213,29 @@ def log_batch(self, log_data):
212213
for log_item in log_data:
213214
log_item["item_id"] = self.stack[-1]
214215
attachment = log_item.get("attachment", None)
215-
del log_item["attachment"]
216+
217+
if "attachment" in log_item:
218+
del log_item["attachment"]
216219

217220
if attachment:
218-
log_item["file"] = {"name": attachment["name"]}
221+
if not isinstance(attachment, dict):
222+
attachment = {"data": attachment}
223+
224+
name = attachment.get("name", str(uuid.uuid4()))
225+
log_item["file"] = {"name": name}
219226
attachments.append(("file", (
220-
attachment["name"],
227+
name,
221228
attachment["data"],
222-
attachment["mime"]
229+
attachment.get("mime", "application/octet-stream")
223230
)))
224231

225-
files = [
226-
("json_request_part", (None, json.dumps(log_data), "application/json")),
227-
]
232+
files = [(
233+
"json_request_part", (
234+
None,
235+
json.dumps(log_data),
236+
"application/json"
237+
)
238+
)]
228239
files.extend(attachments)
229240
r = self.session.post(url=url, files=files)
230241
logger.debug("log_batch - Stack: %s", self.stack)

reportportal_client/service_async.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
from logging import getLogger
1+
import sys
22
import threading
3+
from logging import getLogger
34

45
from six.moves import queue
56

@@ -158,7 +159,7 @@ def terminate(self, nowait=False):
158159
self._post_log_batch()
159160
except Exception as err:
160161
if self.error_handler:
161-
self.error_handler(err)
162+
self.error_handler(sys.exc_info())
162163
else:
163164
raise
164165
finally:
@@ -168,8 +169,10 @@ def terminate(self, nowait=False):
168169
def _post_log_batch(self):
169170
logger.debug("Posting log batch size: %s", len(self.log_batch))
170171
if self.log_batch:
171-
self.rp_client.log_batch(self.log_batch)
172-
self.log_batch = []
172+
try:
173+
self.rp_client.log_batch(self.log_batch)
174+
finally:
175+
self.log_batch = []
173176

174177
def process_log(self, **log_item):
175178
"""
@@ -200,7 +203,7 @@ def process_item(self, item):
200203
getattr(self.rp_client, method)(**kwargs)
201204
except Exception as err:
202205
if self.error_handler:
203-
if not self.error_handler(err):
206+
if not self.error_handler(sys.exc_info()):
204207
self.terminate(nowait=True)
205208
else:
206209
self.terminate(nowait=True)

0 commit comments

Comments
 (0)