Skip to content

Commit

Permalink
Merge pull request #386 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Dec 3, 2024
2 parents 2a4e78c + ed5f381 commit fbb7721
Show file tree
Hide file tree
Showing 17 changed files with 337 additions and 239 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12' ]
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -39,7 +39,7 @@ jobs:
run: tox

- name: Upload coverage to Codecov
if: matrix.python-version == 3.8 && success()
if: matrix.python-version == 3.10 && success()
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Changelog

## [Unreleased]
### Added
- Support for `Python 3.13`, by @HardNorth
- Support for `name` Pytest marker, by @HardNorth
- `rp_hierarchy_test_file` configuration parameter, which controls display of test file name in the hierarchy, by @ramir-dn, @HardNorth
### Fixed
- Agent crash if Client could not be initialized, by @HardNorth
### Changed
- Client version updated on [5.5.10](https://github.com/reportportal/client-Python/releases/tag/5.5.10), by @HardNorth

## [5.4.5]
### Fixed
- Issue [#379](https://github.com/reportportal/agent-python-pytest/issues/379): Fix TypeError when using pytest.skip() in fixtures, by @HardNorth

Expand Down
22 changes: 22 additions & 0 deletions examples/custom_name/test_custom_name_args.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 EPAM Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest

TEST_NAME_ARGS = 'Test name by mark'


@pytest.mark.name(TEST_NAME_ARGS)
def test_name_by_mark_args():
"""Simple example test with the name comes from Pytest mark."""
assert True
22 changes: 22 additions & 0 deletions examples/custom_name/test_custom_name_empty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 EPAM Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest

TEST_NAME_EMPTY = 'examples/custom_name/test_custom_name_empty.py::test_name_by_mark_empty'


@pytest.mark.name()
def test_name_by_mark_empty():
"""Simple example test with the name comes from Pytest mark."""
assert True
22 changes: 22 additions & 0 deletions examples/custom_name/test_custom_name_kwargs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 EPAM Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest

TEST_NAME_KWARGS = 'Test name by mark, kwargs'


@pytest.mark.name(name=TEST_NAME_KWARGS)
def test_name_by_mark_kwargs():
"""Simple example test with the name comes from Pytest mark."""
assert True
61 changes: 20 additions & 41 deletions pytest_reportportal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

from _pytest.config import Config
from reportportal_client import OutputType, ClientType
from reportportal_client.logs import MAX_LOG_BATCH_PAYLOAD_SIZE
from reportportal_client.helpers import to_bool
from reportportal_client.logs import MAX_LOG_BATCH_PAYLOAD_SIZE

try:
# This try/except can go away once we support pytest >= 5.4.0
Expand All @@ -40,6 +40,7 @@ class AgentConfig:
rp_hierarchy_code: bool
rp_dir_level: int
rp_hierarchy_dirs: bool
rp_hierarchy_test_file: bool
rp_dir_path_separator: str
rp_ignore_attributes: set
rp_is_skipped_an_issue: bool
Expand Down Expand Up @@ -71,24 +72,16 @@ class AgentConfig:

def __init__(self, pytest_config: Config) -> None:
"""Initialize required attributes."""
self.rp_rerun = (pytest_config.option.rp_rerun or
pytest_config.getini('rp_rerun'))
self.rp_rerun = (pytest_config.option.rp_rerun or pytest_config.getini('rp_rerun'))
self.rp_endpoint = self.find_option(pytest_config, 'rp_endpoint')
self.rp_hierarchy_code = self.find_option(pytest_config,
'rp_hierarchy_code')
self.rp_dir_level = int(self.find_option(pytest_config,
'rp_hierarchy_dirs_level'))
self.rp_hierarchy_dirs = self.find_option(pytest_config,
'rp_hierarchy_dirs')
self.rp_dir_path_separator = \
self.find_option(pytest_config, 'rp_hierarchy_dir_path_separator')
self.rp_hierarchy_code = to_bool(self.find_option(pytest_config, 'rp_hierarchy_code'))
self.rp_dir_level = int(self.find_option(pytest_config, 'rp_hierarchy_dirs_level'))
self.rp_hierarchy_dirs = to_bool(self.find_option(pytest_config, 'rp_hierarchy_dirs'))
self.rp_dir_path_separator = self.find_option(pytest_config, 'rp_hierarchy_dir_path_separator')
self.rp_hierarchy_test_file = to_bool(self.find_option(pytest_config, 'rp_hierarchy_test_file'))
self.rp_ignore_attributes = set(self.find_option(pytest_config, 'rp_ignore_attributes') or [])
self.rp_is_skipped_an_issue = self.find_option(
pytest_config,
'rp_is_skipped_an_issue'
)
self.rp_issue_id_marks = self.find_option(pytest_config,
'rp_issue_id_marks')
self.rp_is_skipped_an_issue = self.find_option(pytest_config, 'rp_is_skipped_an_issue')
self.rp_issue_id_marks = self.find_option(pytest_config, 'rp_issue_id_marks')
self.rp_bts_issue_url = self.find_option(pytest_config, 'rp_bts_issue_url')
if not self.rp_bts_issue_url:
self.rp_bts_issue_url = self.find_option(pytest_config, 'rp_issue_system_url')
Expand All @@ -103,14 +96,10 @@ def __init__(self, pytest_config: Config) -> None:
self.rp_bts_url = self.find_option(pytest_config, 'rp_bts_url')
self.rp_launch = self.find_option(pytest_config, 'rp_launch')
self.rp_launch_id = self.find_option(pytest_config, 'rp_launch_id')
self.rp_launch_attributes = self.find_option(pytest_config,
'rp_launch_attributes')
self.rp_launch_description = self.find_option(pytest_config,
'rp_launch_description')
self.rp_log_batch_size = int(self.find_option(pytest_config,
'rp_log_batch_size'))
batch_payload_size = self.find_option(
pytest_config, 'rp_log_batch_payload_size')
self.rp_launch_attributes = self.find_option(pytest_config, 'rp_launch_attributes')
self.rp_launch_description = self.find_option(pytest_config, 'rp_launch_description')
self.rp_log_batch_size = int(self.find_option(pytest_config, 'rp_log_batch_size'))
batch_payload_size = self.find_option(pytest_config, 'rp_log_batch_payload_size')
if batch_payload_size:
self.rp_log_batch_payload_size = int(batch_payload_size)
else:
Expand All @@ -119,16 +108,10 @@ def __init__(self, pytest_config: Config) -> None:
self.rp_log_format = self.find_option(pytest_config, 'rp_log_format')
self.rp_thread_logging = to_bool(self.find_option(pytest_config, 'rp_thread_logging') or False)
self.rp_mode = self.find_option(pytest_config, 'rp_mode')
self.rp_parent_item_id = self.find_option(pytest_config,
'rp_parent_item_id')
self.rp_project = self.find_option(pytest_config,
'rp_project')
self.rp_rerun_of = self.find_option(pytest_config,
'rp_rerun_of')
self.rp_skip_connection_test = str(
self.find_option(pytest_config,
'rp_skip_connection_test')).lower() in (
'true', '1', 'yes', 'y')
self.rp_parent_item_id = self.find_option(pytest_config, 'rp_parent_item_id')
self.rp_project = self.find_option(pytest_config, 'rp_project')
self.rp_rerun_of = self.find_option(pytest_config, 'rp_rerun_of')
self.rp_skip_connection_test = to_bool(self.find_option(pytest_config, 'rp_skip_connection_test'))

rp_api_retries_str = self.find_option(pytest_config, 'rp_api_retries')
rp_api_retries = rp_api_retries_str and int(rp_api_retries_str)
Expand Down Expand Up @@ -179,8 +162,7 @@ def __init__(self, pytest_config: Config) -> None:
self.rp_verify_ssl = to_bool(rp_verify_ssl)
except (ValueError, AttributeError):
self.rp_verify_ssl = rp_verify_ssl
self.rp_launch_timeout = int(
self.find_option(pytest_config, 'rp_launch_timeout'))
self.rp_launch_timeout = int(self.find_option(pytest_config, 'rp_launch_timeout'))

self.rp_launch_uuid_print = to_bool(self.find_option(pytest_config, 'rp_launch_uuid_print') or 'False')
print_output = self.find_option(pytest_config, 'rp_launch_uuid_print_output')
Expand Down Expand Up @@ -215,10 +197,7 @@ def find_option(self, pytest_config: Config, option_name: str, default: Any = No
:param default: value to be returned if not found
:return: option value
"""
value = (
getattr(pytest_config.option, option_name, None) or
pytest_config.getini(option_name)
)
value = (getattr(pytest_config.option, option_name, None) or pytest_config.getini(option_name))
if isinstance(value, bool):
return value
return value or default
8 changes: 8 additions & 0 deletions pytest_reportportal/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ def register_markers(config) -> None:
"params [parameter names as list] - use only specified"
"parameters"
)
config.addinivalue_line(
"markers", "name(name): report the test case with a custom Name."
)


def check_connection(agent_config: AgentConfig):
Expand Down Expand Up @@ -505,6 +508,11 @@ def add_shared_option(name, help_str, default=None, action='store'):
'rp_hierarchy_dir_path_separator',
default=os.path.sep,
help='Path separator to display directories in test hierarchy')
parser.addini(
'rp_hierarchy_test_file',
default=True,
type='bool',
help='Show file name in hierarchy')
parser.addini(
'rp_issue_system_url',
default='',
Expand Down
16 changes: 8 additions & 8 deletions pytest_reportportal/rp_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import threading
from contextlib import contextmanager
from functools import wraps
from typing import Any

from reportportal_client import current, set_current
from reportportal_client import RPLogger
Expand Down Expand Up @@ -116,23 +117,22 @@ def patching_logger_class():
try:
def wrap_log(original_func):
@wraps(original_func)
def _log(self, *args, **kwargs):
attachment = kwargs.pop('attachment', None)
def _log(self, *args: list[Any], **kwargs: dict[str, Any]):
my_kwargs = kwargs.copy()
attachment = my_kwargs.pop('attachment', None)
if attachment is not None:
kwargs.setdefault('extra', {}).update(
{'attachment': attachment})
my_kwargs.setdefault('extra', {}).update({'attachment': attachment})

# Python 3.11 start catches stack frames in wrappers,
# so add additional stack level skip to not show it
if sys.version_info >= (3, 11):
my_kwargs = kwargs.copy()
if 'stacklevel' in kwargs:
if 'stacklevel' in my_kwargs:
my_kwargs['stacklevel'] = my_kwargs['stacklevel'] + 1
else:
my_kwargs['stacklevel'] = 2
return original_func(self, *args, **my_kwargs)
else:
return original_func(self, *args, **kwargs)

return original_func(self, *args, **my_kwargs)
return _log

def wrap_makeRecord(original_func):
Expand Down
Loading

0 comments on commit fbb7721

Please sign in to comment.