Skip to content

Commit a870b66

Browse files
authored
Merge pull request #758 from kieran-ryan/ruff-migration
Migrate `black`, `flake8`, `isort`, `pyupgrade` linters and formatters to `ruff`
2 parents 9ff6980 + b96b83e commit a870b66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+146
-95
lines changed

.pre-commit-config.yaml

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,16 @@
11
# See https://pre-commit.com for more information
22
# See https://pre-commit.com/hooks.html for more hooks
33
repos:
4-
- repo: https://github.com/psf/black
5-
# If you update the version here, also update it in tox.ini (py*-pytestlatest-linters)
6-
rev: 24.10.0
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
76
hooks:
8-
- id: black
9-
- repo: https://github.com/pycqa/isort
10-
rev: 5.13.2
7+
- id: trailing-whitespace
8+
- id: end-of-file-fixer
9+
- id: check-yaml
10+
- id: check-added-large-files
11+
- repo: https://github.com/astral-sh/ruff-pre-commit
12+
rev: f0b5944bef86f50d875305821a0ab0d8c601e465 # frozen: v0.8.4
1113
hooks:
12-
- id: isort
13-
name: isort (python)
14-
- repo: https://github.com/pre-commit/pre-commit-hooks
15-
rev: v5.0.0
16-
hooks:
17-
- id: trailing-whitespace
18-
- id: end-of-file-fixer
19-
- id: check-yaml
20-
- id: check-added-large-files
21-
- repo: https://github.com/asottile/pyupgrade
22-
rev: v3.19.0
23-
hooks:
24-
- id: pyupgrade
25-
args: ["--py39-plus"]
26-
- repo: https://github.com/pycqa/flake8
27-
rev: "7.1.1"
28-
hooks:
29-
- id: flake8
30-
additional_dependencies: [
31-
"flake8-pyproject",
32-
"flake8-bugbear",
33-
]
14+
- id: ruff
15+
args: [ --fix ]
16+
- id: ruff-format

.vscode/extensions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"recommendations": [
3+
"charliermarsh.ruff"
4+
]
5+
}

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
# -- Project information -----------------------------------------------------
77
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8+
from __future__ import annotations
89

910
from importlib import metadata as _metadata
1011

pyproject.toml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,29 @@ sphinx-autobuild = "*"
5757
requires = ["poetry-core>=1.0.0"]
5858
build-backend = "poetry.core.masonry.api"
5959

60-
[tool.black]
60+
[tool.ruff]
6161
line-length = 120
62-
target-version = ["py39", "py310", "py311", "py312", "py313"]
63-
64-
[tool.flake8]
65-
# E1: indentation: already covered by `black`
66-
# E2: whitespace: already covered by `black`
67-
# E3: blank line: already covered by `black`
68-
# E501: line length: already covered by `black`
69-
extend-ignore = "E1,E2,E3,E501"
70-
71-
[tool.isort]
72-
profile = "black"
73-
line_length = 120
74-
multi_line_output = 3
62+
target-version = "py39"
63+
lint.select = [
64+
"B", # flake8-bugbear
65+
"BLE", # flake8-blind-except
66+
"C4", # flake8-comprehensions
67+
"E4", # pycodestyle - error - import
68+
"E7", # pycodestyle - error - statement
69+
"E9", # pycodestyle - error - runtime
70+
"F", # pyflakes
71+
"I", # isort
72+
"ISC", # flake8-implicit-str-concat
73+
"PERF", # perflint
74+
"UP", # pyupgrade
75+
]
76+
lint.ignore = [
77+
# Covered by formatter
78+
"ISC001" # single-line-implicit-string-concatenation
79+
]
80+
lint.isort.required-imports = [
81+
"from __future__ import annotations",
82+
]
7583

7684
[tool.coverage.report]
7785
exclude_lines = [

src/pytest_bdd/gherkin_terminal_reporter.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,9 @@ def configure(config: Config) -> None:
3131
raise Exception(
3232
"gherkin-terminal-reporter is not compatible with any other terminal reporter."
3333
"You can use only one terminal reporter."
34-
"Currently '{0}' is used."
35-
"Please decide to use one by deactivating {0} or gherkin-terminal-reporter.".format(
36-
current_reporter.__class__
37-
)
34+
f"Currently '{current_reporter.__class__}' is used."
35+
f"Please decide to use one by deactivating {current_reporter.__class__} "
36+
"or gherkin-terminal-reporter."
3837
)
3938
gherkin_reporter = GherkinTerminalReporter(config)
4039
config.pluginmanager.unregister(current_reporter)

src/pytest_bdd/parser.py

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010

1111
from .exceptions import StepError
1212
from .gherkin_parser import Background as GherkinBackground
13-
from .gherkin_parser import DataTable
13+
from .gherkin_parser import DataTable, GherkinDocument, get_gherkin_document
1414
from .gherkin_parser import Feature as GherkinFeature
15-
from .gherkin_parser import GherkinDocument
1615
from .gherkin_parser import Rule as GherkinRule
1716
from .gherkin_parser import Scenario as GherkinScenario
1817
from .gherkin_parser import Step as GherkinStep
1918
from .gherkin_parser import Tag as GherkinTag
20-
from .gherkin_parser import get_gherkin_document
2119
from .types import STEP_TYPE_BY_PARSER_KEYWORD
2220

2321
PARAM_RE = re.compile(r"<(.+?)>")
@@ -48,7 +46,7 @@ def get_tag_names(tag_data: list[GherkinTag]) -> set[str]:
4846
"""Extract tag names from tag data.
4947
5048
Args:
51-
tag_data (List[dict]): The tag data to extract names from.
49+
tag_data (list[dict]): The tag data to extract names from.
5250
5351
Returns:
5452
set[str]: A set of tag names.
@@ -66,7 +64,7 @@ class Feature:
6664
rel_filename (str): The relative path of the feature file.
6765
name (str): The name of the feature.
6866
tags (set[str]): A set of tags associated with the feature.
69-
background (Optional[Background]): The background steps for the feature, if any.
67+
background (Background | None): The background steps for the feature, if any.
7068
line_number (int): The line number where the feature starts in the file.
7169
description (str): The description of the feature.
7270
"""
@@ -88,10 +86,10 @@ class Examples:
8886
"""Represents examples used in scenarios for parameterization.
8987
9088
Attributes:
91-
line_number (Optional[int]): The line number where the examples start.
92-
name (Optional[str]): The name of the examples.
93-
example_params (List[str]): The names of the parameters for the examples.
94-
examples (List[Sequence[str]]): The list of example rows.
89+
line_number (int | None): The line number where the examples start.
90+
name (str | None): The name of the examples.
91+
example_params (list[str]): The names of the parameters for the examples.
92+
examples (list[Sequence[str]]): The list of example rows.
9593
"""
9694

9795
line_number: int | None = None
@@ -154,11 +152,11 @@ class ScenarioTemplate:
154152
name (str): The name of the scenario.
155153
line_number (int): The line number where the scenario starts in the file.
156154
templated (bool): Whether the scenario is templated.
157-
description (Optional[str]): The description of the scenario.
155+
description (str | None): The description of the scenario.
158156
tags (set[str]): A set of tags associated with the scenario.
159-
_steps (List[Step]): The list of steps in the scenario (internal use only).
160-
examples (Optional[Examples]): The examples used for parameterization in the scenario.
161-
rule (Optional[Rule]): The rule to which the scenario may belong (None = no rule).
157+
_steps (list[Step]): The list of steps in the scenario (internal use only).
158+
examples (Examples | None): The examples used for parameterization in the scenario.
159+
rule (Rule | None): The rule to which the scenario may belong (None = no rule).
162160
"""
163161

164162
feature: Feature
@@ -197,7 +195,7 @@ def steps(self) -> list[Step]:
197195
"""Get all steps for the scenario, including background steps.
198196
199197
Returns:
200-
List[Step]: A list of steps, including any background steps from the feature.
198+
list[Step]: A list of steps, including any background steps from the feature.
201199
"""
202200
return self.all_background_steps + self._steps
203201

@@ -244,8 +242,8 @@ class Scenario:
244242
keyword (str): The keyword used to define the scenario.
245243
name (str): The name of the scenario.
246244
line_number (int): The line number where the scenario starts in the file.
247-
steps (List[Step]): The list of steps in the scenario.
248-
description (Optional[str]): The description of the scenario.
245+
steps (list[Step]): The list of steps in the scenario.
246+
description (str | None): The description of the scenario.
249247
tags (set[str]): A set of tags associated with the scenario.
250248
"""
251249

@@ -270,8 +268,8 @@ class Step:
270268
indent (int): The indentation level of the step.
271269
keyword (str): The keyword used for the step (e.g., 'Given', 'When', 'Then').
272270
failed (bool): Whether the step has failed (internal use only).
273-
scenario (Optional[ScenarioTemplate]): The scenario to which this step belongs (internal use only).
274-
background (Optional[Background]): The background to which this step belongs (internal use only).
271+
scenario (ScenarioTemplate | None): The scenario to which this step belongs (internal use only).
272+
background (Background | None): The background to which this step belongs (internal use only).
275273
"""
276274

277275
type: str
@@ -346,7 +344,7 @@ class Background:
346344
347345
Attributes:
348346
line_number (int): The line number where the background starts in the file.
349-
steps (List[Step]): The list of steps in the background.
347+
steps (list[Step]): The list of steps in the background.
350348
"""
351349

352350
line_number: int
@@ -371,7 +369,7 @@ class FeatureParser:
371369
encoding (str): File encoding of the feature file to parse.
372370
"""
373371

374-
def __init__(self, basedir: str, filename: str, encoding: str = "utf-8"):
372+
def __init__(self, basedir: str, filename: str, encoding: str = "utf-8") -> None:
375373
self.abs_filename = os.path.abspath(os.path.join(basedir, filename))
376374
self.rel_filename = os.path.join(os.path.basename(basedir), filename)
377375
self.encoding = encoding
@@ -380,10 +378,10 @@ def parse_steps(self, steps_data: list[GherkinStep]) -> list[Step]:
380378
"""Parse a list of step data into Step objects.
381379
382380
Args:
383-
steps_data (List[dict]): The list of step data.
381+
steps_data (list[dict]): The list of step data.
384382
385383
Returns:
386-
List[Step]: A list of Step objects.
384+
list[Step]: A list of Step objects.
387385
"""
388386

389387
if not steps_data:
@@ -423,7 +421,7 @@ def parse_scenario(
423421
Args:
424422
scenario_data (dict): The dictionary containing scenario data.
425423
feature (Feature): The feature to which this scenario belongs.
426-
rule (Optional[Rule]): The rule to which this scenario may belong. (None = no rule)
424+
rule (Rule | None): The rule to which this scenario may belong. (None = no rule)
427425
428426
Returns:
429427
ScenarioTemplate: A ScenarioTemplate object representing the parsed scenario.

src/pytest_bdd/scenario.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ def parse_step_arguments(step: Step, context: StepFunctionContext) -> dict[str,
188188
"""Parse step arguments."""
189189
parsed_args = context.parser.parse_arguments(step.name)
190190

191-
assert parsed_args is not None, (
192-
f"Unexpected `NoneType` returned from " f"parse_arguments(...) in parser: {context.parser!r}"
193-
)
191+
assert (
192+
parsed_args is not None
193+
), f"Unexpected `NoneType` returned from parse_arguments(...) in parser: {context.parser!r}"
194194

195195
reserved_args = set(parsed_args.keys()) & STEP_ARGUMENTS_RESERVED_NAMES
196196
if reserved_args:
@@ -386,7 +386,7 @@ def scenario(
386386
feature_name = feature.name or "[Empty]"
387387
raise exceptions.ScenarioNotFound(
388388
f'Scenario "{scenario_name}" in feature "{feature_name}" in {feature.filename} is not found.'
389-
)
389+
) from None
390390

391391
return _get_scenario_decorator(
392392
feature=feature, feature_name=feature_name, templated_scenario=scenario, scenario_name=scenario_name

tests/args/cfparse/test_args.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Step arguments tests."""
22

3+
from __future__ import annotations
4+
35
import textwrap
46

57

tests/args/parse/test_args.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Step arguments tests."""
22

3+
from __future__ import annotations
4+
35
import textwrap
46

57

tests/args/regex/test_args.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Step arguments tests."""
22

3+
from __future__ import annotations
4+
35
import textwrap
46

57

tests/args/test_common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import textwrap
24

35
from pytest_bdd.utils import collect_dumped_objects

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import pytest
24

35
pytest_plugins = "pytester"

tests/datatable/test_datatable.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import textwrap
24

35
from src.pytest_bdd.utils import collect_dumped_objects

tests/feature/test_alias.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test step alias when decorated multiple times."""
22

3+
from __future__ import annotations
4+
35
import textwrap
46

57

tests/feature/test_background.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test feature background."""
22

3+
from __future__ import annotations
4+
35
import textwrap
46

57
FEATURE = '''\

tests/feature/test_description.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test descriptions."""
22

3+
from __future__ import annotations
4+
35
import textwrap
46

57

tests/feature/test_feature_base_dir.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Test feature base dir."""
22

3+
from __future__ import annotations
4+
35
import os
46

57
import pytest
@@ -59,12 +61,10 @@ def test_feature_path_by_param_ok(pytester, base_dir):
5961

6062
def prepare_testdir(pytester, ini_base_dir):
6163
pytester.makeini(
62-
"""
64+
f"""
6365
[pytest]
64-
bdd_features_base_dir={}
65-
""".format(
66-
ini_base_dir
67-
)
66+
bdd_features_base_dir={ini_base_dir}
67+
"""
6868
)
6969

7070
feature_file = pytester.mkdir("features").joinpath("steps.feature")
@@ -77,7 +77,7 @@ def prepare_testdir(pytester, ini_base_dir):
7777
)
7878

7979
pytester.makepyfile(
80-
"""
80+
f"""
8181
import os.path
8282
8383
import pytest
@@ -103,7 +103,7 @@ def test_not_found_by_ini(scenario_name, multiple):
103103
scenarios(FEATURE)
104104
else:
105105
scenario(FEATURE, scenario_name)
106-
assert os.path.abspath(os.path.join('{}', FEATURE)) in str(exc.value)
106+
assert os.path.abspath(os.path.join('{ini_base_dir}', FEATURE)) in str(exc.value)
107107
108108
109109
@pytest.mark.parametrize(
@@ -145,7 +145,5 @@ def test_ok_by_param(scenario_name, multiple):
145145
else:
146146
scenario(FEATURE, scenario_name, features_base_dir='features')
147147
148-
""".format(
149-
ini_base_dir
150-
)
148+
"""
151149
)

0 commit comments

Comments
 (0)