Fix many types

This commit is contained in:
Alessio Bogon 2021-10-20 17:15:03 +02:00
parent 33729841eb
commit 78cf86af32
7 changed files with 53 additions and 48 deletions

View File

@ -18,5 +18,5 @@ warn_unused_configs = true
files = "pytest_bdd/**/*.py" files = "pytest_bdd/**/*.py"
[[tool.mypy.overrides]] [[tool.mypy.overrides]]
module = ["parse", "parse_type"] module = ["parse", "parse_type", "glob2"]
ignore_missing_imports = true ignore_missing_imports = true

View File

@ -49,7 +49,7 @@ class LogBDDCucumberJSON:
def __init__(self, logfile: str) -> None: def __init__(self, logfile: str) -> None:
logfile = os.path.expanduser(os.path.expandvars(logfile)) logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile)) self.logfile = os.path.normpath(os.path.abspath(logfile))
self.features = {} self.features: Dict[str, Dict] = {}
# TODO: Unused method? # TODO: Unused method?
def append(self, obj): def append(self, obj):
@ -62,7 +62,7 @@ class LogBDDCucumberJSON:
:param report: pytest `Report` object :param report: pytest `Report` object
:return: `dict` in form {"status": "<passed|failed|skipped>", ["error_message": "<error_message>"]} :return: `dict` in form {"status": "<passed|failed|skipped>", ["error_message": "<error_message>"]}
""" """
result = {} result: Dict[str, Any] = {}
if report.passed or not step["failed"]: # ignore setup/teardown if report.passed or not step["failed"]: # ignore setup/teardown
result = {"status": "passed"} result = {"status": "passed"}
elif report.failed and step["failed"]: elif report.failed and step["failed"]:

View File

@ -48,7 +48,7 @@ class GherkinTerminalReporter(TerminalReporter):
if not letter and not word: if not letter and not word:
# probably passed setup/teardown # probably passed setup/teardown
return return None
if isinstance(word, tuple): if isinstance(word, tuple):
word, word_markup = word word, word_markup = word
@ -93,3 +93,4 @@ class GherkinTerminalReporter(TerminalReporter):
else: else:
return super().pytest_runtest_logreport(rep) return super().pytest_runtest_logreport(rep)
self.stats.setdefault(cat, []).append(rep) self.stats.setdefault(cat, []).append(rep)
return None

View File

@ -3,7 +3,7 @@ import re
import textwrap import textwrap
import typing import typing
from collections import OrderedDict from collections import OrderedDict
from typing import Any, List, Optional, Set, Tuple from typing import Any, List, Optional, Set, Tuple, cast
from . import exceptions, types from . import exceptions, types
@ -73,6 +73,7 @@ def get_step_type(line: str) -> Optional[str]:
for prefix, _type in STEP_PREFIXES: for prefix, _type in STEP_PREFIXES:
if line.startswith(prefix): if line.startswith(prefix):
return _type return _type
return None
def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> "Feature": def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> "Feature":
@ -96,7 +97,7 @@ def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> "Feat
description="", description="",
) )
scenario: typing.Optional[ScenarioTemplate] = None scenario: typing.Optional[ScenarioTemplate] = None
mode = None mode: Optional[str] = None
prev_mode = None prev_mode = None
description: typing.List[str] = [] description: typing.List[str] = []
step = None step = None
@ -188,11 +189,11 @@ def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> "Feat
) )
elif mode and mode not in (types.FEATURE, types.TAG): elif mode and mode not in (types.FEATURE, types.TAG):
step = Step(name=parsed_line, type=mode, indent=line_indent, line_number=line_number, keyword=keyword) step = Step(name=parsed_line, type=mode, indent=line_indent, line_number=line_number, keyword=keyword)
if feature.background and not scenario: if feature.background and scenario:
target = feature.background feature.background.add_step(step)
else: else:
target = scenario scenario = cast(ScenarioTemplate, scenario)
target.add_step(step) scenario.add_step(step)
prev_line = clean_line prev_line = clean_line
feature.description = "\n".join(description).strip() feature.description = "\n".join(description).strip()
@ -207,22 +208,22 @@ class Feature:
scenarios: OrderedDict, scenarios: OrderedDict,
filename: str, filename: str,
rel_filename: str, rel_filename: str,
name: Optional[Any], name: Optional[str],
tags: Set, tags: Set,
examples: "Examples", examples: "Examples",
background: Optional[Any], background: "Optional[Background]",
line_number: int, line_number: int,
description: str, description: str,
) -> None: ) -> None:
self.scenarios: typing.Dict[str, ScenarioTemplate] = scenarios self.scenarios: typing.Dict[str, ScenarioTemplate] = scenarios
self.rel_filename = rel_filename self.rel_filename: str = rel_filename
self.filename = filename self.filename: str = filename
self.tags = tags self.tags: Set = tags
self.examples = examples self.examples: "Examples" = examples
self.name = name self.name: Optional[str] = name
self.line_number = line_number self.line_number: int = line_number
self.description = description self.description: str = description
self.background = background self.background: "Optional[Background]" = background
class ScenarioTemplate: class ScenarioTemplate:
@ -319,17 +320,17 @@ class Step:
:param int line_number: line number. :param int line_number: line number.
:param str keyword: step keyword. :param str keyword: step keyword.
""" """
self.name = name self.name: str = name
self.keyword = keyword self.keyword: str = keyword
self.lines = [] self.lines: List[str] = []
self.indent = indent self.indent: int = indent
self.type = type self.type: str = type
self.line_number = line_number self.line_number: int = line_number
self.failed = False self.failed: bool = False
self.start = 0 self.start: int = 0 # TODO: Unused
self.stop = 0 self.stop: int = 0 # TODO: Unused
self.scenario: "Optional[Scenario]" = None self.scenario: "Optional[ScenarioTemplate]" = None
self.background = None self.background: "Optional[Background]" = None
def add_line(self, line: str) -> None: def add_line(self, line: str) -> None:
"""Add line to the multiple step. """Add line to the multiple step.
@ -386,9 +387,9 @@ class Background:
:param pytest_bdd.parser.Feature feature: Feature. :param pytest_bdd.parser.Feature feature: Feature.
:param int line_number: Line number. :param int line_number: Line number.
""" """
self.feature = feature self.feature: "Feature" = feature
self.line_number = line_number self.line_number: int = line_number
self.steps = [] self.steps: "typing.List[Step]" = []
def add_step(self, step: "Step") -> None: def add_step(self, step: "Step") -> None:
"""Add step to the background.""" """Add step to the background."""
@ -402,10 +403,10 @@ class Examples:
def __init__(self) -> None: def __init__(self) -> None:
"""Initialize examples instance.""" """Initialize examples instance."""
self.example_params = [] self.example_params: List[str] = []
self.examples = [] self.examples: List[List[str]] = []
self.vertical_examples = [] self.vertical_examples: List[List[str]] = []
self.line_number = None self.line_number: Optional[int] = None
self.name = None self.name = None
def set_param_names(self, keys: List[str]) -> None: def set_param_names(self, keys: List[str]) -> None:

View File

@ -3,7 +3,7 @@
import re as base_re import re as base_re
from functools import partial from functools import partial
from typing import Any, Dict from typing import Any, Dict, cast
import parse as base_parse import parse as base_parse
from parse_type import cfparse as base_cfparse from parse_type import cfparse as base_cfparse
@ -40,6 +40,7 @@ class re(StepParser):
:return: `dict` of step arguments :return: `dict` of step arguments
""" """
# TODO: This is a potential bug, as groupdict can return None (found with typing)
return self.regex.match(name).groupdict() return self.regex.match(name).groupdict()
def is_matching(self, name: str) -> bool: def is_matching(self, name: str) -> bool:
@ -60,7 +61,7 @@ class parse(StepParser):
:return: `dict` of step arguments :return: `dict` of step arguments
""" """
return self.parser.parse(name).named return cast(Dict[str, Any], self.parser.parse(name).named)
def is_matching(self, name: str) -> bool: def is_matching(self, name: str) -> bool:
"""Match given name with the step name.""" """Match given name with the step name."""

View File

@ -62,9 +62,10 @@ def find_argumented_step_fixture_name(
except FixtureLookupError: except FixtureLookupError:
continue continue
return parser_name return parser_name
return None
def _find_step_function(request: FixtureRequest, step: "Step", scenario: "Scenario") -> Callable: def _find_step_function(request: FixtureRequest, step: "Step", scenario: "Scenario") -> Any:
"""Match the step defined by the regular expression pattern. """Match the step defined by the regular expression pattern.
:param request: PyTest request object. :param request: PyTest request object.
@ -81,9 +82,9 @@ def _find_step_function(request: FixtureRequest, step: "Step", scenario: "Scenar
except FixtureLookupError: except FixtureLookupError:
try: try:
# Could not find a fixture with the same name, let's see if there is a parser involved # Could not find a fixture with the same name, let's see if there is a parser involved
name = find_argumented_step_fixture_name(name, step.type, request._fixturemanager, request) argumented_name = find_argumented_step_fixture_name(name, step.type, request._fixturemanager, request)
if name: if argumented_name:
return request.getfixturevalue(name) return request.getfixturevalue(argumented_name)
raise raise
except FixtureLookupError: except FixtureLookupError:
raise exceptions.StepDefinitionNotFoundError( raise exceptions.StepDefinitionNotFoundError(
@ -167,15 +168,15 @@ def _get_scenario_decorator(
"scenario function can only be used as a decorator. Refer to the documentation." "scenario function can only be used as a decorator. Refer to the documentation."
) )
[fn] = args [fn] = args
args = get_args(fn) func_args = get_args(fn)
# We need to tell pytest that the original function requires its fixtures, # We need to tell pytest that the original function requires its fixtures,
# otherwise indirect fixtures would not work. # otherwise indirect fixtures would not work.
@pytest.mark.usefixtures(*args) @pytest.mark.usefixtures(*func_args)
def scenario_wrapper(request: FixtureRequest, _pytest_bdd_example: Dict[str, str]) -> Optional[Any]: def scenario_wrapper(request: FixtureRequest, _pytest_bdd_example: Dict[str, str]) -> Optional[Any]:
scenario = templated_scenario.render(_pytest_bdd_example) scenario = templated_scenario.render(_pytest_bdd_example)
_execute_scenario(feature, scenario, request) _execute_scenario(feature, scenario, request)
fixture_values = [request.getfixturevalue(arg) for arg in args] fixture_values = [request.getfixturevalue(arg) for arg in func_args]
return fn(*fixture_values) return fn(*fixture_values)
example_parametrizations = collect_example_parametrizations(templated_scenario) example_parametrizations = collect_example_parametrizations(templated_scenario)

View File

@ -8,9 +8,10 @@ from sys import _getframe
from typing import Any, Callable, Dict, List from typing import Any, Callable, Dict, List
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from _pytest.config import Config
from _pytest.pytester import RunResult from _pytest.pytester import RunResult
CONFIG_STACK = [] CONFIG_STACK: "List[Config]" = []
def get_args(func: Callable) -> List[str]: def get_args(func: Callable) -> List[str]: