1
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
2
// Licensed under the MIT License.
3
3
4
+ #[ cfg( windows) ]
4
5
use crate :: known;
6
+ #[ cfg( windows) ]
5
7
use crate :: known:: Environment ;
8
+ #[ cfg( windows) ]
6
9
use crate :: locator:: { Locator , LocatorResult } ;
10
+ #[ cfg( windows) ]
7
11
use crate :: messaging:: PythonEnvironment ;
12
+ #[ cfg( windows) ]
8
13
use crate :: utils:: PythonEnv ;
14
+ #[ cfg( windows) ]
9
15
use std:: path:: Path ;
16
+ #[ cfg( windows) ]
10
17
use std:: path:: PathBuf ;
18
+ #[ cfg( windows) ]
19
+ use winreg:: RegKey ;
11
20
21
+ #[ cfg( windows) ]
12
22
pub fn is_windows_python_executable ( path : & PathBuf ) -> bool {
13
23
let name = path. file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_lowercase ( ) ;
14
24
// TODO: Is it safe to assume the number 3?
15
25
name. starts_with ( "python3." ) && name. ends_with ( ".exe" )
16
26
}
17
27
28
+ #[ cfg( windows) ]
18
29
fn list_windows_store_python_executables (
19
30
environment : & dyn known:: Environment ,
20
- ) -> Option < Vec < PathBuf > > {
21
- let mut python_envs: Vec < PathBuf > = vec ! [ ] ;
31
+ ) -> Option < Vec < PythonEnvironment > > {
32
+ let mut python_envs: Vec < PythonEnvironment > = vec ! [ ] ;
22
33
let home = environment. get_user_home ( ) ?;
23
34
let apps_path = Path :: new ( & home)
24
35
. join ( "AppData" )
25
36
. join ( "Local" )
26
37
. join ( "Microsoft" )
27
38
. join ( "WindowsApps" ) ;
28
- for file in std:: fs:: read_dir ( apps_path) . ok ( ) ? {
29
- match file {
30
- Ok ( file) => {
31
- let path = file. path ( ) ;
32
- if path. is_file ( ) && is_windows_python_executable ( & path) {
33
- python_envs. push ( path) ;
39
+ let hkcu = winreg:: RegKey :: predef ( winreg:: enums:: HKEY_CURRENT_USER ) ;
40
+ for file in std:: fs:: read_dir ( apps_path) . ok ( ) ?. filter_map ( Result :: ok) {
41
+ let path = file. path ( ) ;
42
+ if let Some ( name) = path. file_name ( ) {
43
+ let exe = path. join ( "python.exe" ) ;
44
+ if name
45
+ . to_str ( )
46
+ . unwrap_or_default ( )
47
+ . starts_with ( "PythonSoftwareFoundation.Python." )
48
+ && exe. is_file ( )
49
+ && exe. exists ( )
50
+ {
51
+ if let Some ( result) =
52
+ get_package_display_name_and_location ( name. to_string_lossy ( ) . to_string ( ) , & hkcu)
53
+ {
54
+ let env = PythonEnvironment {
55
+ display_name : Some ( result. display_name ) ,
56
+ name : None ,
57
+ python_executable_path : Some ( exe. clone ( ) ) ,
58
+ version : result. version ,
59
+ category : crate :: messaging:: PythonEnvironmentCategory :: WindowsStore ,
60
+ sys_prefix_path : Some ( PathBuf :: from ( result. env_path . clone ( ) ) ) ,
61
+ env_path : Some ( PathBuf :: from ( result. env_path . clone ( ) ) ) ,
62
+ env_manager : None ,
63
+ project_path : None ,
64
+ python_run_command : Some ( vec ! [ exe. to_string_lossy( ) . to_string( ) ] ) ,
65
+ } ;
66
+ python_envs. push ( env) ;
34
67
}
35
68
}
36
- Err ( _) => { }
37
69
}
38
70
}
39
71
40
72
Some ( python_envs)
41
73
}
42
74
75
+ #[ cfg( windows) ]
76
+ fn get_package_full_name_from_registry ( name : String , hkcu : & RegKey ) -> Option < String > {
77
+ let key = format ! ( "Software\\ Classes\\ Local Settings\\ Software\\ Microsoft\\ Windows\\ CurrentVersion\\ AppModel\\ SystemAppData\\ {}\\ Schemas" , name) ;
78
+ let package_key = hkcu. open_subkey ( key) . ok ( ) ?;
79
+ let value = package_key. get_value ( "PackageFullName" ) . unwrap_or_default ( ) ;
80
+ Some ( value)
81
+ }
82
+
83
+ #[ derive( Debug ) ]
84
+ #[ cfg( windows) ]
85
+ struct StorePythonInfo {
86
+ display_name : String ,
87
+ version : Option < String > ,
88
+ env_path : String ,
89
+ }
90
+
91
+ #[ cfg( windows) ]
92
+ fn get_package_display_name_and_location ( name : String , hkcu : & RegKey ) -> Option < StorePythonInfo > {
93
+ if let Some ( name) = get_package_full_name_from_registry ( name, & hkcu) {
94
+ let key = format ! ( "Software\\ Classes\\ Local Settings\\ Software\\ Microsoft\\ Windows\\ CurrentVersion\\ AppModel\\ Repository\\ Packages\\ {}" , name) ;
95
+ let package_key = hkcu. open_subkey ( key) . ok ( ) ?;
96
+ let display_name = package_key. get_value ( "DisplayName" ) . ok ( ) ?;
97
+ let env_path = package_key. get_value ( "PackageRootFolder" ) . ok ( ) ?;
98
+
99
+ let regex = regex:: Regex :: new ( "PythonSoftwareFoundation.Python.((\\ d+\\ .?)*)_.*" ) . ok ( ) ?;
100
+ let version = match regex. captures ( & name) ?. get ( 1 ) {
101
+ Some ( version) => Some ( version. as_str ( ) . to_string ( ) ) ,
102
+ None => None ,
103
+ } ;
104
+
105
+ return Some ( StorePythonInfo {
106
+ display_name,
107
+ version,
108
+ env_path,
109
+ } ) ;
110
+ }
111
+ None
112
+ }
113
+
114
+ #[ cfg( windows) ]
43
115
pub struct WindowsStore < ' a > {
44
116
pub environment : & ' a dyn Environment ,
45
117
}
46
118
119
+ #[ cfg( windows) ]
47
120
impl WindowsStore < ' _ > {
48
121
#[ allow( dead_code) ]
49
122
pub fn with < ' a > ( environment : & ' a impl Environment ) -> WindowsStore {
50
123
WindowsStore { environment }
51
124
}
52
125
}
53
126
127
+ #[ cfg( windows) ]
54
128
impl Locator for WindowsStore < ' _ > {
55
129
fn resolve ( & self , env : & PythonEnv ) -> Option < PythonEnvironment > {
56
130
if is_windows_python_executable ( & env. executable ) {
@@ -71,14 +145,7 @@ impl Locator for WindowsStore<'_> {
71
145
}
72
146
73
147
fn find ( & mut self ) -> Option < LocatorResult > {
74
- let mut environments: Vec < PythonEnvironment > = vec ! [ ] ;
75
- if let Some ( envs) = list_windows_store_python_executables ( self . environment ) {
76
- envs. iter ( ) . for_each ( |env| {
77
- if let Some ( env) = self . resolve ( & & PythonEnv :: from ( env. clone ( ) ) ) {
78
- environments. push ( env) ;
79
- }
80
- } ) ;
81
- }
148
+ let environments = list_windows_store_python_executables ( self . environment ) ?;
82
149
83
150
if environments. is_empty ( ) {
84
151
None
0 commit comments