@@ -6,6 +6,7 @@ use crate::known::Environment;
6
6
use crate :: locator:: Locator ;
7
7
use crate :: locator:: LocatorResult ;
8
8
use crate :: messaging;
9
+ use crate :: messaging:: Architecture ;
9
10
use crate :: messaging:: EnvManager ;
10
11
use crate :: messaging:: EnvManagerType ;
11
12
use crate :: messaging:: PythonEnvironment ;
@@ -14,9 +15,11 @@ use crate::utils::{find_python_binary_path, get_environment_key, get_environment
14
15
use log:: trace;
15
16
use log:: warn;
16
17
use regex:: Regex ;
18
+ use serde:: Deserialize ;
17
19
use std:: collections:: HashMap ;
18
20
use std:: collections:: HashSet ;
19
21
use std:: env;
22
+ use std:: fs:: read_to_string;
20
23
use std:: path:: { Path , PathBuf } ;
21
24
22
25
/// Specifically returns the file names that are valid for 'conda' on windows
@@ -57,6 +60,13 @@ struct CondaPackage {
57
60
#[ allow( dead_code) ]
58
61
path : PathBuf ,
59
62
version : String ,
63
+ arch : Option < Architecture > ,
64
+ }
65
+
66
+ #[ derive( Deserialize , Debug ) ]
67
+ struct CondaMetaPackageStructure {
68
+ channel : Option < String > ,
69
+ // version: Option<String>,
60
70
}
61
71
62
72
/// Get the path to the json file along with the version of a package in the conda environment from the 'conda-meta' directory.
@@ -72,16 +82,38 @@ fn get_conda_package_json_path(path: &Path, package: &str) -> Option<CondaPackag
72
82
let path = entry. path ( ) ;
73
83
let file_name = path. file_name ( ) ?. to_string_lossy ( ) ;
74
84
if file_name. starts_with ( & package_name) && file_name. ends_with ( ".json" ) {
75
- match regex. clone ( ) . ok ( ) . unwrap ( ) . captures ( & file_name) ?. get ( 1 ) {
76
- Some ( version) => Some ( CondaPackage {
85
+ if let Some ( version) = regex. clone ( ) . ok ( ) . unwrap ( ) . captures ( & file_name) ?. get ( 1 ) {
86
+ let mut arch: Option < Architecture > = None ;
87
+ // Sample contents
88
+ // {
89
+ // "build": "h966fe2a_2",
90
+ // "build_number": 2,
91
+ // "channel": "https://repo.anaconda.com/pkgs/main/win-64",
92
+ // "constrains": [],
93
+ // }
94
+ // 32bit channel is https://repo.anaconda.com/pkgs/main/win-32/
95
+ // 64bit channel is "channel": "https://repo.anaconda.com/pkgs/main/osx-arm64",
96
+ if let Some ( contents) = read_to_string ( & path) . ok ( ) {
97
+ if let Some ( js) =
98
+ serde_json:: from_str :: < CondaMetaPackageStructure > ( & contents) . ok ( )
99
+ {
100
+ if let Some ( channel) = js. channel {
101
+ if channel. ends_with ( "64" ) {
102
+ arch = Some ( Architecture :: X64 ) ;
103
+ } else if channel. ends_with ( "32" ) {
104
+ arch = Some ( Architecture :: X86 ) ;
105
+ }
106
+ }
107
+ }
108
+ }
109
+ return Some ( CondaPackage {
77
110
path : path. clone ( ) ,
78
111
version : version. as_str ( ) . to_string ( ) ,
79
- } ) ,
80
- None => None ,
112
+ arch ,
113
+ } ) ;
81
114
}
82
- } else {
83
- None
84
115
}
116
+ None
85
117
} )
86
118
}
87
119
@@ -202,6 +234,8 @@ fn get_conda_manager(path: &PathBuf) -> Option<EnvManager> {
202
234
executable_path : conda_exe,
203
235
version : Some ( conda_pkg. version ) ,
204
236
tool : EnvManagerType :: Conda ,
237
+ company : None ,
238
+ company_display_name : None ,
205
239
} )
206
240
}
207
241
@@ -213,6 +247,7 @@ struct CondaEnvironment {
213
247
python_executable_path : Option < PathBuf > ,
214
248
version : Option < String > ,
215
249
conda_install_folder : Option < String > ,
250
+ arch : Option < Architecture > ,
216
251
}
217
252
fn get_conda_environment_info ( env_path : & PathBuf , named : bool ) -> Option < CondaEnvironment > {
218
253
let metadata = env_path. metadata ( ) ;
@@ -229,6 +264,7 @@ fn get_conda_environment_info(env_path: &PathBuf, named: bool) -> Option<CondaEn
229
264
python_executable_path : Some ( python_binary) ,
230
265
version : Some ( package_info. version ) ,
231
266
conda_install_folder,
267
+ arch : package_info. arch ,
232
268
} ) ;
233
269
} else {
234
270
return Some ( CondaEnvironment {
@@ -238,6 +274,7 @@ fn get_conda_environment_info(env_path: &PathBuf, named: bool) -> Option<CondaEn
238
274
python_executable_path : Some ( python_binary) ,
239
275
version : None ,
240
276
conda_install_folder,
277
+ arch : None ,
241
278
} ) ;
242
279
}
243
280
} else {
@@ -248,6 +285,7 @@ fn get_conda_environment_info(env_path: &PathBuf, named: bool) -> Option<CondaEn
248
285
python_executable_path : None ,
249
286
version : None ,
250
287
conda_install_folder,
288
+ arch : None ,
251
289
} ) ;
252
290
}
253
291
}
@@ -631,11 +669,17 @@ fn get_root_python_environment(path: &PathBuf, manager: &EnvManager) -> Option<P
631
669
if let Some ( package_info) = get_conda_package_json_path ( & path, "python" ) {
632
670
let conda_exe = manager. executable_path . to_str ( ) . unwrap ( ) . to_string ( ) ;
633
671
return Some ( PythonEnvironment {
634
- display_name : None ,
635
- name : Some ( "base" . to_string ( ) ) ,
672
+ // Do not set the name to `base`
673
+ // Ideally we would like to see this idetnfieid as a base env.
674
+ // However if user has 2 conda installations, then the second base env
675
+ // will be activated in python extension using first conda executable and -n base,
676
+ // I.e. base env of the first install will be activated instead of this.
677
+ // Hence lets always just give the path.
678
+ // name: Some("base".to_string()),
636
679
category : messaging:: PythonEnvironmentCategory :: Conda ,
637
680
python_executable_path : Some ( python_exe) ,
638
681
version : Some ( package_info. version ) ,
682
+ arch : package_info. arch ,
639
683
env_path : Some ( path. clone ( ) ) ,
640
684
env_manager : Some ( manager. clone ( ) ) ,
641
685
python_run_command : Some ( vec ! [
@@ -645,8 +689,7 @@ fn get_root_python_environment(path: &PathBuf, manager: &EnvManager) -> Option<P
645
689
path. to_str( ) . unwrap( ) . to_string( ) ,
646
690
"python" . to_string( ) ,
647
691
] ) ,
648
- project_path : None ,
649
- arch : None ,
692
+ ..Default :: default ( )
650
693
} ) ;
651
694
}
652
695
None
@@ -660,78 +703,82 @@ fn get_conda_environments_in_specified_install_path(
660
703
let mut environments: Vec < PythonEnvironment > = vec ! [ ] ;
661
704
let mut detected_envs: HashSet < String > = HashSet :: new ( ) ;
662
705
let mut detected_managers: HashSet < String > = HashSet :: new ( ) ;
663
- if conda_install_folder. is_dir ( ) && conda_install_folder. exists ( ) {
664
- if let Some ( manager) = get_conda_manager ( & conda_install_folder) {
665
- // 1. Base environment.
666
- if let Some ( env) = get_root_python_environment ( & conda_install_folder, & manager) {
667
- if let Some ( env_path) = env. clone ( ) . env_path {
668
- possible_conda_envs. remove ( & env_path) ;
669
- let key = env_path. to_string_lossy ( ) . to_string ( ) ;
670
- if !detected_envs. contains ( & key) {
671
- detected_envs. insert ( key) ;
672
- environments. push ( env) ;
673
- }
706
+ if !conda_install_folder. is_dir ( ) || !conda_install_folder. exists ( ) {
707
+ return None ;
708
+ }
709
+
710
+ if let Some ( manager) = get_conda_manager ( & conda_install_folder) {
711
+ // 1. Base environment.
712
+ if let Some ( env) = get_root_python_environment ( & conda_install_folder, & manager) {
713
+ if let Some ( env_path) = env. clone ( ) . env_path {
714
+ possible_conda_envs. remove ( & env_path) ;
715
+ let key = env_path. to_string_lossy ( ) . to_string ( ) ;
716
+ if !detected_envs. contains ( & key) {
717
+ detected_envs. insert ( key) ;
718
+ environments. push ( env) ;
674
719
}
675
720
}
721
+ }
722
+
723
+ // 2. All environments in the `<conda install folder>/envs` folder
724
+ let mut envs: Vec < CondaEnvironment > = vec ! [ ] ;
725
+ if let Some ( environments) =
726
+ get_environments_from_envs_folder_in_conda_directory ( conda_install_folder)
727
+ {
728
+ environments. iter ( ) . for_each ( |env| {
729
+ possible_conda_envs. remove ( & env. env_path ) ;
730
+ envs. push ( env. clone ( ) ) ;
731
+ } ) ;
732
+ }
676
733
677
- // 2. All environments in the `<conda install folder>/envs` folder
678
- let mut envs: Vec < CondaEnvironment > = vec ! [ ] ;
679
- if let Some ( environments) =
680
- get_environments_from_envs_folder_in_conda_directory ( conda_install_folder)
734
+ // 3. All environments in the environments.txt and other locations (such as `conda config --show envs_dirs`)
735
+ // Only include those environments that were created by the specific conda installation
736
+ // Ignore environments that are in the env sub directory of the conda folder, as those would have been
737
+ // tracked elsewhere, we're only interested in conda envs located in other parts of the file system created using the -p flag.
738
+ // E.g conda_install_folder is `<home>/<conda install folder>`
739
+ // Then all folders such as `<home>/<conda install folder>/envs/env1` can be ignored
740
+ // As these would have been discovered in previous step.
741
+ for ( key, env) in possible_conda_envs. clone ( ) . iter ( ) {
742
+ if env
743
+ . env_path
744
+ . to_string_lossy ( )
745
+ . contains ( conda_install_folder. to_str ( ) . unwrap ( ) )
681
746
{
682
- environments. iter ( ) . for_each ( |env| {
683
- possible_conda_envs. remove ( & env. env_path ) ;
684
- envs. push ( env. clone ( ) ) ;
685
- } ) ;
747
+ continue ;
686
748
}
687
-
688
- // 3. All environments in the environments.txt and other locations (such as `conda config --show envs_dirs`)
689
- // Only include those environments that were created by the specific conda installation
690
- // Ignore environments that are in the env sub directory of the conda folder, as those would have been
691
- // tracked elsewhere, we're only interested in conda envs located in other parts of the file system created using the -p flag.
692
- // E.g conda_install_folder is `<home>/<conda install folder>`
693
- // Then all folders such as `<home>/<conda install folder>/envs/env1` can be ignored
694
- // As these would have been discovered in previous step.
695
- for ( key, env) in possible_conda_envs. clone ( ) . iter ( ) {
696
- if env
697
- . env_path
698
- . to_string_lossy ( )
699
- . contains ( conda_install_folder. to_str ( ) . unwrap ( ) )
700
- {
701
- continue ;
702
- }
703
- if was_conda_environment_created_by_specific_conda ( & env, conda_install_folder) {
704
- envs. push ( env. clone ( ) ) ;
705
- possible_conda_envs. remove ( key) ;
706
- }
749
+ if was_conda_environment_created_by_specific_conda ( & env, conda_install_folder) {
750
+ envs. push ( env. clone ( ) ) ;
751
+ possible_conda_envs. remove ( key) ;
707
752
}
753
+ }
708
754
709
- // Finally construct the PythonEnvironment objects
710
- envs. iter ( ) . for_each ( |env| {
711
- let exe = env. python_executable_path . clone ( ) ;
712
- let env = PythonEnvironment :: new (
713
- None ,
714
- Some ( env. name . clone ( ) ) ,
715
- exe. clone ( ) ,
716
- messaging:: PythonEnvironmentCategory :: Conda ,
717
- env. version . clone ( ) ,
718
- Some ( env. env_path . clone ( ) ) ,
719
- Some ( manager. clone ( ) ) ,
720
- get_activation_command ( env, & manager) ,
721
- ) ;
722
- if let Some ( key) = get_environment_key ( & env) {
723
- if !detected_envs. contains ( & key) {
724
- detected_envs. insert ( key) ;
725
- environments. push ( env) ;
726
- }
755
+ // Finally construct the PythonEnvironment objects
756
+ envs. iter ( ) . for_each ( |env| {
757
+ let exe = env. python_executable_path . clone ( ) ;
758
+ let arch = env. arch . clone ( ) ;
759
+ let mut env = PythonEnvironment :: new (
760
+ None ,
761
+ Some ( env. name . clone ( ) ) ,
762
+ exe. clone ( ) ,
763
+ messaging:: PythonEnvironmentCategory :: Conda ,
764
+ env. version . clone ( ) ,
765
+ Some ( env. env_path . clone ( ) ) ,
766
+ Some ( manager. clone ( ) ) ,
767
+ get_activation_command ( env, & manager) ,
768
+ ) ;
769
+ env. arch = arch;
770
+ if let Some ( key) = get_environment_key ( & env) {
771
+ if !detected_envs. contains ( & key) {
772
+ detected_envs. insert ( key) ;
773
+ environments. push ( env) ;
727
774
}
728
- } ) ;
729
-
730
- let key = get_environment_manager_key ( & manager) ;
731
- if !detected_managers. contains ( & key) {
732
- detected_managers. insert ( key) ;
733
- managers. push ( manager) ;
734
775
}
776
+ } ) ;
777
+
778
+ let key = get_environment_manager_key ( & manager) ;
779
+ if !detected_managers. contains ( & key) {
780
+ detected_managers. insert ( key) ;
781
+ managers. push ( manager) ;
735
782
}
736
783
}
737
784
@@ -973,6 +1020,9 @@ impl Conda<'_> {
973
1020
974
1021
impl CondaLocator for Conda < ' _ > {
975
1022
fn find_in ( & mut self , possible_conda_folder : & PathBuf ) -> Option < LocatorResult > {
1023
+ if !is_conda_install_location ( possible_conda_folder) {
1024
+ return None ;
1025
+ }
976
1026
let mut possible_conda_envs = get_known_conda_envs_from_various_locations ( self . environment ) ;
977
1027
self . filter_result ( get_conda_environments_in_specified_install_path (
978
1028
possible_conda_folder,
0 commit comments