pytest-split 0.9.0__py3-none-any.whl → 0.11.0__py3-none-any.whl
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_split/algorithms.py +112 -88
- pytest_split/cli.py +1 -5
- pytest_split/ipynb_compatibility.py +2 -4
- pytest_split/plugin.py +3 -5
- {pytest_split-0.9.0.dist-info → pytest_split-0.11.0.dist-info}/METADATA +9 -8
- pytest_split-0.11.0.dist-info/RECORD +11 -0
- {pytest_split-0.9.0.dist-info → pytest_split-0.11.0.dist-info}/WHEEL +1 -1
- pytest_split-0.9.0.dist-info/RECORD +0 -11
- {pytest_split-0.9.0.dist-info → pytest_split-0.11.0.dist-info}/entry_points.txt +0 -0
- {pytest_split-0.9.0.dist-info → pytest_split-0.11.0.dist-info/licenses}/LICENSE +0 -0
pytest_split/algorithms.py
CHANGED
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
import enum
|
|
2
|
-
import functools
|
|
3
2
|
import heapq
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
4
|
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: "
|
|
15
|
-
deselected: "
|
|
12
|
+
selected: "list[nodes.Item]"
|
|
13
|
+
deselected: "list[nodes.Item]"
|
|
16
14
|
duration: float
|
|
17
15
|
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
class AlgorithmBase(ABC):
|
|
18
|
+
"""Abstract base class for the algorithm implementations."""
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def __call__(
|
|
22
|
+
self, splits: int, items: "list[nodes.Item]", durations: "dict[str, float]"
|
|
23
|
+
) -> "list[TestGroup]":
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def __hash__(self) -> int:
|
|
27
|
+
return hash(self.__class__.__name__)
|
|
28
|
+
|
|
29
|
+
def __eq__(self, other: object) -> bool:
|
|
30
|
+
if not isinstance(other, AlgorithmBase):
|
|
31
|
+
return NotImplemented
|
|
32
|
+
return self.__class__.__name__ == other.__class__.__name__
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LeastDurationAlgorithm(AlgorithmBase):
|
|
22
36
|
"""
|
|
23
37
|
Split tests into groups by runtime.
|
|
24
38
|
It walks the test items, starting with the test with largest duration.
|
|
@@ -34,60 +48,65 @@ def least_duration(
|
|
|
34
48
|
:return:
|
|
35
49
|
List of groups
|
|
36
50
|
"""
|
|
37
|
-
items_with_durations = _get_items_with_durations(items, durations)
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
def __call__(
|
|
53
|
+
self, splits: int, items: "list[nodes.Item]", durations: "dict[str, float]"
|
|
54
|
+
) -> "list[TestGroup]":
|
|
55
|
+
items_with_durations = _get_items_with_durations(items, durations)
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
# sort in ascending order
|
|
50
|
-
sorted_items_with_durations = sorted(
|
|
51
|
-
items_with_durations_indexed, key=lambda tup: tup[1], reverse=True
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
selected: List[List[Tuple[nodes.Item, int]]] = [[] for _ in range(splits)]
|
|
55
|
-
deselected: List[List[nodes.Item]] = [[] for _ in range(splits)]
|
|
56
|
-
duration: List[float] = [0 for _ in range(splits)]
|
|
57
|
-
|
|
58
|
-
# create a heap of the form (summed_durations, group_index)
|
|
59
|
-
heap: List[Tuple[float, int]] = [(0, i) for i in range(splits)]
|
|
60
|
-
heapq.heapify(heap)
|
|
61
|
-
for item, item_duration, original_index in sorted_items_with_durations:
|
|
62
|
-
# get group with smallest sum
|
|
63
|
-
summed_durations, group_idx = heapq.heappop(heap)
|
|
64
|
-
new_group_durations = summed_durations + item_duration
|
|
65
|
-
|
|
66
|
-
# store assignment
|
|
67
|
-
selected[group_idx].append((item, original_index))
|
|
68
|
-
duration[group_idx] = new_group_durations
|
|
69
|
-
for i in range(splits):
|
|
70
|
-
if i != group_idx:
|
|
71
|
-
deselected[i].append(item)
|
|
72
|
-
|
|
73
|
-
# store new duration - in case of ties it sorts by the group_idx
|
|
74
|
-
heapq.heappush(heap, (new_group_durations, group_idx))
|
|
75
|
-
|
|
76
|
-
groups = []
|
|
77
|
-
for i in range(splits):
|
|
78
|
-
# sort the items by their original index to maintain relative ordering
|
|
79
|
-
# we don't care about the order of deselected items
|
|
80
|
-
s = [
|
|
81
|
-
item for item, original_index in sorted(selected[i], key=lambda tup: tup[1])
|
|
57
|
+
# add index of item in list
|
|
58
|
+
items_with_durations_indexed = [
|
|
59
|
+
(*tup, i) for i, tup in enumerate(items_with_durations)
|
|
82
60
|
]
|
|
83
|
-
group = TestGroup(selected=s, deselected=deselected[i], duration=duration[i])
|
|
84
|
-
groups.append(group)
|
|
85
|
-
return groups
|
|
86
61
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
62
|
+
# Sort by name to ensure it's always the same order
|
|
63
|
+
items_with_durations_indexed = sorted(
|
|
64
|
+
items_with_durations_indexed, key=lambda tup: str(tup[0])
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# sort in ascending order
|
|
68
|
+
sorted_items_with_durations = sorted(
|
|
69
|
+
items_with_durations_indexed, key=lambda tup: tup[1], reverse=True
|
|
70
|
+
)
|
|
71
|
+
|
|
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)]
|
|
75
|
+
|
|
76
|
+
# create a heap of the form (summed_durations, group_index)
|
|
77
|
+
heap: list[tuple[float, int]] = [(0, i) for i in range(splits)]
|
|
78
|
+
heapq.heapify(heap)
|
|
79
|
+
for item, item_duration, original_index in sorted_items_with_durations:
|
|
80
|
+
# get group with smallest sum
|
|
81
|
+
summed_durations, group_idx = heapq.heappop(heap)
|
|
82
|
+
new_group_durations = summed_durations + item_duration
|
|
83
|
+
|
|
84
|
+
# store assignment
|
|
85
|
+
selected[group_idx].append((item, original_index))
|
|
86
|
+
duration[group_idx] = new_group_durations
|
|
87
|
+
for i in range(splits):
|
|
88
|
+
if i != group_idx:
|
|
89
|
+
deselected[i].append(item)
|
|
90
|
+
|
|
91
|
+
# store new duration - in case of ties it sorts by the group_idx
|
|
92
|
+
heapq.heappush(heap, (new_group_durations, group_idx))
|
|
93
|
+
|
|
94
|
+
groups = []
|
|
95
|
+
for i in range(splits):
|
|
96
|
+
# sort the items by their original index to maintain relative ordering
|
|
97
|
+
# we don't care about the order of deselected items
|
|
98
|
+
s = [
|
|
99
|
+
item
|
|
100
|
+
for item, original_index in sorted(selected[i], key=lambda tup: tup[1])
|
|
101
|
+
]
|
|
102
|
+
group = TestGroup(
|
|
103
|
+
selected=s, deselected=deselected[i], duration=duration[i]
|
|
104
|
+
)
|
|
105
|
+
groups.append(group)
|
|
106
|
+
return groups
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class DurationBasedChunksAlgorithm(AlgorithmBase):
|
|
91
110
|
"""
|
|
92
111
|
Split tests into groups by runtime.
|
|
93
112
|
Ensures tests are split into non-overlapping groups.
|
|
@@ -99,33 +118,39 @@ def duration_based_chunks(
|
|
|
99
118
|
:param durations: Our cached test runtimes. Assumes contains timings only of relevant tests
|
|
100
119
|
:return: List of TestGroup
|
|
101
120
|
"""
|
|
102
|
-
items_with_durations = _get_items_with_durations(items, durations)
|
|
103
|
-
time_per_group = sum(map(itemgetter(1), items_with_durations)) / splits
|
|
104
|
-
|
|
105
|
-
selected: List[List[nodes.Item]] = [[] for i in range(splits)]
|
|
106
|
-
deselected: List[List[nodes.Item]] = [[] for i in range(splits)]
|
|
107
|
-
duration: List[float] = [0 for i in range(splits)]
|
|
108
|
-
|
|
109
|
-
group_idx = 0
|
|
110
|
-
for item, item_duration in items_with_durations:
|
|
111
|
-
if duration[group_idx] >= time_per_group:
|
|
112
|
-
group_idx += 1
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
for i in range(splits)
|
|
123
|
-
|
|
122
|
+
def __call__(
|
|
123
|
+
self, splits: int, items: "list[nodes.Item]", durations: "dict[str, float]"
|
|
124
|
+
) -> "list[TestGroup]":
|
|
125
|
+
items_with_durations = _get_items_with_durations(items, durations)
|
|
126
|
+
time_per_group = sum(map(itemgetter(1), items_with_durations)) / splits
|
|
127
|
+
|
|
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)]
|
|
131
|
+
|
|
132
|
+
group_idx = 0
|
|
133
|
+
for item, item_duration in items_with_durations:
|
|
134
|
+
if duration[group_idx] >= time_per_group:
|
|
135
|
+
group_idx += 1
|
|
136
|
+
|
|
137
|
+
selected[group_idx].append(item)
|
|
138
|
+
for i in range(splits):
|
|
139
|
+
if i != group_idx:
|
|
140
|
+
deselected[i].append(item)
|
|
141
|
+
duration[group_idx] += item_duration
|
|
142
|
+
|
|
143
|
+
return [
|
|
144
|
+
TestGroup(
|
|
145
|
+
selected=selected[i], deselected=deselected[i], duration=duration[i]
|
|
146
|
+
)
|
|
147
|
+
for i in range(splits)
|
|
148
|
+
]
|
|
124
149
|
|
|
125
150
|
|
|
126
151
|
def _get_items_with_durations(
|
|
127
|
-
items: "
|
|
128
|
-
) -> "
|
|
152
|
+
items: "list[nodes.Item]", durations: "dict[str, float]"
|
|
153
|
+
) -> "list[tuple[nodes.Item, float]]":
|
|
129
154
|
durations = _remove_irrelevant_durations(items, durations)
|
|
130
155
|
avg_duration_per_test = _get_avg_duration_per_test(durations)
|
|
131
156
|
items_with_durations = [
|
|
@@ -134,7 +159,7 @@ def _get_items_with_durations(
|
|
|
134
159
|
return items_with_durations
|
|
135
160
|
|
|
136
161
|
|
|
137
|
-
def _get_avg_duration_per_test(durations: "
|
|
162
|
+
def _get_avg_duration_per_test(durations: "dict[str, float]") -> float:
|
|
138
163
|
if durations:
|
|
139
164
|
avg_duration_per_test = sum(durations.values()) / len(durations)
|
|
140
165
|
else:
|
|
@@ -144,8 +169,8 @@ def _get_avg_duration_per_test(durations: "Dict[str, float]") -> float:
|
|
|
144
169
|
|
|
145
170
|
|
|
146
171
|
def _remove_irrelevant_durations(
|
|
147
|
-
items: "
|
|
148
|
-
) -> "
|
|
172
|
+
items: "list[nodes.Item]", durations: "dict[str, float]"
|
|
173
|
+
) -> "dict[str, float]":
|
|
149
174
|
# Filtering down durations to relevant ones ensures the avg isn't skewed by irrelevant data
|
|
150
175
|
test_ids = [item.nodeid for item in items]
|
|
151
176
|
durations = {name: durations[name] for name in test_ids if name in durations}
|
|
@@ -153,10 +178,9 @@ def _remove_irrelevant_durations(
|
|
|
153
178
|
|
|
154
179
|
|
|
155
180
|
class Algorithms(enum.Enum):
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
least_duration = functools.partial(least_duration)
|
|
181
|
+
duration_based_chunks = DurationBasedChunksAlgorithm()
|
|
182
|
+
least_duration = LeastDurationAlgorithm()
|
|
159
183
|
|
|
160
184
|
@staticmethod
|
|
161
|
-
def names() -> "
|
|
185
|
+
def names() -> "list[str]":
|
|
162
186
|
return [x.name for x in Algorithms]
|
pytest_split/cli.py
CHANGED
|
@@ -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: "
|
|
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: "
|
|
49
|
-
) -> "
|
|
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
|
"""
|
pytest_split/plugin.py
CHANGED
|
@@ -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") -> "
|
|
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: "
|
|
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:
|
|
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:
|
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-split
|
|
3
|
-
Version: 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.
|
|
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
|
-
Classifier: Programming Language :: Python :: 3.
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Classifier: Typing :: Typed
|
|
24
|
-
Requires-Dist: pytest (>=5,<
|
|
24
|
+
Requires-Dist: pytest (>=5,<10)
|
|
25
25
|
Project-URL: Documentation, https://jerry-git.github.io/pytest-split
|
|
26
|
+
Project-URL: Homepage, https://jerry-git.github.io/pytest-split
|
|
26
27
|
Project-URL: Repository, https://github.com/jerry-git/pytest-split
|
|
27
28
|
Description-Content-Type: text/markdown
|
|
28
29
|
|
|
@@ -124,7 +125,7 @@ The `least_duration` algorithm walks the list of tests and assigns each test to
|
|
|
124
125
|
* Clone this repository
|
|
125
126
|
* Requirements:
|
|
126
127
|
* [Poetry](https://python-poetry.org/)
|
|
127
|
-
* Python 3.
|
|
128
|
+
* Python 3.10+
|
|
128
129
|
* Create a virtual environment and install the dependencies
|
|
129
130
|
|
|
130
131
|
```sh
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pytest_split/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
pytest_split/algorithms.py,sha256=Ry1kbD_5vJgkUR4ubhmRPYanMbdxUh-Yym8vZpvn1Lk,6931
|
|
3
|
+
pytest_split/cli.py,sha256=AAQMPzLxDv0xoxrzg0LHJ3nTdjYxUc9UKoseNJdV8Ek,931
|
|
4
|
+
pytest_split/ipynb_compatibility.py,sha256=IEq56DJUVOYnHnqZmqJHVITJn8vZfVQKRwGPIxSU1JU,2075
|
|
5
|
+
pytest_split/plugin.py,sha256=ank0PKNwtoDGV3aj91FaJ8rZzqsfLu70WSvL3fpmWgM,7796
|
|
6
|
+
pytest_split/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
pytest_split-0.11.0.dist-info/METADATA,sha256=p_eg9FiwZKoM1DFrUeadGEdNWGAFmaCvewkO2XbPB_Y,9518
|
|
8
|
+
pytest_split-0.11.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
|
|
9
|
+
pytest_split-0.11.0.dist-info/entry_points.txt,sha256=TQBEnYSUICZtzsc1CUyiYrOHpEll6HBDzIFvjJyo9F8,114
|
|
10
|
+
pytest_split-0.11.0.dist-info/licenses/LICENSE,sha256=mMm6KABGgOUinR1qBjELBaTHOU91sKkaWnprZ0RBilU,1058
|
|
11
|
+
pytest_split-0.11.0.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
pytest_split/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
pytest_split/algorithms.py,sha256=4rNNyPzZskHgorFsGId_XHi2MzjkyZ1Lj8_TZD4B60k,6112
|
|
3
|
-
pytest_split/cli.py,sha256=_Cc3h5jwOJyX7NZT4yil_ymG0g6Rw0257gh_63VoqzE,1011
|
|
4
|
-
pytest_split/ipynb_compatibility.py,sha256=zBul7z3mB3UgM03IppqFpTZTdb2Vj1NrqxRC0kAgrCw,2104
|
|
5
|
-
pytest_split/plugin.py,sha256=z3dTS3rK0wI2bj62Yw2O3y8120UfMiWnc6CJJyig-0k,7857
|
|
6
|
-
pytest_split/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
pytest_split-0.9.0.dist-info/LICENSE,sha256=mMm6KABGgOUinR1qBjELBaTHOU91sKkaWnprZ0RBilU,1058
|
|
8
|
-
pytest_split-0.9.0.dist-info/METADATA,sha256=IieeBEBthWLh2UzL7NkPRJZp0zoP-mIXjparUiVv-gQ,9480
|
|
9
|
-
pytest_split-0.9.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
10
|
-
pytest_split-0.9.0.dist-info/entry_points.txt,sha256=TQBEnYSUICZtzsc1CUyiYrOHpEll6HBDzIFvjJyo9F8,114
|
|
11
|
-
pytest_split-0.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|