Skip to content

Commit 76de97d

Browse files
committed
Minor performance improvement for relative markers
- cache last nodeid component to avoid looping over all items
1 parent 6a85b3f commit 76de97d

File tree

4 files changed

+57
-30
lines changed

4 files changed

+57
-30
lines changed

perf_tests/test_relative.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ def test_performance_relative(fixture_path_relative):
4141
args = [fixture_path_relative]
4242
TimedSorter.nr_marks = 400
4343
pytest.main(args, [pytest_order])
44-
assert TimedSorter.elapsed < 0.25
44+
assert TimedSorter.elapsed < 0.15

perf_tests/test_relative_dense.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ def test_performance_relative(fixture_path_relative_dense):
4141
args = [fixture_path_relative_dense]
4242
TimedSorter.nr_marks = 900
4343
pytest.main(args, [pytest_order])
44-
assert TimedSorter.elapsed < 0.25
44+
assert TimedSorter.elapsed < 0.15

pytest_order/sorter.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,14 @@ def __init__(self, config, items):
7373
self.settings = Settings(config)
7474
self.items = [Item(item) for item in items]
7575
self.node_ids = OrderedDict()
76+
self.node_id_last = {}
7677
for item in self.items:
7778
self.node_ids[item.node_id] = item
79+
last_parts = item.node_id.split("/")[-1].split("::")
80+
# save last nodeid component to avoid to iterate over all
81+
# items for each label
82+
self.node_id_last.setdefault(
83+
last_parts[-1], []).append(item.node_id)
7884
self.rel_marks = []
7985
self.dep_marks = []
8086

@@ -154,8 +160,8 @@ def handle_order_mark(self, item):
154160
else:
155161
warn("Unknown order attribute:'{}'".format(order))
156162
order = None
157-
self.handle_relative_mark(item, mark)
158163
item.order = order
164+
self.handle_relative_mark(item, mark)
159165
if order is not None:
160166
item.nr_rel_items = 0
161167
return order
@@ -164,13 +170,18 @@ def item_from_label(self, label, item, is_cls_mark):
164170
label = self.node_id_from_label(label)
165171
item_id = item.node_id
166172
label_len = len(label)
167-
for node_id in self.node_ids:
168-
if node_id.endswith(label):
169-
id_start = node_id[:-label_len]
170-
if is_cls_mark and id_start.count("::") == 2:
171-
continue
172-
if item_id.startswith(id_start):
173-
return self.node_ids[node_id]
173+
last_comp = label.split("/")[-1].split("::")[-1]
174+
try:
175+
node_ids = self.node_id_last[last_comp]
176+
for node_id in node_ids:
177+
if node_id.endswith(label):
178+
id_start = node_id[:-label_len]
179+
if is_cls_mark and id_start.count("::") == 2:
180+
continue
181+
if item_id.startswith(id_start):
182+
return self.node_ids[node_id]
183+
except KeyError:
184+
return
174185

175186
def items_from_class_label(self, label, item):
176187
items = []
@@ -190,7 +201,7 @@ def items_from_class_label(self, label, item):
190201
def node_id_from_label(label):
191202
if "." in label:
192203
label_comp = label.split(".")
193-
label = "/".join(label_comp[:-1]) + ".py::" + label_comp[-1]
204+
label = ".py::".join(["/".join(label_comp[:-1]), label_comp[-1]])
194205
return label
195206

196207
def handle_before_or_after_mark(self, item, mark, marker_name, is_after):
@@ -217,16 +228,15 @@ def is_mark_for_class():
217228
else:
218229
if is_mark_for_class():
219230
items = self.items_from_class_label(marker_name, item)
220-
if items:
221-
for item_for_label in items:
222-
rel_mark = RelativeMark(item_for_label,
223-
item, move_after=is_after)
224-
if is_after:
225-
self.rel_marks.append(rel_mark)
226-
else:
227-
self.rel_marks.insert(0, rel_mark)
228-
item.inc_rel_marks()
229-
return True
231+
for item_for_label in items:
232+
rel_mark = RelativeMark(item_for_label,
233+
item, move_after=is_after)
234+
if is_after:
235+
self.rel_marks.append(rel_mark)
236+
else:
237+
self.rel_marks.insert(0, rel_mark)
238+
item.inc_rel_marks()
239+
return items
230240
return False
231241

232242
def handle_relative_mark(self, item, mark):
@@ -261,7 +271,7 @@ def resolve_dependency_markers(self, dep_marks, aliases):
261271
self.dep_marks.append(RelativeMark(aliases[name], item,
262272
move_after=True))
263273
else:
264-
label = prefix + "::" + name
274+
label = "::".join([prefix, name])
265275
if label in aliases:
266276
for item in items:
267277
self.dep_marks.append(
@@ -514,9 +524,7 @@ def __init__(self, item):
514524
self.item = item
515525
self.nr_rel_items = 0
516526
self.order = None
517-
# cache properties that are called often for the same item
518527
self._node_id = None
519-
self._label = None
520528

521529
def inc_rel_marks(self):
522530
if self.order is None:
@@ -542,9 +550,7 @@ def node_id(self):
542550

543551
@property
544552
def label(self):
545-
if self._label is None:
546-
self._label = self.node_id.replace(".py::", ".").replace("/", ".")
547-
return self._label
553+
return self.node_id.replace(".py::", ".").replace("/", ".")
548554

549555

550556
class ItemList:
@@ -601,9 +607,8 @@ def sort_numbered_items(self):
601607
return sorted_list
602608

603609
def print_unhandled_items(self):
604-
msg = ""
605-
msg += " ".join([mark.item.label for mark in self.rel_marks])
606-
msg += " ".join([mark.item.label for mark in self.dep_marks])
610+
msg = " ".join([mark.item.label for mark in self.rel_marks] +
611+
[mark.item.label for mark in self.dep_marks])
607612
if msg:
608613
sys.stdout.write(
609614
"\nWARNING: cannot execute test relative to others: ")

tests/test_class_marks.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ def test_2(self): pass
4141
]
4242

4343

44+
def test_invalid_class_mark(item_names_for, capsys):
45+
tests_content = """
46+
import pytest
47+
48+
@pytest.mark.order(after="Test3")
49+
class Test1:
50+
def test_1(self): pass
51+
def test_2(self): pass
52+
53+
class Test2:
54+
def test_1(self): pass
55+
def test_2(self): pass
56+
"""
57+
58+
assert item_names_for(tests_content) == [
59+
"Test1::test_1", "Test1::test_2", "Test2::test_1", "Test2::test_2"
60+
]
61+
out, err = capsys.readouterr()
62+
assert ("WARNING: cannot execute test relative to others: Test3 "
63+
"- ignoring the marker" in out)
64+
65+
4466
def test_before_class_mark(item_names_for):
4567
tests_content = """
4668
import pytest

0 commit comments

Comments
 (0)