@@ -192,24 +192,9 @@ fn get_conda_env_name(
192192 // E.g. conda env is = <conda install>/envs/<env name>
193193 // Then we can use `<conda install>/bin/conda activate -n <env name>`
194194 //
195- // Handle three cases:
196- // 1. conda_dir known, prefix is under it → name-based activation works, return folder name
197- // 2. conda_dir known, prefix NOT under it → external env, check history to distinguish -n vs -p
198- // 3. conda_dir unknown → can't do name-based activation without knowing conda installation,
199- // return None so activation uses full path (fixes issue #329 for path-based envs)
200- match conda_dir {
201- Some ( dir) => {
202- if !prefix. starts_with ( dir) {
203- // External environment - check history to see if created with -n or -p
204- name = get_conda_env_name_from_history_file ( env_path, prefix) ;
205- }
206- // else: env is under conda_dir/envs/, name-based activation works
207- }
208- None => {
209- // No known conda installation directory
210- // Name-based activation requires knowing which conda to use, so return None
211- // This ensures path-based activation is used (correct for --prefix envs too)
212- name = None ;
195+ if let Some ( conda_dir) = conda_dir {
196+ if !prefix. starts_with ( conda_dir) {
197+ name = get_conda_env_name_from_history_file ( env_path, prefix) ;
213198 }
214199 }
215200
@@ -221,47 +206,36 @@ fn get_conda_env_name(
221206 * And example is `# cmd: <conda install directory>\Scripts\conda-script.py create -n sample``
222207 * And example is `# cmd: conda create -n sample``
223208 *
224- * This function returns the name of the conda environment based on how it was created:
225- * - If created with --name/-n (name-based): returns the folder name (can use named activation)
226- * - Otherwise: returns None (activation must use full path for safety)
227- *
228- * This is used for external environments (not under conda_dir). We only return a name
229- * if we can positively confirm it was created with -n/--name, because that's the only
230- * case where name-based activation works reliably for external environments.
209+ * This function returns the name of the conda environment.
231210 */
232211fn get_conda_env_name_from_history_file ( env_path : & Path , prefix : & Path ) -> Option < String > {
233212 let name = prefix
234213 . file_name ( )
235214 . map ( |name| name. to_str ( ) . unwrap_or_default ( ) . to_string ( ) ) ;
236215
237- if let Some ( ref name) = name {
216+ if let Some ( name) = name {
238217 if let Some ( line) = get_conda_creation_line_from_history ( env_path) {
239218 // Sample lines
240219 // # cmd: <conda install directory>\Scripts\conda-script.py create -n samlpe1
241220 // # cmd: <conda install directory>\Scripts\conda-script.py create -p <full path>
242221 // # cmd: /Users/donjayamanne/miniconda3/bin/conda create -n conda1
243- // # cmd: /usr/bin/conda create --prefix ./prefix-envs/.conda1 python=3.12 -y
244-
245- // Only return a name if we can confirm it was created with -n/--name
246- // This matches the original behavior where we checked for the exact name in the cmd
247- if is_name_based_conda_env ( & line) {
248- return Some ( name. clone ( ) ) ;
222+ if is_conda_env_name_in_cmd ( line, & name) {
223+ return Some ( name) ;
249224 }
250225 }
251226 }
252- // If we can't confirm name-based creation, return None for safe path-based activation
253227 None
254228}
255229
256- /// Check if the conda create command used -- name or -n (name-based environment)
257- fn is_name_based_conda_env ( cmd_line : & str ) -> bool {
258- // Look for " -n " or " --name " after "create"
259- let lower = cmd_line . to_lowercase ( ) ;
260- if let Some ( create_idx ) = lower . find ( " create " ) {
261- let after_create = & lower [ create_idx.. ] ;
262- return after_create . contains ( " -n " ) || after_create . contains ( " --name " ) ;
263- }
264- false
230+ fn is_conda_env_name_in_cmd ( cmd_line : String , name : & str ) -> bool {
231+ // Sample lines
232+ // # cmd: <conda install directory>\Scripts\conda-script.py create -n samlpe1
233+ // # cmd: <conda install directory>\Scripts\conda-script.py create -p <full path>
234+ // # cmd: /Users/donjayamanne/miniconda3/bin/conda create -n conda1
235+ // # cmd_line: "# cmd: /usr/bin/conda create -p ./prefix-envs/.conda1 python=3.12 -y"
236+ // Look for " -n <name>" in the command line
237+ cmd_line . contains ( format ! ( "-n {name}" ) . as_str ( ) )
238+ || cmd_line . contains ( format ! ( "--name {name}" ) . as_str ( ) )
265239}
266240
267241fn get_conda_dir_from_cmd ( cmd_line : String ) -> Option < PathBuf > {
@@ -389,54 +363,27 @@ mod tests {
389363 }
390364
391365 #[ test]
392- fn test_is_name_based_conda_env ( ) {
393- // Name-based environments use --name or -n
394- assert ! ( is_name_based_conda_env(
395- "# cmd: /usr/bin/conda create -n myenv python=3.12"
396- ) ) ;
397- assert ! ( is_name_based_conda_env(
398- "# cmd: /usr/bin/conda create --name myenv python=3.12"
399- ) ) ;
400- assert ! ( is_name_based_conda_env(
401- "# cmd: C:\\ Users\\ user\\ miniconda3\\ Scripts\\ conda.exe create -n myenv python=3.9"
402- ) ) ;
403-
404- // Path-based environments use --prefix or -p
405- assert ! ( !is_name_based_conda_env(
406- "# cmd: /usr/bin/conda create --prefix .conda python=3.12"
407- ) ) ;
408- assert ! ( !is_name_based_conda_env(
409- "# cmd: /usr/bin/conda create -p .conda python=3.12"
410- ) ) ;
411- }
366+ #[ cfg( unix) ]
367+ fn verify_conda_env_name ( ) {
368+ let line = "# cmd: /Users/donjayamanne/.pyenv/versions/mambaforge-22.11.1-3/lib/python3.10/site-packages/conda/__main__.py create --yes --name .conda python=3.12" ;
369+ assert ! ( is_conda_env_name_in_cmd( line. to_string( ) , ".conda" ) ) ;
412370
413- /// Test that when conda_dir is unknown, we return None for name.
414- /// This is correct because name-based activation requires knowing which conda installation
415- /// to use. Without conda_dir, activation must use the full path.
416- /// This also fixes issue #329 where path-based envs incorrectly got a name.
417- #[ test]
418- fn env_returns_none_name_when_conda_dir_unknown ( ) {
419- // Create a temp directory structure simulating a conda env
420- let temp_dir = std:: env:: temp_dir ( ) . join ( "pet_test_conda_dir_unknown" ) ;
421- let conda_meta_dir = temp_dir. join ( "myenv" ) . join ( "conda-meta" ) ;
422- std:: fs:: create_dir_all ( & conda_meta_dir) . unwrap ( ) ;
371+ let mut line = "# cmd: /Users/donjayamanne/.pyenv/versions/mambaforge-22.11.1-3/lib/python3.10/site-packages/conda/__main__.py create --yes -n .conda python=3.12" ;
372+ assert ! ( is_conda_env_name_in_cmd( line. to_string( ) , ".conda" ) ) ;
423373
424- let env_path = temp_dir. join ( "myenv" ) ;
374+ line = "# cmd: /Users/donjayamanne/.pyenv/versions/mambaforge-22.11.1-3/lib/python3.10/site-packages/conda/__main__.py create --yes --name .conda python=3.12" ;
375+ assert ! ( !is_conda_env_name_in_cmd( line. to_string( ) , "base" ) ) ;
425376
426- // When conda_dir is None, name should be None (can't do named activation)
427- let name = get_conda_env_name ( & env_path, & env_path, & None ) ;
428- assert ! (
429- name. is_none( ) ,
430- "When conda_dir is unknown, name should be None for path-based activation, got {:?}" ,
431- name
432- ) ;
377+ line = "# cmd: /Users/donjayamanne/.pyenv/versions/mambaforge-22.11.1-3/lib/python3.10/site-packages/conda/__main__.py create --yes -p .conda python=3.12" ;
378+ assert ! ( !is_conda_env_name_in_cmd( line. to_string( ) , "base" ) ) ;
433379
434- // Cleanup
435- let _ = std :: fs :: remove_dir_all ( & temp_dir ) ;
380+ line = "# cmd: /Users/donjayamanne/.pyenv/versions/mambaforge-22.11.1-3/lib/python3.10/site-packages/conda/__main__.py create --yes -p .conda python=3.12" ;
381+ assert ! ( !is_conda_env_name_in_cmd ( line . to_string ( ) , ".conda" ) ) ;
436382 }
437383
438384 /// Test that external environments (not under conda_dir) created with --prefix
439385 /// return None for name, so activation uses path instead of name.
386+ /// This is the fix for issue #329.
440387 #[ test]
441388 fn external_path_based_env_returns_none_name ( ) {
442389 // Create a temp directory simulating an external path-based conda env
@@ -468,15 +415,16 @@ mod tests {
468415 }
469416
470417 /// Test that external environments (not under conda_dir) created with -n
471- /// return the name for name-based activation.
418+ /// return the name for name-based activation, but only if the folder name matches .
472419 #[ test]
473420 fn external_name_based_env_returns_name ( ) {
474421 // Create a temp directory simulating an external name-based conda env
475422 let temp_dir = std:: env:: temp_dir ( ) . join ( "pet_test_external_name_env" ) ;
476423 let conda_meta_dir = temp_dir. join ( "myenv" ) . join ( "conda-meta" ) ;
477424 std:: fs:: create_dir_all ( & conda_meta_dir) . unwrap ( ) ;
478425
479- // Write a history file showing the env was created with -n (name-based)
426+ // Write a history file showing the env was created with -n myenv (name-based)
427+ // Note: the folder name "myenv" matches the -n argument "myenv"
480428 let history_file = conda_meta_dir. join ( "history" ) ;
481429 std:: fs:: write (
482430 & history_file,
@@ -492,7 +440,7 @@ mod tests {
492440 assert_eq ! (
493441 name,
494442 Some ( "myenv" . to_string( ) ) ,
495- "Name-based external env should return the name"
443+ "Name-based external env should return the name when folder matches "
496444 ) ;
497445
498446 // Cleanup
@@ -547,30 +495,30 @@ mod tests {
547495 let _ = std:: fs:: remove_dir_all ( & temp_dir) ;
548496 }
549497
550- /// Test that external env with history but no -n/-p flags returns None.
551- /// Edge case: history exists but create command doesn't clearly indicate name or path based .
498+ /// Test that external env with history but folder name doesn't match -n argument returns None.
499+ /// This prevents wrong activation when env was moved/renamed after creation .
552500 #[ test]
553- fn external_env_with_ambiguous_history_returns_none_name ( ) {
501+ fn external_env_with_mismatched_name_returns_none ( ) {
554502 // Create a temp directory simulating an external conda env
555- let temp_dir = std:: env:: temp_dir ( ) . join ( "pet_test_external_ambiguous" ) ;
556- let conda_meta_dir = temp_dir. join ( "myenv" ) . join ( "conda-meta" ) ;
503+ let temp_dir = std:: env:: temp_dir ( ) . join ( "pet_test_external_mismatch" ) ;
504+ // Folder is named "renamed_env" but was created with -n "original_name"
505+ let conda_meta_dir = temp_dir. join ( "renamed_env" ) . join ( "conda-meta" ) ;
557506 std:: fs:: create_dir_all ( & conda_meta_dir) . unwrap ( ) ;
558507
559- // Write a history file without -n or -p (edge case, maybe cloned env)
560508 let history_file = conda_meta_dir. join ( "history" ) ;
561509 std:: fs:: write (
562510 & history_file,
563- "# some other history content \n # no create command here \n " ,
511+ "# cmd: /usr/bin/conda create -n original_name python=3.12 \n " ,
564512 )
565513 . unwrap ( ) ;
566514
567- let env_path = temp_dir. join ( "myenv " ) ;
515+ let env_path = temp_dir. join ( "renamed_env " ) ;
568516 let conda_dir = Some ( std:: path:: PathBuf :: from ( "/some/other/conda" ) ) ;
569517
570518 let name = get_conda_env_name ( & env_path, & env_path, & conda_dir) ;
571519 assert ! (
572520 name. is_none( ) ,
573- "External env with ambiguous history should return None, got {:?}" ,
521+ "External env with mismatched name should return None, got {:?}" ,
574522 name
575523 ) ;
576524
0 commit comments