Skip to content

Commit f223bbe

Browse files
committed
Types refactoring
1 parent f49eb08 commit f223bbe

File tree

3 files changed

+63
-100
lines changed

3 files changed

+63
-100
lines changed

reportportal_client/aio/client.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,16 @@ async def close(self) -> None:
208208
await self._session.close()
209209
self._session = None
210210

211-
async def __get_item_url(self, item_id_future: Union[str, Task[str]]) -> Optional[str]:
211+
async def __get_item_url(self, item_id_future: Union[Optional[str], Task[Optional[str]]]) -> Optional[str]:
212212
item_id = await await_if_necessary(item_id_future)
213-
if item_id is NOT_FOUND:
213+
if item_id is NOT_FOUND or item_id is None:
214214
logger.warning('Attempt to make request for non-existent id.')
215215
return
216216
return root_uri_join(self.base_url_v2, 'item', item_id)
217217

218-
async def __get_launch_url(self, launch_uuid_future: Union[str, Task[str]]) -> Optional[str]:
218+
async def __get_launch_url(self, launch_uuid_future: Union[Optional[str], Task[Optional[str]]]) -> Optional[str]:
219219
launch_uuid = await await_if_necessary(launch_uuid_future)
220-
if launch_uuid is NOT_FOUND:
220+
if launch_uuid is NOT_FOUND or launch_uuid is None:
221221
logger.warning('Attempt to make request for non-existent launch.')
222222
return
223223
return root_uri_join(self.base_url_v2, 'launch', launch_uuid, 'finish')
@@ -442,7 +442,7 @@ async def update_test_item(self,
442442

443443
async def __get_launch_uuid_url(self, launch_uuid_future: Union[str, Task[str]]) -> Optional[str]:
444444
launch_uuid = await await_if_necessary(launch_uuid_future)
445-
if launch_uuid is NOT_FOUND:
445+
if launch_uuid is NOT_FOUND or launch_uuid is None:
446446
logger.warning('Attempt to make request for non-existent Launch UUID.')
447447
return
448448
logger.debug('get_launch_info - ID: %s', launch_uuid)
@@ -466,9 +466,9 @@ async def get_launch_info(self, launch_uuid_future: Union[str, Task[str]]) -> Op
466466
logger.warning('get_launch_info - Launch info: Failed to fetch launch ID from the API.')
467467
return launch_info
468468

469-
async def __get_item_uuid_url(self, item_uuid_future: Union[str, Task[str]]) -> Optional[str]:
469+
async def __get_item_uuid_url(self, item_uuid_future: Union[Optional[str], Task[Optional[str]]]) -> Optional[str]:
470470
item_uuid = await await_if_necessary(item_uuid_future)
471-
if item_uuid is NOT_FOUND:
471+
if item_uuid is NOT_FOUND or item_uuid is None:
472472
logger.warning('Attempt to make request for non-existent UUID.')
473473
return
474474
return root_uri_join(self.base_url_v1, 'item', 'uuid', item_uuid)
@@ -972,7 +972,7 @@ def client(self) -> Client:
972972
return self.__client
973973

974974
@property
975-
def launch_uuid(self) -> Optional[Task[str]]:
975+
def launch_uuid(self) -> Task[Optional[str]]:
976976
"""Return current Launch UUID.
977977
978978
:return: UUID string.
@@ -1102,13 +1102,13 @@ def current_item(self) -> Task[_T]:
11021102
"""
11031103
return self._item_stack.last()
11041104

1105-
async def __empty_str(self):
1105+
async def __empty_str(self) -> str:
11061106
return ""
11071107

1108-
async def __empty_dict(self):
1108+
async def __empty_dict(self) -> dict:
11091109
return {}
11101110

1111-
async def __int_value(self):
1111+
async def __int_value(self) -> int:
11121112
return -1
11131113

11141114
def start_launch(self,
@@ -1118,7 +1118,7 @@ def start_launch(self,
11181118
attributes: Optional[Union[list, dict]] = None,
11191119
rerun: bool = False,
11201120
rerun_of: Optional[str] = None,
1121-
**kwargs) -> Task[str]:
1121+
**kwargs) -> Task[Optional[str]]:
11221122
"""Start a new Launch with the given arguments.
11231123
11241124
:param name: Launch name.
@@ -1151,7 +1151,7 @@ def start_test_item(self,
11511151
retry: bool = False,
11521152
test_case_id: Optional[str] = None,
11531153
retry_of: Optional[str] = None,
1154-
**kwargs: Any) -> Task[str]:
1154+
**kwargs: Any) -> Task[Optional[str]]:
11551155
"""Start Test Case/Suite/Step/Nested Step Item.
11561156
11571157
:param name: Name of the Test Item.
@@ -1191,7 +1191,7 @@ def finish_test_item(self,
11911191
retry: bool = False,
11921192
test_case_id: Optional[str] = None,
11931193
retry_of: Optional[str] = None,
1194-
**kwargs: Any) -> Task[str]:
1194+
**kwargs: Any) -> Task[Optional[str]]:
11951195
"""Finish Test Suite/Case/Step/Nested Step Item.
11961196
11971197
:param item_id: ID of the Test Item.
@@ -1219,7 +1219,7 @@ def finish_launch(self,
12191219
end_time: str,
12201220
status: Optional[str] = None,
12211221
attributes: Optional[Union[list, dict]] = None,
1222-
**kwargs: Any) -> Task[str]:
1222+
**kwargs: Any) -> Task[Optional[str]]:
12231223
"""Finish a Launch.
12241224
12251225
:param end_time: Launch end time.
@@ -1242,7 +1242,7 @@ def finish_launch(self,
12421242
def update_test_item(self,
12431243
item_uuid: Task[str],
12441244
attributes: Optional[Union[list, dict]] = None,
1245-
description: Optional[str] = None) -> Task:
1245+
description: Optional[str] = None) -> Task[Optional[str]]:
12461246
"""Update existing Test Item at the ReportPortal.
12471247
12481248
:param item_uuid: Test Item UUID returned on the item start.
@@ -1255,7 +1255,7 @@ def update_test_item(self,
12551255
result_task = self.create_task(result_coro)
12561256
return result_task
12571257

1258-
def get_launch_info(self) -> Task[dict]:
1258+
def get_launch_info(self) -> Task[Optional[dict]]:
12591259
"""Get current Launch information.
12601260
12611261
:return: Launch information in dictionary.
@@ -1266,7 +1266,7 @@ def get_launch_info(self) -> Task[dict]:
12661266
result_task = self.create_task(result_coro)
12671267
return result_task
12681268

1269-
def get_item_id_by_uuid(self, item_uuid_future: Task[str]) -> Task[str]:
1269+
def get_item_id_by_uuid(self, item_uuid_future: Task[Optional[str]]) -> Task[Optional[str]]:
12701270
"""Get Test Item ID by the given Item UUID.
12711271
12721272
:param item_uuid_future: Str or Task UUID returned on the Item start.
@@ -1276,7 +1276,7 @@ def get_item_id_by_uuid(self, item_uuid_future: Task[str]) -> Task[str]:
12761276
result_task = self.create_task(result_coro)
12771277
return result_task
12781278

1279-
def get_launch_ui_id(self) -> Task[int]:
1279+
def get_launch_ui_id(self) -> Task[Optional[int]]:
12801280
"""Get Launch ID of the current Launch.
12811281
12821282
:return: Launch ID of the Launch. None if not found.
@@ -1287,7 +1287,7 @@ def get_launch_ui_id(self) -> Task[int]:
12871287
result_task = self.create_task(result_coro)
12881288
return result_task
12891289

1290-
def get_launch_ui_url(self) -> Task[str]:
1290+
def get_launch_ui_url(self) -> Task[Optional[str]]:
12911291
"""Get full quality URL of the current Launch.
12921292
12931293
:return: Launch URL string.
@@ -1298,7 +1298,7 @@ def get_launch_ui_url(self) -> Task[str]:
12981298
result_task = self.create_task(result_coro)
12991299
return result_task
13001300

1301-
def get_project_settings(self) -> Task[dict]:
1301+
def get_project_settings(self) -> Task[Optional[str]]:
13021302
"""Get settings of the current Project.
13031303
13041304
:return: Settings response in Dictionary.
@@ -1314,7 +1314,7 @@ async def _log(self, log_rq: AsyncRPRequestLog) -> Optional[Tuple[str, ...]]:
13141314
return await self._log_batch(await self._log_batcher.append_async(log_rq))
13151315

13161316
def log(self, time: str, message: str, level: Optional[Union[int, str]] = None,
1317-
attachment: Optional[dict] = None, item_id: Optional[Task[str]] = None) -> Task[Tuple[str, ...]]:
1317+
attachment: Optional[dict] = None, item_id: Optional[Task[str]] = None) -> Task[Optional[Tuple[str, ...]]]:
13181318
"""Send Log message to the ReportPortal and attach it to a Test Item or Launch.
13191319
13201320
This method stores Log messages in internal batch and sent it when batch is full, so not every method
@@ -1338,6 +1338,11 @@ def close(self) -> None:
13381338
self.create_task(self.__client.close()).blocking_result()
13391339

13401340

1341+
def heartbeat(self):
1342+
"""Heartbeat function to keep the loop running."""
1343+
self._loop.call_at(self._loop.time() + 0.1, heartbeat, self)
1344+
1345+
13411346
class ThreadedRPClient(_RPClient):
13421347
"""Synchronous-asynchronous ReportPortal Client which uses background Thread to execute async coroutines.
13431348
@@ -1369,7 +1374,7 @@ def __init_task_list(self, task_list: Optional[BackgroundTaskList[Task[_T]]] = N
13691374
def __heartbeat(self):
13701375
# We operate on our own loop with daemon thread, so we will exit in any way when main thread exit,
13711376
# so we can iterate forever
1372-
self._loop.call_at(self._loop.time() + 0.1, self.__heartbeat)
1377+
heartbeat(self)
13731378

13741379
def __init_loop(self, loop: Optional[asyncio.AbstractEventLoop] = None):
13751380
self._thread = None
@@ -1471,6 +1476,8 @@ def finish_tasks(self):
14711476
break
14721477
logs = self._log_batcher.flush()
14731478
if logs:
1479+
# We use own Task Factory in which we add the following method to the Task class
1480+
# noinspection PyUnresolvedReferences
14741481
self._loop.create_task(self._log_batch(logs)).blocking_result()
14751482

14761483
def clone(self) -> 'ThreadedRPClient':

reportportal_client/steps/__init__.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,38 @@ def test_my_nested_step():
4343
4444
"""
4545
from functools import wraps
46+
from typing import Callable, ParamSpec, TypeVar, Optional, Dict, Union, Type
4647

48+
from reportportal_client import RP
49+
# noinspection PyProtectedMember
50+
from reportportal_client._internal.aio.tasks import Task
4751
# noinspection PyProtectedMember
4852
from reportportal_client._internal.local import current
4953
from reportportal_client.helpers import get_function_params, timestamp
5054

51-
NESTED_STEP_ITEMS = ('step', 'scenario', 'before_class', 'before_groups',
52-
'before_method', 'before_suite', 'before_test',
53-
'after_test', 'after_suite', 'after_class',
54-
'after_groups', 'after_method')
55+
NESTED_STEP_ITEMS = ('step', 'scenario', 'before_class', 'before_groups', 'before_method', 'before_suite',
56+
'before_test', 'after_test', 'after_suite', 'after_class', 'after_groups', 'after_method')
57+
58+
_Param = ParamSpec("_Param")
59+
_Return = TypeVar("_Return")
5560

5661

5762
# noinspection PyUnresolvedReferences
5863
class StepReporter:
5964
"""Nested Steps context handling class."""
6065

61-
def __init__(self, rp_client):
66+
client: RP
67+
68+
def __init__(self, rp_client: RP):
6269
"""Initialize required attributes.
6370
6471
:param rp_client: ReportPortal client which will be used to report
6572
steps
6673
"""
6774
self.client = rp_client
6875

69-
def start_nested_step(self, name, start_time, parameters=None, **_):
76+
def start_nested_step(self, name: str, start_time: str, parameters: Optional[Dict[str, Any]] = None,
77+
**_: Dict[str, Any]) -> Union[Optional[str], Task[Optional[str]]]:
7078
"""Start Nested Step on ReportPortal.
7179
7280
:param name: Nested Step name
@@ -79,7 +87,8 @@ def start_nested_step(self, name, start_time, parameters=None, **_):
7987
return self.client.start_test_item(
8088
name, start_time, 'step', has_stats=False, parameters=parameters, parent_item_id=parent_id)
8189

82-
def finish_nested_step(self, item_id, end_time, status=None, **_):
90+
def finish_nested_step(self, item_id: str, end_time: str, status: str = None,
91+
**_: Dict[str, Any]) -> Union[Optional[str], Task[Optional[str]]]:
8392
"""Finish a Nested Step on ReportPortal.
8493
8594
:param item_id: Nested Step item ID
@@ -89,10 +98,16 @@ def finish_nested_step(self, item_id, end_time, status=None, **_):
8998
return self.client.finish_test_item(item_id, end_time, status=status)
9099

91100

92-
class Step:
101+
class Step(Callable[[_Param], _Return]):
93102
"""Step context handling class."""
94103

95-
def __init__(self, name, params, status, rp_client):
104+
name: str
105+
params: Dict
106+
status: str
107+
client: Optional[RP]
108+
__item_id: Union[Optional[str], Task[Optional[str]]]
109+
110+
def __init__(self, name: str, params: Dict, status: str, rp_client: Optional[RP]):
96111
"""Initialize required attributes.
97112
98113
:param name: Nested Step name
@@ -108,7 +123,7 @@ def __init__(self, name, params, status, rp_client):
108123
self.client = rp_client
109124
self.__item_id = None
110125

111-
def __enter__(self):
126+
def __enter__(self) -> None:
112127
"""Enter the runtime context related to this object."""
113128
# Cannot call _local.current() early since it will be initialized
114129
# before client put something in there
@@ -124,7 +139,7 @@ def __enter__(self):
124139
param_str = 'Parameters: ' + '; '.join(param_list)
125140
rp_client.log(timestamp(), param_str, level='INFO', item_id=self.__item_id)
126141

127-
def __exit__(self, exc_type, exc_val, exc_tb):
142+
def __exit__(self, exc_type: Type[BaseException], exc_val, exc_tb) -> None:
128143
"""Exit the runtime context related to this object."""
129144
# Cannot call local.current() early since it will be initialized before client put something in there
130145
rp_client = self.client or current()
@@ -138,25 +153,27 @@ def __exit__(self, exc_type, exc_val, exc_tb):
138153
step_status = 'FAILED'
139154
rp_client.step_reporter.finish_nested_step(self.__item_id, timestamp(), step_status)
140155

141-
def __call__(self, func):
156+
def __call__(self, *args, **kwargs):
142157
"""Wrap and call a function reference.
143158
144159
:param func: function reference
145160
"""
161+
func = args[0]
146162

147163
@wraps(func)
148-
def wrapper(*args, **kwargs):
164+
def wrapper(*my_args, **my_kwargs):
149165
__tracebackhide__ = True
150166
params = self.params
151167
if params is None:
152-
params = get_function_params(func, args, kwargs)
168+
params = get_function_params(func, my_args, my_kwargs)
153169
with Step(self.name, params, self.status, self.client):
154-
return func(*args, **kwargs)
170+
return func(*my_args, **my_kwargs)
155171

156172
return wrapper
157173

158174

159-
def step(name_source, params=None, status='PASSED', rp_client=None):
175+
def step(name_source: Union[Callable[[_Param], _Return], str], params: Optional[Dict] = None, status: str = 'PASSED',
176+
rp_client: Optional[RP] = None) -> Callable[[_Param], _Return]:
160177
"""Nested step report function.
161178
162179
Create a Nested Step inside a test method on ReportPortal.

reportportal_client/steps/__init__.pyi

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)