Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ------------------------------------------------

Expand Down
3 changes: 2 additions & 1 deletion perf_tests/test_dependencies.py
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
3 changes: 2 additions & 1 deletion perf_tests/test_ordinal.py
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
3 changes: 2 additions & 1 deletion perf_tests/test_relative.py
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
3 changes: 2 additions & 1 deletion perf_tests/test_relative_dense.py
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
37 changes: 19 additions & 18 deletions src/pytest_order/item.py
Original file line number Diff line number Diff line change
@@ -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")


Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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:
Expand All @@ -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).
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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."""
Expand Down
41 changes: 21 additions & 20 deletions src/pytest_order/plugin.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/pytest_order/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from enum import Enum
from typing import ClassVar
from warnings import warn

from _pytest.config import Config
Expand All @@ -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,
Expand Down
16 changes: 9 additions & 7 deletions src/pytest_order/sorter.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand All @@ -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 = []
Expand Down
2 changes: 1 addition & 1 deletion tests/test_class_marks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/test_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions tests/test_order_scope_level.py
Original file line number Diff line number Diff line change
Expand Up @@ -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*"
)
]
)
14 changes: 8 additions & 6 deletions tests/test_relative_ordering.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
)
Expand Down Expand Up @@ -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"
)
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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_*"
)
]
)

Expand Down
1 change: 1 addition & 0 deletions tests/test_xdist_handling.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from textwrap import dedent

import pytest

import pytest_order


Expand Down