@@ -89,3 +89,172 @@ def test_find_sub_dirs_sys_path_no_math():
8989def test_find_sub_dirs_all_sitepackages_no_match ():
9090 result = find_sub_dirs_all_sitepackages ((NONEXISTENT ,))
9191 assert result == []
92+
93+
94+ def test_find_sub_dirs_all_sitepackages_venv_order (mocker , tmp_path ):
95+ """Test that in a venv with --system-site-packages, search order is: venv, user, system.
96+
97+ This test verifies fix for issue #1716: user-site-packages should come after
98+ venv site-packages but before system site-packages.
99+ """
100+ # Create test directories
101+ venv_site = tmp_path / "venv" / "lib" / "python3.12" / "site-packages"
102+ user_site = tmp_path / "user" / ".local" / "lib" / "python3.12" / "site-packages"
103+ system_site = tmp_path / "system" / "lib" / "python3.12" / "dist-packages"
104+
105+ venv_site .mkdir (parents = True )
106+ user_site .mkdir (parents = True )
107+ system_site .mkdir (parents = True )
108+
109+ # Create a test subdirectory in each
110+ test_subdir = ("nvidia" , "cuda_runtime" , "lib" )
111+ (venv_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
112+ (user_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
113+ (system_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
114+
115+ # Mock site.getsitepackages() to return venv first, then system
116+ mocker .patch (
117+ "cuda.pathfinder._utils.find_sub_dirs.site.getsitepackages" ,
118+ return_value = [str (venv_site ), str (system_site )],
119+ )
120+ # Mock user site-packages
121+ mocker .patch (
122+ "cuda.pathfinder._utils.find_sub_dirs.site.getusersitepackages" ,
123+ return_value = str (user_site ),
124+ )
125+ mocker .patch (
126+ "cuda.pathfinder._utils.find_sub_dirs.site.ENABLE_USER_SITE" ,
127+ True ,
128+ )
129+ # Mock sys.prefix != sys.base_prefix to simulate venv
130+ mocker .patch (
131+ "cuda.pathfinder._utils.find_sub_dirs.sys.prefix" ,
132+ str (tmp_path / "venv" ),
133+ )
134+ mocker .patch (
135+ "cuda.pathfinder._utils.find_sub_dirs.sys.base_prefix" ,
136+ str (tmp_path / "system" ),
137+ )
138+
139+ # Clear cache to ensure mocks take effect
140+ from cuda .pathfinder ._utils .find_sub_dirs import find_sub_dirs_cached
141+
142+ find_sub_dirs_cached .cache_clear ()
143+
144+ result = find_sub_dirs_all_sitepackages (test_subdir )
145+
146+ # Verify order: venv should come first, then user, then system
147+ assert len (result ) == 3
148+ assert result [0 ] == str (venv_site / "nvidia" / "cuda_runtime" / "lib" )
149+ assert result [1 ] == str (user_site / "nvidia" / "cuda_runtime" / "lib" )
150+ assert result [2 ] == str (system_site / "nvidia" / "cuda_runtime" / "lib" )
151+
152+
153+ def test_find_sub_dirs_all_sitepackages_non_venv_order (mocker , tmp_path ):
154+ """Test that outside a venv, search order is: user, system.
155+
156+ This verifies PEP 370 behavior: user-site-packages should come before
157+ system site-packages when not in a venv.
158+ """
159+ # Create test directories
160+ user_site = tmp_path / "user" / ".local" / "lib" / "python3.12" / "site-packages"
161+ system_site = tmp_path / "system" / "lib" / "python3.12" / "dist-packages"
162+
163+ user_site .mkdir (parents = True )
164+ system_site .mkdir (parents = True )
165+
166+ # Create a test subdirectory in each
167+ test_subdir = ("nvidia" , "cuda_runtime" , "lib" )
168+ (user_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
169+ (system_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
170+
171+ # Mock site.getsitepackages() to return only system site-packages
172+ mocker .patch (
173+ "cuda.pathfinder._utils.find_sub_dirs.site.getsitepackages" ,
174+ return_value = [str (system_site )],
175+ )
176+ # Mock user site-packages
177+ mocker .patch (
178+ "cuda.pathfinder._utils.find_sub_dirs.site.getusersitepackages" ,
179+ return_value = str (user_site ),
180+ )
181+ mocker .patch (
182+ "cuda.pathfinder._utils.find_sub_dirs.site.ENABLE_USER_SITE" ,
183+ True ,
184+ )
185+ # Mock sys.prefix == sys.base_prefix to simulate non-venv
186+ mocker .patch (
187+ "cuda.pathfinder._utils.find_sub_dirs.sys.prefix" ,
188+ str (tmp_path / "system" ),
189+ )
190+ mocker .patch (
191+ "cuda.pathfinder._utils.find_sub_dirs.sys.base_prefix" ,
192+ str (tmp_path / "system" ),
193+ )
194+
195+ # Clear cache to ensure mocks take effect
196+ from cuda .pathfinder ._utils .find_sub_dirs import find_sub_dirs_cached
197+
198+ find_sub_dirs_cached .cache_clear ()
199+
200+ result = find_sub_dirs_all_sitepackages (test_subdir )
201+
202+ # Verify order: user should come first, then system
203+ assert len (result ) == 2
204+ assert result [0 ] == str (user_site / "nvidia" / "cuda_runtime" / "lib" )
205+ assert result [1 ] == str (system_site / "nvidia" / "cuda_runtime" / "lib" )
206+
207+
208+ def test_find_sub_dirs_all_sitepackages_venv_first_match (mocker , tmp_path ):
209+ """Test that in a venv, venv site-packages is searched first (fixes #1716).
210+
211+ This test ensures that when a file exists in both venv and user site-packages,
212+ the venv version is found first, matching Python's import behavior.
213+ """
214+ # Create test directories
215+ venv_site = tmp_path / "venv" / "lib" / "python3.12" / "site-packages"
216+ user_site = tmp_path / "user" / ".local" / "lib" / "python3.12" / "site-packages"
217+
218+ venv_site .mkdir (parents = True )
219+ user_site .mkdir (parents = True )
220+
221+ # Create the same subdirectory in both
222+ test_subdir = ("nvidia" , "cuda_runtime" , "lib" )
223+ (venv_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
224+ (user_site / "nvidia" / "cuda_runtime" / "lib" ).mkdir (parents = True )
225+
226+ # Mock site.getsitepackages() to return venv first
227+ mocker .patch (
228+ "cuda.pathfinder._utils.find_sub_dirs.site.getsitepackages" ,
229+ return_value = [str (venv_site )],
230+ )
231+ # Mock user site-packages
232+ mocker .patch (
233+ "cuda.pathfinder._utils.find_sub_dirs.site.getusersitepackages" ,
234+ return_value = str (user_site ),
235+ )
236+ mocker .patch (
237+ "cuda.pathfinder._utils.find_sub_dirs.site.ENABLE_USER_SITE" ,
238+ True ,
239+ )
240+ # Mock sys.prefix != sys.base_prefix to simulate venv
241+ mocker .patch (
242+ "cuda.pathfinder._utils.find_sub_dirs.sys.prefix" ,
243+ str (tmp_path / "venv" ),
244+ )
245+ mocker .patch (
246+ "cuda.pathfinder._utils.find_sub_dirs.sys.base_prefix" ,
247+ str (tmp_path / "system" ),
248+ )
249+
250+ # Clear cache to ensure mocks take effect
251+ from cuda .pathfinder ._utils .find_sub_dirs import find_sub_dirs_cached
252+
253+ find_sub_dirs_cached .cache_clear ()
254+
255+ result = find_sub_dirs_all_sitepackages (test_subdir )
256+
257+ # Verify venv comes first (this would fail with old code that puts user first)
258+ assert len (result ) >= 2
259+ assert result [0 ] == str (venv_site / "nvidia" / "cuda_runtime" / "lib" )
260+ assert result [1 ] == str (user_site / "nvidia" / "cuda_runtime" / "lib" )
0 commit comments