Use newer typing syntax

This commit is contained in:
Alessio Bogon 2022-02-24 16:00:36 +01:00
parent 021aa3a2e3
commit 644df2ee52
14 changed files with 110 additions and 94 deletions

View File

@ -22,4 +22,4 @@ repos:
rev: v2.31.0
hooks:
- id: pyupgrade
args: ["--py37-plus", "--keep-runtime-typing"]
args: ["--py37-plus"]

View File

@ -5,9 +5,11 @@ import json
import math
import os
import time
from typing import TYPE_CHECKING, Any, Dict, List
import typing
if typing.TYPE_CHECKING:
from typing import Any
if TYPE_CHECKING:
from _pytest.config import Config
from _pytest.config.argparsing import Parser
from _pytest.reports import TestReport
@ -50,20 +52,20 @@ class LogBDDCucumberJSON:
def __init__(self, logfile: str) -> None:
logfile = os.path.expanduser(os.path.expandvars(logfile))
self.logfile = os.path.normpath(os.path.abspath(logfile))
self.features: Dict[str, Dict] = {}
self.features: dict[str, dict] = {}
# TODO: Unused method?
def append(self, obj):
self.features[-1].append(obj)
def _get_result(self, step: Dict[str, Any], report: TestReport, error_message: bool = False) -> Dict[str, Any]:
def _get_result(self, step: dict[str, Any], report: TestReport, error_message: bool = False) -> dict[str, Any]:
"""Get scenario test run result.
:param step: `Step` step we get result for
:param report: pytest `Report` object
:return: `dict` in form {"status": "<passed|failed|skipped>", ["error_message": "<error_message>"]}
"""
result: Dict[str, Any] = {}
result: dict[str, Any] = {}
if report.passed or not step["failed"]: # ignore setup/teardown
result = {"status": "passed"}
elif report.failed and step["failed"]:
@ -73,7 +75,7 @@ class LogBDDCucumberJSON:
result["duration"] = int(math.floor((10**9) * step["duration"])) # nanosec
return result
def _serialize_tags(self, item: Dict[str, Any]) -> List[Dict[str, Any]]:
def _serialize_tags(self, item: dict[str, Any]) -> list[dict[str, Any]]:
"""Serialize item's tags.
:param item: json-serialized `Scenario` or `Feature`.
@ -98,7 +100,7 @@ class LogBDDCucumberJSON:
# skip if there isn't a result or scenario has no steps
return
def stepmap(step: Dict[str, Any]) -> Dict[str, Any]:
def stepmap(step: dict[str, Any]) -> dict[str, Any]:
error_message = False
if step["failed"] and not scenario.setdefault("failed", False):
scenario["failed"] = True

View File

@ -26,14 +26,13 @@ one line.
from __future__ import annotations
import os.path
import typing
import glob2
from .parser import Feature, parse_feature
# Global features dictionary
features: typing.Dict[str, Feature] = {}
features: dict[str, Feature] = {}
def get_feature(base_path: str, filename: str, encoding: str = "utf-8") -> Feature:
@ -58,7 +57,7 @@ def get_feature(base_path: str, filename: str, encoding: str = "utf-8") -> Featu
return feature
def get_features(paths: typing.List[str], **kwargs) -> typing.List[Feature]:
def get_features(paths: list[str], **kwargs) -> list[Feature]:
"""Get features for given paths.
:param list paths: `list` of paths (file or dirs)

View File

@ -3,14 +3,9 @@ from __future__ import annotations
import itertools
import os.path
from typing import TYPE_CHECKING, Any, List, Optional, Tuple
from typing import TYPE_CHECKING
import py
from _pytest.config import Config
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureDef, FixtureManager
from _pytest.main import Session
from _pytest.python import Function
from mako.lookup import TemplateLookup
from .feature import get_features
@ -19,6 +14,14 @@ from .steps import get_step_fixture_name
from .types import STEP_TYPES
if TYPE_CHECKING:
from typing import Any
from _pytest.config import Config
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureDef, FixtureManager
from _pytest.main import Session
from _pytest.python import Function
from .parser import Feature, ScenarioTemplate, Step
template_lookup = TemplateLookup(directories=[os.path.join(os.path.dirname(__file__), "templates")])
@ -45,13 +48,13 @@ def add_options(parser: Parser) -> None:
)
def cmdline_main(config: Config) -> Optional[int]:
def cmdline_main(config: Config) -> int | None:
"""Check config option to show missing code."""
if config.option.generate_missing:
return show_missing_code(config)
def generate_code(features: List[Feature], scenarios: List[ScenarioTemplate], steps: List[Step]) -> str:
def generate_code(features: list[Feature], scenarios: list[ScenarioTemplate], steps: list[Step]) -> str:
"""Generate test code for the given filenames."""
grouped_steps = group_steps(steps)
template = template_lookup.get_template("test.py.mak")
@ -72,7 +75,7 @@ def show_missing_code(config: Config) -> int:
return wrap_session(config, _show_missing_code_main)
def print_missing_code(scenarios: List[ScenarioTemplate], steps: List[Step]) -> None:
def print_missing_code(scenarios: list[ScenarioTemplate], steps: list[Step]) -> None:
"""Print missing code with TerminalWriter."""
tw = py.io.TerminalWriter()
scenario = step = None
@ -120,7 +123,7 @@ def print_missing_code(scenarios: List[ScenarioTemplate], steps: List[Step]) ->
def _find_step_fixturedef(
fixturemanager: FixtureManager, item: Function, name: str, type_: str
) -> Optional[Tuple[FixtureDef]]:
) -> tuple[FixtureDef] | None:
"""Find step fixturedef.
:param request: PyTest Item object.
@ -139,7 +142,7 @@ def _find_step_fixturedef(
return None
def parse_feature_files(paths: List[str], **kwargs: Any) -> Tuple[List[Feature], List[ScenarioTemplate], List[Step]]:
def parse_feature_files(paths: list[str], **kwargs: Any) -> tuple[list[Feature], list[ScenarioTemplate], list[Step]]:
"""Parse feature files of given paths.
:param paths: `list` of paths (file or dirs)
@ -158,7 +161,7 @@ def parse_feature_files(paths: List[str], **kwargs: Any) -> Tuple[List[Feature],
return features, scenarios, steps
def group_steps(steps: List[Step]) -> List[Step]:
def group_steps(steps: list[Step]) -> list[Step]:
"""Group steps by type."""
steps = sorted(steps, key=lambda step: step.type)
seen_steps = set()

View File

@ -1,11 +1,14 @@
from __future__ import annotations
from typing import Any, Optional
import typing
from _pytest.config import Config
from _pytest.config.argparsing import Parser
from _pytest.reports import TestReport
from _pytest.terminal import TerminalReporter
if typing.TYPE_CHECKING:
from typing import Any
from _pytest.config import Config
from _pytest.config.argparsing import Parser
from _pytest.reports import TestReport
from _pytest.terminal import TerminalReporter
def add_options(parser: Parser) -> None:
@ -43,7 +46,7 @@ class GherkinTerminalReporter(TerminalReporter):
def __init__(self, config: Config) -> None:
super().__init__(config)
def pytest_runtest_logreport(self, report: TestReport) -> Optional[Any]:
def pytest_runtest_logreport(self, report: TestReport) -> Any | None:
rep = report
res = self.config.hook.pytest_report_teststatus(report=rep, config=self.config)
cat, letter, word = res

View File

@ -5,7 +5,7 @@ import re
import textwrap
import typing
from collections import OrderedDict
from typing import Any, List, Optional, Set, Tuple, cast
from typing import cast
from . import exceptions, types
@ -28,7 +28,7 @@ STEP_PREFIXES = [
]
def split_line(line: str) -> List[str]:
def split_line(line: str) -> list[str]:
"""Split the given Examples line.
:param str|unicode line: Feature file Examples line.
@ -38,7 +38,7 @@ def split_line(line: str) -> List[str]:
return [cell.replace("\\|", "|").strip() for cell in SPLIT_LINE_RE.split(line)[1:-1]]
def parse_line(line: str) -> Tuple[str, str]:
def parse_line(line: str) -> tuple[str, str]:
"""Parse step line to get the step prefix (Scenario, Given, When, Then or And) and the actual step name.
:param line: Line of the Feature file.
@ -64,7 +64,7 @@ def strip_comments(line: str) -> str:
return line.strip()
def get_step_type(line: str) -> Optional[str]:
def get_step_type(line: str) -> str | None:
"""Detect step type by the beginning of the line.
:param str line: Line of the Feature file.
@ -96,10 +96,10 @@ def parse_feature(basedir: str, filename: str, encoding: str = "utf-8") -> Featu
background=None,
description="",
)
scenario: typing.Optional[ScenarioTemplate] = None
mode: Optional[str] = None
scenario: ScenarioTemplate | None = None
mode: str | None = None
prev_mode = None
description: typing.List[str] = []
description: list[str] = []
step = None
multiline_step = False
prev_line = None
@ -186,20 +186,20 @@ class Feature:
scenarios: OrderedDict,
filename: str,
rel_filename: str,
name: Optional[str],
tags: Set,
background: Optional[Background],
name: str | None,
tags: set,
background: Background | None,
line_number: int,
description: str,
) -> None:
self.scenarios: typing.Dict[str, ScenarioTemplate] = scenarios
self.scenarios: dict[str, ScenarioTemplate] = scenarios
self.rel_filename: str = rel_filename
self.filename: str = filename
self.tags: Set = tags
self.name: Optional[str] = name
self.tags: set = tags
self.name: str | None = name
self.line_number: int = line_number
self.description: str = description
self.background: Optional[Background] = background
self.background: Background | None = background
class ScenarioTemplate:
@ -216,7 +216,7 @@ class ScenarioTemplate:
"""
self.feature = feature
self.name = name
self._steps: typing.List[Step] = []
self._steps: list[Step] = []
self.examples = Examples()
self.line_number = line_number
self.tags = tags or set()
@ -230,7 +230,7 @@ class ScenarioTemplate:
self._steps.append(step)
@property
def steps(self) -> List[Step]:
def steps(self) -> list[Step]:
background = self.feature.background
return (background.steps if background else []) + self._steps
@ -252,7 +252,7 @@ class Scenario:
"""Scenario."""
def __init__(self, feature: Feature, name: str, line_number: int, steps: typing.List[Step], tags=None):
def __init__(self, feature: Feature, name: str, line_number: int, steps: list[Step], tags=None):
"""Scenario constructor.
:param pytest_bdd.parser.Feature feature: Feature.
@ -283,15 +283,15 @@ class Step:
"""
self.name: str = name
self.keyword: str = keyword
self.lines: List[str] = []
self.lines: list[str] = []
self.indent: int = indent
self.type: str = type
self.line_number: int = line_number
self.failed: bool = False
self.start: int = 0 # TODO: Unused
self.stop: int = 0 # TODO: Unused
self.scenario: Optional[ScenarioTemplate] = None
self.background: Optional[Background] = None
self.scenario: ScenarioTemplate | None = None
self.background: Background | None = None
def add_line(self, line: str) -> None:
"""Add line to the multiple step.
@ -326,7 +326,7 @@ class Step:
return f'{self.type.capitalize()} "{self.name}"'
@property
def params(self) -> Tuple[str, ...]:
def params(self) -> tuple[str, ...]:
"""Get step params."""
return tuple(frozenset(STEP_PARAM_RE.findall(self.name)))
@ -350,7 +350,7 @@ class Background:
"""
self.feature: Feature = feature
self.line_number: int = line_number
self.steps: typing.List[Step] = []
self.steps: list[Step] = []
def add_step(self, step: Step) -> None:
"""Add step to the background."""
@ -364,26 +364,26 @@ class Examples:
def __init__(self) -> None:
"""Initialize examples instance."""
self.example_params: List[str] = []
self.examples: List[List[str]] = []
self.line_number: Optional[int] = None
self.example_params: list[str] = []
self.examples: list[list[str]] = []
self.line_number: int | None = None
self.name = None
def set_param_names(self, keys: List[str]) -> None:
def set_param_names(self, keys: list[str]) -> None:
"""Set parameter names.
:param names: `list` of `string` parameter names.
"""
self.example_params = [str(key) for key in keys]
def add_example(self, values: List[str]) -> None:
def add_example(self, values: list[str]) -> None:
"""Add example.
:param values: `list` of `string` parameter values.
"""
self.examples.append(values)
def add_example_row(self, param: str, values: List[str]) -> None:
def add_example_row(self, param: str, values: list[str]) -> None:
"""Add example row.
:param param: `str` parameter name
@ -395,7 +395,7 @@ class Examples:
)
self.example_params.append(param)
def as_contexts(self) -> typing.Iterable[typing.Dict[str, typing.Any]]:
def as_contexts(self) -> typing.Iterable[dict[str, typing.Any]]:
if not self.examples:
return
@ -410,7 +410,7 @@ class Examples:
return bool(self.examples)
def get_tags(line: Optional[str]) -> Set[str]:
def get_tags(line: str | None) -> set[str]:
"""Get tags out of the given line.
:param str line: Feature file text line.

View File

@ -34,7 +34,7 @@ class re(StepParser):
super().__init__(name)
self.regex = base_re.compile(self.name, *args, **kwargs)
def parse_arguments(self, name: str) -> Dict[str, str]:
def parse_arguments(self, name: str) -> dict[str, str]:
"""Get step arguments.
:return: `dict` of step arguments
@ -55,7 +55,7 @@ class parse(StepParser):
super().__init__(name)
self.parser = base_parse.compile(self.name, *args, **kwargs)
def parse_arguments(self, name: str) -> Dict[str, Any]:
def parse_arguments(self, name: str) -> dict[str, Any]:
"""Get step arguments.
:return: `dict` of step arguments
@ -87,7 +87,7 @@ class string(StepParser):
name = str(name)
super().__init__(name)
def parse_arguments(self, name: str) -> Dict:
def parse_arguments(self, name: str) -> dict:
"""No parameters are available for simple string step.
:return: `dict` of step arguments

View File

@ -1,7 +1,7 @@
"""Pytest plugin entry point. Used for any fixtures needed."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, Optional
from typing import TYPE_CHECKING
import pytest
@ -9,6 +9,8 @@ from . import cucumber_json, generation, gherkin_terminal_reporter, given, repor
from .utils import CONFIG_STACK
if TYPE_CHECKING:
from typing import Any, Callable, Iterator
from _pytest.config import Config, PytestPluginManager
from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureRequest
@ -34,7 +36,7 @@ def trace():
@pytest.fixture
def _pytest_bdd_example() -> Dict:
def _pytest_bdd_example() -> dict:
"""The current scenario outline parametrization.
This is used internally by pytest_bdd.
@ -91,7 +93,7 @@ def pytest_bdd_step_error(
scenario: Scenario,
step: Step,
step_func: Callable,
step_func_args: Dict,
step_func_args: dict,
exception: Exception,
) -> None:
reporting.step_error(request, feature, scenario, step, step_func, step_func_args, exception)
@ -111,12 +113,12 @@ def pytest_bdd_after_step(
scenario: Scenario,
step: Step,
step_func: Callable,
step_func_args: Dict[str, Any],
step_func_args: dict[str, Any],
) -> None:
reporting.after_step(request, feature, scenario, step, step_func, step_func_args)
def pytest_cmdline_main(config: Config) -> Optional[int]:
def pytest_cmdline_main(config: Config) -> int | None:
return generation.cmdline_main(config)

View File

@ -6,9 +6,11 @@ that enriches the pytest test reporting.
from __future__ import annotations
import time
from typing import TYPE_CHECKING, Any, Callable, Dict
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any, Callable
from _pytest.fixtures import FixtureRequest
from _pytest.nodes import Item
from _pytest.reports import TestReport
@ -31,7 +33,7 @@ class StepReport:
self.step = step
self.started = time.perf_counter()
def serialize(self) -> Dict[str, Any]:
def serialize(self) -> dict[str, Any]:
"""Serialize the step execution report.
:return: Serialized step execution report.
@ -97,7 +99,7 @@ class ScenarioReport:
"""
self.step_reports.append(step_report)
def serialize(self) -> Dict[str, Any]:
def serialize(self) -> dict[str, Any]:
"""Serialize scenario execution report in order to transfer reporting from nodes in the distributed mode.
:return: Serialized report.
@ -155,7 +157,7 @@ def step_error(
scenario: Scenario,
step: Step,
step_func: Callable,
step_func_args: Dict,
step_func_args: dict,
exception: Exception,
) -> None:
"""Finalize the step report as failed."""
@ -173,7 +175,7 @@ def after_step(
scenario: Scenario,
step: Step,
step_func: Callable,
step_func_args: Dict,
step_func_args: dict,
) -> None:
"""Finalize the step report as successful."""
request.node.__scenario_report__.current_step_report.finalize(failed=False)

View File

@ -16,7 +16,6 @@ import collections
import os
import re
import typing
from typing import Any, Callable, Dict, Iterator, Optional, Union
import pytest
from _pytest.fixtures import FixtureLookupError, FixtureManager, FixtureRequest, call_fixture_func
@ -27,6 +26,8 @@ from .steps import get_step_fixture_name, inject_fixture
from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path
if typing.TYPE_CHECKING:
from typing import Any, Callable, Iterator
from _pytest.mark.structures import ParameterSet
from .parser import Feature, Scenario, ScenarioTemplate, Step
@ -36,8 +37,8 @@ ALPHA_REGEX = re.compile(r"^\d+_*")
def find_argumented_step_fixture_name(
name: str, type_: str, fixturemanager: FixtureManager, request: Optional[FixtureRequest] = None
) -> Optional[str]:
name: str, type_: str, fixturemanager: FixtureManager, request: FixtureRequest | None = None
) -> str | None:
"""Find argumented step fixture name."""
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
for fixturename, fixturedefs in list(fixturemanager._arg2fixturedefs.items()):
@ -178,7 +179,7 @@ def _get_scenario_decorator(
# We need to tell pytest that the original function requires its fixtures,
# otherwise indirect fixtures would not work.
@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]) -> Any | None:
scenario = templated_scenario.render(_pytest_bdd_example)
_execute_scenario(feature, scenario, request)
fixture_values = [request.getfixturevalue(arg) for arg in func_args]
@ -205,7 +206,7 @@ def _get_scenario_decorator(
def collect_example_parametrizations(
templated_scenario: ScenarioTemplate,
) -> typing.Optional[typing.List[ParameterSet]]:
) -> list[ParameterSet] | None:
# We need to evaluate these iterators and store them as lists, otherwise
# we won't be able to do the cartesian product later (the second iterator will be consumed)
contexts = list(templated_scenario.examples.as_contexts())
@ -276,7 +277,7 @@ def make_string_literal(string: str) -> str:
return "'{}'".format(string.replace("'", "\\'"))
def get_python_name_generator(name: str) -> Iterator[Union[Iterator, Iterator[str]]]:
def get_python_name_generator(name: str) -> Iterator[Iterator | Iterator[str]]:
"""Generate a sequence of suitable python names out of given arbitrary string name."""
python_name = make_python_name(name)
suffix = ""

View File

@ -36,7 +36,7 @@ def given_beautiful_article(article):
"""
from __future__ import annotations
from typing import Any, Callable, Dict, Optional, Union
import typing
import pytest
from _pytest.fixtures import FixtureDef, FixtureRequest
@ -45,6 +45,9 @@ from .parsers import get_parser
from .types import GIVEN, THEN, WHEN
from .utils import get_caller_module_locals
if typing.TYPE_CHECKING:
from typing import Any, Callable
def get_step_fixture_name(name: str, type_: str) -> str:
"""Get step fixture name.
@ -59,8 +62,8 @@ def get_step_fixture_name(name: str, type_: str) -> str:
def given(
name: Any,
converters: Union[Dict[str, Callable], Dict[str, type], None] = None,
target_fixture: Optional[str] = None,
converters: dict[str, Callable] | dict[str, type] | None = None,
target_fixture: str | None = None,
) -> Callable:
"""Given step decorator.
@ -74,7 +77,7 @@ def given(
return _step_decorator(GIVEN, name, converters=converters, target_fixture=target_fixture)
def when(name: Any, converters: Optional[Dict[str, type]] = None, target_fixture: Optional[str] = None) -> Callable:
def when(name: Any, converters: dict[str, type] | None = None, target_fixture: str | None = None) -> Callable:
"""When step decorator.
:param name: Step name or a parser object.
@ -87,7 +90,7 @@ def when(name: Any, converters: Optional[Dict[str, type]] = None, target_fixture
return _step_decorator(WHEN, name, converters=converters, target_fixture=target_fixture)
def then(name: Any, converters: Optional[Dict[str, type]] = None, target_fixture: Optional[str] = None) -> Callable:
def then(name: Any, converters: dict[str, type] | None = None, target_fixture: str | None = None) -> Callable:
"""Then step decorator.
:param name: Step name or a parser object.
@ -103,8 +106,8 @@ def then(name: Any, converters: Optional[Dict[str, type]] = None, target_fixture
def _step_decorator(
step_type: str,
step_name: Any,
converters: Union[Dict[str, Callable], Dict[str, type], None] = None,
target_fixture: Optional[str] = None,
converters: dict[str, Callable] | dict[str, type] | None = None,
target_fixture: str | None = None,
) -> Callable:
"""Step decorator for the type and the name.

View File

@ -7,16 +7,17 @@ import re
import typing
from inspect import getframeinfo, signature
from sys import _getframe
from typing import Any, Callable, Dict, List
if typing.TYPE_CHECKING:
from typing import Any, Callable
from _pytest.config import Config
from _pytest.pytester import RunResult
CONFIG_STACK: List[Config] = []
CONFIG_STACK: list[Config] = []
def get_args(func: Callable) -> List[str]:
def get_args(func: Callable) -> list[str]:
"""Get a list of argument names for a function.
:param func: The function to inspect.
@ -28,7 +29,7 @@ def get_args(func: Callable) -> List[str]:
return [param.name for param in params if param.kind == param.POSITIONAL_OR_KEYWORD]
def get_caller_module_locals(depth: int = 2) -> Dict[str, Any]:
def get_caller_module_locals(depth: int = 2) -> dict[str, Any]:
"""Get the caller module locals dictionary.
We use sys._getframe instead of inspect.stack(0) because the latter is way slower, since it iterates over

View File

@ -1,5 +1,3 @@
from typing import Iterator
import pytest
from tests.utils import PYTEST_6

View File

@ -1,11 +1,13 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional
import typing
import pytest
from packaging.utils import Version
if TYPE_CHECKING:
if typing.TYPE_CHECKING:
from typing import Any
from _pytest.pytester import RunResult
PYTEST_VERSION = Version(pytest.__version__)
@ -22,7 +24,7 @@ if PYTEST_6:
errors: int = 0,
xpassed: int = 0,
xfailed: int = 0,
) -> Optional[Any]:
) -> Any | None:
"""Compatibility function for result.assert_outcomes"""
return result.assert_outcomes(
errors=errors, passed=passed, skipped=skipped, failed=failed, xpassed=xpassed, xfailed=xfailed