Skip to content

Commit 2c240f8

Browse files
committed
Refactor ipynb stuff into a new module, minor lint fixes
1 parent 36683a6 commit 2c240f8

File tree

3 files changed

+72
-77
lines changed

3 files changed

+72
-77
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from typing import TYPE_CHECKING
2+
3+
if TYPE_CHECKING:
4+
from typing import List
5+
6+
from _pytest import nodes
7+
8+
from pytest_split.algorithms import TestGroup
9+
10+
11+
def _reorganize_broken_up_ipynbs(group: "TestGroup", items: "List[nodes.Item]") -> None:
12+
"""
13+
Ensures that group doesn't contain partial IPy notebook cells.
14+
15+
``pytest-split`` might, in principle, break up the cells of an
16+
IPython notebook into different test groups, in which case the tests
17+
most likely fail (for starters, libraries are imported in Cell 0, so
18+
all subsequent calls to the imported libraries in the following cells
19+
will raise ``NameError``).
20+
21+
"""
22+
item_node_ids = [item.nodeid for item in items]
23+
24+
# Deal with broken up notebooks at the beginning of the test group
25+
if not group.selected or not _is_ipy_notebook(group.selected[0].nodeid):
26+
return
27+
first = group.selected[0].nodeid
28+
siblings = _find_sibiling_ipynb_cells(first, item_node_ids)
29+
if first != siblings[0]:
30+
for item in list(group.selected):
31+
if item.nodeid in siblings:
32+
group.deselected.append(item)
33+
group.selected.remove(item)
34+
35+
# Deal with broken up notebooks at the end of the test group
36+
if not group.selected or not _is_ipy_notebook(group.selected[-1].nodeid):
37+
return
38+
last = group.selected[-1].nodeid
39+
siblings = _find_sibiling_ipynb_cells(last, item_node_ids)
40+
if last != siblings[-1]:
41+
for item in list(group.deselected):
42+
if item.nodeid in siblings:
43+
group.deselected.remove(item)
44+
group.selected.append(item)
45+
46+
47+
def _find_sibiling_ipynb_cells(ipynb_node_id: str, item_node_ids: "List[str]") -> "List[str]":
48+
"""
49+
Returns all sibiling IPyNb cells given an IPyNb cell nodeid.
50+
"""
51+
fpath = ipynb_node_id.split("::")[0]
52+
return [item for item in item_node_ids if fpath in item]
53+
54+
55+
def _is_ipy_notebook(node_id: str) -> bool:
56+
"""
57+
Returns True if node_id is an IPython notebook, otherwise False.
58+
"""
59+
fpath = node_id.split("::")[0]
60+
return fpath.endswith(".ipynb")

src/pytest_split/plugin.py

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from _pytest.reports import TestReport
88

99
from pytest_split import algorithms
10+
from pytest_split.ipynb_compatibility import _reorganize_broken_up_ipynbs
1011

1112
if TYPE_CHECKING:
1213
from typing import Dict, List, Optional, Union
@@ -16,7 +17,6 @@
1617
from _pytest.config.argparsing import Parser
1718
from _pytest.main import ExitCode
1819

19-
from pytest_split.algorithms import TestGroup
2020

2121
# Ugly hack for freezegun compatibility: https://github.com/spulec/freezegun/issues/286
2222
STORE_DURATIONS_SETUP_AND_TEARDOWN_THRESHOLD = 60 * 10 # seconds
@@ -185,60 +185,6 @@ def pytest_collection_modifyitems(
185185
return None
186186

187187

188-
def _reorganize_broken_up_ipynbs(group: "TestGroup", items: list) -> None:
189-
"""
190-
Ensures that group doesn't contain partial IPy notebook cells.
191-
192-
``pytest-split`` might, in principle, break up the cells of an
193-
IPython notebook into different test groups, in which case the tests
194-
most likely fail (for starters, libraries are imported in Cell 0, so
195-
all subsequent calls to the imported libraries in the following cells
196-
will raise ``NameError``).
197-
198-
"""
199-
item_node_ids = [item.nodeid for item in items]
200-
201-
# Deal with broken up notebooks at the beginning of the test group
202-
if not group.selected or not _is_ipy_notebook(group.selected[0].nodeid):
203-
return
204-
first = group.selected[0].nodeid
205-
sibilings = _find_sibiling_ipynb_cells(first, item_node_ids)
206-
if first != sibilings[0]:
207-
for item in list(group.selected):
208-
if item.nodeid in sibilings:
209-
group.deselected.append(item)
210-
group.selected.remove(item)
211-
212-
# Deal with broken up notebooks at the end of the test group
213-
if not group.selected or not _is_ipy_notebook(group.selected[-1].nodeid):
214-
return
215-
last = group.selected[-1].nodeid
216-
sibilings = _find_sibiling_ipynb_cells(last, item_node_ids)
217-
if last != sibilings[-1]:
218-
for item in list(group.deselected):
219-
if item.nodeid in sibilings:
220-
group.deselected.remove(item)
221-
group.selected.append(item)
222-
223-
224-
def _find_sibiling_ipynb_cells(
225-
ipynb_node_id: str, item_node_ids: "List[str]"
226-
) -> "List[str]":
227-
"""
228-
Returns all sibiling IPyNb cells given an IPyNb cell nodeid.
229-
"""
230-
fpath = ipynb_node_id.split("::")[0]
231-
return [item for item in item_node_ids if fpath in item]
232-
233-
234-
def _is_ipy_notebook(node_id: str) -> bool:
235-
"""
236-
Returns True if node_id is an IPython notebook, otherwise False.
237-
"""
238-
fpath = node_id.split("::")[0]
239-
return fpath.endswith(".ipynb")
240-
241-
242188
class PytestSplitCachePlugin(Base):
243189
"""
244190
The cache plugin writes durations to our durations file.

tests/test_ipynb.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44

55
from pytest_split.algorithms import Algorithms
6-
from pytest_split.plugin import _reorganize_broken_up_ipynbs
6+
from pytest_split.ipynb_compatibility import _reorganize_broken_up_ipynbs
77

88
item = namedtuple("item", "nodeid")
99

@@ -30,22 +30,22 @@ def test__reorganize_broken_up_ipynbs(self, algo_name):
3030
}
3131
items = [item(x) for x in durations.keys()]
3232
algo = Algorithms[algo_name].value
33-
g1, g2, g3 = algo(splits=3, items=items, durations=durations)
33+
groups = algo(splits=3, items=items, durations=durations)
3434

35-
assert g1.selected == [
35+
assert groups[0].selected == [
3636
item(nodeid="temp/nbs/test_1.ipynb::Cell 0"),
3737
item(nodeid="temp/nbs/test_1.ipynb::Cell 1"),
3838
item(nodeid="temp/nbs/test_1.ipynb::Cell 2"),
3939
item(nodeid="temp/nbs/test_2.ipynb::Cell 0"),
4040
item(nodeid="temp/nbs/test_2.ipynb::Cell 1"),
4141
]
42-
assert g2.selected == [
42+
assert groups[1].selected == [
4343
item(nodeid="temp/nbs/test_2.ipynb::Cell 2"),
4444
item(nodeid="temp/nbs/test_2.ipynb::Cell 3"),
4545
item(nodeid="temp/nbs/test_3.ipynb::Cell 0"),
4646
item(nodeid="temp/nbs/test_3.ipynb::Cell 1"),
4747
]
48-
assert g3.selected == [
48+
assert groups[2].selected == [
4949
item(nodeid="temp/nbs/test_3.ipynb::Cell 2"),
5050
item(nodeid="temp/nbs/test_3.ipynb::Cell 3"),
5151
item(nodeid="temp/nbs/test_3.ipynb::Cell 4"),
@@ -54,8 +54,8 @@ def test__reorganize_broken_up_ipynbs(self, algo_name):
5454
item(nodeid="temp/nbs/test_4.ipynb::Cell 2"),
5555
]
5656

57-
_reorganize_broken_up_ipynbs(g1, items)
58-
assert g1.selected == [
57+
_reorganize_broken_up_ipynbs(groups[0], items)
58+
assert groups[0].selected == [
5959
item(nodeid="temp/nbs/test_1.ipynb::Cell 0"),
6060
item(nodeid="temp/nbs/test_1.ipynb::Cell 1"),
6161
item(nodeid="temp/nbs/test_1.ipynb::Cell 2"),
@@ -65,29 +65,18 @@ def test__reorganize_broken_up_ipynbs(self, algo_name):
6565
item(nodeid="temp/nbs/test_2.ipynb::Cell 3"),
6666
]
6767

68-
_reorganize_broken_up_ipynbs(g2, items)
69-
assert g2.selected == [
68+
_reorganize_broken_up_ipynbs(groups[1], items)
69+
assert groups[1].selected == [
7070
item(nodeid="temp/nbs/test_3.ipynb::Cell 0"),
7171
item(nodeid="temp/nbs/test_3.ipynb::Cell 1"),
7272
item(nodeid="temp/nbs/test_3.ipynb::Cell 2"),
7373
item(nodeid="temp/nbs/test_3.ipynb::Cell 3"),
7474
item(nodeid="temp/nbs/test_3.ipynb::Cell 4"),
7575
]
7676

77-
_reorganize_broken_up_ipynbs(g3, items)
78-
assert g3.selected == [
77+
_reorganize_broken_up_ipynbs(groups[2], items)
78+
assert groups[2].selected == [
7979
item(nodeid="temp/nbs/test_4.ipynb::Cell 0"),
8080
item(nodeid="temp/nbs/test_4.ipynb::Cell 1"),
8181
item(nodeid="temp/nbs/test_4.ipynb::Cell 2"),
8282
]
83-
84-
85-
if __name__ == "__main__":
86-
import warnings
87-
88-
warnings.filterwarnings("ignore")
89-
90-
import pytest
91-
92-
args = "test_ipynb.py"
93-
retcode = pytest.main(args.split())

0 commit comments

Comments
 (0)