@@ -2,6 +2,7 @@ use std::collections::BTreeSet;
2
2
use std:: fmt:: Write ;
3
3
4
4
use anyhow:: Result ;
5
+ use itertools:: Either ;
5
6
use owo_colors:: OwoColorize ;
6
7
use rustc_hash:: FxHashSet ;
7
8
use uv_cache:: Cache ;
@@ -29,6 +30,7 @@ pub(crate) async fn list(
29
30
kinds : PythonListKinds ,
30
31
all_versions : bool ,
31
32
all_platforms : bool ,
33
+ show_urls : bool ,
32
34
python_preference : PythonPreference ,
33
35
python_downloads : PythonDownloads ,
34
36
cache : & Cache ,
@@ -38,6 +40,11 @@ pub(crate) async fn list(
38
40
if python_preference != PythonPreference :: OnlySystem {
39
41
let download_request = match kinds {
40
42
PythonListKinds :: Installed => None ,
43
+ PythonListKinds :: Downloads => Some ( if all_platforms {
44
+ PythonDownloadRequest :: default ( )
45
+ } else {
46
+ PythonDownloadRequest :: from_env ( ) ?
47
+ } ) ,
41
48
PythonListKinds :: Default => {
42
49
if python_downloads. is_automatic ( ) {
43
50
Some ( if all_platforms {
@@ -61,48 +68,60 @@ pub(crate) async fn list(
61
68
. flatten ( ) ;
62
69
63
70
for download in downloads {
64
- output. insert ( ( download. key ( ) . clone ( ) , Kind :: Download , None ) ) ;
71
+ output. insert ( (
72
+ download. key ( ) . clone ( ) ,
73
+ Kind :: Download ,
74
+ Either :: Right ( download. url ( ) ) ,
75
+ ) ) ;
65
76
}
66
77
} ;
67
78
68
- let installed = find_python_installations (
69
- & PythonRequest :: Any ,
70
- EnvironmentPreference :: OnlySystem ,
71
- python_preference,
72
- cache,
73
- )
74
- // Raise discovery errors if critical
75
- . filter ( |result| {
76
- result
77
- . as_ref ( )
78
- . err ( )
79
- . map_or ( true , DiscoveryError :: is_critical)
80
- } )
81
- . collect :: < Result < Vec < Result < PythonInstallation , PythonNotFound > > , DiscoveryError > > ( ) ?
82
- . into_iter ( )
83
- // Drop any "missing" installations
84
- . filter_map ( Result :: ok) ;
85
-
86
- for installation in installed {
87
- let kind = if matches ! ( installation. source( ) , PythonSource :: Managed ) {
88
- Kind :: Managed
89
- } else {
90
- Kind :: System
79
+ let installed =
80
+ match kinds {
81
+ PythonListKinds :: Installed | PythonListKinds :: Default => {
82
+ Some ( find_python_installations (
83
+ & PythonRequest :: Any ,
84
+ EnvironmentPreference :: OnlySystem ,
85
+ python_preference,
86
+ cache,
87
+ )
88
+ // Raise discovery errors if critical
89
+ . filter ( |result| {
90
+ result
91
+ . as_ref ( )
92
+ . err ( )
93
+ . map_or ( true , DiscoveryError :: is_critical)
94
+ } )
95
+ . collect :: < Result < Vec < Result < PythonInstallation , PythonNotFound > > , DiscoveryError > > ( ) ?
96
+ . into_iter ( )
97
+ // Drop any "missing" installations
98
+ . filter_map ( Result :: ok) )
99
+ }
100
+ PythonListKinds :: Downloads => None ,
91
101
} ;
92
- output. insert ( (
93
- installation. key ( ) ,
94
- kind,
95
- Some ( installation. interpreter ( ) . sys_executable ( ) . to_path_buf ( ) ) ,
96
- ) ) ;
102
+
103
+ if let Some ( installed) = installed {
104
+ for installation in installed {
105
+ let kind = if matches ! ( installation. source( ) , PythonSource :: Managed ) {
106
+ Kind :: Managed
107
+ } else {
108
+ Kind :: System
109
+ } ;
110
+ output. insert ( (
111
+ installation. key ( ) ,
112
+ kind,
113
+ Either :: Left ( installation. interpreter ( ) . sys_executable ( ) . to_path_buf ( ) ) ,
114
+ ) ) ;
115
+ }
97
116
}
98
117
99
118
let mut seen_minor = FxHashSet :: default ( ) ;
100
119
let mut seen_patch = FxHashSet :: default ( ) ;
101
120
let mut seen_paths = FxHashSet :: default ( ) ;
102
121
let mut include = Vec :: new ( ) ;
103
- for ( key, kind, path ) in output. iter ( ) . rev ( ) {
122
+ for ( key, kind, uri ) in output. iter ( ) . rev ( ) {
104
123
// Do not show the same path more than once
105
- if let Some ( path) = path {
124
+ if let Either :: Left ( path) = uri {
106
125
if !seen_paths. insert ( path) {
107
126
continue ;
108
127
}
@@ -142,38 +161,45 @@ pub(crate) async fn list(
142
161
}
143
162
}
144
163
}
145
- include. push ( ( key, path ) ) ;
164
+ include. push ( ( key, uri ) ) ;
146
165
}
147
166
148
167
// Compute the width of the first column.
149
168
let width = include
150
169
. iter ( )
151
170
. fold ( 0usize , |acc, ( key, _) | acc. max ( key. to_string ( ) . len ( ) ) ) ;
152
171
153
- for ( key, path ) in include {
172
+ for ( key, uri ) in include {
154
173
let key = key. to_string ( ) ;
155
- if let Some ( path) = path {
156
- let is_symlink = fs_err:: symlink_metadata ( path) ?. is_symlink ( ) ;
157
- if is_symlink {
158
- writeln ! (
159
- printer. stdout( ) ,
160
- "{key:width$} {} -> {}" ,
161
- path. user_display( ) . cyan( ) ,
162
- path. read_link( ) ?. user_display( ) . cyan( )
163
- ) ?;
164
- } else {
165
- writeln ! (
166
- printer. stdout( ) ,
167
- "{key:width$} {}" ,
168
- path. user_display( ) . cyan( )
169
- ) ?;
174
+ match uri {
175
+ Either :: Left ( path) => {
176
+ let is_symlink = fs_err:: symlink_metadata ( path) ?. is_symlink ( ) ;
177
+ if is_symlink {
178
+ writeln ! (
179
+ printer. stdout( ) ,
180
+ "{key:width$} {} -> {}" ,
181
+ path. user_display( ) . cyan( ) ,
182
+ path. read_link( ) ?. user_display( ) . cyan( )
183
+ ) ?;
184
+ } else {
185
+ writeln ! (
186
+ printer. stdout( ) ,
187
+ "{key:width$} {}" ,
188
+ path. user_display( ) . cyan( )
189
+ ) ?;
190
+ }
191
+ }
192
+ Either :: Right ( url) => {
193
+ if show_urls {
194
+ writeln ! ( printer. stdout( ) , "{key:width$} {}" , url. dimmed( ) ) ?;
195
+ } else {
196
+ writeln ! (
197
+ printer. stdout( ) ,
198
+ "{key:width$} {}" ,
199
+ "<download available>" . dimmed( )
200
+ ) ?;
201
+ }
170
202
}
171
- } else {
172
- writeln ! (
173
- printer. stdout( ) ,
174
- "{key:width$} {}" ,
175
- "<download available>" . dimmed( )
176
- ) ?;
177
203
}
178
204
}
179
205
0 commit comments