Skip to content

Commit

Permalink
Merge pull request #110 from elchupanebrej/facelift
Browse files Browse the repository at this point in the history
Update pytest versions support
  • Loading branch information
elchupanebrej authored Aug 31, 2024
2 parents 213ffbe + 35074dc commit deb34cd
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 46 deletions.
6 changes: 5 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Planned
- parse_error
- undefined_parameter_type

- Add support of python 3.12 at CI
- Add mode to execute scenarios with missing/failing steps
- Remove

Expand All @@ -50,6 +49,11 @@ Planned
- Add support of native legacy cucumber-json
- Test against https://github.com/cucumber/json-formatter

Unreleased
----------
- Add support of python 3.12 at CI
- Add Support of pytest 8

2.1.0
----------
- Using official cucmber ci-environment lib
Expand Down
42 changes: 21 additions & 21 deletions docs/features.rst
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
.. NOTE:: Features below are part of end-to-end test suite; You always could find most specific
use cases of **pytest-bdd-ng** by investigation of its regression
test suite https://github.com/elchupanebrej/pytest-bdd-ng/tree/default/tests

Tutorial launch.feature
!!!!!!!!!!!!!!!!!!!!!!!

.. include:: ../Features/Tutorial launch.feature
:code: gherkin

Gherkin feature launch by pytest.feature
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. include:: ../Features/Gherkin feature launch by pytest.feature
:code: gherkin

Tags for Scenario Outlines examples.feature
###########################################

.. include:: ../Features/Scenario/Outline/Tags for Scenario Outlines examples.feature
:code: gherkin
.. NOTE:: Features below are part of end-to-end test suite; You always could find most specific
use cases of **pytest-bdd-ng** by investigation of its regression
test suite https://github.com/elchupanebrej/pytest-bdd-ng/tree/default/tests

Gherkin feature launch by pytest.feature
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. include:: ../Features/Gherkin feature launch by pytest.feature
:code: gherkin

Tutorial launch.feature
!!!!!!!!!!!!!!!!!!!!!!!

.. include:: ../Features/Tutorial launch.feature
:code: gherkin

Tags for Scenario Outlines examples.feature
###########################################

.. include:: ../Features/Scenario/Outline/Tags for Scenario Outlines examples.feature
:code: gherkin
8 changes: 5 additions & 3 deletions src/pytest_bdd/compatibility/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,20 @@


# region pytest version dependent imports
def is_pytest_version_greater(version: str):
def is_pytest_version_greater_or_equal(version: str):
return compare_distribution_version("pytest", version, ge)


PYTEST6, PYTEST61, PYTEST62, PYTEST7, PYTEST8 = map(
is_pytest_version_greater,
PYTEST6, PYTEST61, PYTEST62, PYTEST7, PYTEST8, PYTEST81, PYTEST83 = map(
is_pytest_version_greater_or_equal,
[
"6.0",
"6.1",
"6.2",
"7.0",
"8.0",
"8.1",
"8.3",
],
)

Expand Down
18 changes: 12 additions & 6 deletions src/pytest_bdd/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from operator import attrgetter
from typing import Optional, Union

from _pytest.mark import Mark
from decopatch import function_decorator
from makefun import wraps
from pytest import fixture
Expand Down Expand Up @@ -52,12 +53,17 @@ def hook(request: FixtureRequest, *args, **kwargs):
# mypy@Python 3.8 complains "ABCMeta" has no attribute "parse" [attr-defined] what is wrong
parsed_expression: TagExpression = _ExpressionType.parse(_expression) # type: ignore[attr-defined]

get_tags = {
HookKind.mark: lambda: {mark.name for mark in request.node.iter_markers()},
HookKind.tag: lambda: set(map(attrgetter("name"), request.getfixturevalue("scenario").tags)),
}[_kind]

is_matching = parsed_expression.evaluate(get_tags())
get_marks = lambda: list(
{
HookKind.mark: request.node.iter_markers(),
HookKind.tag: map(
lambda tag: Mark(tag.name, args=tuple(), kwargs={}), # type: ignore[no-any-return]
request.getfixturevalue("scenario").tags,
),
}[_kind]
)

is_matching = parsed_expression.evaluate(get_marks())

is_function = isfunction(func)
is_generator_function = isgeneratorfunction(func)
Expand Down
51 changes: 40 additions & 11 deletions src/pytest_bdd/tag_expression.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from itertools import cycle
from typing import AbstractSet, Optional, Protocol, Type, TypeVar, runtime_checkable
from operator import attrgetter
from typing import AbstractSet, List, Optional, Protocol, Type, TypeVar, Union, runtime_checkable

from _pytest.mark import Mark
from attr import attrib, attrs
from cucumber_tag_expressions import TagExpressionError, TagExpressionParser

from pytest_bdd.compatibility.pytest import PYTEST6
from pytest_bdd.compatibility.pytest import PYTEST6, PYTEST83

if PYTEST6:
from pytest_bdd.compatibility.pytest import Expression, MarkMatcher, ParseError
Expand All @@ -18,12 +20,12 @@ class TagExpression(Protocol):
def parse(cls: Type[TagExpressionType], expression: str) -> TagExpressionType:
raise NotImplementedError # pragma: no cover

def evaluate(self, tags: AbstractSet[str]) -> bool:
def evaluate(self, marks: List[Mark]) -> bool:
raise NotImplementedError # pragma: no cover


@attrs
class _MarksTagExpression(TagExpression):
class _ModernTagExpression(TagExpression):
expression: Optional["Expression"] = attrib()

@classmethod
Expand All @@ -33,8 +35,25 @@ def parse(cls, expression):
except ParseError as e:
raise ValueError(f"Unable parse mark expression: {expression}: {e}") from e

def evaluate(self, tags):
return self.expression.evaluate(MarkMatcher(tags)) if self.expression is not None else True

@attrs
class _EnhancedMarksTagExpression(_ModernTagExpression):
"""Used for 8.3<=pytest"""

def evaluate(self, marks):
return self.expression.evaluate(MarkMatcher.from_markers(marks)) if self.expression is not None else True


@attrs
class _MarksTagExpression(_ModernTagExpression):
"""Used for 6.0<=pytest<8.3"""

def evaluate(self, marks):
return (
self.expression.evaluate(MarkMatcher(map(attrgetter("name"), marks)))
if self.expression is not None
else True
)


@attrs
Expand All @@ -54,11 +73,21 @@ def parse(cls, expression):
pass
return cls(expression=expression if expression != "" else None)

def evaluate(self, tags):
return eval(self.expression, {}, dict(zip(tags, cycle([True])))) if self.expression is not None else True
def evaluate(self, marks):
return (
eval(self.expression, {}, dict(zip(map(attrgetter("name"), marks), cycle([True]))))
if self.expression is not None
else True
)


MarksTagExpression = _MarksTagExpression if PYTEST6 else _FallbackMarksTagExpression
MarksTagExpression: Type[Union[_EnhancedMarksTagExpression, _MarksTagExpression, _FallbackMarksTagExpression]]
if PYTEST83:
MarksTagExpression = _EnhancedMarksTagExpression
elif PYTEST6:
MarksTagExpression = _MarksTagExpression
else:
MarksTagExpression = _FallbackMarksTagExpression


@attrs
Expand All @@ -72,5 +101,5 @@ def parse(cls, expression):
except TagExpressionError as e:
raise ValueError(f"Unable parse tag expression: {expression}: {e}") from e

def evaluate(self, tags):
return self.expression.evaluate(tags)
def evaluate(self, marks):
return self.expression.evaluate(map(attrgetter("name"), marks))
4 changes: 2 additions & 2 deletions src/pytest_bdd/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
runtime_checkable,
)

from pytest_bdd.compatibility.pytest import FixtureDef, fail
from pytest_bdd.compatibility.pytest import PYTEST8, PYTEST81, FixtureDef, fail
from pytest_bdd.const import ALPHA_REGEX, PYTHON_REPLACE_REGEX

if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -156,7 +156,7 @@ def inject_fixture(request, arg, value):
"""

fd = FixtureDef(
fixturemanager=request._fixturemanager,
**({"config": request.config} if PYTEST81 else {"fixturemanager": request._fixturemanager}),
baseid=None,
argname=arg,
func=lambda: value,
Expand Down
7 changes: 5 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[tox]
envlist =
py312-pre-commit-lin
py312-pytest{625, 80, 74, 73, 72, 71, 70, latest}-mypy-lin
py312-pytest{625, 80, 74, 73, 72, 71, 70, latest}-coverage-lin
py312-pytest{625, 83, 82, 81, 80, 74, 73, 72, 71, 70, latest}-mypy-lin
py312-pytest{625, 83, 82, 81, 80, 74, 73, 72, 71, 70, latest}-coverage-lin
py312-pytestlatest-gherkin{24, latest}-xdist-coverage-{lin, win, mac}
py312-pytestlatets-allure-coverage-{lin, win, mac}
py39-pytest{62, 61, 60, 54, 53, 52, 51, 50}-coverage-lin
Expand Down Expand Up @@ -37,6 +37,9 @@ deps =
pytest73: pytest~=7.3.0
pytest74: pytest~=7.4.0
pytest80: pytest~=8.0.0
pytest81: pytest~=8.1.0
pytest82: pytest~=8.2.0
pytest83: pytest~=8.3.0
pytestlatest: pytest
xdist: pytest-xdist
setenv =
Expand Down

0 comments on commit deb34cd

Please sign in to comment.