@@ -11,7 +11,8 @@ use pet_core::{
1111 os_environment:: Environment ,
1212 python_environment:: { PythonEnvironment , PythonEnvironmentKind } ,
1313 reporter:: Reporter ,
14- Configuration , Locator , LocatorKind , LocatorResult ,
14+ Configuration , Locator , LocatorKind , LocatorResult , RefreshStatePersistence ,
15+ RefreshStateSyncScope ,
1516} ;
1617use pet_virtualenv:: is_virtualenv;
1718use regex:: Regex ;
@@ -137,7 +138,6 @@ impl Poetry {
137138 }
138139 }
139140 fn clear ( & self ) {
140- self . poetry_executable . write ( ) . unwrap ( ) . take ( ) ;
141141 self . search_result . write ( ) . unwrap ( ) . take ( ) ;
142142 }
143143 pub fn from ( environment : & dyn Environment ) -> Poetry {
@@ -152,6 +152,34 @@ impl Poetry {
152152 . clone_from ( & search_result) ;
153153 }
154154
155+ pub fn merge_search_result_from ( & self , source : & Poetry ) {
156+ let source_search_result = source. search_result . read ( ) . unwrap ( ) . clone ( ) ;
157+ let Some ( source_search_result) = source_search_result else {
158+ return ;
159+ } ;
160+
161+ let mut merged = self
162+ . search_result
163+ . read ( )
164+ . unwrap ( )
165+ . clone ( )
166+ . unwrap_or ( LocatorResult {
167+ managers : vec ! [ ] ,
168+ environments : vec ! [ ] ,
169+ } ) ;
170+ merged. managers . extend ( source_search_result. managers ) ;
171+ merged. managers . sort ( ) ;
172+ merged. managers . dedup ( ) ;
173+
174+ merged
175+ . environments
176+ . extend ( source_search_result. environments ) ;
177+ merged. environments . sort ( ) ;
178+ merged. environments . dedup ( ) ;
179+
180+ self . search_result . write ( ) . unwrap ( ) . replace ( merged) ;
181+ }
182+
155183 fn find_with_cache ( & self ) -> Option < LocatorResult > {
156184 // First check if we have cached results
157185 {
@@ -226,17 +254,39 @@ impl Locator for Poetry {
226254 fn get_kind ( & self ) -> LocatorKind {
227255 LocatorKind :: Poetry
228256 }
257+ fn refresh_state ( & self ) -> RefreshStatePersistence {
258+ RefreshStatePersistence :: SyncedDiscoveryState
259+ }
260+ fn sync_refresh_state_from ( & self , source : & dyn Locator , scope : & RefreshStateSyncScope ) {
261+ let source = source. as_any ( ) . downcast_ref :: < Poetry > ( ) . unwrap_or_else ( || {
262+ panic ! (
263+ "attempted to sync Poetry state from {:?}" ,
264+ source. get_kind( )
265+ )
266+ } ) ;
267+ match scope {
268+ RefreshStateSyncScope :: Full => self . sync_search_result_from ( source) ,
269+ RefreshStateSyncScope :: GlobalFiltered ( kind)
270+ if self . supported_categories ( ) . contains ( kind) =>
271+ {
272+ self . sync_search_result_from ( source)
273+ }
274+ RefreshStateSyncScope :: Workspace => self . merge_search_result_from ( source) ,
275+ RefreshStateSyncScope :: GlobalFiltered ( _) => { }
276+ }
277+ }
229278 fn configure ( & self , config : & Configuration ) {
279+ let mut ws_dirs = self . workspace_directories . write ( ) . unwrap ( ) ;
280+ ws_dirs. clear ( ) ;
230281 if let Some ( workspace_directories) = & config. workspace_directories {
231- let mut ws_dirs = self . workspace_directories . write ( ) . unwrap ( ) ;
232- ws_dirs. clear ( ) ;
233282 if !workspace_directories. is_empty ( ) {
234283 ws_dirs. extend ( workspace_directories. clone ( ) ) ;
235284 }
236285 }
237- if let Some ( exe) = & config. poetry_executable {
238- self . poetry_executable . write ( ) . unwrap ( ) . replace ( exe. clone ( ) ) ;
239- }
286+ self . poetry_executable
287+ . write ( )
288+ . unwrap ( )
289+ . clone_from ( & config. poetry_executable ) ;
240290 }
241291
242292 fn supported_categories ( & self ) -> Vec < PythonEnvironmentKind > {
@@ -349,4 +399,91 @@ mod tests {
349399 Some ( "fresh" )
350400 ) ;
351401 }
402+
403+ #[ test]
404+ fn test_workspace_scope_merges_search_results ( ) {
405+ let environment = EnvironmentApi :: new ( ) ;
406+ let target = Poetry :: from ( & environment) ;
407+ let source = Poetry :: from ( & environment) ;
408+
409+ target
410+ . search_result
411+ . write ( )
412+ . unwrap ( )
413+ . replace ( LocatorResult {
414+ managers : vec ! [ ] ,
415+ environments : vec ! [ PythonEnvironment {
416+ name: Some ( "existing" . to_string( ) ) ,
417+ kind: Some ( PythonEnvironmentKind :: Poetry ) ,
418+ ..Default :: default ( )
419+ } ] ,
420+ } ) ;
421+
422+ source
423+ . search_result
424+ . write ( )
425+ . unwrap ( )
426+ . replace ( LocatorResult {
427+ managers : vec ! [ ] ,
428+ environments : vec ! [ PythonEnvironment {
429+ name: Some ( "workspace" . to_string( ) ) ,
430+ kind: Some ( PythonEnvironmentKind :: Poetry ) ,
431+ ..Default :: default ( )
432+ } ] ,
433+ } ) ;
434+
435+ target. sync_refresh_state_from ( & source, & RefreshStateSyncScope :: Workspace ) ;
436+
437+ let result = target. search_result . read ( ) . unwrap ( ) . clone ( ) . unwrap ( ) ;
438+ let mut names = result
439+ . environments
440+ . iter ( )
441+ . map ( |environment| environment. name . clone ( ) . unwrap ( ) )
442+ . collect :: < Vec < String > > ( ) ;
443+ names. sort ( ) ;
444+
445+ assert_eq ! ( names, vec![ "existing" . to_string( ) , "workspace" . to_string( ) ] ) ;
446+ }
447+
448+ #[ test]
449+ fn test_clear_preserves_configured_poetry_executable ( ) {
450+ let environment = EnvironmentApi :: new ( ) ;
451+ let poetry = Poetry :: from ( & environment) ;
452+ let configured = PathBuf :: from ( "/configured/poetry" ) ;
453+
454+ poetry. configure ( & Configuration {
455+ poetry_executable : Some ( configured. clone ( ) ) ,
456+ ..Default :: default ( )
457+ } ) ;
458+ poetry
459+ . search_result
460+ . write ( )
461+ . unwrap ( )
462+ . replace ( LocatorResult {
463+ managers : vec ! [ ] ,
464+ environments : vec ! [ ] ,
465+ } ) ;
466+
467+ poetry. clear ( ) ;
468+
469+ assert_eq ! (
470+ poetry. poetry_executable. read( ) . unwrap( ) . clone( ) ,
471+ Some ( configured)
472+ ) ;
473+ assert ! ( poetry. search_result. read( ) . unwrap( ) . is_none( ) ) ;
474+ }
475+
476+ #[ test]
477+ fn test_configure_clears_poetry_executable_when_unset ( ) {
478+ let environment = EnvironmentApi :: new ( ) ;
479+ let poetry = Poetry :: from ( & environment) ;
480+
481+ poetry. configure ( & Configuration {
482+ poetry_executable : Some ( PathBuf :: from ( "/configured/poetry" ) ) ,
483+ ..Default :: default ( )
484+ } ) ;
485+ poetry. configure ( & Configuration :: default ( ) ) ;
486+
487+ assert ! ( poetry. poetry_executable. read( ) . unwrap( ) . is_none( ) ) ;
488+ }
352489}
0 commit comments