@@ -2,6 +2,7 @@ use std::collections::BTreeSet;
22use std:: fmt:: Write ;
33
44use anyhow:: Result ;
5+ use itertools:: Either ;
56use owo_colors:: OwoColorize ;
67use rustc_hash:: FxHashSet ;
78use uv_cache:: Cache ;
@@ -29,6 +30,7 @@ pub(crate) async fn list(
2930 kinds : PythonListKinds ,
3031 all_versions : bool ,
3132 all_platforms : bool ,
33+ show_urls : bool ,
3234 python_preference : PythonPreference ,
3335 python_downloads : PythonDownloads ,
3436 cache : & Cache ,
@@ -38,6 +40,11 @@ pub(crate) async fn list(
3840 if python_preference != PythonPreference :: OnlySystem {
3941 let download_request = match kinds {
4042 PythonListKinds :: Installed => None ,
43+ PythonListKinds :: Downloads => Some ( if all_platforms {
44+ PythonDownloadRequest :: default ( )
45+ } else {
46+ PythonDownloadRequest :: from_env ( ) ?
47+ } ) ,
4148 PythonListKinds :: Default => {
4249 if python_downloads. is_automatic ( ) {
4350 Some ( if all_platforms {
@@ -61,48 +68,60 @@ pub(crate) async fn list(
6168 . flatten ( ) ;
6269
6370 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+ ) ) ;
6576 }
6677 } ;
6778
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 ,
91101 } ;
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+ }
97116 }
98117
99118 let mut seen_minor = FxHashSet :: default ( ) ;
100119 let mut seen_patch = FxHashSet :: default ( ) ;
101120 let mut seen_paths = FxHashSet :: default ( ) ;
102121 let mut include = Vec :: new ( ) ;
103- for ( key, kind, path ) in output. iter ( ) . rev ( ) {
122+ for ( key, kind, uri ) in output. iter ( ) . rev ( ) {
104123 // Do not show the same path more than once
105- if let Some ( path) = path {
124+ if let Either :: Left ( path) = uri {
106125 if !seen_paths. insert ( path) {
107126 continue ;
108127 }
@@ -142,38 +161,45 @@ pub(crate) async fn list(
142161 }
143162 }
144163 }
145- include. push ( ( key, path ) ) ;
164+ include. push ( ( key, uri ) ) ;
146165 }
147166
148167 // Compute the width of the first column.
149168 let width = include
150169 . iter ( )
151170 . fold ( 0usize , |acc, ( key, _) | acc. max ( key. to_string ( ) . len ( ) ) ) ;
152171
153- for ( key, path ) in include {
172+ for ( key, uri ) in include {
154173 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+ }
170202 }
171- } else {
172- writeln ! (
173- printer. stdout( ) ,
174- "{key:width$} {}" ,
175- "<download available>" . dimmed( )
176- ) ?;
177203 }
178204 }
179205
0 commit comments