Merge pull request #554 from pytest-dev/misc-internal-improvements

Misc internal improvements
This commit is contained in:
Alessio Bogon 2022-08-20 12:26:47 +02:00 committed by GitHub
commit f6865058c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 115 deletions

View File

@ -117,7 +117,7 @@ def print_missing_code(scenarios: list[ScenarioTemplate], steps: list[Step]) ->
tw.line()
features = sorted(
{scenario.feature for scenario in scenarios}, key=lambda feature: feature.name or feature.filename
(scenario.feature for scenario in scenarios), key=lambda feature: feature.name or feature.filename
)
code = generate_code(features, scenarios, steps)
tw.write(code)
@ -145,9 +145,7 @@ def parse_feature_files(paths: list[str], **kwargs: Any) -> tuple[list[Feature],
itertools.chain.from_iterable(feature.scenarios.values() for feature in features),
key=lambda scenario: (scenario.feature.name or scenario.feature.filename, scenario.name),
)
steps = sorted(
set(itertools.chain.from_iterable(scenario.steps for scenario in scenarios)), key=lambda step: step.name
)
steps = sorted((step for scenario in scenarios for step in scenario.steps), key=lambda step: step.name)
return features, scenarios, steps

View File

@ -5,6 +5,7 @@ import re
import textwrap
import typing
from collections import OrderedDict
from dataclasses import dataclass, field
from typing import cast
from . import exceptions, types
@ -28,7 +29,7 @@ STEP_PREFIXES = [
]
if typing.TYPE_CHECKING:
from typing import Any, Iterable, Mapping, Match
from typing import Any, Iterable, Mapping, Match, Sequence
def split_line(line: str) -> list[str]:
@ -187,55 +188,34 @@ def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> Featu
return feature
@dataclass
class Feature:
"""Feature."""
def __init__(
self,
scenarios: OrderedDict,
filename: str,
rel_filename: str,
name: str | None,
tags: set,
background: Background | None,
line_number: int,
description: str,
) -> None:
self.scenarios: dict[str, ScenarioTemplate] = scenarios
self.rel_filename: str = rel_filename
self.filename: str = filename
self.tags: set = tags
self.name: str | None = name
self.line_number: int = line_number
self.description: str = description
self.background: Background | None = background
scenarios: OrderedDict[str, ScenarioTemplate]
filename: str
rel_filename: str
name: str | None
tags: set[str]
background: Background | None
line_number: int
description: str
@dataclass
class ScenarioTemplate:
"""A scenario template.
Created when parsing the feature file, it will then be combined with the examples to create a Scenario."""
def __init__(self, feature: Feature, name: str, line_number: int, templated: bool, tags=None) -> None:
Created when parsing the feature file, it will then be combined with the examples to create a Scenario.
"""
:param str name: Scenario name.
:param int line_number: Scenario line number.
:param set tags: Set of tags.
"""
self.feature = feature
self.name = name
self._steps: list[Step] = []
self.examples = Examples()
self.line_number = line_number
self.tags = tags or set()
self.templated = templated
feature: Feature
name: str
line_number: int
templated: bool
tags: set[str] = field(default_factory=set)
examples: Examples | None = field(default_factory=lambda: Examples())
_steps: list[Step] = field(init=False, default_factory=list)
def add_step(self, step: Step) -> None:
"""Add step to the scenario.
:param pytest_bdd.parser.Step step: Step.
"""
step.scenario = self
self._steps.append(step)
@ -263,50 +243,38 @@ class ScenarioTemplate:
return Scenario(feature=self.feature, name=self.name, line_number=self.line_number, steps=steps, tags=self.tags)
@dataclass
class Scenario:
"""Scenario."""
def __init__(self, feature: Feature, name: str, line_number: int, steps: list[Step] = None, tags=None) -> None:
"""Scenario constructor.
:param pytest_bdd.parser.Feature feature: Feature.
:param str name: Scenario name.
:param int line_number: Scenario line number.
:param set tags: Set of tags.
"""
if steps is None:
steps = []
self.feature = feature
self.name = name
self.steps = steps
self.line_number = line_number
self.tags = tags or set()
self.failed = False
feature: Feature
name: str
line_number: int
steps: list[Step]
tags: set[str] = field(default_factory=set)
@dataclass
class Step:
"""Step."""
type: str
_name: str
line_number: int
indent: int
keyword: str
failed: bool = field(init=False, default=False)
scenario: ScenarioTemplate | None = field(init=False, default=None)
background: Background | None = field(init=False, default=None)
lines: list[str] = field(init=False, default_factory=list)
def __init__(self, name: str, type: str, indent: int, line_number: int, keyword: str) -> None:
"""Step constructor.
self.name = name
self.type = type
self.indent = indent
self.line_number = line_number
self.keyword = keyword
:param str name: step name.
:param str type: step type.
:param int indent: step text indent.
:param int line_number: line number.
:param str keyword: step keyword.
"""
self.name: str = name
self.keyword: str = keyword
self.lines: list[str] = []
self.indent: int = indent
self.type: str = type
self.line_number: int = line_number
self.failed: bool = False
self.scenario: ScenarioTemplate | None = None
self.background: Background | None = None
self.failed = False
self.scenario = None
self.background = None
self.lines = []
def add_line(self, line: str) -> None:
"""Add line to the multiple step.
@ -317,7 +285,6 @@ class Step:
@property
def name(self) -> str:
"""Get step name."""
multilines_content = textwrap.dedent("\n".join(self.lines)) if self.lines else ""
# Remove the multiline quotes, if present.
@ -333,7 +300,6 @@ class Step:
@name.setter
def name(self, value: str) -> None:
"""Set step name."""
self._name = value
def __str__(self) -> str:
@ -342,10 +308,9 @@ class Step:
@property
def params(self) -> tuple[str, ...]:
"""Get step params."""
return tuple(frozenset(STEP_PARAM_RE.findall(self.name)))
def render(self, context: Mapping[str, Any]):
def render(self, context: Mapping[str, Any]) -> str:
def replacer(m: Match):
varname = m.group(1)
return str(context[varname])
@ -353,19 +318,11 @@ class Step:
return STEP_PARAM_RE.sub(replacer, self.name)
@dataclass
class Background:
"""Background."""
def __init__(self, feature: Feature, line_number: int) -> None:
"""Background constructor.
:param pytest_bdd.parser.Feature feature: Feature.
:param int line_number: Line number.
"""
self.feature: Feature = feature
self.line_number: int = line_number
self.steps: list[Step] = []
feature: Feature
line_number: int
steps: list[Step] = field(init=False, default_factory=list)
def add_step(self, step: Step) -> None:
"""Add step to the background."""
@ -373,29 +330,20 @@ class Background:
self.steps.append(step)
@dataclass
class Examples:
"""Example table."""
def __init__(self) -> None:
"""Initialize examples instance."""
self.example_params: list[str] = []
self.examples: list[list[str]] = []
self.line_number: int | None = None
self.name = None
line_number: int | None = field(default=None)
name: str | None = field(default=None)
def set_param_names(self, keys: list[str]) -> None:
"""Set parameter names.
example_params: list[str] = field(init=False, default_factory=list)
examples: list[Sequence[str]] = field(init=False, default_factory=list)
:param names: `list` of `string` parameter names.
"""
def set_param_names(self, keys: Iterable[str]) -> None:
self.example_params = [str(key) for key in keys]
def add_example(self, values: list[str]) -> None:
"""Add example.
:param values: `list` of `string` parameter values.
"""
def add_example(self, values: Sequence[str]) -> None:
self.examples.append(values)
def as_contexts(self) -> Iterable[dict[str, Any]]:
@ -409,7 +357,6 @@ class Examples:
yield dict(zip(header, row))
def __bool__(self) -> bool:
"""Bool comparison."""
return bool(self.examples)