Skip to content

Commit fbb7721

Browse files
authored
Merge pull request #386 from reportportal/develop
Release
2 parents 2a4e78c + ed5f381 commit fbb7721

File tree

17 files changed

+337
-239
lines changed

17 files changed

+337
-239
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
runs-on: ubuntu-latest
2121
strategy:
2222
matrix:
23-
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12' ]
23+
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ]
2424
steps:
2525
- name: Checkout repository
2626
uses: actions/checkout@v4
@@ -39,7 +39,7 @@ jobs:
3939
run: tox
4040

4141
- name: Upload coverage to Codecov
42-
if: matrix.python-version == 3.8 && success()
42+
if: matrix.python-version == 3.10 && success()
4343
uses: codecov/codecov-action@v4
4444
with:
4545
token: ${{ secrets.CODECOV_TOKEN }}

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
# Changelog
22

33
## [Unreleased]
4+
### Added
5+
- Support for `Python 3.13`, by @HardNorth
6+
- Support for `name` Pytest marker, by @HardNorth
7+
- `rp_hierarchy_test_file` configuration parameter, which controls display of test file name in the hierarchy, by @ramir-dn, @HardNorth
8+
### Fixed
9+
- Agent crash if Client could not be initialized, by @HardNorth
10+
### Changed
11+
- Client version updated on [5.5.10](https://github.com/reportportal/client-Python/releases/tag/5.5.10), by @HardNorth
12+
13+
## [5.4.5]
414
### Fixed
515
- Issue [#379](https://github.com/reportportal/agent-python-pytest/issues/379): Fix TypeError when using pytest.skip() in fixtures, by @HardNorth
616

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2024 EPAM Systems
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import pytest
15+
16+
TEST_NAME_ARGS = 'Test name by mark'
17+
18+
19+
@pytest.mark.name(TEST_NAME_ARGS)
20+
def test_name_by_mark_args():
21+
"""Simple example test with the name comes from Pytest mark."""
22+
assert True
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2024 EPAM Systems
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import pytest
15+
16+
TEST_NAME_EMPTY = 'examples/custom_name/test_custom_name_empty.py::test_name_by_mark_empty'
17+
18+
19+
@pytest.mark.name()
20+
def test_name_by_mark_empty():
21+
"""Simple example test with the name comes from Pytest mark."""
22+
assert True
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2024 EPAM Systems
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import pytest
15+
16+
TEST_NAME_KWARGS = 'Test name by mark, kwargs'
17+
18+
19+
@pytest.mark.name(name=TEST_NAME_KWARGS)
20+
def test_name_by_mark_kwargs():
21+
"""Simple example test with the name comes from Pytest mark."""
22+
assert True

pytest_reportportal/config.py

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
from _pytest.config import Config
2121
from reportportal_client import OutputType, ClientType
22-
from reportportal_client.logs import MAX_LOG_BATCH_PAYLOAD_SIZE
2322
from reportportal_client.helpers import to_bool
23+
from reportportal_client.logs import MAX_LOG_BATCH_PAYLOAD_SIZE
2424

2525
try:
2626
# This try/except can go away once we support pytest >= 5.4.0
@@ -40,6 +40,7 @@ class AgentConfig:
4040
rp_hierarchy_code: bool
4141
rp_dir_level: int
4242
rp_hierarchy_dirs: bool
43+
rp_hierarchy_test_file: bool
4344
rp_dir_path_separator: str
4445
rp_ignore_attributes: set
4546
rp_is_skipped_an_issue: bool
@@ -71,24 +72,16 @@ class AgentConfig:
7172

7273
def __init__(self, pytest_config: Config) -> None:
7374
"""Initialize required attributes."""
74-
self.rp_rerun = (pytest_config.option.rp_rerun or
75-
pytest_config.getini('rp_rerun'))
75+
self.rp_rerun = (pytest_config.option.rp_rerun or pytest_config.getini('rp_rerun'))
7676
self.rp_endpoint = self.find_option(pytest_config, 'rp_endpoint')
77-
self.rp_hierarchy_code = self.find_option(pytest_config,
78-
'rp_hierarchy_code')
79-
self.rp_dir_level = int(self.find_option(pytest_config,
80-
'rp_hierarchy_dirs_level'))
81-
self.rp_hierarchy_dirs = self.find_option(pytest_config,
82-
'rp_hierarchy_dirs')
83-
self.rp_dir_path_separator = \
84-
self.find_option(pytest_config, 'rp_hierarchy_dir_path_separator')
77+
self.rp_hierarchy_code = to_bool(self.find_option(pytest_config, 'rp_hierarchy_code'))
78+
self.rp_dir_level = int(self.find_option(pytest_config, 'rp_hierarchy_dirs_level'))
79+
self.rp_hierarchy_dirs = to_bool(self.find_option(pytest_config, 'rp_hierarchy_dirs'))
80+
self.rp_dir_path_separator = self.find_option(pytest_config, 'rp_hierarchy_dir_path_separator')
81+
self.rp_hierarchy_test_file = to_bool(self.find_option(pytest_config, 'rp_hierarchy_test_file'))
8582
self.rp_ignore_attributes = set(self.find_option(pytest_config, 'rp_ignore_attributes') or [])
86-
self.rp_is_skipped_an_issue = self.find_option(
87-
pytest_config,
88-
'rp_is_skipped_an_issue'
89-
)
90-
self.rp_issue_id_marks = self.find_option(pytest_config,
91-
'rp_issue_id_marks')
83+
self.rp_is_skipped_an_issue = self.find_option(pytest_config, 'rp_is_skipped_an_issue')
84+
self.rp_issue_id_marks = self.find_option(pytest_config, 'rp_issue_id_marks')
9285
self.rp_bts_issue_url = self.find_option(pytest_config, 'rp_bts_issue_url')
9386
if not self.rp_bts_issue_url:
9487
self.rp_bts_issue_url = self.find_option(pytest_config, 'rp_issue_system_url')
@@ -103,14 +96,10 @@ def __init__(self, pytest_config: Config) -> None:
10396
self.rp_bts_url = self.find_option(pytest_config, 'rp_bts_url')
10497
self.rp_launch = self.find_option(pytest_config, 'rp_launch')
10598
self.rp_launch_id = self.find_option(pytest_config, 'rp_launch_id')
106-
self.rp_launch_attributes = self.find_option(pytest_config,
107-
'rp_launch_attributes')
108-
self.rp_launch_description = self.find_option(pytest_config,
109-
'rp_launch_description')
110-
self.rp_log_batch_size = int(self.find_option(pytest_config,
111-
'rp_log_batch_size'))
112-
batch_payload_size = self.find_option(
113-
pytest_config, 'rp_log_batch_payload_size')
99+
self.rp_launch_attributes = self.find_option(pytest_config, 'rp_launch_attributes')
100+
self.rp_launch_description = self.find_option(pytest_config, 'rp_launch_description')
101+
self.rp_log_batch_size = int(self.find_option(pytest_config, 'rp_log_batch_size'))
102+
batch_payload_size = self.find_option(pytest_config, 'rp_log_batch_payload_size')
114103
if batch_payload_size:
115104
self.rp_log_batch_payload_size = int(batch_payload_size)
116105
else:
@@ -119,16 +108,10 @@ def __init__(self, pytest_config: Config) -> None:
119108
self.rp_log_format = self.find_option(pytest_config, 'rp_log_format')
120109
self.rp_thread_logging = to_bool(self.find_option(pytest_config, 'rp_thread_logging') or False)
121110
self.rp_mode = self.find_option(pytest_config, 'rp_mode')
122-
self.rp_parent_item_id = self.find_option(pytest_config,
123-
'rp_parent_item_id')
124-
self.rp_project = self.find_option(pytest_config,
125-
'rp_project')
126-
self.rp_rerun_of = self.find_option(pytest_config,
127-
'rp_rerun_of')
128-
self.rp_skip_connection_test = str(
129-
self.find_option(pytest_config,
130-
'rp_skip_connection_test')).lower() in (
131-
'true', '1', 'yes', 'y')
111+
self.rp_parent_item_id = self.find_option(pytest_config, 'rp_parent_item_id')
112+
self.rp_project = self.find_option(pytest_config, 'rp_project')
113+
self.rp_rerun_of = self.find_option(pytest_config, 'rp_rerun_of')
114+
self.rp_skip_connection_test = to_bool(self.find_option(pytest_config, 'rp_skip_connection_test'))
132115

133116
rp_api_retries_str = self.find_option(pytest_config, 'rp_api_retries')
134117
rp_api_retries = rp_api_retries_str and int(rp_api_retries_str)
@@ -179,8 +162,7 @@ def __init__(self, pytest_config: Config) -> None:
179162
self.rp_verify_ssl = to_bool(rp_verify_ssl)
180163
except (ValueError, AttributeError):
181164
self.rp_verify_ssl = rp_verify_ssl
182-
self.rp_launch_timeout = int(
183-
self.find_option(pytest_config, 'rp_launch_timeout'))
165+
self.rp_launch_timeout = int(self.find_option(pytest_config, 'rp_launch_timeout'))
184166

185167
self.rp_launch_uuid_print = to_bool(self.find_option(pytest_config, 'rp_launch_uuid_print') or 'False')
186168
print_output = self.find_option(pytest_config, 'rp_launch_uuid_print_output')
@@ -215,10 +197,7 @@ def find_option(self, pytest_config: Config, option_name: str, default: Any = No
215197
:param default: value to be returned if not found
216198
:return: option value
217199
"""
218-
value = (
219-
getattr(pytest_config.option, option_name, None) or
220-
pytest_config.getini(option_name)
221-
)
200+
value = (getattr(pytest_config.option, option_name, None) or pytest_config.getini(option_name))
222201
if isinstance(value, bool):
223202
return value
224203
return value or default

pytest_reportportal/plugin.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def register_markers(config) -> None:
165165
"params [parameter names as list] - use only specified"
166166
"parameters"
167167
)
168+
config.addinivalue_line(
169+
"markers", "name(name): report the test case with a custom Name."
170+
)
168171

169172

170173
def check_connection(agent_config: AgentConfig):
@@ -505,6 +508,11 @@ def add_shared_option(name, help_str, default=None, action='store'):
505508
'rp_hierarchy_dir_path_separator',
506509
default=os.path.sep,
507510
help='Path separator to display directories in test hierarchy')
511+
parser.addini(
512+
'rp_hierarchy_test_file',
513+
default=True,
514+
type='bool',
515+
help='Show file name in hierarchy')
508516
parser.addini(
509517
'rp_issue_system_url',
510518
default='',

pytest_reportportal/rp_logging.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import threading
1919
from contextlib import contextmanager
2020
from functools import wraps
21+
from typing import Any
2122

2223
from reportportal_client import current, set_current
2324
from reportportal_client import RPLogger
@@ -116,23 +117,22 @@ def patching_logger_class():
116117
try:
117118
def wrap_log(original_func):
118119
@wraps(original_func)
119-
def _log(self, *args, **kwargs):
120-
attachment = kwargs.pop('attachment', None)
120+
def _log(self, *args: list[Any], **kwargs: dict[str, Any]):
121+
my_kwargs = kwargs.copy()
122+
attachment = my_kwargs.pop('attachment', None)
121123
if attachment is not None:
122-
kwargs.setdefault('extra', {}).update(
123-
{'attachment': attachment})
124+
my_kwargs.setdefault('extra', {}).update({'attachment': attachment})
125+
124126
# Python 3.11 start catches stack frames in wrappers,
125127
# so add additional stack level skip to not show it
126128
if sys.version_info >= (3, 11):
127-
my_kwargs = kwargs.copy()
128-
if 'stacklevel' in kwargs:
129+
if 'stacklevel' in my_kwargs:
129130
my_kwargs['stacklevel'] = my_kwargs['stacklevel'] + 1
130131
else:
131132
my_kwargs['stacklevel'] = 2
132133
return original_func(self, *args, **my_kwargs)
133134
else:
134-
return original_func(self, *args, **kwargs)
135-
135+
return original_func(self, *args, **my_kwargs)
136136
return _log
137137

138138
def wrap_makeRecord(original_func):

0 commit comments

Comments
 (0)