1+ import re
12import sys
2- from warnings import warn
3+ from collections import OrderedDict
34from contextlib import suppress
45from typing import Optional , List , Dict , Tuple , cast
6+ from warnings import warn
57
68from _pytest .config import Config
79from _pytest .mark import Mark
1012from .item import Item , ItemList , ItemGroup , filter_marks , move_item , RelativeMark
1113from .settings import Settings , Scope
1214
13- try :
14- from typing import OrderedDict
15- except ImportError :
16- # In Python <3.7.2, we need to stub it
17- from collections import OrderedDict as OrderedDict_cls
18- from typing import MutableMapping , TypeVar
19-
20- KT = TypeVar ("KT" )
21- VT = TypeVar ("VT" )
22-
23- class OrderedDict (OrderedDict_cls , MutableMapping [KT , VT ]): # type: ignore
24- pass
25-
26-
2715orders_map = {
2816 "first" : 0 ,
2917 "second" : 1 ,
@@ -112,7 +100,7 @@ def mark_binning(
112100 self ,
113101 item : Item ,
114102 dep_marks : Dict [Tuple [str , Scope , str ], List [Item ]],
115- aliases : Dict [str , Item ],
103+ aliases : Dict [str , List [ Item ] ],
116104 ) -> None :
117105 """
118106 Collect relevant markers for the given item.
@@ -138,7 +126,7 @@ def handle_dependency_mark(
138126 item : Item ,
139127 has_order : bool ,
140128 dep_marks : Dict [Tuple [str , Scope , str ], List [Item ]],
141- aliases : Dict [str , Item ],
129+ aliases : Dict [str , List [ Item ] ],
142130 ) -> None :
143131 # always order dependencies if an order mark is present
144132 # otherwise only if order-dependencies is set
@@ -159,7 +147,7 @@ def handle_dependency_mark(
159147 # of the nodeid, depending on the scope
160148 if not name_mark :
161149 name_mark = item .node_id
162- aliases [ name_mark ] = item
150+ aliases . setdefault ( name_mark , []). append ( item )
163151
164152 def handle_order_marks (self , item : Item ) -> None :
165153 marks = item .item .iter_markers ("order" )
@@ -289,7 +277,7 @@ def warn_about_unknown_test(item: Item, rel_mark: str) -> None:
289277 )
290278
291279 def collect_markers (self ) -> None :
292- aliases : Dict [str , Item ] = {}
280+ aliases : Dict [str , List [ Item ] ] = {}
293281 dep_marks : Dict [Tuple [str , Scope , str ], List [Item ]] = {}
294282 for item in self .items :
295283 self .mark_binning (item , dep_marks , aliases )
@@ -298,27 +286,49 @@ def collect_markers(self) -> None:
298286 def resolve_dependency_markers (
299287 self ,
300288 dep_marks : Dict [Tuple [str , Scope , str ], List [Item ]],
301- aliases : Dict [str , Item ],
289+ aliases : Dict [str , List [ Item ] ],
302290 ) -> None :
303291 for (name , _ , prefix ), items in dep_marks .items ():
304292 if name in aliases :
305293 for item in items :
306- self .dep_marks .append (
307- RelativeMark (aliases [name ], item , move_after = True )
308- )
294+ alias = self .matching_alias (aliases [name ], item )
295+ self .dep_marks .append (RelativeMark (alias , item , move_after = True ))
309296 else :
310297 label = "::" .join ((prefix , name ))
311298 if label in aliases :
312299 for item in items :
300+ alias = self .matching_alias (aliases [label ], item )
313301 self .dep_marks .append (
314- RelativeMark (aliases [ label ] , item , move_after = True )
302+ RelativeMark (alias , item , move_after = True )
315303 )
316304 else :
317305 sys .stdout .write (
318306 "\n WARNING: Cannot resolve the dependency marker '{}' "
319307 "- ignoring it." .format (name )
320308 )
321309
310+ @staticmethod
311+ def matching_alias (aliases : List [Item ], item : Item ) -> Item :
312+ if len (aliases ) == 1 :
313+ return aliases [0 ]
314+
315+ # handle the rare case that several tests have the same alias name
316+ # we use the item that best matches the node id of the dependent item
317+ max_matching_parts = 0
318+ node_id_parts = re .split ("(::|/)" , item .node_id )
319+ matching_item = aliases [0 ]
320+ for alias_item in aliases :
321+ alias_node_id_parts = re .split ("(::|/)" , alias_item .node_id )
322+ nr_matching_parts = 0
323+ for n , a in zip (node_id_parts , alias_node_id_parts ):
324+ if n != a :
325+ break
326+ nr_matching_parts += 1
327+ if nr_matching_parts > max_matching_parts :
328+ max_matching_parts = nr_matching_parts
329+ matching_item = alias_item
330+ return matching_item
331+
322332
323333def module_item_groups (items : List [Item ]) -> Dict [str , List [Item ]]:
324334 """
0 commit comments