diff options
| author | robot-piglet <[email protected]> | 2025-08-06 12:21:25 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2025-08-06 12:45:52 +0300 |
| commit | b278c497092da9902bf6443e607adea8b8e0f62c (patch) | |
| tree | 1133d38f2f98ec56f492a02c19166fc181ecce2d /contrib/python/allure-pytest | |
| parent | ba4771ba91781c6aef0bfa2506302c4b3e2a2e73 (diff) | |
Intermediate changes
commit_hash:65a45f10733cde088e0f615846f2d8beb4279a81
Diffstat (limited to 'contrib/python/allure-pytest')
| -rw-r--r-- | contrib/python/allure-pytest/.dist-info/METADATA | 4 | ||||
| -rw-r--r-- | contrib/python/allure-pytest/allure_pytest/listener.py | 2 | ||||
| -rw-r--r-- | contrib/python/allure-pytest/allure_pytest/stash.py | 61 | ||||
| -rw-r--r-- | contrib/python/allure-pytest/allure_pytest/utils.py | 72 | ||||
| -rw-r--r-- | contrib/python/allure-pytest/ya.make | 3 |
5 files changed, 118 insertions, 24 deletions
diff --git a/contrib/python/allure-pytest/.dist-info/METADATA b/contrib/python/allure-pytest/.dist-info/METADATA index 96d707c9148..1407f592c09 100644 --- a/contrib/python/allure-pytest/.dist-info/METADATA +++ b/contrib/python/allure-pytest/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: allure-pytest -Version: 2.14.3 +Version: 2.15.0 Summary: Allure pytest integration Home-page: https://allurereport.org/ Author: Qameta Software Inc., Stanislav Seliverstov @@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Description-Content-Type: text/markdown Requires-Dist: pytest>=4.5.0 -Requires-Dist: allure-python-commons==2.14.3 +Requires-Dist: allure-python-commons==2.15.0 Dynamic: author Dynamic: author-email Dynamic: classifier diff --git a/contrib/python/allure-pytest/allure_pytest/listener.py b/contrib/python/allure-pytest/allure_pytest/listener.py index 11153630911..42b7ff49176 100644 --- a/contrib/python/allure-pytest/allure_pytest/listener.py +++ b/contrib/python/allure-pytest/allure_pytest/listener.py @@ -19,6 +19,7 @@ from allure_commons.types import LabelType, AttachmentType, ParameterMode from allure_pytest.utils import allure_description, allure_description_html from allure_pytest.utils import allure_labels, allure_links, pytest_markers from allure_pytest.utils import allure_full_name, allure_package, allure_name +from allure_pytest.utils import allure_title_path from allure_pytest.utils import allure_suite_labels from allure_pytest.utils import get_status, get_status_details from allure_pytest.utils import get_outcome_status, get_outcome_status_details @@ -109,6 +110,7 @@ class AllureListener: test_result.name = allure_name(item, params, param_id) full_name = allure_full_name(item) test_result.fullName = full_name + test_result.titlePath = [*allure_title_path(item)] test_result.testCaseId = md5(full_name) test_result.description = allure_description(item) test_result.descriptionHtml = allure_description_html(item) diff --git a/contrib/python/allure-pytest/allure_pytest/stash.py b/contrib/python/allure-pytest/allure_pytest/stash.py new file mode 100644 index 00000000000..31d9302b802 --- /dev/null +++ b/contrib/python/allure-pytest/allure_pytest/stash.py @@ -0,0 +1,61 @@ +import pytest +from functools import wraps + +HAS_STASH = hasattr(pytest, 'StashKey') + + +def create_stashkey_safe(): + """ + If pytest stash is available, returns a new stash key. + Otherwise, returns `None`. + """ + + return pytest.StashKey() if HAS_STASH else None + + +def stash_get_safe(item, key): + """ + If pytest stash is available and contains the key, retrieves the associated value. + Otherwise, returns `None`. + """ + + if HAS_STASH and key in item.stash: + return item.stash[key] + + +def stash_set_safe(item: pytest.Item, key, value): + """ + If pytest stash is available, associates the value with the key in the stash. + Otherwise, does nothing. + """ + + if HAS_STASH: + item.stash[key] = value + + +def stashed(arg=None): + """ + Cashes the result of the decorated function in the pytest item stash. + The first argument of the function must be a pytest item. + + In pytest<7.0 the stash is not available, so the decorator does nothing. + """ + + key = create_stashkey_safe() if arg is None or callable(arg) else arg + + def decorator(func): + if not HAS_STASH: + return func + + @wraps(func) + def wrapper(item, *args, **kwargs): + if key in item.stash: + return item.stash[key] + + value = func(item, *args, **kwargs) + item.stash[key] = value + return value + + return wrapper + + return decorator(arg) if callable(arg) else decorator diff --git a/contrib/python/allure-pytest/allure_pytest/utils.py b/contrib/python/allure-pytest/allure_pytest/utils.py index 19145510fa6..56594a09503 100644 --- a/contrib/python/allure-pytest/allure_pytest/utils.py +++ b/contrib/python/allure-pytest/allure_pytest/utils.py @@ -1,11 +1,11 @@ import pytest -from itertools import chain, islice +from itertools import repeat from allure_commons.utils import SafeFormatter, md5 from allure_commons.utils import format_exception, format_traceback from allure_commons.model2 import Status from allure_commons.model2 import StatusDetails from allure_commons.types import LabelType - +from allure_pytest.stash import stashed ALLURE_DESCRIPTION_MARK = 'allure_description' ALLURE_DESCRIPTION_HTML_MARK = 'allure_description_html' @@ -30,6 +30,24 @@ MARK_NAMES_TO_IGNORE = { } +class ParsedPytestNodeId: + def __init__(self, nodeid): + filepath, *class_names, function_segment = ensure_len(nodeid.split("::"), 2) + self.filepath = filepath + self.path_segments = filepath.split('/') + *parent_dirs, filename = ensure_len(self.path_segments, 1) + self.parent_package = '.'.join(parent_dirs) + self.module = filename.rsplit(".", 1)[0] + self.package = '.'.join(filter(None, [self.parent_package, self.module])) + self.class_names = class_names + self.test_function = function_segment.split("[", 1)[0] + + +@stashed +def parse_nodeid(item): + return ParsedPytestNodeId(item.nodeid) + + def get_marker_value(item, keyword): marker = item.get_closest_marker(keyword) return marker.args[0] if marker and marker.args else None @@ -101,9 +119,7 @@ def should_convert_mark_to_tag(mark): def allure_package(item): - parts = item.nodeid.split('::') - path = parts[0].rsplit('.', 1)[0] - return path.replace('/', '.') + return parse_nodeid(item).package def allure_name(item, parameters, param_id=None): @@ -122,27 +138,41 @@ def allure_name(item, parameters, param_id=None): def allure_full_name(item: pytest.Item): - package = allure_package(item) - class_name = f".{item.parent.name}" if isinstance(item.parent, pytest.Class) else '' - test = item.originalname if isinstance(item, pytest.Function) else item.name.split("[")[0] - full_name = f'{package}{class_name}#{test}' + nodeid = parse_nodeid(item) + class_part = ("." + ".".join(nodeid.class_names)) if nodeid.class_names else "" + test = item.originalname if isinstance(item, pytest.Function) else nodeid.test_function + full_name = f"{nodeid.package}{class_part}#{test}" return full_name +def allure_title_path(item): + nodeid = parse_nodeid(item) + return list( + filter(None, [*nodeid.path_segments, *nodeid.class_names]), + ) + + +def ensure_len(value, min_length, fill_value=None): + yield from value + yield from repeat(fill_value, min_length - len(value)) + + def allure_suite_labels(item): - head, possibly_clazz, tail = islice(chain(item.nodeid.split('::'), [None], [None]), 3) - clazz = possibly_clazz if tail else None - file_name, path = islice(chain(reversed(head.rsplit('/', 1)), [None]), 2) - module = file_name.split('.')[0] - package = path.replace('/', '.') if path else None - pairs = dict(zip([LabelType.PARENT_SUITE, LabelType.SUITE, LabelType.SUB_SUITE], [package, module, clazz])) - labels = dict(allure_labels(item)) - default_suite_labels = [] - for label, value in pairs.items(): - if label not in labels.keys() and value: - default_suite_labels.append((label, value)) + nodeid = parse_nodeid(item) + + default_suite_labels = { + LabelType.PARENT_SUITE: nodeid.parent_package, + LabelType.SUITE: nodeid.module, + LabelType.SUB_SUITE: " > ".join(nodeid.class_names), + } + + existing_labels = dict(allure_labels(item)) + resolved_default_suite_labels = [] + for label, value in default_suite_labels.items(): + if label not in existing_labels and value: + resolved_default_suite_labels.append((label, value)) - return default_suite_labels + return resolved_default_suite_labels def get_outcome_status(outcome): diff --git a/contrib/python/allure-pytest/ya.make b/contrib/python/allure-pytest/ya.make index 1680e08c937..156fdc8d0ee 100644 --- a/contrib/python/allure-pytest/ya.make +++ b/contrib/python/allure-pytest/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(2.14.3) +VERSION(2.15.0) LICENSE(Apache-2.0) @@ -20,6 +20,7 @@ PY_SRCS( allure_pytest/helper.py allure_pytest/listener.py allure_pytest/plugin.py + allure_pytest/stash.py allure_pytest/utils.py ) |
