1515import abc
1616import contextlib
1717import copy
18+ import inspect
1819import io
1920import os
2021import re
2728from pathlib import Path
2829from typing import Dict , List , NamedTuple , Optional
2930
30- from _pytest .nodes import Item
31- from _pytest .python import Function
31+ import pytest
3232from defs .trt_test_alternative import print_error , print_info
3333
3434from ..common import get_trt_llm_lib_dir
@@ -798,51 +798,71 @@ def _write_result(self, full_test_name: str,
798798 os .path .join (output_dir , yaml_name )))
799799
800800
801- def generate_one_test_node (session , config , domain_name , test_name , test_func ):
801+ def _find_collected_dir_node (items , rootpath ):
802+ """Return the real rootdir ``Dir`` collection node created by pytest.
803+
804+ Since pytest 9.1, fixture visibility is matched by the actual collection-node
805+ hierarchy instead of the textual nodeid (pytest issue #11785). Dynamically
806+ generated perf nodes must therefore descend from this real ``Dir`` node to
807+ inherit the fixtures declared in ``conftest.py``.
808+ """
809+ for item in items :
810+ node = item
811+ while node is not None :
812+ if isinstance (node , pytest .Dir ) and node .path == rootpath :
813+ return node
814+ node = node .parent
815+ return None
816+
817+
818+ def generate_one_test_node (session ,
819+ config ,
820+ domain_name ,
821+ test_name ,
822+ test_func ,
823+ parent_dir = None ,
824+ module_cache = None ):
802825 """
803826 A helper function to create a PyTest item with the specific name and specific test function.
804827 """
805828
806- # Create the parent Item node.
807- # Pytest 8.x upgrade compatibility.
808- # We should never import Pytest internals within test-definitions.
829+ # Attach the node under the real Dir -> Module hierarchy so it inherits the
830+ # fixtures declared in conftest.py. Since pytest 9.1 fixture visibility is
831+ # matched by the collection-node hierarchy rather than the textual nodeid
832+ # (pytest issue #11785), a node attached directly to the session can no longer
833+ # resolve those fixtures. The legacy nodeid is restored afterwards so that
834+ # test-list filtering and --test-prefix rewriting keep working unchanged.
835+ if parent_dir is not None :
836+ if module_cache is None :
837+ module_cache = {}
838+ module_path = Path (inspect .getfile (test_func )).resolve ()
839+ module = module_cache .get (module_path )
840+ if module is None :
841+ module = pytest .Module .from_parent (parent_dir , path = module_path )
842+ module_cache [module_path ] = module
843+ item = pytest .Function .from_parent (module ,
844+ name = test_name ,
845+ callobj = test_func )
846+ item .obj = test_func
847+ item ._nodeid = f"{ domain_name } ::{ test_name } "
848+ return item
849+
850+ # Fallback when no collected Dir node is available (e.g. nothing was collected
851+ # before the dynamic generation): mirror the legacy session-attached behavior.
809852 # TODO: TRT-23565
810- parent = None
811- if hasattr (Item , "from_parent" ):
812-
813- class TrtexecItem (Item ):
814-
815- def __init__ (self , ** kwargs ):
816- super ().__init__ (** kwargs )
853+ class TrtexecItem (pytest .Item ):
817854
818- def runtest (self ):
819- return test_func ()
820-
821- parent = TrtexecItem .from_parent (session ,
822- name = domain_name ,
823- nodeid = domain_name )
824- else :
825- parent = Item (name = domain_name ,
826- parent = session ,
827- config = config ,
828- session = session ,
829- nodeid = domain_name )
855+ def runtest (self ):
856+ return test_func ()
830857
858+ parent = TrtexecItem .from_parent (session ,
859+ name = domain_name ,
860+ nodeid = domain_name )
831861 parent .obj = None
832862
833- # Create the Function node for the test.
834- # For pytest 8.x compatibility
835- # TODO: TRT-23565
836- item = None
837- if hasattr (Function , "from_parent" ):
838- item = Function .from_parent (parent , name = test_name , callobj = test_func )
839- else :
840- item = Function (name = test_name ,
841- parent = parent ,
842- config = config ,
843- session = session ,
844- callobj = test_func )
845-
863+ item = pytest .Function .from_parent (parent ,
864+ name = test_name ,
865+ callobj = test_func )
846866 item .obj = test_func
847867
848868 # This has to be set but can be random as it isn't used.
@@ -869,6 +889,12 @@ def generate_test_nodes(session, config, items, valid_prefixes: List[str],
869889 except FileNotFoundError :
870890 pass
871891
892+ # Reuse the real rootdir Dir node that pytest created during collection so the
893+ # generated nodes inherit conftest fixtures (see generate_one_test_node). The
894+ # module node is created once and shared across all generated cases.
895+ parent_dir = _find_collected_dir_node (items , config .rootpath )
896+ module_cache = {}
897+
872898 # Go through all test names and find the ones that need to be generated.
873899 for test_name in all_tests :
874900
@@ -882,7 +908,8 @@ def generate_test_nodes(session, config, items, valid_prefixes: List[str],
882908 # Generate the test node and append it to the Pytest item list.
883909 items .append (
884910 generate_one_test_node (session , config , domain_name ,
885- short_test_name , test_func ))
911+ short_test_name , test_func , parent_dir ,
912+ module_cache ))
886913 print (f"Dynamically generated test node: { test_name } " )
887914
888915 return items
0 commit comments