@@ -115,7 +115,7 @@ fn find_python(
115
115
#[ allow( non_snake_case) ]
116
116
let UV_TEST_PYTHON_PATH = env:: var_os ( "UV_TEST_PYTHON_PATH" ) ;
117
117
118
- let override_path = UV_TEST_PYTHON_PATH . is_some ( ) ;
118
+ let use_override = UV_TEST_PYTHON_PATH . is_some ( ) ;
119
119
let possible_names = selector. possible_names ( ) ;
120
120
121
121
#[ allow( non_snake_case) ]
@@ -182,11 +182,17 @@ fn find_python(
182
182
}
183
183
}
184
184
185
- if cfg ! ( windows) && !override_path {
185
+ if cfg ! ( windows) && !use_override {
186
186
// Use `py` to find the python installation on the system.
187
- match windows:: py_list_paths ( selector, platform, cache) {
188
- Ok ( Some ( interpreter) ) => return Ok ( Some ( interpreter) ) ,
189
- Ok ( None ) => { }
187
+ match windows:: py_list_paths ( ) {
188
+ Ok ( paths) => {
189
+ for entry in paths {
190
+ let installation = PythonInstallation :: PyListPath ( entry) ;
191
+ if let Some ( interpreter) = installation. select ( selector, platform, cache) ? {
192
+ return Ok ( Some ( interpreter) ) ;
193
+ }
194
+ }
195
+ }
190
196
Err ( Error :: PyList ( error) ) => {
191
197
if error. kind ( ) == std:: io:: ErrorKind :: NotFound {
192
198
debug ! ( "`py` is not installed" ) ;
@@ -209,6 +215,8 @@ fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
209
215
#[ allow( non_snake_case) ]
210
216
let UV_TEST_PYTHON_PATH = env:: var_os ( "UV_TEST_PYTHON_PATH" ) ;
211
217
218
+ let use_override = UV_TEST_PYTHON_PATH . is_some ( ) ;
219
+
212
220
#[ allow( non_snake_case) ]
213
221
let PATH = UV_TEST_PYTHON_PATH
214
222
. or ( env:: var_os ( "PATH" ) )
@@ -231,30 +239,62 @@ fn find_executable<R: AsRef<OsStr> + Into<OsString> + Copy>(
231
239
}
232
240
}
233
241
242
+ if cfg ! ( windows) && !use_override {
243
+ // Use `py` to find the python installation on the system.
244
+ match windows:: py_list_paths ( ) {
245
+ Ok ( paths) => {
246
+ for entry in paths {
247
+ // Ex) `--python python3.12.exe`
248
+ if entry. executable_path . file_name ( ) == Some ( requested. as_ref ( ) ) {
249
+ return Ok ( Some ( entry. executable_path ) ) ;
250
+ }
251
+
252
+ // Ex) `--python python3.12`
253
+ if entry
254
+ . executable_path
255
+ . file_stem ( )
256
+ . is_some_and ( |stem| stem == requested. as_ref ( ) )
257
+ {
258
+ return Ok ( Some ( entry. executable_path ) ) ;
259
+ }
260
+ }
261
+ }
262
+ Err ( Error :: PyList ( error) ) => {
263
+ if error. kind ( ) == std:: io:: ErrorKind :: NotFound {
264
+ debug ! ( "`py` is not installed" ) ;
265
+ }
266
+ }
267
+ Err ( error) => return Err ( error) ,
268
+ }
269
+ }
270
+
234
271
Ok ( None )
235
272
}
236
273
274
+ #[ derive( Debug , Clone ) ]
275
+ struct PyListPath {
276
+ major : u8 ,
277
+ minor : u8 ,
278
+ executable_path : PathBuf ,
279
+ }
280
+
237
281
#[ derive( Debug , Clone ) ]
238
282
enum PythonInstallation {
239
- PyListPath {
240
- major : u8 ,
241
- minor : u8 ,
242
- executable_path : PathBuf ,
243
- } ,
283
+ PyListPath ( PyListPath ) ,
244
284
Interpreter ( Interpreter ) ,
245
285
}
246
286
247
287
impl PythonInstallation {
248
288
fn major ( & self ) -> u8 {
249
289
match self {
250
- Self :: PyListPath { major, .. } => * major,
290
+ Self :: PyListPath ( PyListPath { major, .. } ) => * major,
251
291
Self :: Interpreter ( interpreter) => interpreter. python_major ( ) ,
252
292
}
253
293
}
254
294
255
295
fn minor ( & self ) -> u8 {
256
296
match self {
257
- Self :: PyListPath { minor, .. } => * minor,
297
+ Self :: PyListPath ( PyListPath { minor, .. } ) => * minor,
258
298
Self :: Interpreter ( interpreter) => interpreter. python_minor ( ) ,
259
299
}
260
300
}
@@ -268,6 +308,7 @@ impl PythonInstallation {
268
308
) -> Result < Option < Interpreter > , Error > {
269
309
let selected = match selector {
270
310
PythonVersionSelector :: Default => true ,
311
+
271
312
PythonVersionSelector :: Major ( major) => self . major ( ) == major,
272
313
273
314
PythonVersionSelector :: MajorMinor ( major, minor) => {
@@ -302,9 +343,9 @@ impl PythonInstallation {
302
343
cache : & Cache ,
303
344
) -> Result < Interpreter , Error > {
304
345
match self {
305
- Self :: PyListPath {
346
+ Self :: PyListPath ( PyListPath {
306
347
executable_path, ..
307
- } => Interpreter :: query ( & executable_path, platform. clone ( ) , cache) ,
348
+ } ) => Interpreter :: query ( & executable_path, platform. clone ( ) , cache) ,
308
349
Self :: Interpreter ( interpreter) => Ok ( interpreter) ,
309
350
}
310
351
}
@@ -373,11 +414,8 @@ mod windows {
373
414
use regex:: Regex ;
374
415
use tracing:: info_span;
375
416
376
- use platform_host:: Platform ;
377
- use uv_cache:: Cache ;
378
-
379
- use crate :: python_query:: { PythonInstallation , PythonVersionSelector } ;
380
- use crate :: { Error , Interpreter } ;
417
+ use crate :: python_query:: PyListPath ;
418
+ use crate :: Error ;
381
419
382
420
/// ```text
383
421
/// -V:3.12 C:\Users\Ferris\AppData\Local\Programs\Python\Python312\python.exe
@@ -392,11 +430,7 @@ mod windows {
392
430
///
393
431
/// The command takes 8ms on my machine.
394
432
/// TODO(konstin): Implement <https://peps.python.org/pep-0514/> to read python installations from the registry instead.
395
- pub ( super ) fn py_list_paths (
396
- selector : PythonVersionSelector ,
397
- platform : & Platform ,
398
- cache : & Cache ,
399
- ) -> Result < Option < Interpreter > , Error > {
433
+ pub ( super ) fn py_list_paths ( ) -> Result < Vec < PyListPath > , Error > {
400
434
let output = info_span ! ( "py_list_paths" )
401
435
. in_scope ( || Command :: new ( "py" ) . arg ( "--list-paths" ) . output ( ) )
402
436
. map_err ( Error :: PyList ) ?;
@@ -421,24 +455,23 @@ mod windows {
421
455
stderr : String :: from_utf8_lossy ( & output. stderr ) . trim ( ) . to_string ( ) ,
422
456
} ) ?;
423
457
424
- for captures in PY_LIST_PATHS . captures_iter ( & stdout) {
425
- let ( _, [ major, minor, path] ) = captures. extract ( ) ;
426
-
427
- if let ( Some ( major) , Some ( minor) ) = ( major. parse :: < u8 > ( ) . ok ( ) , minor. parse :: < u8 > ( ) . ok ( ) )
428
- {
429
- let installation = PythonInstallation :: PyListPath {
430
- major,
431
- minor,
432
- executable_path : PathBuf :: from ( path) ,
433
- } ;
434
-
435
- if let Some ( interpreter) = installation. select ( selector, platform, cache) ? {
436
- return Ok ( Some ( interpreter) ) ;
458
+ Ok ( PY_LIST_PATHS
459
+ . captures_iter ( & stdout)
460
+ . filter_map ( |captures| {
461
+ let ( _, [ major, minor, path] ) = captures. extract ( ) ;
462
+ if let ( Some ( major) , Some ( minor) ) =
463
+ ( major. parse :: < u8 > ( ) . ok ( ) , minor. parse :: < u8 > ( ) . ok ( ) )
464
+ {
465
+ Some ( PyListPath {
466
+ major,
467
+ minor,
468
+ executable_path : PathBuf :: from ( path) ,
469
+ } )
470
+ } else {
471
+ None
437
472
}
438
- }
439
- }
440
-
441
- Ok ( None )
473
+ } )
474
+ . collect ( ) )
442
475
}
443
476
444
477
/// On Windows we might encounter the windows store proxy shim (Enabled in Settings/Apps/Advanced app settings/App execution aliases).
0 commit comments