20
20
21
21
import asyncio
22
22
import logging
23
+ import sys
24
+ import traceback
23
25
from dataclasses import dataclass
26
+ from datetime import datetime
24
27
from typing import Any , Callable , List , Optional , Tuple , TypeVar , Union
25
28
26
29
import aiohttp
48
51
49
52
50
53
class HttpRequest :
51
- """This model stores attributes related to ReportPortal HTTP requests."""
54
+ """This object stores attributes related to ReportPortal HTTP requests and makes them ."""
52
55
53
56
session_method : Callable
54
57
url : Any
@@ -121,8 +124,8 @@ def priority(self, value: Priority) -> None:
121
124
def make (self ) -> Optional [RPResponse ]:
122
125
"""Make HTTP request to the ReportPortal API.
123
126
124
- The method catches any request preparation error to not fail reporting. Since we are reporting tool
125
- and should not fail tests.
127
+ The method catches any request error to not fail reporting. Since we are reporting tool and should not fail
128
+ tests.
126
129
127
130
:return: wrapped HTTP response or None in case of failure
128
131
"""
@@ -141,8 +144,45 @@ def make(self) -> Optional[RPResponse]:
141
144
logger .warning ("ReportPortal %s request failed" , self .name , exc_info = exc )
142
145
143
146
147
+ class ErrorPrintingHttpRequest (HttpRequest ):
148
+ """This is specific request object which catches any request error and prints it to the "std.err".
149
+
150
+ The object is supposed to be used in logging methods only to prevent infinite recursion of logging, when logging
151
+ framework configured to log everything to ReportPortal. In this case if a request to ReportPortal fails, the
152
+ failure will be logged to ReportPortal once again and, for example, in case of endpoint configuration error, it
153
+ will also fail and will be logged again. So, the recursion will never end.
154
+
155
+ This class is used to prevent this situation. It catches any request error and prints it to the "std.err".
156
+ """
157
+
158
+ def make (self ) -> Optional [RPResponse ]:
159
+ """Make HTTP request to the ReportPortal API.
160
+
161
+ The method catches any request error and prints it to the "std.err".
162
+
163
+ :return: wrapped HTTP response or None in case of failure
164
+ """
165
+ # noinspection PyBroadException
166
+ try :
167
+ return RPResponse (
168
+ self .session_method (
169
+ self .url ,
170
+ data = self .data ,
171
+ json = self .json ,
172
+ files = self .files ,
173
+ verify = self .verify_ssl ,
174
+ timeout = self .http_timeout ,
175
+ )
176
+ )
177
+ except Exception :
178
+ print (
179
+ f"{ datetime .now ().isoformat ()} - [ERROR] - ReportPortal request error:\n { traceback .format_exc ()} " ,
180
+ file = sys .stderr ,
181
+ )
182
+
183
+
144
184
class AsyncHttpRequest (HttpRequest ):
145
- """This model stores attributes related to asynchronous ReportPortal HTTP requests."""
185
+ """This object stores attributes related to asynchronous ReportPortal HTTP requests and make them ."""
146
186
147
187
def __init__ (
148
188
self ,
@@ -166,8 +206,8 @@ def __init__(
166
206
async def make (self ) -> Optional [AsyncRPResponse ]:
167
207
"""Asynchronously make HTTP request to the ReportPortal API.
168
208
169
- The method catches any request preparation error to not fail reporting. Since we are reporting tool
170
- and should not fail tests.
209
+ The method catches any request error to not fail reporting. Since we are reporting tool and should not fail
210
+ tests.
171
211
172
212
:return: wrapped HTTP response or None in case of failure
173
213
"""
@@ -182,6 +222,39 @@ async def make(self) -> Optional[AsyncRPResponse]:
182
222
logger .warning ("ReportPortal %s request failed" , self .name , exc_info = exc )
183
223
184
224
225
+ class ErrorPrintingAsyncHttpRequest (AsyncHttpRequest ):
226
+ """This is specific request object which catches any request error and prints it to the "std.err".
227
+
228
+ The object is supposed to be used in logging methods only to prevent infinite recursion of logging, when logging
229
+ framework configured to log everything to ReportPortal. In this case if a request to ReportPortal fails, the
230
+ failure will be logged to ReportPortal once again and, for example, in case of endpoint configuration error, it
231
+ will also fail and will be logged again. So, the recursion will never end.
232
+
233
+ This class is used to prevent this situation. It catches any request error and prints it to the "std.err".
234
+ """
235
+
236
+ async def make (self ) -> Optional [AsyncRPResponse ]:
237
+ """Asynchronously make HTTP request to the ReportPortal API.
238
+
239
+ The method catches any request error and prints it to the "std.err".
240
+
241
+ :return: wrapped HTTP response or None in case of failure
242
+ """
243
+ url = await await_if_necessary (self .url )
244
+ if not url :
245
+ return None
246
+ data = await await_if_necessary (self .data )
247
+ json = await await_if_necessary (self .json )
248
+ # noinspection PyBroadException
249
+ try :
250
+ return AsyncRPResponse (await self .session_method (url , data = data , json = json ))
251
+ except Exception :
252
+ print (
253
+ f"{ datetime .now ().isoformat ()} - [ERROR] - ReportPortal request error:\n { traceback .format_exc ()} " ,
254
+ file = sys .stderr ,
255
+ )
256
+
257
+
185
258
class RPRequestBase (metaclass = AbstractBaseClass ):
186
259
"""Base class for specific ReportPortal request models.
187
260
0 commit comments