pytest-sort 1.3.1__tar.gz → 1.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest-sort-1.3.1/src/pytest_sort.egg-info → pytest_sort-1.3.3}/PKG-INFO +6 -4
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/README.md +2 -2
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/pyproject.toml +23 -18
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort/core.py +15 -12
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort/database.py +4 -16
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort/diffcov.py +7 -4
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort/plugin.py +9 -7
- {pytest-sort-1.3.1 → pytest_sort-1.3.3/src/pytest_sort.egg-info}/PKG-INFO +6 -4
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort.egg-info/requires.txt +1 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/test/test_config.py +5 -5
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/test/test_core.py +39 -45
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/LICENSE +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/setup.cfg +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort/__init__.py +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort/config.py +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort.egg-info/SOURCES.txt +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort.egg-info/dependency_links.txt +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort.egg-info/entry_points.txt +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/src/pytest_sort.egg-info/top_level.txt +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/test/test_database.py +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/test/test_diffcov.py +0 -0
- {pytest-sort-1.3.1 → pytest_sort-1.3.3}/test/test_plugin.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-sort
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Tools for sorting test cases
|
|
5
5
|
Author-email: WiredNerd <pbuschmail-pytestsort@yahoo.com>
|
|
6
6
|
Maintainer-email: WiredNerd <pbuschmail-pytestsort@yahoo.com>
|
|
@@ -34,15 +34,17 @@ Classifier: Programming Language :: Python :: 3
|
|
|
34
34
|
Classifier: License :: OSI Approved :: MIT License
|
|
35
35
|
Classifier: Operating System :: OS Independent
|
|
36
36
|
Classifier: Framework :: Pytest
|
|
37
|
-
Requires-Python: >=3.
|
|
37
|
+
Requires-Python: >=3.10
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
40
40
|
Requires-Dist: pytest>=7.4.0
|
|
41
41
|
Requires-Dist: whatthepatch>=1.0.5
|
|
42
42
|
Requires-Dist: coverage>=7.3.1
|
|
43
|
+
Requires-Dist: zipp>=3.19.1
|
|
44
|
+
Dynamic: license-file
|
|
43
45
|
|
|
44
46
|
[](https://github.com/WiredNerd/pytest-sort)
|
|
45
|
-
[](https://pypi.org/project/pytest-sort)
|
|
46
48
|
[](https://pypi.org/project/pytest-sort)
|
|
47
49
|
[](https://pypistats.org/packages/pytest-sort)
|
|
48
50
|
[](https://github.com/WiredNerd/pytest-sort/blob/main/LICENSE)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[](https://github.com/WiredNerd/pytest-sort)
|
|
2
|
-
[](https://pypi.org/project/pytest-sort)
|
|
3
3
|
[](https://pypi.org/project/pytest-sort)
|
|
4
4
|
[](https://pypistats.org/packages/pytest-sort)
|
|
5
5
|
[](https://github.com/WiredNerd/pytest-sort/blob/main/LICENSE)
|
|
@@ -61,4 +61,4 @@ Or by opening an issue: https://github.com/WiredNerd/pytest-sort/issues
|
|
|
61
61
|
|
|
62
62
|
## License
|
|
63
63
|
|
|
64
|
-
The project is licensed under the MIT license.
|
|
64
|
+
The project is licensed under the MIT license.
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytest-sort"
|
|
7
7
|
description = "Tools for sorting test cases"
|
|
8
|
-
version = "1.3.
|
|
8
|
+
version = "1.3.3"
|
|
9
9
|
license = { file = "LICENSE" }
|
|
10
10
|
keywords = [
|
|
11
11
|
"test",
|
|
@@ -24,14 +24,14 @@ maintainers = [
|
|
|
24
24
|
{ name = "WiredNerd", email = "pbuschmail-pytestsort@yahoo.com" },
|
|
25
25
|
]
|
|
26
26
|
readme = "README.md"
|
|
27
|
-
requires-python = ">=3.
|
|
27
|
+
requires-python = ">=3.10"
|
|
28
28
|
classifiers = [
|
|
29
29
|
"Programming Language :: Python :: 3",
|
|
30
30
|
"License :: OSI Approved :: MIT License",
|
|
31
31
|
"Operating System :: OS Independent",
|
|
32
32
|
"Framework :: Pytest",
|
|
33
33
|
]
|
|
34
|
-
dependencies = ["pytest>=7.4.0", "whatthepatch>=1.0.5", "coverage>=7.3.1"]
|
|
34
|
+
dependencies = ["pytest>=7.4.0", "whatthepatch>=1.0.5", "coverage>=7.3.1", "zipp>=3.19.1"]
|
|
35
35
|
|
|
36
36
|
[project.urls]
|
|
37
37
|
"Documentation" = "https://pytest-sort.readthedocs.io/"
|
|
@@ -64,7 +64,7 @@ command_line = "pytest -x --assert=plain -o pythonpath='{PYTHONPATH}' --sort-mod
|
|
|
64
64
|
|
|
65
65
|
[tool.black]
|
|
66
66
|
line-length = 120
|
|
67
|
-
target-version = ['
|
|
67
|
+
target-version = ['py310', 'py311', 'py312', 'py313', 'py314']
|
|
68
68
|
skip-string-normalization = true
|
|
69
69
|
color = true
|
|
70
70
|
|
|
@@ -76,19 +76,8 @@ files = "src"
|
|
|
76
76
|
mypy_path = "src"
|
|
77
77
|
|
|
78
78
|
[tool.ruff]
|
|
79
|
-
target-version = "
|
|
80
|
-
|
|
81
|
-
select = ["ALL"]
|
|
82
|
-
ignore = [
|
|
83
|
-
"ANN101", # Missing type annotation for self in method
|
|
84
|
-
"COM812", # missing-trailing-comma
|
|
85
|
-
"D203", # 1 blank line required before class docstring
|
|
86
|
-
"D213", # multi-line-summary-second-line
|
|
87
|
-
"EM101", # Exception must not use a string literal, assign to variable first
|
|
88
|
-
"ISC001", # single-line-implicit-string-concatenation
|
|
89
|
-
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
90
|
-
"TRY003", # Avoid specifying long messages outside the exception class
|
|
91
|
-
]
|
|
79
|
+
target-version = "py310"
|
|
80
|
+
output-format = "full"
|
|
92
81
|
line-length = 120
|
|
93
82
|
exclude = [
|
|
94
83
|
".eggs",
|
|
@@ -104,10 +93,25 @@ exclude = [
|
|
|
104
93
|
"venv",
|
|
105
94
|
]
|
|
106
95
|
|
|
96
|
+
[tool.ruff.lint]
|
|
97
|
+
select = ["ALL"]
|
|
98
|
+
ignore = [
|
|
99
|
+
"COM812", # missing-trailing-comma
|
|
100
|
+
"D203", # 1 blank line required before class docstring
|
|
101
|
+
"D213", # multi-line-summary-second-line
|
|
102
|
+
"EM101", # Exception must not use a string literal, assign to variable first
|
|
103
|
+
"ISC001", # single-line-implicit-string-concatenation
|
|
104
|
+
"S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
|
|
105
|
+
"TRY003", # Avoid specifying long messages outside the exception class
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
[tool.ruff.lint.flake8-pytest-style]
|
|
109
|
+
fixture-parentheses = true
|
|
110
|
+
|
|
107
111
|
[tool.ruff.lint.isort]
|
|
108
112
|
known-first-party = ["pytest_sort"]
|
|
109
113
|
|
|
110
|
-
[tool.ruff.per-file-ignores]
|
|
114
|
+
[tool.ruff.lint.per-file-ignores]
|
|
111
115
|
"src/pytest_sort/core.py" = [
|
|
112
116
|
"ARG005", # Unused lambda argument
|
|
113
117
|
"T201", # print found
|
|
@@ -136,4 +140,5 @@ known-first-party = ["pytest_sort"]
|
|
|
136
140
|
"PLR2004", # Magic value used in comparison
|
|
137
141
|
"S101", # Use of `assert` detected
|
|
138
142
|
"SLF001", # Private member accessed
|
|
143
|
+
"RUF059", # Unpacked variable is never used
|
|
139
144
|
]
|
|
@@ -4,9 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import random
|
|
7
|
-
import sys
|
|
8
7
|
from functools import partial
|
|
9
|
-
from typing import TYPE_CHECKING, Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
10
9
|
|
|
11
10
|
import pytest
|
|
12
11
|
from _pytest import nodes as pytest_nodes
|
|
@@ -15,14 +14,18 @@ from pytest_sort.config import SortConfig
|
|
|
15
14
|
from pytest_sort.database import get_all_totals, get_stats
|
|
16
15
|
from pytest_sort.diffcov import get_diff_test_scores, get_mut_test_scores
|
|
17
16
|
|
|
18
|
-
md5: Callable = hashlib.md5
|
|
19
|
-
if sys.version_info >= (3, 9):
|
|
20
|
-
md5: Callable = partial(hashlib.md5, usedforsecurity=False) # type: ignore[no-redef]
|
|
21
|
-
|
|
22
|
-
|
|
23
17
|
if TYPE_CHECKING:
|
|
18
|
+
import sys
|
|
19
|
+
|
|
24
20
|
from _pytest.terminal import TerminalReporter
|
|
25
21
|
|
|
22
|
+
if sys.version_info < (3, 14): # nomut
|
|
23
|
+
from typing import Callable # noqa: UP035 # pragma: no cover
|
|
24
|
+
else:
|
|
25
|
+
from collections.abc import Callable
|
|
26
|
+
|
|
27
|
+
md5: Callable = partial(hashlib.md5, usedforsecurity=False) # type: ignore[no-redef]
|
|
28
|
+
|
|
26
29
|
|
|
27
30
|
def create_bucket_id_from_node(node: pytest_nodes.Node | None) -> str:
|
|
28
31
|
"""Extract portion of nodeid needed for bucket key."""
|
|
@@ -141,7 +144,7 @@ def validate_sort_marker(sort_marker: pytest.Mark, node_id: str) -> tuple:
|
|
|
141
144
|
return (mode, bucket)
|
|
142
145
|
|
|
143
146
|
try:
|
|
144
|
-
|
|
147
|
+
mode, bucket_temp = mark_sort_parse(*sort_marker.args, **sort_marker.kwargs)
|
|
145
148
|
except TypeError as e:
|
|
146
149
|
msg = f"Incorrect arguments on marker 'sort'. Target:{node_id}"
|
|
147
150
|
raise TypeError(msg) from e
|
|
@@ -169,7 +172,7 @@ def get_marker_settings(node: pytest_nodes.Node) -> tuple:
|
|
|
169
172
|
node_id = node.nodeid
|
|
170
173
|
|
|
171
174
|
if node.parent:
|
|
172
|
-
|
|
175
|
+
mode, bucket, bucket_id, bucket_sort_key, item_sort_key = get_marker_settings(node.parent)
|
|
173
176
|
|
|
174
177
|
for order in node.iter_markers("order"):
|
|
175
178
|
sort_key = validate_order_marker(order, node_id)
|
|
@@ -180,7 +183,7 @@ def get_marker_settings(node: pytest_nodes.Node) -> tuple:
|
|
|
180
183
|
bucket_id = create_bucket_id_from_node(node)
|
|
181
184
|
|
|
182
185
|
for sort in node.iter_markers("sort"):
|
|
183
|
-
|
|
186
|
+
mode, bucket_temp = validate_sort_marker(sort, node_id)
|
|
184
187
|
|
|
185
188
|
if bucket_temp == "self":
|
|
186
189
|
bucket_id = create_bucket_id_from_node(node)
|
|
@@ -200,7 +203,7 @@ def create_sort_keys(item: pytest.Item, idx: int, count: int) -> None:
|
|
|
200
203
|
|
|
201
204
|
Store in SortConfig.item_sort_keys and SortConfig.bucket_sort_keys
|
|
202
205
|
"""
|
|
203
|
-
|
|
206
|
+
mode, bucket, bucket_id, bucket_sort_key, item_sort_key = get_marker_settings(item)
|
|
204
207
|
|
|
205
208
|
SortConfig.item_sort_keys[item.nodeid] = item_sort_key or create_item_key[mode or SortConfig.mode](item, idx, count)
|
|
206
209
|
|
|
@@ -254,7 +257,7 @@ def print_recorded_times_report(terminal_reporter: TerminalReporter) -> None:
|
|
|
254
257
|
|
|
255
258
|
print(
|
|
256
259
|
f"\n*** {'pytest-sort maximum recorded times'.ljust(node_id_width)}"
|
|
257
|
-
f"{'Nanoseconds'.center(stat_width*4 - 4)} ***",
|
|
260
|
+
f"{'Nanoseconds'.center(stat_width * 4 - 4)} ***",
|
|
258
261
|
)
|
|
259
262
|
print(
|
|
260
263
|
f"{'Test Case'.ljust(node_id_width)} {'setup'.rjust(stat_width)} "
|
|
@@ -31,24 +31,12 @@ def update_test_cases(recorded_times: dict) -> None:
|
|
|
31
31
|
"""Update Test Case Data with specfiied duration(s) and recalculate total(s)."""
|
|
32
32
|
_load_data()
|
|
33
33
|
|
|
34
|
-
for nodeid in recorded_times:
|
|
34
|
+
for nodeid, recorded_node in recorded_times.items():
|
|
35
35
|
node_data = _sort_data.get(nodeid, {})
|
|
36
36
|
|
|
37
|
-
node_data["setup"] = node_data.get("setup", 0)
|
|
38
|
-
node_data["call"] = node_data.get("call", 0)
|
|
39
|
-
node_data["teardown"] = node_data.get("teardown", 0)
|
|
40
|
-
|
|
41
|
-
setup = recorded_times[nodeid].get("setup", 0)
|
|
42
|
-
if node_data["setup"] < setup:
|
|
43
|
-
node_data["setup"] = setup
|
|
44
|
-
|
|
45
|
-
call = recorded_times[nodeid].get("call", 0)
|
|
46
|
-
if node_data["call"] < call:
|
|
47
|
-
node_data["call"] = call
|
|
48
|
-
|
|
49
|
-
teardown = recorded_times[nodeid].get("teardown", 0)
|
|
50
|
-
if node_data["teardown"] < teardown:
|
|
51
|
-
node_data["teardown"] = teardown
|
|
37
|
+
node_data["setup"] = max(node_data.get("setup", 0), recorded_node.get("setup", 0))
|
|
38
|
+
node_data["call"] = max(node_data.get("call", 0), recorded_node.get("call", 0))
|
|
39
|
+
node_data["teardown"] = max(node_data.get("teardown", 0), recorded_node.get("teardown", 0))
|
|
52
40
|
|
|
53
41
|
node_data["total"] = node_data["setup"] + node_data["call"] + node_data["teardown"]
|
|
54
42
|
|
|
@@ -7,11 +7,14 @@ import re
|
|
|
7
7
|
import shlex
|
|
8
8
|
import subprocess
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
11
|
|
|
12
12
|
import whatthepatch
|
|
13
13
|
from coverage.sqldata import CoverageData
|
|
14
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Generator
|
|
17
|
+
|
|
15
18
|
# pytest-cov populates test context as <nodeid>|(setup|run|teardown)
|
|
16
19
|
PARSE_TEST_CONTEXT = re.compile(r"(?P<nodeid>.*)\|(?P<when>setup|run|teardown)")
|
|
17
20
|
|
|
@@ -50,10 +53,10 @@ def get_mut_changed_lines() -> dict[Path, set[int]]:
|
|
|
50
53
|
"""
|
|
51
54
|
changed_lines = {}
|
|
52
55
|
mut_source_file = os.environ.get("MUT_SOURCE_FILE", None)
|
|
53
|
-
mut_lineno = os.environ.get("MUT_LINENO", 0)
|
|
56
|
+
mut_lineno = os.environ.get("MUT_LINENO", "0")
|
|
54
57
|
mut_end_lineno = os.environ.get("MUT_END_LINENO", mut_lineno)
|
|
55
58
|
|
|
56
|
-
if mut_source_file and mut_lineno:
|
|
59
|
+
if mut_source_file and int(mut_lineno):
|
|
57
60
|
rpath = Path(mut_source_file).resolve()
|
|
58
61
|
changed_lines[rpath] = set(range(int(mut_lineno), int(mut_end_lineno) + 1))
|
|
59
62
|
|
|
@@ -73,7 +76,7 @@ def get_line_coverage() -> Generator[tuple[Path, str, str, int], Any, None]:
|
|
|
73
76
|
for test in tests:
|
|
74
77
|
match = PARSE_TEST_CONTEXT.search(test)
|
|
75
78
|
if match:
|
|
76
|
-
|
|
79
|
+
nodeid, when = match.group("nodeid", "when")
|
|
77
80
|
yield rpath, nodeid, when, line
|
|
78
81
|
|
|
79
82
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
@@ -12,10 +12,12 @@ from pytest_sort.core import print_recorded_times_report, sort_items
|
|
|
12
12
|
from pytest_sort.database import clear_db, update_test_cases
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Generator
|
|
16
|
+
|
|
15
17
|
from _pytest.terminal import TerminalReporter
|
|
16
18
|
|
|
17
19
|
|
|
18
|
-
@pytest.hookimpl
|
|
20
|
+
@pytest.hookimpl # nomut: Decorator
|
|
19
21
|
def pytest_addoption(parser: pytest.Parser) -> None:
|
|
20
22
|
"""pytest_sort: Add command line and ini options to pytest."""
|
|
21
23
|
group = parser.getgroup("pytest-sort")
|
|
@@ -63,7 +65,7 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
|
|
63
65
|
group.addoption("--sort_debug", action="store_true", dest="sort_debug", help=argparse.SUPPRESS)
|
|
64
66
|
|
|
65
67
|
|
|
66
|
-
@pytest.hookimpl
|
|
68
|
+
@pytest.hookimpl # nomut: Decorator
|
|
67
69
|
def pytest_configure(config: pytest.Config) -> None:
|
|
68
70
|
"""pytest_sort: Add markers to pytest."""
|
|
69
71
|
config.addinivalue_line(
|
|
@@ -78,7 +80,7 @@ def pytest_configure(config: pytest.Config) -> None:
|
|
|
78
80
|
SortConfig.from_pytest(config)
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
@pytest.hookimpl
|
|
83
|
+
@pytest.hookimpl # nomut: Decorator
|
|
82
84
|
def pytest_report_header(config: pytest.Config) -> str: # noqa: ARG001
|
|
83
85
|
"""pytest_sort: Build Header for pytest to display."""
|
|
84
86
|
header = "pytest-sort:"
|
|
@@ -89,7 +91,7 @@ def pytest_report_header(config: pytest.Config) -> str: # noqa: ARG001
|
|
|
89
91
|
return header
|
|
90
92
|
|
|
91
93
|
|
|
92
|
-
@pytest.hookimpl
|
|
94
|
+
@pytest.hookimpl # nomut: Decorator
|
|
93
95
|
def pytest_collection_modifyitems(
|
|
94
96
|
session: pytest.Session, # noqa: ARG001
|
|
95
97
|
config: pytest.Config, # noqa: ARG001
|
|
@@ -102,7 +104,7 @@ def pytest_collection_modifyitems(
|
|
|
102
104
|
sort_items(items)
|
|
103
105
|
|
|
104
106
|
|
|
105
|
-
@pytest.hookimpl(hookwrapper=True) #
|
|
107
|
+
@pytest.hookimpl(hookwrapper=True) # nomut
|
|
106
108
|
def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo) -> Generator:
|
|
107
109
|
"""pytest_sort: Record test runtimes in memory."""
|
|
108
110
|
if SortConfig.record and call.when in ("setup", "call", "teardown"):
|
|
@@ -116,7 +118,7 @@ def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo) -> Gener
|
|
|
116
118
|
yield
|
|
117
119
|
|
|
118
120
|
|
|
119
|
-
@pytest.hookimpl
|
|
121
|
+
@pytest.hookimpl # nomut: Decorator
|
|
120
122
|
def pytest_terminal_summary(
|
|
121
123
|
terminalreporter: TerminalReporter,
|
|
122
124
|
exitstatus: int, # noqa: ARG001
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-sort
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Tools for sorting test cases
|
|
5
5
|
Author-email: WiredNerd <pbuschmail-pytestsort@yahoo.com>
|
|
6
6
|
Maintainer-email: WiredNerd <pbuschmail-pytestsort@yahoo.com>
|
|
@@ -34,15 +34,17 @@ Classifier: Programming Language :: Python :: 3
|
|
|
34
34
|
Classifier: License :: OSI Approved :: MIT License
|
|
35
35
|
Classifier: Operating System :: OS Independent
|
|
36
36
|
Classifier: Framework :: Pytest
|
|
37
|
-
Requires-Python: >=3.
|
|
37
|
+
Requires-Python: >=3.10
|
|
38
38
|
Description-Content-Type: text/markdown
|
|
39
39
|
License-File: LICENSE
|
|
40
40
|
Requires-Dist: pytest>=7.4.0
|
|
41
41
|
Requires-Dist: whatthepatch>=1.0.5
|
|
42
42
|
Requires-Dist: coverage>=7.3.1
|
|
43
|
+
Requires-Dist: zipp>=3.19.1
|
|
44
|
+
Dynamic: license-file
|
|
43
45
|
|
|
44
46
|
[](https://github.com/WiredNerd/pytest-sort)
|
|
45
|
-
[](https://pypi.org/project/pytest-sort)
|
|
46
48
|
[](https://pypi.org/project/pytest-sort)
|
|
47
49
|
[](https://pypistats.org/packages/pytest-sort)
|
|
48
50
|
[](https://github.com/WiredNerd/pytest-sort/blob/main/LICENSE)
|
|
@@ -104,7 +104,7 @@ class TestSortConfig:
|
|
|
104
104
|
|
|
105
105
|
def test_from_pytest_mode_invalid(self):
|
|
106
106
|
pytest_config = self.PytestConfig({"sort_mode": "September"}, {})
|
|
107
|
-
with pytest.raises(ValueError, match="^Invalid Value for sort-mode='September'$"):
|
|
107
|
+
with pytest.raises(ValueError, match=r"^Invalid Value for sort-mode='September'$"):
|
|
108
108
|
config.SortConfig.from_pytest(pytest_config)
|
|
109
109
|
|
|
110
110
|
@pytest.mark.parametrize(
|
|
@@ -128,7 +128,7 @@ class TestSortConfig:
|
|
|
128
128
|
|
|
129
129
|
def test_from_pytest_bucket_invalid(self):
|
|
130
130
|
pytest_config = self.PytestConfig({"sort_bucket": "September"}, {})
|
|
131
|
-
with pytest.raises(ValueError, match="^Invalid Value for sort-bucket='September'$"):
|
|
131
|
+
with pytest.raises(ValueError, match=r"^Invalid Value for sort-bucket='September'$"):
|
|
132
132
|
config.SortConfig.from_pytest(pytest_config)
|
|
133
133
|
|
|
134
134
|
@pytest.mark.parametrize(
|
|
@@ -155,12 +155,12 @@ class TestSortConfig:
|
|
|
155
155
|
|
|
156
156
|
def test_from_pytest_bucket_mode_invalid(self):
|
|
157
157
|
pytest_config = self.PytestConfig({"sort_bucket_mode": "September"}, {})
|
|
158
|
-
with pytest.raises(ValueError, match="^Invalid Value for sort-bucket-mode='September'$"):
|
|
158
|
+
with pytest.raises(ValueError, match=r"^Invalid Value for sort-bucket-mode='September'$"):
|
|
159
159
|
config.SortConfig.from_pytest(pytest_config)
|
|
160
160
|
|
|
161
161
|
def test_from_pytest_sort_record_conflict(self):
|
|
162
162
|
pytest_config = self.PytestConfig({"sort_no_record": True, "sort_record": True}, {})
|
|
163
|
-
with pytest.raises(ValueError, match="^Do not use both --sort-record-times and --sort-no-record-times$"):
|
|
163
|
+
with pytest.raises(ValueError, match=r"^Do not use both --sort-record-times and --sort-no-record-times$"):
|
|
164
164
|
config.SortConfig.from_pytest(pytest_config)
|
|
165
165
|
|
|
166
166
|
@pytest.mark.parametrize(
|
|
@@ -202,7 +202,7 @@ class TestSortConfig:
|
|
|
202
202
|
|
|
203
203
|
def test_from_pytest_seed_invalid(self):
|
|
204
204
|
pytest_config = self.PytestConfig({}, {"sort_seed": "ABC"})
|
|
205
|
-
with pytest.raises(ValueError, match="^Invalid seed value 'ABC' must be int$"):
|
|
205
|
+
with pytest.raises(ValueError, match=r"^Invalid seed value 'ABC' must be int$"):
|
|
206
206
|
config.SortConfig.from_pytest(pytest_config)
|
|
207
207
|
|
|
208
208
|
@pytest.mark.parametrize(
|
|
@@ -2,7 +2,7 @@ import hashlib
|
|
|
2
2
|
import importlib
|
|
3
3
|
import sys
|
|
4
4
|
from functools import partial
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import ClassVar
|
|
6
6
|
from unittest import mock
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
@@ -10,9 +10,12 @@ from _pytest import nodes as pytest_nodes
|
|
|
10
10
|
|
|
11
11
|
from pytest_sort import config, core
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if sys.version_info < (3, 14): # nomut
|
|
14
|
+
from typing import Callable # noqa: UP035 # pragma: no cover
|
|
15
|
+
else:
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
|
|
18
|
+
md5: Callable = partial(hashlib.md5, usedforsecurity=False) # type: ignore[no-redef]
|
|
16
19
|
|
|
17
20
|
|
|
18
21
|
class TestImports:
|
|
@@ -21,23 +24,12 @@ class TestImports:
|
|
|
21
24
|
with mock.patch("hashlib.md5") as mock_md5:
|
|
22
25
|
yield mock_md5
|
|
23
26
|
|
|
24
|
-
@pytest.fixture()
|
|
25
|
-
def version_info(self):
|
|
26
|
-
with mock.patch("sys.version_info") as version_info:
|
|
27
|
-
yield version_info
|
|
28
|
-
|
|
29
27
|
@pytest.fixture(autouse=True)
|
|
30
28
|
def _cleanup(self):
|
|
31
29
|
yield
|
|
32
30
|
importlib.reload(core)
|
|
33
31
|
|
|
34
|
-
def
|
|
35
|
-
version_info.__ge__ = lambda _, __: False
|
|
36
|
-
importlib.reload(core)
|
|
37
|
-
assert core.md5 == mock_md5
|
|
38
|
-
|
|
39
|
-
def test_import_md5_39(self, version_info, mock_md5):
|
|
40
|
-
version_info.__ge__ = lambda _, v: v == (3, 9)
|
|
32
|
+
def test_import_md5_39(self, mock_md5):
|
|
41
33
|
importlib.reload(core)
|
|
42
34
|
assert isinstance(core.md5, partial)
|
|
43
35
|
assert core.md5.func == mock_md5
|
|
@@ -92,7 +84,7 @@ class TestCreateBucketIdForNode:
|
|
|
92
84
|
assert core.create_bucket_id_from_node(node) == bucket_id
|
|
93
85
|
|
|
94
86
|
def test_create_bucket_id_for_package(self, mock_objects):
|
|
95
|
-
|
|
87
|
+
session, package, module, cls, func = mock_objects
|
|
96
88
|
|
|
97
89
|
assert core.create_bucket_id_for_package(session) == "session"
|
|
98
90
|
assert core.create_bucket_id_for_package(package) == "tests/core/"
|
|
@@ -104,7 +96,7 @@ class TestCreateBucketIdForNode:
|
|
|
104
96
|
assert core.create_bucket_id_for_package(module) == ""
|
|
105
97
|
|
|
106
98
|
def test_create_bucket_id_for_module(self, mock_objects):
|
|
107
|
-
|
|
99
|
+
session, package, module, cls, func = mock_objects
|
|
108
100
|
|
|
109
101
|
assert core.create_bucket_id_for_module(session) == "session"
|
|
110
102
|
assert core.create_bucket_id_for_module(package) == "tests/core/"
|
|
@@ -116,7 +108,7 @@ class TestCreateBucketIdForNode:
|
|
|
116
108
|
assert core.create_bucket_id_for_module(cls) == ""
|
|
117
109
|
|
|
118
110
|
def test_create_bucket_id_for_class(self, mock_objects):
|
|
119
|
-
|
|
111
|
+
session, package, module, cls, func = mock_objects
|
|
120
112
|
|
|
121
113
|
assert core.create_bucket_id_for_class(session) == "session"
|
|
122
114
|
assert core.create_bucket_id_for_class(package) == "tests/core/"
|
|
@@ -130,7 +122,7 @@ class TestCreateBucketIdForNode:
|
|
|
130
122
|
|
|
131
123
|
class TestCreateBucketId:
|
|
132
124
|
def test_create_bucket_id(self, mock_objects):
|
|
133
|
-
|
|
125
|
+
session, package, module, cls, func = mock_objects
|
|
134
126
|
|
|
135
127
|
assert core.create_bucket_id["session"](func) == ""
|
|
136
128
|
assert core.create_bucket_id["package"](func) == "tests/core/"
|
|
@@ -141,7 +133,7 @@ class TestCreateBucketId:
|
|
|
141
133
|
assert core.create_bucket_id["grandparent"](func) == "tests/core/test_core.py"
|
|
142
134
|
|
|
143
135
|
def test_create_item_key(self, mock_objects):
|
|
144
|
-
|
|
136
|
+
session, package, module, cls, func = mock_objects
|
|
145
137
|
|
|
146
138
|
assert core.create_item_key["ordered"](func, 5, 20) == 6
|
|
147
139
|
assert core.create_item_key["reverse"](func, 5, 20) == 15
|
|
@@ -152,21 +144,21 @@ class TestCreateBucketId:
|
|
|
152
144
|
assert 0 <= core.create_item_key["random"](func, 5, 20) < 1
|
|
153
145
|
|
|
154
146
|
def test_create_item_key_fastest(self, mock_objects):
|
|
155
|
-
|
|
147
|
+
session, package, module, cls, func = mock_objects
|
|
156
148
|
|
|
157
149
|
assert core.create_item_key["fastest"](func, 5, 20) == 0
|
|
158
150
|
core.SortConfig.item_totals = {func.nodeid: 123}
|
|
159
151
|
assert core.create_item_key["fastest"](func, 5, 20) == 123
|
|
160
152
|
|
|
161
153
|
def test_create_item_key_diffcov(self, mock_objects):
|
|
162
|
-
|
|
154
|
+
session, package, module, cls, func = mock_objects
|
|
163
155
|
|
|
164
156
|
assert core.create_item_key["diffcov"](func, 5, 20) == 0
|
|
165
157
|
core.SortConfig.diff_cov_scores = {func.nodeid: -123}
|
|
166
158
|
assert core.create_item_key["diffcov"](func, 5, 20) == -123
|
|
167
159
|
|
|
168
160
|
def test_create_item_key_mutcov(self, mock_objects):
|
|
169
|
-
|
|
161
|
+
session, package, module, cls, func = mock_objects
|
|
170
162
|
|
|
171
163
|
assert core.create_item_key["mutcov"](func, 5, 20) == 0
|
|
172
164
|
core.SortConfig.mut_cov_scores = {func.nodeid: -123}
|
|
@@ -232,10 +224,12 @@ class TestValidateMarker:
|
|
|
232
224
|
order_marker.args = []
|
|
233
225
|
order_marker.kwargs = {}
|
|
234
226
|
|
|
235
|
-
with pytest.raises(
|
|
227
|
+
with pytest.raises(
|
|
228
|
+
TypeError, match=r"^Incorrect arguments on marker 'order'. Target:testnodeid$"
|
|
229
|
+
) as type_error:
|
|
236
230
|
core.validate_order_marker(order_marker, "testnodeid")
|
|
237
231
|
|
|
238
|
-
assert
|
|
232
|
+
assert isinstance(type_error.value.__cause__, TypeError)
|
|
239
233
|
|
|
240
234
|
@pytest.mark.parametrize(
|
|
241
235
|
("args", "kwargs", "key"),
|
|
@@ -258,10 +252,10 @@ class TestValidateMarker:
|
|
|
258
252
|
sort_marker.args = []
|
|
259
253
|
sort_marker.kwargs = {}
|
|
260
254
|
|
|
261
|
-
with pytest.raises(TypeError, match="^Incorrect arguments on marker 'sort'. Target:testnodeid$") as type_error:
|
|
255
|
+
with pytest.raises(TypeError, match=r"^Incorrect arguments on marker 'sort'. Target:testnodeid$") as type_error:
|
|
262
256
|
core.validate_sort_marker(sort_marker, "testnodeid")
|
|
263
257
|
|
|
264
|
-
assert
|
|
258
|
+
assert isinstance(type_error.value.__cause__, TypeError)
|
|
265
259
|
|
|
266
260
|
def test_validate_sort_marker_value_error(self):
|
|
267
261
|
sort_marker = mock.MagicMock()
|
|
@@ -269,7 +263,7 @@ class TestValidateMarker:
|
|
|
269
263
|
sort_marker.kwargs = {}
|
|
270
264
|
|
|
271
265
|
with pytest.raises(
|
|
272
|
-
ValueError, match="^Invalid Value for 'mode' on 'sort' marker. Value:invalid Target:testnodeid$"
|
|
266
|
+
ValueError, match=r"^Invalid Value for 'mode' on 'sort' marker. Value:invalid Target:testnodeid$"
|
|
273
267
|
) as type_error:
|
|
274
268
|
core.validate_sort_marker(sort_marker, "testnodeid")
|
|
275
269
|
|
|
@@ -278,7 +272,7 @@ class TestValidateMarker:
|
|
|
278
272
|
|
|
279
273
|
class TestMarkerSettings:
|
|
280
274
|
def test_get_marker_settings_order_function(self, mock_objects):
|
|
281
|
-
|
|
275
|
+
session, package, module, cls, func = mock_objects
|
|
282
276
|
|
|
283
277
|
order_1 = mock.MagicMock()
|
|
284
278
|
order_1.args = [1]
|
|
@@ -296,7 +290,7 @@ class TestMarkerSettings:
|
|
|
296
290
|
)
|
|
297
291
|
|
|
298
292
|
def test_get_marker_settings_order_cls(self, mock_objects):
|
|
299
|
-
|
|
293
|
+
session, package, module, cls, func = mock_objects
|
|
300
294
|
|
|
301
295
|
order_1 = mock.MagicMock()
|
|
302
296
|
order_1.args = [1]
|
|
@@ -319,7 +313,7 @@ class TestMarkerSettings:
|
|
|
319
313
|
)
|
|
320
314
|
|
|
321
315
|
def test_get_marker_settings_order_mod(self, mock_objects):
|
|
322
|
-
|
|
316
|
+
session, package, module, cls, func = mock_objects
|
|
323
317
|
|
|
324
318
|
order_1 = mock.MagicMock()
|
|
325
319
|
order_1.args = [1]
|
|
@@ -342,7 +336,7 @@ class TestMarkerSettings:
|
|
|
342
336
|
)
|
|
343
337
|
|
|
344
338
|
def test_get_marker_settings_sort_self(self, mock_objects):
|
|
345
|
-
|
|
339
|
+
session, package, module, cls, func = mock_objects
|
|
346
340
|
|
|
347
341
|
order_1 = mock.MagicMock()
|
|
348
342
|
order_1.args = [1]
|
|
@@ -370,7 +364,7 @@ class TestMarkerSettings:
|
|
|
370
364
|
)
|
|
371
365
|
|
|
372
366
|
def test_get_marker_settings_sort_bucket(self, mock_objects):
|
|
373
|
-
|
|
367
|
+
session, package, module, cls, func = mock_objects
|
|
374
368
|
|
|
375
369
|
order_1 = mock.MagicMock()
|
|
376
370
|
order_1.args = [1]
|
|
@@ -391,12 +385,12 @@ class TestMarkerSettings:
|
|
|
391
385
|
assert core.get_marker_settings(func) == ("random", "parent", None, None, 1)
|
|
392
386
|
|
|
393
387
|
def test_get_marker_settings_no_markers(self, mock_objects):
|
|
394
|
-
|
|
388
|
+
session, package, module, cls, func = mock_objects
|
|
395
389
|
|
|
396
390
|
assert core.get_marker_settings(func) == (None, None, None, None, None)
|
|
397
391
|
|
|
398
392
|
def test_get_marker_settings_error(self, mock_objects):
|
|
399
|
-
|
|
393
|
+
session, package, module, cls, func = mock_objects
|
|
400
394
|
|
|
401
395
|
sort_random = mock.MagicMock()
|
|
402
396
|
sort_random.args = ["random", "hello"]
|
|
@@ -424,7 +418,7 @@ class TestCreateSortKey:
|
|
|
424
418
|
def test_marker_bucketid_sortkey(self, get_marker_settings, mock_objects):
|
|
425
419
|
get_marker_settings.return_value = (None, None, "test/core", None, 1234)
|
|
426
420
|
config.SortConfig.bucket_mode = "ordered"
|
|
427
|
-
|
|
421
|
+
session, package, module, cls, func = mock_objects
|
|
428
422
|
|
|
429
423
|
core.create_sort_keys(func, 6, 60)
|
|
430
424
|
|
|
@@ -438,7 +432,7 @@ class TestCreateSortKey:
|
|
|
438
432
|
get_marker_settings.return_value = ("reverse", "class", None, None, None)
|
|
439
433
|
config.SortConfig.mode = "ordered"
|
|
440
434
|
config.SortConfig.bucket_mode = "ordered"
|
|
441
|
-
|
|
435
|
+
session, package, module, cls, func = mock_objects
|
|
442
436
|
|
|
443
437
|
core.create_sort_keys(func, 6, 60)
|
|
444
438
|
|
|
@@ -453,7 +447,7 @@ class TestCreateSortKey:
|
|
|
453
447
|
config.SortConfig.mode = "ordered"
|
|
454
448
|
config.SortConfig.bucket_mode = "ordered"
|
|
455
449
|
config.SortConfig.bucket = "module"
|
|
456
|
-
|
|
450
|
+
session, package, module, cls, func = mock_objects
|
|
457
451
|
|
|
458
452
|
core.create_sort_keys(func, 6, 60)
|
|
459
453
|
|
|
@@ -468,7 +462,7 @@ class TestCreateSortKey:
|
|
|
468
462
|
config.SortConfig.mode = "ordered"
|
|
469
463
|
config.SortConfig.bucket = "module"
|
|
470
464
|
config.SortConfig.bucket_mode = "reverse"
|
|
471
|
-
|
|
465
|
+
session, package, module, cls, func = mock_objects
|
|
472
466
|
|
|
473
467
|
core.create_sort_keys(func, 6, 60)
|
|
474
468
|
|
|
@@ -481,7 +475,7 @@ class TestCreateSortKey:
|
|
|
481
475
|
config.SortConfig.mode = "reverse"
|
|
482
476
|
config.SortConfig.bucket = "module"
|
|
483
477
|
config.SortConfig.bucket_mode = "ordered"
|
|
484
|
-
|
|
478
|
+
session, package, module, cls, func = mock_objects
|
|
485
479
|
|
|
486
480
|
core.create_sort_keys(func, 6, 60)
|
|
487
481
|
|
|
@@ -494,7 +488,7 @@ class TestCreateSortKey:
|
|
|
494
488
|
config.SortConfig.mode = "fastest"
|
|
495
489
|
config.SortConfig.bucket = "class"
|
|
496
490
|
config.SortConfig.bucket_mode = "fastest"
|
|
497
|
-
|
|
491
|
+
session, package, module, cls, func = mock_objects
|
|
498
492
|
|
|
499
493
|
config.SortConfig.item_totals = {
|
|
500
494
|
func.nodeid: 1.1,
|
|
@@ -512,7 +506,7 @@ class TestCreateSortKey:
|
|
|
512
506
|
config.SortConfig.mode = "ordered"
|
|
513
507
|
config.SortConfig.bucket = "module"
|
|
514
508
|
config.SortConfig.bucket_mode = "ordered"
|
|
515
|
-
|
|
509
|
+
session, package, module, cls, func = mock_objects
|
|
516
510
|
|
|
517
511
|
core.create_sort_keys(func, 0, 60)
|
|
518
512
|
core.create_sort_keys(func, 1, 60)
|
|
@@ -526,7 +520,7 @@ class TestCreateSortKey:
|
|
|
526
520
|
config.SortConfig.mode = "ordered"
|
|
527
521
|
config.SortConfig.bucket = "module"
|
|
528
522
|
config.SortConfig.bucket_mode = "md5"
|
|
529
|
-
|
|
523
|
+
session, package, module, cls, func = mock_objects
|
|
530
524
|
|
|
531
525
|
md5.return_value.digest.side_effect = [b"DEF", b"ABC", b"GHI"]
|
|
532
526
|
|
|
@@ -537,7 +531,7 @@ class TestCreateSortKey:
|
|
|
537
531
|
assert config.SortConfig.bucket_sort_keys == {module.nodeid: b"ABC"}
|
|
538
532
|
|
|
539
533
|
def test_get_item_sort_key(self, mock_objects):
|
|
540
|
-
|
|
534
|
+
session, package, module, cls, func = mock_objects
|
|
541
535
|
|
|
542
536
|
core.SortConfig.item_sort_keys = {func.nodeid: 1.1}
|
|
543
537
|
core.SortConfig.item_bucket_id = {func.nodeid: cls.nodeid}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|