2020
2121import asyncio
2222import logging
23+ import sys
24+ import traceback
2325from dataclasses import dataclass
26+ from datetime import datetime
2427from typing import Any , Callable , List , Optional , Tuple , TypeVar , Union
2528
2629import aiohttp
4851
4952
5053class HttpRequest :
51- """This model stores attributes related to ReportPortal HTTP requests."""
54+ """This object stores attributes related to ReportPortal HTTP requests and makes them ."""
5255
5356 session_method : Callable
5457 url : Any
@@ -121,8 +124,8 @@ def priority(self, value: Priority) -> None:
121124 def make (self ) -> Optional [RPResponse ]:
122125 """Make HTTP request to the ReportPortal API.
123126
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.
126129
127130 :return: wrapped HTTP response or None in case of failure
128131 """
@@ -141,8 +144,45 @@ def make(self) -> Optional[RPResponse]:
141144 logger .warning ("ReportPortal %s request failed" , self .name , exc_info = exc )
142145
143146
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+
144184class 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 ."""
146186
147187 def __init__ (
148188 self ,
@@ -166,8 +206,8 @@ def __init__(
166206 async def make (self ) -> Optional [AsyncRPResponse ]:
167207 """Asynchronously make HTTP request to the ReportPortal API.
168208
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.
171211
172212 :return: wrapped HTTP response or None in case of failure
173213 """
@@ -182,6 +222,39 @@ async def make(self) -> Optional[AsyncRPResponse]:
182222 logger .warning ("ReportPortal %s request failed" , self .name , exc_info = exc )
183223
184224
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+
185258class RPRequestBase (metaclass = AbstractBaseClass ):
186259 """Base class for specific ReportPortal request models.
187260
0 commit comments