diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 78e433a..f102fc2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ default_language_version: repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.15.22" + rev: "v0.16.0" hooks: - id: ruff args: ["--fix"] diff --git a/docs/source/conf.py b/docs/source/conf.py index 6413b4b..f68a225 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,7 +18,7 @@ sys.path.insert(0, os.path.abspath("../../src")) -from pytest_order import __version__ # noqa: E402 +from pytest_order import __version__ # -- General configuration ------------------------------------------------ diff --git a/perf_tests/test_dependencies.py b/perf_tests/test_dependencies.py index 37dd527..98d7931 100644 --- a/perf_tests/test_dependencies.py +++ b/perf_tests/test_dependencies.py @@ -1,7 +1,8 @@ -from unittest import mock from textwrap import dedent +from unittest import mock import pytest + from perf_tests.util import TimedSorter pytest_plugins = ["pytester"] diff --git a/perf_tests/test_ordinal.py b/perf_tests/test_ordinal.py index a269bb2..98253b2 100644 --- a/perf_tests/test_ordinal.py +++ b/perf_tests/test_ordinal.py @@ -1,7 +1,8 @@ -from unittest import mock from textwrap import dedent +from unittest import mock import pytest + from perf_tests.util import TimedSorter pytest_plugins = ["pytester"] diff --git a/perf_tests/test_relative.py b/perf_tests/test_relative.py index f70e3dd..229fe70 100644 --- a/perf_tests/test_relative.py +++ b/perf_tests/test_relative.py @@ -1,7 +1,8 @@ -from unittest import mock from textwrap import dedent +from unittest import mock import pytest + from perf_tests.util import TimedSorter pytest_plugins = ["pytester"] diff --git a/perf_tests/test_relative_dense.py b/perf_tests/test_relative_dense.py index 54158b0..a3a05e3 100644 --- a/perf_tests/test_relative_dense.py +++ b/perf_tests/test_relative_dense.py @@ -1,7 +1,8 @@ -from unittest import mock from textwrap import dedent +from unittest import mock import pytest + from perf_tests.util import TimedSorter pytest_plugins = ["pytester"] diff --git a/src/pytest_order/item.py b/src/pytest_order/item.py index 7c6be64..b14a898 100644 --- a/src/pytest_order/item.py +++ b/src/pytest_order/item.py @@ -1,12 +1,13 @@ +from __future__ import annotations + import sys -from typing import Optional, Generic, TypeVar from collections import defaultdict +from typing import Generic, TypeVar from pytest import Function, UsageError from .settings import Scope, Settings - _ItemType = TypeVar("_ItemType", "Item", "ItemGroup") @@ -16,8 +17,8 @@ class Item: def __init__(self, item: Function, collection_index: int = 0) -> None: self.item: Function = item self.nr_rel_items: int = 0 - self.order: Optional[int] = None - self._node_id: Optional[str] = None + self.order: int | None = None + self._node_id: str | None = None self.collection_index: int = collection_index def inc_rel_marks(self) -> None: @@ -51,8 +52,8 @@ def __init__( items: list[Item], settings: Settings, scope: Scope, - rel_marks: list["RelativeMark[Item]"], - dep_marks: list["RelativeMark[Item]"], + rel_marks: list[RelativeMark[Item]], + dep_marks: list[RelativeMark[Item]], ) -> None: self.items = items self.settings = settings @@ -160,9 +161,9 @@ def handle_dep_marks(self, sorted_list: list[Item]) -> None: @staticmethod def handle_relative_marks( - marks: list["RelativeMark[Item]"], + marks: list[RelativeMark[Item]], sorted_list: list[Item], - all_marks: list["RelativeMark[Item]"], + all_marks: list[RelativeMark[Item]], ) -> None: for mark in reversed(marks): if move_item(mark, sorted_list): @@ -189,7 +190,7 @@ def print_unhandled_items(self) -> None: for item in failed_items: item.item.fixturenames.insert(0, "fail_after_cannot_order") - def group_order(self) -> Optional[int]: + def group_order(self) -> int | None: if self.start_items: return self.start_items[0][0] elif self.end_items: @@ -198,10 +199,10 @@ def group_order(self) -> Optional[int]: def _sort_by_topology( self, - items: list["Item"], - rel_marks: list["RelativeMark[Item]"], - dep_marks: list["RelativeMark[Item]"], - ) -> tuple[list["Item"], bool]: + items: list[Item], + rel_marks: list[RelativeMark[Item]], + dep_marks: list[RelativeMark[Item]], + ) -> tuple[list[Item], bool]: """ Order items so that all relative constraints are satisfied while staying as close as possible to the incoming order (the absolute-ordinal baseline). @@ -261,7 +262,7 @@ class ItemGroup: """ def __init__( - self, items: Optional[list[Item]] = None, order: Optional[int] = None + self, items: list[Item] | None = None, order: int | None = None ) -> None: self.items: list[Item] = items or [] self.order = order @@ -275,7 +276,7 @@ def dec_rel_marks(self) -> None: if self.order is None: self.nr_rel_items -= 1 - def extend(self, groups: list["ItemGroup"], order: Optional[int]) -> None: + def extend(self, groups: list[ItemGroup], order: int | None) -> None: for group in groups: self.items.extend(group.items) self.order = order @@ -339,9 +340,9 @@ def move_item(mark: RelativeMark[_ItemType], sorted_items: list[_ItemType]) -> b def _build_predecessors( - marks: list["RelativeMark[Item]"], - item_set: set["Item"], -) -> "defaultdict[Item, list[Item]]": + marks: list[RelativeMark[Item]], + item_set: set[Item], +) -> defaultdict[Item, list[Item]]: """Map each item to the items that must run before it, derived from the relative marks. A mark either places item_to_move after item (move_after) or before it.""" diff --git a/src/pytest_order/plugin.py b/src/pytest_order/plugin.py index ce0a618..8348df1 100644 --- a/src/pytest_order/plugin.py +++ b/src/pytest_order/plugin.py @@ -1,11 +1,11 @@ -from collections.abc import Generator, Callable +from collections.abc import Callable, Generator import pytest -from pytest import Function from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.main import Session from _pytest.mark import Mark +from pytest import Function from .sorter import Sorter @@ -157,24 +157,25 @@ def pytest_generate_tests(metafunc): Make parametrized tests with corresponding order marks. """ - if getattr(metafunc, "function", False): - if getattr(metafunc.function, "pytestmark", False): - # Get list of order marks - marks = metafunc.function.pytestmark - order_marks = [mark for mark in marks if mark.name == "order"] - if len(order_marks) > 1: - # Remove all order marks - metafunc.function.pytestmark = [ - mark for mark in marks if mark.name != "order" - ] - # Prepare arguments for parametrization with order marks - args = [ - pytest.param(_get_mark_description(mark), marks=[mark]) - for mark in order_marks - ] - if "order" not in metafunc.fixturenames: - metafunc.fixturenames.append("order") - metafunc.parametrize("order", args) + if getattr(metafunc, "function", False) and getattr( + metafunc.function, "pytestmark", False + ): + # Get list of order marks + marks = metafunc.function.pytestmark + order_marks = [mark for mark in marks if mark.name == "order"] + if len(order_marks) > 1: + # Remove all order marks + metafunc.function.pytestmark = [ + mark for mark in marks if mark.name != "order" + ] + # Prepare arguments for parametrization with order marks + args = [ + pytest.param(_get_mark_description(mark), marks=[mark]) + for mark in order_marks + ] + if "order" not in metafunc.fixturenames: + metafunc.fixturenames.append("order") + metafunc.parametrize("order", args) @pytest.fixture diff --git a/src/pytest_order/settings.py b/src/pytest_order/settings.py index fe4c5ea..1b796ec 100644 --- a/src/pytest_order/settings.py +++ b/src/pytest_order/settings.py @@ -1,4 +1,5 @@ from enum import Enum +from typing import ClassVar from warnings import warn from _pytest.config import Config @@ -13,7 +14,7 @@ class Scope(Enum): class Settings: """Holds all configuration settings.""" - valid_scopes = { + valid_scopes: ClassVar = { "class": Scope.CLASS, "module": Scope.MODULE, "session": Scope.SESSION, diff --git a/src/pytest_order/sorter.py b/src/pytest_order/sorter.py index f4aac11..eca7b57 100644 --- a/src/pytest_order/sorter.py +++ b/src/pytest_order/sorter.py @@ -1,16 +1,18 @@ +from __future__ import annotations + import re import sys from collections import OrderedDict from contextlib import suppress -from typing import Optional, cast +from typing import cast from warnings import warn from _pytest.config import Config from _pytest.mark import Mark from pytest import Function, UsageError -from .item import Item, ItemList, ItemGroup, filter_marks, move_item, RelativeMark -from .settings import Settings, Scope +from .item import Item, ItemGroup, ItemList, RelativeMark, filter_marks, move_item +from .settings import Scope, Settings orders_map = { "first": 0, @@ -298,7 +300,7 @@ def resolve_dependency_markers( alias = self.matching_alias(aliases[name], item) self.dep_marks.append(RelativeMark(alias, item, move_after=True)) else: - label = "::".join((prefix, name)) + label = f"{prefix}::{name}" if label in aliases: for item in items: alias = self.matching_alias(aliases[label], item) @@ -512,13 +514,13 @@ def collect_group_marks( group_to_move.inc_rel_marks() return group_marks - def group_for_item(self, item: Item) -> Optional[ItemGroup]: + def group_for_item(self, item: Item) -> ItemGroup | None: for group in self.groups: if item in group.items: return group return None - def sorted_groups(self) -> tuple[Optional[int], list[ItemGroup]]: + def sorted_groups(self) -> tuple[int | None, list[ItemGroup]]: group_order = self.sort_by_ordinal_markers() length = len(self.rel_marks) + len(self.dep_marks) if length == 0: @@ -532,7 +534,7 @@ def sorted_groups(self) -> tuple[Optional[int], list[ItemGroup]]: self.handle_rel_marks(self.dep_marks) return group_order, self.groups - def sort_by_ordinal_markers(self) -> Optional[int]: + def sort_by_ordinal_markers(self) -> int | None: start_groups = [] middle_groups = [] end_groups = [] diff --git a/tests/test_class_marks.py b/tests/test_class_marks.py index 3edfead..61ca60d 100644 --- a/tests/test_class_marks.py +++ b/tests/test_class_marks.py @@ -61,7 +61,7 @@ def test_2(self): pass "Test2::test_1", "Test2::test_2", ] - out, err = capsys.readouterr() + out, _ = capsys.readouterr() assert ( "WARNING: cannot execute 'test_2' relative to others: " "'Test3' - ignoring the marker" in out diff --git a/tests/test_dependency.py b/tests/test_dependency.py index 7d30a95..34b2f2d 100644 --- a/tests/test_dependency.py +++ b/tests/test_dependency.py @@ -262,7 +262,7 @@ def test_c(self): "Test::test_b", "Test::test_c", ] - out, err = capsys.readouterr() + out, _ = capsys.readouterr() warning = "Cannot resolve the dependency marker 'test_c' - ignoring it" assert warning in out @@ -565,7 +565,7 @@ def test_c(self): "Test::test_b", "Test::test_c", ] - out, err = capsys.readouterr() + out, _ = capsys.readouterr() warning = "Cannot resolve the dependency marker 'test_3' - ignoring it." assert warning in out diff --git a/tests/test_misc.py b/tests/test_misc.py index 0480bfa..077103b 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -19,7 +19,7 @@ def test_version_valid(): def test_markers_registered(capsys): pytest.main(["--markers"]) - out, err = capsys.readouterr() + out, _ = capsys.readouterr() assert "@pytest.mark.order" in out # only order is supported as marker assert out.count("Provided by pytest-order.") == 1 diff --git a/tests/test_order_scope_level.py b/tests/test_order_scope_level.py index 624811b..57bb166 100644 --- a/tests/test_order_scope_level.py +++ b/tests/test_order_scope_level.py @@ -135,7 +135,9 @@ def test_invalid_scope(fixture_path): result.assert_outcomes(passed=12, failed=0) result.stdout.fnmatch_lines( [ - "*UserWarning: order-scope-level cannot be used " - "together with --order-scope=module*" + ( + "*UserWarning: order-scope-level cannot be used " + "together with --order-scope=module*" + ) ] ) diff --git a/tests/test_relative_ordering.py b/tests/test_relative_ordering.py index 3758717..7c5325a 100644 --- a/tests/test_relative_ordering.py +++ b/tests/test_relative_ordering.py @@ -414,7 +414,7 @@ def test_2(): pass """ assert item_names_for(test_content) == ["test_1", "test_2"] - out, err = capsys.readouterr() + out, _ = capsys.readouterr() warning = ( "cannot execute 'test_1' relative to others: 'some_module.py::test_2' " "- ignoring the marker" @@ -437,7 +437,7 @@ def test_3(): pass """ assert item_names_for(test_content) == ["test_1", "test_2", "test_3"] - out, err = capsys.readouterr() + out, _ = capsys.readouterr() warning = ( "cannot execute 'test_2' relative to others: 'test_4' - ignoring the marker" ) @@ -518,7 +518,7 @@ def test_3(self): "Test::test_2", "Test::test_3", ] - out, err = capsys.readouterr() + out, _ = capsys.readouterr() warning = ( "cannot execute 'test_2' relative to others: 'test_4' - ignoring the marker" ) @@ -546,7 +546,7 @@ def test_3(): # test_3 and test_1 are topologically sorted: test_3 → test_1 assert item_names_for(test_content) == ["test_2", "test_3", "test_1"] # No warning should be issued since the constraints are consistent - out, err = capsys.readouterr() + out, _ = capsys.readouterr() assert "cannot execute test relative to others" not in out @@ -612,8 +612,10 @@ def test_3(): result.assert_outcomes(passed=0, failed=0) result.stderr.fnmatch_lines( [ - "ERROR: pytest-order: cannot execute test relative to others: " - "test_failed_ordering.py::test_* test_failed_ordering.py::test_*" + ( + "ERROR: pytest-order: cannot execute test relative to others: " + "test_failed_ordering.py::test_* test_failed_ordering.py::test_*" + ) ] ) diff --git a/tests/test_xdist_handling.py b/tests/test_xdist_handling.py index 0ac6f2c..fb61e9b 100644 --- a/tests/test_xdist_handling.py +++ b/tests/test_xdist_handling.py @@ -1,6 +1,7 @@ from textwrap import dedent import pytest + import pytest_order