18
18
import os
19
19
from functools import wraps
20
20
from mimetypes import guess_type
21
+ from typing import Optional , Dict , Union , Any
22
+ from queue import LifoQueue
23
+ from warnings import warn
21
24
22
25
from reportportal_client .helpers import gen_attributes
23
26
24
- from .exception import RobotServiceException
25
27
from .model import Keyword , Launch , Test , LogMessage , Suite
26
28
from .service import RobotService
27
29
from .static import MAIN_SUITE_ID , PABOT_WIHOUT_LAUNCH_ID_MSG
30
32
logger = logging .getLogger (__name__ )
31
33
32
34
35
+ class _LifoQueue (LifoQueue ):
36
+ def last (self ):
37
+ with self .mutex :
38
+ if self ._qsize ():
39
+ return self .queue [- 1 ]
40
+
41
+
33
42
def check_rp_enabled (func ):
34
43
"""Verify is RP is enabled in config."""
35
44
@wraps (func )
@@ -41,18 +50,22 @@ def wrap(*args, **kwargs):
41
50
return wrap
42
51
43
52
44
- class listener (object ):
53
+ # noinspection PyPep8Naming
54
+ class listener :
45
55
"""Robot Framework listener interface for reporting to Report Portal."""
46
56
57
+ _items : _LifoQueue = ...
58
+ _service : Optional [RobotService ] = ...
59
+ _variables : Optional [Variables ] = ...
47
60
ROBOT_LISTENER_API_VERSION = 2
48
61
49
- def __init__ (self ):
62
+ def __init__ (self ) -> None :
50
63
"""Initialize listener attributes."""
51
- self ._items = []
64
+ self ._items = _LifoQueue ()
52
65
self ._service = None
53
66
self ._variables = None
54
67
55
- def _build_msg_struct (self , message ) :
68
+ def _build_msg_struct (self , message : Dict ) -> LogMessage :
56
69
"""Check if the given message comes from our custom logger or not.
57
70
58
71
:param message: Message passed by the Robot Framework
@@ -66,18 +79,21 @@ def _build_msg_struct(self, message):
66
79
msg .item_id = getattr (self .current_item , 'rp_item_id' , None )
67
80
return msg
68
81
69
- def _finish_current_item (self ):
70
- """Remove the last item from the self._items list."""
71
- return self ._items .pop ()
82
+ def _add_current_item (self , item : Union [Keyword , Launch , Suite , Test ]) -> None :
83
+ """Add the last item from the self._items queue."""
84
+ self ._items .put (item )
85
+
86
+ def _remove_current_item (self ) -> Union [Keyword , Launch , Suite , Test ]:
87
+ """Remove the last item from the self._items queue."""
88
+ return self ._items .get ()
72
89
73
90
@property
74
- def current_item (self ):
75
- """Get the last item from the self._items list."""
76
- if self ._items :
77
- return self ._items [- 1 ]
91
+ def current_item (self ) -> Optional [Union [Keyword , Launch , Suite , Test ]]:
92
+ """Get the last item from the self._items queue."""
93
+ return self ._items .last ()
78
94
79
95
@check_rp_enabled
80
- def log_message (self , message ) :
96
+ def log_message (self , message : Dict ) -> None :
81
97
"""Send log message to the Report Portal.
82
98
83
99
:param message: Message passed by the Robot Framework
@@ -87,7 +103,7 @@ def log_message(self, message):
87
103
self .service .log (message = msg )
88
104
89
105
@check_rp_enabled
90
- def log_message_with_image (self , msg , image ):
106
+ def log_message_with_image (self , msg : Dict , image : str ):
91
107
"""Send log message to the Report Portal.
92
108
93
109
:param msg: Message passed by the Robot Framework
@@ -105,37 +121,27 @@ def log_message_with_image(self, msg, image):
105
121
self .service .log (message = mes )
106
122
107
123
@property
108
- def parent_id (self ):
124
+ def parent_id (self ) -> Optional [ str ] :
109
125
"""Get rp_item_id attribute of the current item."""
110
126
return getattr (self .current_item , 'rp_item_id' , None )
111
127
112
128
@property
113
- def service (self ):
129
+ def service (self ) -> RobotService :
114
130
"""Initialize instance of the RobotService."""
115
- if self .variables .enabled and self ._service is None :
131
+ if self .variables .enabled and not self ._service :
116
132
self ._service = RobotService ()
117
- self ._service .init_service (
118
- endpoint = self .variables .endpoint ,
119
- project = self .variables .project ,
120
- api_key = self .variables .api_key ,
121
- log_batch_size = self .variables .log_batch_size ,
122
- pool_size = self .variables .pool_size ,
123
- skipped_issue = self .variables .skipped_issue ,
124
- verify_ssl = self .variables .verify_ssl ,
125
- log_batch_payload_size = self .variables .log_batch_payload_size ,
126
- launch_id = self .variables .launch_id ,
127
- )
133
+ self ._service .init_service (self .variables )
128
134
return self ._service
129
135
130
136
@property
131
- def variables (self ):
137
+ def variables (self ) -> Variables :
132
138
"""Get instance of the variables.Variables class."""
133
139
if not self ._variables :
134
140
self ._variables = Variables ()
135
141
return self ._variables
136
142
137
143
@check_rp_enabled
138
- def start_launch (self , attributes , ts = None ):
144
+ def start_launch (self , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
139
145
"""Start a new launch at the Report Portal.
140
146
141
147
:param attributes: Dictionary passed by the Robot Framework
@@ -146,7 +152,7 @@ def start_launch(self, attributes, ts=None):
146
152
launch .doc = self .variables .launch_doc or launch .doc
147
153
if not self .variables .launch_id :
148
154
if self .variables .pabot_used :
149
- raise RobotServiceException (PABOT_WIHOUT_LAUNCH_ID_MSG )
155
+ warn (PABOT_WIHOUT_LAUNCH_ID_MSG , stacklevel = 2 )
150
156
logger .debug ('ReportPortal - Start Launch: {0}' .format (
151
157
launch .attributes ))
152
158
self .service .start_launch (
@@ -159,7 +165,7 @@ def start_launch(self, attributes, ts=None):
159
165
self .service .rp .launch_id = self .variables .launch_id
160
166
161
167
@check_rp_enabled
162
- def start_suite (self , name , attributes , ts = None ):
168
+ def start_suite (self , name : str , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
163
169
"""Start a new test suite at the Report Portal.
164
170
165
171
:param name: Test suite name
@@ -175,23 +181,23 @@ def start_suite(self, name, attributes, ts=None):
175
181
.format (attributes ))
176
182
suite = Suite (name , attributes )
177
183
suite .rp_item_id = self .service .start_suite (suite = suite , ts = ts )
178
- self ._items . append (suite )
184
+ self ._add_current_item (suite )
179
185
else :
180
186
logger .debug ('ReportPortal - Start Suite: {0}' .format (attributes ))
181
187
suite = Suite (name , attributes )
182
188
suite .rp_parent_item_id = self .parent_id
183
189
suite .rp_item_id = self .service .start_suite (suite = suite , ts = ts )
184
- self ._items . append (suite )
190
+ self ._add_current_item (suite )
185
191
186
192
@check_rp_enabled
187
- def end_suite (self , _ , attributes , ts = None ):
193
+ def end_suite (self , _ : Optional [ str ] , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
188
194
"""Finish started test suite at the Report Portal.
189
195
190
196
:param attributes: Dictionary passed by the Robot Framework
191
197
:param ts: Timestamp(used by the ResultVisitor)
192
198
"""
193
199
if attributes ['id' ] == MAIN_SUITE_ID :
194
- suite = self ._finish_current_item ().update (attributes )
200
+ suite = self ._remove_current_item ().update (attributes )
195
201
logger .debug ('ReportPortal - End Suite: {0}'
196
202
.format (suite .attributes ))
197
203
self .service .finish_suite (suite = suite , ts = ts )
@@ -200,13 +206,13 @@ def end_suite(self, _, attributes, ts=None):
200
206
msg = 'ReportPortal - End Launch: {0}' .format (attributes ))
201
207
self .service .finish_launch (launch = launch , ts = ts )
202
208
else :
203
- suite = self ._finish_current_item ().update (attributes )
209
+ suite = self ._remove_current_item ().update (attributes )
204
210
logger .debug (
205
211
'ReportPortal - End Suite: {0}' .format (suite .attributes ))
206
212
self .service .finish_suite (suite = suite , ts = ts )
207
213
208
214
@check_rp_enabled
209
- def start_test (self , name , attributes , ts = None ):
215
+ def start_test (self , name : str , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
210
216
"""Start a new test case at the Report Portal.
211
217
212
218
:param name: Test case name
@@ -223,10 +229,10 @@ def start_test(self, name, attributes, ts=None):
223
229
self .variables .test_attributes + test .tags )
224
230
test .rp_parent_item_id = self .parent_id
225
231
test .rp_item_id = self .service .start_test (test = test , ts = ts )
226
- self ._items . append (test )
232
+ self ._add_current_item (test )
227
233
228
234
@check_rp_enabled
229
- def end_test (self , _ , attributes , ts = None ):
235
+ def end_test (self , _ : Optional [ str ] , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
230
236
"""Finish started test case at the Report Portal.
231
237
232
238
:param attributes: Dictionary passed by the Robot Framework
@@ -240,11 +246,11 @@ def end_test(self, _, attributes, ts=None):
240
246
if test .message :
241
247
self .log_message ({'message' : test .message , 'level' : 'DEBUG' })
242
248
logger .debug ('ReportPortal - End Test: {0}' .format (test .attributes ))
243
- self ._finish_current_item ()
249
+ self ._remove_current_item ()
244
250
self .service .finish_test (test = test , ts = ts )
245
251
246
252
@check_rp_enabled
247
- def start_keyword (self , name , attributes , ts = None ):
253
+ def start_keyword (self , name : str , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
248
254
"""Start a new keyword(test step) at the Report Portal.
249
255
250
256
:param name: Keyword name
@@ -256,20 +262,20 @@ def start_keyword(self, name, attributes, ts=None):
256
262
kwd .rp_parent_item_id = self .parent_id
257
263
logger .debug ('ReportPortal - Start Keyword: {0}' .format (attributes ))
258
264
kwd .rp_item_id = self .service .start_keyword (keyword = kwd , ts = ts )
259
- self ._items . append (kwd )
265
+ self ._add_current_item (kwd )
260
266
261
267
@check_rp_enabled
262
- def end_keyword (self , _ , attributes , ts = None ):
268
+ def end_keyword (self , _ : Optional [ str ] , attributes : Dict , ts : Optional [ Any ] = None ) -> None :
263
269
"""Finish started keyword at the Report Portal.
264
270
265
271
:param attributes: Dictionary passed by the Robot Framework
266
272
:param ts: Timestamp(used by the ResultVisitor)
267
273
"""
268
- kwd = self ._finish_current_item ().update (attributes )
274
+ kwd = self ._remove_current_item ().update (attributes )
269
275
logger .debug ('ReportPortal - End Keyword: {0}' .format (kwd .attributes ))
270
276
self .service .finish_keyword (keyword = kwd , ts = ts )
271
277
272
- def log_file (self , log_path ) :
278
+ def log_file (self , log_path : str ) -> None :
273
279
"""Attach HTML log file created by Robot Framework to RP launch.
274
280
275
281
:param log_path: Path to the log file
@@ -278,7 +284,7 @@ def log_file(self, log_path):
278
284
message = {'message' : 'Execution log' , 'level' : 'INFO' }
279
285
self .log_message_with_image (message , log_path )
280
286
281
- def report_file (self , report_path ) :
287
+ def report_file (self , report_path : str ) -> None :
282
288
"""Attach HTML report created by Robot Framework to RP launch.
283
289
284
290
:param report_path: Path to the report file
@@ -287,7 +293,7 @@ def report_file(self, report_path):
287
293
message = {'message' : 'Execution report' , 'level' : 'INFO' }
288
294
self .log_message_with_image (message , report_path )
289
295
290
- def xunit_file (self , xunit_path ) :
296
+ def xunit_file (self , xunit_path : str ) -> None :
291
297
"""Attach XUnit file created by Robot Framework to RP launch.
292
298
293
299
:param xunit_path: Path to the XUnit file
@@ -297,6 +303,6 @@ def xunit_file(self, xunit_path):
297
303
self .log_message_with_image (message , xunit_path )
298
304
299
305
@check_rp_enabled
300
- def close (self ):
306
+ def close (self ) -> None :
301
307
"""Call service terminate when the whole test execution is done."""
302
308
self .service .terminate_service ()
0 commit comments