pytest-split 0.10.0__tar.gz → 0.11.0__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.
@@ -1,29 +1,29 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pytest-split
3
- Version: 0.10.0
3
+ Version: 0.11.0
4
4
  Summary: Pytest plugin which splits the test suite to equally sized sub suites based on test execution time.
5
- Home-page: https://jerry-git.github.io/pytest-split
6
5
  License: MIT
6
+ License-File: LICENSE
7
7
  Keywords: pytest,plugin,split,tests
8
8
  Author: Jerry Pussinen
9
9
  Author-email: jerry.pussinen@gmail.com
10
- Requires-Python: >=3.8.1,<4.0
10
+ Requires-Python: >=3.10,<4.0
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Operating System :: OS Independent
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
20
  Classifier: Programming Language :: Python :: 3.13
22
- Classifier: Programming Language :: Python :: 3.8
21
+ Classifier: Programming Language :: Python :: 3.14
23
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
23
  Classifier: Typing :: Typed
25
- Requires-Dist: pytest (>=5,<9)
24
+ Requires-Dist: pytest (>=5,<10)
26
25
  Project-URL: Documentation, https://jerry-git.github.io/pytest-split
26
+ Project-URL: Homepage, https://jerry-git.github.io/pytest-split
27
27
  Project-URL: Repository, https://github.com/jerry-git/pytest-split
28
28
  Description-Content-Type: text/markdown
29
29
 
@@ -125,7 +125,7 @@ The `least_duration` algorithm walks the list of tests and assigns each test to
125
125
  * Clone this repository
126
126
  * Requirements:
127
127
  * [Poetry](https://python-poetry.org/)
128
- * Python 3.8+
128
+ * Python 3.10+
129
129
  * Create a virtual environment and install the dependencies
130
130
 
131
131
  ```sh
@@ -96,7 +96,7 @@ The `least_duration` algorithm walks the list of tests and assigns each test to
96
96
  * Clone this repository
97
97
  * Requirements:
98
98
  * [Poetry](https://python-poetry.org/)
99
- * Python 3.8+
99
+ * Python 3.10+
100
100
  * Create a virtual environment and install the dependencies
101
101
 
102
102
  ```sh
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pytest-split"
3
- version = "0.10.0"
3
+ version = "0.11.0"
4
4
  description = "Pytest plugin which splits the test suite to equally sized sub suites based on test execution time."
5
5
  authors = [
6
6
  "Jerry Pussinen <jerry.pussinen@gmail.com>",
@@ -18,12 +18,11 @@ classifiers = [
18
18
  "Operating System :: OS Independent",
19
19
  "Programming Language :: Python",
20
20
  "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.8",
22
- "Programming Language :: Python :: 3.9",
23
21
  "Programming Language :: Python :: 3.10",
24
22
  "Programming Language :: Python :: 3.11",
25
23
  "Programming Language :: Python :: 3.12",
26
24
  "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
27
26
  "Topic :: Software Development :: Libraries :: Python Modules",
28
27
  "Typing :: Typed",
29
28
  ]
@@ -33,11 +32,11 @@ packages = [{ include = 'pytest_split', from = 'src' }]
33
32
 
34
33
 
35
34
  [tool.poetry.dependencies]
36
- python = ">=3.8.1, <4.0"
37
- pytest = "^5 | ^6 | ^7 | ^8"
35
+ python = ">=3.10, <4.0"
36
+ pytest = "^5 | ^6 | ^7 | ^8 | ^9"
38
37
 
39
38
 
40
- [tool.poetry.dev-dependencies]
39
+ [tool.poetry.group.dev.dependencies]
41
40
  importlib-metadata = "==4.11.*"
42
41
  mkdocstrings = {version = ">=0.18", extras = ["python"]}
43
42
  mkdocs-material = "*"
@@ -61,7 +60,7 @@ slowest-tests = "pytest_split.cli:list_slowest_tests"
61
60
  pytest-split = "pytest_split.plugin"
62
61
 
63
62
  [tool.black]
64
- target-version = ["py37", "py38", "py39"]
63
+ target-version = ["py310", "py311", "py312", "py313", "py314"]
65
64
  include = '\.pyi?$'
66
65
 
67
66
  [tool.pytest.ini_options]
@@ -93,7 +92,7 @@ disallow_untyped_calls = false
93
92
 
94
93
 
95
94
  [tool.ruff]
96
- target-version = "py38" # The lowest supported version
95
+ target-version = "py310" # The lowest supported version
97
96
 
98
97
  [tool.ruff.lint]
99
98
  # By default, enable all the lint rules.
@@ -5,14 +5,12 @@ from operator import itemgetter
5
5
  from typing import TYPE_CHECKING, NamedTuple
6
6
 
7
7
  if TYPE_CHECKING:
8
- from typing import Dict, List, Tuple
9
-
10
8
  from _pytest import nodes
11
9
 
12
10
 
13
11
  class TestGroup(NamedTuple):
14
- selected: "List[nodes.Item]"
15
- deselected: "List[nodes.Item]"
12
+ selected: "list[nodes.Item]"
13
+ deselected: "list[nodes.Item]"
16
14
  duration: float
17
15
 
18
16
 
@@ -21,8 +19,8 @@ class AlgorithmBase(ABC):
21
19
 
22
20
  @abstractmethod
23
21
  def __call__(
24
- self, splits: int, items: "List[nodes.Item]", durations: "Dict[str, float]"
25
- ) -> "List[TestGroup]":
22
+ self, splits: int, items: "list[nodes.Item]", durations: "dict[str, float]"
23
+ ) -> "list[TestGroup]":
26
24
  pass
27
25
 
28
26
  def __hash__(self) -> int:
@@ -52,8 +50,8 @@ class LeastDurationAlgorithm(AlgorithmBase):
52
50
  """
53
51
 
54
52
  def __call__(
55
- self, splits: int, items: "List[nodes.Item]", durations: "Dict[str, float]"
56
- ) -> "List[TestGroup]":
53
+ self, splits: int, items: "list[nodes.Item]", durations: "dict[str, float]"
54
+ ) -> "list[TestGroup]":
57
55
  items_with_durations = _get_items_with_durations(items, durations)
58
56
 
59
57
  # add index of item in list
@@ -71,12 +69,12 @@ class LeastDurationAlgorithm(AlgorithmBase):
71
69
  items_with_durations_indexed, key=lambda tup: tup[1], reverse=True
72
70
  )
73
71
 
74
- selected: List[List[Tuple[nodes.Item, int]]] = [[] for _ in range(splits)]
75
- deselected: List[List[nodes.Item]] = [[] for _ in range(splits)]
76
- duration: List[float] = [0 for _ in range(splits)]
72
+ selected: list[list[tuple[nodes.Item, int]]] = [[] for _ in range(splits)]
73
+ deselected: list[list[nodes.Item]] = [[] for _ in range(splits)]
74
+ duration: list[float] = [0 for _ in range(splits)]
77
75
 
78
76
  # create a heap of the form (summed_durations, group_index)
79
- heap: List[Tuple[float, int]] = [(0, i) for i in range(splits)]
77
+ heap: list[tuple[float, int]] = [(0, i) for i in range(splits)]
80
78
  heapq.heapify(heap)
81
79
  for item, item_duration, original_index in sorted_items_with_durations:
82
80
  # get group with smallest sum
@@ -122,14 +120,14 @@ class DurationBasedChunksAlgorithm(AlgorithmBase):
122
120
  """
123
121
 
124
122
  def __call__(
125
- self, splits: int, items: "List[nodes.Item]", durations: "Dict[str, float]"
126
- ) -> "List[TestGroup]":
123
+ self, splits: int, items: "list[nodes.Item]", durations: "dict[str, float]"
124
+ ) -> "list[TestGroup]":
127
125
  items_with_durations = _get_items_with_durations(items, durations)
128
126
  time_per_group = sum(map(itemgetter(1), items_with_durations)) / splits
129
127
 
130
- selected: List[List[nodes.Item]] = [[] for i in range(splits)]
131
- deselected: List[List[nodes.Item]] = [[] for i in range(splits)]
132
- duration: List[float] = [0 for i in range(splits)]
128
+ selected: list[list[nodes.Item]] = [[] for i in range(splits)]
129
+ deselected: list[list[nodes.Item]] = [[] for i in range(splits)]
130
+ duration: list[float] = [0 for i in range(splits)]
133
131
 
134
132
  group_idx = 0
135
133
  for item, item_duration in items_with_durations:
@@ -151,8 +149,8 @@ class DurationBasedChunksAlgorithm(AlgorithmBase):
151
149
 
152
150
 
153
151
  def _get_items_with_durations(
154
- items: "List[nodes.Item]", durations: "Dict[str, float]"
155
- ) -> "List[Tuple[nodes.Item, float]]":
152
+ items: "list[nodes.Item]", durations: "dict[str, float]"
153
+ ) -> "list[tuple[nodes.Item, float]]":
156
154
  durations = _remove_irrelevant_durations(items, durations)
157
155
  avg_duration_per_test = _get_avg_duration_per_test(durations)
158
156
  items_with_durations = [
@@ -161,7 +159,7 @@ def _get_items_with_durations(
161
159
  return items_with_durations
162
160
 
163
161
 
164
- def _get_avg_duration_per_test(durations: "Dict[str, float]") -> float:
162
+ def _get_avg_duration_per_test(durations: "dict[str, float]") -> float:
165
163
  if durations:
166
164
  avg_duration_per_test = sum(durations.values()) / len(durations)
167
165
  else:
@@ -171,8 +169,8 @@ def _get_avg_duration_per_test(durations: "Dict[str, float]") -> float:
171
169
 
172
170
 
173
171
  def _remove_irrelevant_durations(
174
- items: "List[nodes.Item]", durations: "Dict[str, float]"
175
- ) -> "Dict[str, float]":
172
+ items: "list[nodes.Item]", durations: "dict[str, float]"
173
+ ) -> "dict[str, float]":
176
174
  # Filtering down durations to relevant ones ensures the avg isn't skewed by irrelevant data
177
175
  test_ids = [item.nodeid for item in items]
178
176
  durations = {name: durations[name] for name in test_ids if name in durations}
@@ -184,5 +182,5 @@ class Algorithms(enum.Enum):
184
182
  least_duration = LeastDurationAlgorithm()
185
183
 
186
184
  @staticmethod
187
- def names() -> "List[str]":
185
+ def names() -> "list[str]":
188
186
  return [x.name for x in Algorithms]
@@ -1,9 +1,5 @@
1
1
  import argparse
2
2
  import json
3
- from typing import TYPE_CHECKING
4
-
5
- if TYPE_CHECKING:
6
- from typing import Dict
7
3
 
8
4
 
9
5
  def list_slowest_tests() -> None:
@@ -28,7 +24,7 @@ def list_slowest_tests() -> None:
28
24
  return _list_slowest_tests(json.load(args.durations_path), args.count)
29
25
 
30
26
 
31
- def _list_slowest_tests(durations: "Dict[str, float]", count: int) -> None:
27
+ def _list_slowest_tests(durations: "dict[str, float]", count: int) -> None:
32
28
  slowest_tests = tuple(
33
29
  sorted(durations.items(), key=lambda item: item[1], reverse=True)
34
30
  )[:count]
@@ -1,8 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
3
  if TYPE_CHECKING:
4
- from typing import List
5
-
6
4
  from pytest_split.algorithms import TestGroup
7
5
 
8
6
 
@@ -45,8 +43,8 @@ def ensure_ipynb_compatibility(group: "TestGroup", items: list) -> None: # type
45
43
 
46
44
 
47
45
  def _find_sibiling_ipynb_cells(
48
- ipynb_node_id: str, item_node_ids: "List[str]"
49
- ) -> "List[str]":
46
+ ipynb_node_id: str, item_node_ids: "list[str]"
47
+ ) -> "list[str]":
50
48
  """
51
49
  Returns all sibling IPyNb cells given an IPyNb cell nodeid.
52
50
  """
@@ -10,8 +10,6 @@ from pytest_split import algorithms
10
10
  from pytest_split.ipynb_compatibility import ensure_ipynb_compatibility
11
11
 
12
12
  if TYPE_CHECKING:
13
- from typing import Dict, List, Optional, Union
14
-
15
13
  from _pytest import nodes
16
14
  from _pytest.config import Config
17
15
  from _pytest.config.argparsing import Parser
@@ -77,7 +75,7 @@ def pytest_addoption(parser: "Parser") -> None:
77
75
 
78
76
 
79
77
  @pytest.hookimpl(tryfirst=True)
80
- def pytest_cmdline_main(config: "Config") -> "Optional[Union[int, ExitCode]]":
78
+ def pytest_cmdline_main(config: "Config") -> "int | ExitCode | None":
81
79
  """
82
80
  Validate options.
83
81
  """
@@ -153,7 +151,7 @@ class PytestSplitPlugin(Base):
153
151
 
154
152
  @hookimpl(trylast=True)
155
153
  def pytest_collection_modifyitems(
156
- self, config: "Config", items: "List[nodes.Item]"
154
+ self, config: "Config", items: "list[nodes.Item]"
157
155
  ) -> None:
158
156
  """
159
157
  Collect and select the tests we want to run, and deselect the rest.
@@ -193,7 +191,7 @@ class PytestSplitCachePlugin(Base):
193
191
  https://github.com/pytest-dev/pytest/blob/main/src/_pytest/main.py#L308
194
192
  """
195
193
  terminal_reporter = self.config.pluginmanager.get_plugin("terminalreporter")
196
- test_durations: Dict[str, float] = {}
194
+ test_durations: dict[str, float] = {}
197
195
 
198
196
  for test_reports in terminal_reporter.stats.values(): # type: ignore[union-attr]
199
197
  for test_report in test_reports:
File without changes