@@ -172,24 +172,27 @@ impl Conda {
172172 where
173173 F : FnOnce ( ) -> Option < CondaEnvironmentDetails > ,
174174 {
175+ let cache_key = norm_case ( path) ;
175176 let fingerprint_before = CondaEnvironmentFingerprint :: from_prefix ( path) ;
176177 if let Some ( fingerprint) = & fingerprint_before {
177178 if let Some ( cached) = self
178179 . environment_info_cache
179180 . read ( )
180181 . expect ( "conda environment info cache lock poisoned" )
181- . get ( path )
182+ . get ( & cache_key )
182183 . filter ( |cached| & cached. fingerprint == fingerprint)
183184 {
184- return Some ( cached. details . clone ( ) ) ;
185+ let mut details = cached. details . clone ( ) ;
186+ details. environment . prefix = Some ( path. to_path_buf ( ) ) ;
187+ return Some ( details) ;
185188 }
186189 }
187190
188191 let Some ( details) = load ( ) else {
189192 self . environment_info_cache
190193 . write ( )
191194 . expect ( "conda environment info cache lock poisoned" )
192- . remove ( path ) ;
195+ . remove ( & cache_key ) ;
193196 return None ;
194197 } ;
195198 let fingerprint_after = CondaEnvironmentFingerprint :: from_prefix ( path) ;
@@ -199,14 +202,14 @@ impl Conda {
199202 . expect ( "conda environment info cache lock poisoned" ) ;
200203 if fingerprint_before. is_some ( ) && fingerprint_before == fingerprint_after {
201204 cache. insert (
202- path . to_path_buf ( ) ,
205+ cache_key ,
203206 CachedCondaEnvironment {
204207 fingerprint : fingerprint_after. expect ( "fingerprint checked as present" ) ,
205208 details : details. clone ( ) ,
206209 } ,
207210 ) ;
208211 } else {
209- cache. remove ( path ) ;
212+ cache. remove ( & cache_key ) ;
210213 }
211214
212215 Some ( details)
@@ -458,7 +461,8 @@ impl Locator for Conda {
458461 }
459462
460463 let possible_conda_envs = get_conda_environment_paths ( & env_vars, & executable) ;
461- let active_prefixes: HashSet < PathBuf > = possible_conda_envs. iter ( ) . cloned ( ) . collect ( ) ;
464+ let active_prefixes: HashSet < PathBuf > =
465+ possible_conda_envs. iter ( ) . map ( norm_case) . collect ( ) ;
462466 for path in possible_conda_envs {
463467 s. spawn ( move || {
464468 let details = self . get_environment_details ( & path) ?;
@@ -594,4 +598,41 @@ mod tests {
594598
595599 fs:: remove_dir_all ( prefix) . unwrap ( ) ;
596600 }
601+
602+ #[ cfg( windows) ]
603+ #[ test]
604+ fn environment_info_cache_normalizes_windows_keys ( ) {
605+ static NEXT_ID : AtomicUsize = AtomicUsize :: new ( 0 ) ;
606+
607+ let prefix = std:: env:: temp_dir ( ) . join ( format ! (
608+ "pet-conda-environment-cache-case-{}-{}" ,
609+ std:: process:: id( ) ,
610+ NEXT_ID . fetch_add( 1 , Ordering :: Relaxed )
611+ ) ) ;
612+ let conda_meta = prefix. join ( "conda-meta" ) ;
613+ fs:: create_dir_all ( & conda_meta) . unwrap ( ) ;
614+ fs:: write ( conda_meta. join ( "history" ) , "history" ) . unwrap ( ) ;
615+
616+ let alternate_separators = PathBuf :: from ( prefix. to_string_lossy ( ) . replace ( '\\' , "/" ) ) ;
617+ let environment = EnvironmentApi :: new ( ) ;
618+ let locator = Conda :: from ( & environment) ;
619+ let loads = AtomicUsize :: new ( 0 ) ;
620+
621+ locator
622+ . get_or_load_environment_details ( & prefix, || {
623+ loads. fetch_add ( 1 , Ordering :: Relaxed ) ;
624+ Some ( test_details ( & prefix, 1 ) )
625+ } )
626+ . unwrap ( ) ;
627+ let cached = locator
628+ . get_or_load_environment_details ( & alternate_separators, || {
629+ panic ! ( "equivalent Windows paths should reuse the cache" )
630+ } )
631+ . unwrap ( ) ;
632+
633+ assert_eq ! ( loads. load( Ordering :: Relaxed ) , 1 ) ;
634+ assert_eq ! ( cached. environment. prefix, Some ( alternate_separators) ) ;
635+
636+ fs:: remove_dir_all ( prefix) . unwrap ( ) ;
637+ }
597638}
0 commit comments