Repository: conda/conda

Issue:
conda init does nothing for xonsh
<!--
Hi!  Read this; it's important.

This is an issue tracker for conda -- the package manager.  File feature requests
for conda here, as well as bug reports about something conda has messed up.

If your issue is a bug report for:
  * a specific conda package from Anaconda ('defaults' channel):
      ==> file at https://github.com/ContinuumIO/anaconda-issues
  * a specific conda package from conda-forge:
      ==> file at the corresponding feedstock under https://github.com/conda-forge
  * repo.anaconda.com access and service:
      ==> file at https://github.com/ContinuumIO/anaconda-issues
  * anaconda.org access and service:
      ==> file at https://anaconda.org/contact/report
  * commands under 'conda build':
      ==> file at https://github.com/conda/conda-build
  * commands under 'conda env':
      ==> please file it here!
  * all other conda commands that start with 'conda':
      ==> please file it here!

If you continue on,

   **please include all requested information below.**

If a maintainer determines the information is required to understand 
your issue, and if it is not provided, your issue may be
closed automatically.

-->

## Current Behavior
<!-- What actually happens?
     If you want to include console output, please use "Steps to Reproduce" below. -->

I'm using xonsh on ubuntu. I first tried to `conda activate base` (or some other environment) in but then I get an error and suggestion to run `conda init`. Next I run `conda init` without error, but no change is reported and I cannot get `conda activate base` to work.


### Steps to Reproduce
<!-- If the current behavior is a bug, please provide specific, minimal steps to independently reproduce.
     Include the exact conda commands that reproduce the issue and their output between the ticks below. -->

```xonsh
λ which conda
/home/pletnes/miniconda3/bin/conda
λ conda activate base
CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'. 
To initialize your shell, run
$ conda init
Currently supported shells are:
  - bash
  - fish
  - tcsh
  - xonsh
  - zsh
  - powershell
See 'conda init --help' for more information and options.
IMPORTANT: You may need to close and restart your shell after running 'conda init'.

λ conda init xonsh
no change     /home/pletnes/miniconda3/condabin/conda
no change     /home/pletnes/miniconda3/bin/conda
no change     /home/pletnes/miniconda3/bin/conda-env
no change     /home/pletnes/miniconda3/bin/activate
no change     /home/pletnes/miniconda3/bin/deactivate
no change     /home/pletnes/miniconda3/etc/profile.d/conda.sh
no change     /home/pletnes/miniconda3/etc/fish/conf.d/conda.fish
no change     /home/pletnes/miniconda3/shell/condabin/Conda.psm1
no change     /home/pletnes/miniconda3/shell/condabin/conda-hook.ps1
no change     /home/pletnes/miniconda3/lib/python3.7/site-packages/xonsh/conda.xsh
no change     /home/pletnes/miniconda3/etc/profile.d/conda.csh

λ conda activate base
xonsh: For full traceback set: $XONSH_SHOW_TRACEBACK = True
ModuleNotFoundError: No module named 'conda'
```

## Expected Behavior
<!-- What do you think should happen? -->

I hoped `conda init xonsh` would create the necessary PATH (or similar) settings in `~/.xonshrc` so that `conda activate <some-env>` works flawlessly.

## Environment Information
<details open><summary><code>`conda info`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda info' -->

```
     active environment : base
    active env location : /home/pletnes/miniconda3
            shell level : 1
       user config file : /home/pletnes/.condarc
 populated config files :
          conda version : 4.6.3
    conda-build version : not installed
         python version : 3.7.1.final.0
       base environment : /home/pletnes/miniconda3  (writable)
           channel URLs : https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/free/linux-64
                          https://repo.anaconda.com/pkgs/free/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
          package cache : /home/pletnes/miniconda3/pkgs
                          /home/pletnes/.conda/pkgs
       envs directories : /home/pletnes/miniconda3/envs
                          /home/pletnes/.conda/envs
               platform : linux-64
             user-agent : conda/4.6.3 requests/2.21.0 CPython/3.7.1 Linux/4.4.0-142-generic ubuntu/16.04.5 glibc/2.23
                UID:GID : 3506278:17
             netrc file : None
           offline mode : False
```

And xonsh version:
```
$ xonsh --version    
xonsh/0.8.10
```
</p></details>


<details open><summary><code>`conda config --show-sources`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda config --show-sources' -->

```
$ conda config --show-sources
# this returned blank?
```
</p></details>


<details><summary><code>`conda list --show-channel-urls`</code></summary><p>
<!-- between the ticks below, paste the output of 'conda list --show-channel-urls' -->

```
$ conda list --show-channel-urls
# packages in environment at /home/paul/miniconda3:
#
# Name                    Version                   Build  Channel
asn1crypto                0.24.0                   py37_0    defaults
ca-certificates           2019.1.23                     0    defaults
certifi                   2018.11.29               py37_0    defaults
cffi                      1.11.5           py37he75722e_1    defaults
chardet                   3.0.4                    py37_1    defaults
conda                     4.6.3                    py37_0    defaults
conda-env                 2.6.0                         1    defaults
cryptography              2.4.2            py37h1ba5d50_0    defaults
idna                      2.8                      py37_0    defaults
libedit                   3.1.20170329         h6b74fdf_2    defaults
libffi                    3.2.1                hd88cf55_4    defaults
libgcc-ng                 8.2.0                hdf63c60_1    defaults
libstdcxx-ng              8.2.0                hdf63c60_1    defaults
ncurses                   6.1                  he6710b0_1    defaults
openssl                   1.1.1a               h7b6447c_0    defaults
pip                       18.1                     py37_0    defaults
pycosat                   0.6.3            py37h14c3975_0    defaults
pycparser                 2.19                     py37_0    defaults
pyopenssl                 18.0.0                   py37_0    defaults
pysocks                   1.6.8                    py37_0    defaults
python                    3.7.1                h0371630_7    defaults
readline                  7.0                  h7b6447c_5    defaults
requests                  2.21.0                   py37_0    defaults
ruamel_yaml               0.15.46          py37h14c3975_0    defaults
setuptools                40.6.3                   py37_0    defaults
six                       1.12.0                   py37_0    defaults
sqlite                    3.26.0               h7b6447c_0    defaults
tk                        8.6.8                hbc83047_0    defaults
urllib3                   1.24.1                   py37_0    defaults
wheel                     0.32.3                   py37_0    defaults
xz                        5.2.4                h14c3975_4    defaults
yaml                      0.1.7                had09818_2    defaults
zlib                      1.2.11               h7b6447c_3    defaults
```
</p></details>



Discussion:

Resolving patch:
diff --git a/conda/activate.py b/conda/activate.py
--- a/conda/activate.py
+++ b/conda/activate.py
@@ -838,7 +838,7 @@ def __init__(self, arguments=None):
         self.sep = '/'
         self.path_conversion = native_path_to_unix
         self.script_extension = '.xsh'
-        self.tempfile_extension = '.xsh'
+        self.tempfile_extension = None
         self.command_join = '\n'
 
         self.unset_var_tmpl = 'del $%s'
@@ -851,7 +851,7 @@ def __init__(self, arguments=None):
         super(XonshActivator, self).__init__(arguments)
 
     def _hook_preamble(self):
-        return 'CONDA_EXE = "%s"' % context.conda_exe
+        return '$CONDA_EXE = "%s"' % context.conda_exe
 
 
 class CmdExeActivator(_Activator):
diff --git a/conda/core/initialize.py b/conda/core/initialize.py
--- a/conda/core/initialize.py
+++ b/conda/core/initialize.py
@@ -414,7 +414,7 @@ def make_install_plan(conda_prefix):
         plan.append({
             'function': install_conda_xsh.__name__,
             'kwargs': {
-                'target_path': join(site_packages_dir, 'xonsh', 'conda.xsh'),
+                'target_path': join(site_packages_dir, 'xontrib', 'conda.xsh'),
                 'conda_prefix': conda_prefix,
             },
         })
@@ -493,6 +493,32 @@ def make_initialize_plan(conda_prefix, shells, for_user, for_system, anaconda_pr
                 },
             })
 
+    if 'xonsh' in shells:
+        if for_user:
+            config_xonsh_path = expand(join('~', '.xonshrc'))
+            plan.append({
+                'function': init_xonsh_user.__name__,
+                'kwargs': {
+                    'target_path': config_xonsh_path,
+                    'conda_prefix': conda_prefix,
+                    'reverse': reverse,
+                },
+            })
+
+        if for_system:
+            if on_win:
+                config_xonsh_path = expand(join('%ALLUSERSPROFILE%', 'xonsh', 'xonshrc'))
+            else:
+                config_xonsh_path = '/etc/xonshrc'
+            plan.append({
+                'function': init_xonsh_user.__name__,
+                'kwargs': {
+                    'target_path': config_xonsh_path,
+                    'conda_prefix': conda_prefix,
+                    'reverse': reverse,
+                },
+            })
+
     if 'tcsh' in shells and for_user:
         tcshrc_path = expand(join('~', '.tcshrc'))
         plan.append({
@@ -1066,6 +1092,91 @@ def init_fish_user(target_path, conda_prefix, reverse):
         return Result.NO_CHANGE
 
 
+def _config_xonsh_content(conda_prefix):
+    if on_win:
+        from ..activate import native_path_to_unix
+        conda_exe = native_path_to_unix(join(conda_prefix, 'Scripts', 'conda.exe'))
+    else:
+        conda_exe = join(conda_prefix, 'bin', 'conda')
+    conda_initialize_content = dals("""
+    # >>> conda initialize >>>
+    # !! Contents within this block are managed by 'conda init' !!
+    import sys as _sys
+    from types import ModuleType as _ModuleType
+    _mod = _ModuleType("xontrib.conda",
+                       "Autogenerated from $({conda_exe} shell.xonsh hook)")
+    __xonsh__.execer.exec($("{conda_exe}" "shell.xonsh" "hook"),
+                          glbs=_mod.__dict__,
+                          filename="$({conda_exe} shell.xonsh hook)")
+    _sys.modules["xontrib.conda"] = _mod
+    del _sys, _mod, _ModuleType
+    # <<< conda initialize <<<
+    """).format(conda_exe=conda_exe)
+    return conda_initialize_content
+
+
+def init_xonsh_user(target_path, conda_prefix, reverse):
+    # target_path: ~/.xonshrc
+    user_rc_path = target_path
+
+    try:
+        with open(user_rc_path) as fh:
+            rc_content = fh.read()
+    except FileNotFoundError:
+        rc_content = ''
+    except:
+        raise
+
+    rc_original_content = rc_content
+
+    conda_init_comment = "# commented out by conda initialize"
+    conda_initialize_content = _config_xonsh_content(conda_prefix)
+    if reverse:
+        # uncomment any lines that were commented by prior conda init run
+        rc_content = re.sub(
+            r"#\s(.*?)\s*{}".format(conda_init_comment),
+            r"\1",
+            rc_content,
+            flags=re.MULTILINE,
+        )
+
+        # remove any conda init sections added
+        rc_content = re.sub(
+            r"^\s*" + CONDA_INITIALIZE_RE_BLOCK,
+            "",
+            rc_content,
+            flags=re.DOTALL | re.MULTILINE
+        )
+    else:
+        replace_str = "__CONDA_REPLACE_ME_123__"
+        rc_content = re.sub(
+            CONDA_INITIALIZE_RE_BLOCK,
+            replace_str,
+            rc_content,
+            flags=re.MULTILINE,
+        )
+        # TODO: maybe remove all but last of replace_str, if there's more than one occurrence
+        rc_content = rc_content.replace(replace_str, conda_initialize_content)
+
+        if "# >>> conda initialize >>>" not in rc_content:
+            rc_content += '\n{0}\n'.format(conda_initialize_content)
+
+    if rc_content != rc_original_content:
+        if context.verbosity:
+            print('\n')
+            print(target_path)
+            print(make_diff(rc_original_content, rc_content))
+        if not context.dry_run:
+            # Make the directory if needed.
+            if not exists(dirname(user_rc_path)):
+                mkdir_p(dirname(user_rc_path))
+            with open(user_rc_path, 'w') as fh:
+                fh.write(rc_content)
+        return Result.MODIFIED
+    else:
+        return Result.NO_CHANGE
+
+
 def _bashrc_content(conda_prefix, shell):
     if on_win:
         from ..activate import native_path_to_unix


Test patch:
diff --git a/tests/core/test_initialize.py b/tests/core/test_initialize.py
--- a/tests/core/test_initialize.py
+++ b/tests/core/test_initialize.py
@@ -209,7 +209,7 @@ def test_make_install_plan(self):
                         "function": "install_conda_xsh",
                         "kwargs": {
                             "conda_prefix": "/darwin",
-                            "target_path": "/darwin/lib/python2.6/site-packages\\xonsh\\conda.xsh"
+                            "target_path": "/darwin/lib/python2.6/site-packages\\xontrib\\conda.xsh"
                         }
                     },
                     {
@@ -295,7 +295,7 @@ def test_make_install_plan(self):
                         "function": "install_conda_xsh",
                         "kwargs": {
                             "conda_prefix": "/darwin",
-                            "target_path": "/darwin/lib/python2.6/site-packages/xonsh/conda.xsh"
+                            "target_path": "/darwin/lib/python2.6/site-packages/xontrib/conda.xsh"
                         }
                     },
                     {
@@ -344,7 +344,7 @@ def test_make_entry_point(self):
                 assert ep_contents == dals("""
                 # -*- coding: utf-8 -*-
                 import sys
-    
+
                 if __name__ == '__main__':
                     from conda.entry.point import run
                     sys.exit(run())
@@ -354,7 +354,7 @@ def test_make_entry_point(self):
                 #!%s/bin/python
                 # -*- coding: utf-8 -*-
                 import sys
-    
+
                 if __name__ == '__main__':
                     from conda.entry.point import run
                     sys.exit(run())
@@ -444,9 +444,9 @@ def test_install_conda_xsh(self):
 
             first_line, remainder = created_file_contents.split('\n', 1)
             if on_win:
-                assert first_line == 'CONDA_EXE = "%s"' % join(conda_prefix, 'Scripts', 'conda.exe')
+                assert first_line == '$CONDA_EXE = "%s"' % join(conda_prefix, 'Scripts', 'conda.exe')
             else:
-                assert first_line == 'CONDA_EXE = "%s"' % join(conda_prefix, 'bin', 'conda')
+                assert first_line == '$CONDA_EXE = "%s"' % join(conda_prefix, 'bin', 'conda')
 
             with open(join(CONDA_PACKAGE_ROOT, 'shell', 'conda.xsh')) as fh:
                 original_contents = fh.read()
@@ -708,19 +708,19 @@ def test_init_sh_user_unix(self):
             export PATH="/some/other/conda/bin:$PATH"
             export PATH="%(prefix)s/bin:$PATH"
               export PATH="%(prefix)s/bin:$PATH"
-              
+
             # >>> conda initialize >>>
             __conda_setup="$('%(prefix)s/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
             if [ $? -eq 0 ]; then
             fi
             unset __conda_setup
             # <<< conda initialize <<<
-            
+
             . etc/profile.d/conda.sh
             . etc/profile.d/coda.sh
             . /somewhere/etc/profile.d/conda.sh
             source /etc/profile.d/conda.sh
-            
+
             \t source %(prefix)s/etc/profile.d/conda.sh
             """) % {
                 'prefix': win_path_backout(abspath(conda_temp_prefix)),
@@ -738,7 +738,7 @@ def test_init_sh_user_unix(self):
             export PATH="/some/other/conda/bin:$PATH"
             # export PATH="%(prefix)s/bin:$PATH"  # commented out by conda initialize
             # export PATH="%(prefix)s/bin:$PATH"  # commented out by conda initialize
-            
+
             # >>> conda initialize >>>
             # !! Contents within this block are managed by 'conda init' !!
             __conda_setup="$('%(prefix)s/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
@@ -753,12 +753,12 @@ def test_init_sh_user_unix(self):
             fi
             unset __conda_setup
             # <<< conda initialize <<<
-            
+
             # . etc/profile.d/conda.sh  # commented out by conda initialize
             . etc/profile.d/coda.sh
             # . /somewhere/etc/profile.d/conda.sh  # commented out by conda initialize
             # source /etc/profile.d/conda.sh  # commented out by conda initialize
-            
+
             # source %(prefix)s/etc/profile.d/conda.sh  # commented out by conda initialize
             """) % {
                 'prefix': win_path_backout(abspath(conda_temp_prefix)),
@@ -770,12 +770,12 @@ def test_init_sh_user_unix(self):
             export PATH="/some/other/conda/bin:$PATH"
             export PATH="%(prefix)s/bin:$PATH"
             export PATH="%(prefix)s/bin:$PATH"
-            
+
             . etc/profile.d/conda.sh
             . etc/profile.d/coda.sh
             . /somewhere/etc/profile.d/conda.sh
             source /etc/profile.d/conda.sh
-            
+
             source %(prefix)s/etc/profile.d/conda.sh
             """) % {
                 'prefix': win_path_backout(abspath(conda_temp_prefix)),
@@ -850,12 +850,12 @@ def test_init_sh_user_windows(self):
             expected_reversed_content = dals("""
             source /c/conda/Scripts/activate root
             . $(cygpath 'c:\\conda\\Scripts\\activate') root
-            
+
             . etc/profile.d/conda.sh
             . etc/profile.d/coda.sh
             . /somewhere/etc/profile.d/conda.sh
             source /etc/profile.d/conda.sh
-            
+
             source %(prefix)s/etc/profile.d/conda.sh
             """) % {
                 'prefix': win_path_ok(abspath(conda_prefix)),
diff --git a/tests/test_activate.py b/tests/test_activate.py
--- a/tests/test_activate.py
+++ b/tests/test_activate.py
@@ -981,11 +981,7 @@ def test_xonsh_basic(self):
             rc = activate_main(['', 'shell.xonsh'] + activate_args + [self.prefix])
         assert not c.stderr
         assert rc == 0
-        activate_result = c.stdout
-
-        with open(activate_result) as fh:
-            activate_data = fh.read()
-        rm_rf(activate_result)
+        activate_data = c.stdout
 
         new_path_parts = activator._add_prefix_to_path(self.prefix)
         conda_exe_export, conda_exe_unset = activator.get_scripts_export_unset_vars()
@@ -1014,13 +1010,10 @@ def test_xonsh_basic(self):
         }):
             activator = XonshActivator()
             with captured() as c:
-                assert activate_main(['', 'shell.xonsh'] + reactivate_args) == 0
+                rc = activate_main(['', 'shell.xonsh'] + reactivate_args)
             assert not c.stderr
-            reactivate_result = c.stdout
-
-            with open(reactivate_result) as fh:
-                reactivate_data = fh.read()
-            rm_rf(reactivate_result)
+            assert rc == 0
+            reactivate_data = c.stdout
 
             new_path_parts = activator._replace_prefix_in_path(self.prefix, self.prefix)
             e_reactivate_data = dals("""
@@ -1030,23 +1023,21 @@ def test_xonsh_basic(self):
             $CONDA_PROMPT_MODIFIER = '(%(native_prefix)s) '
             source "%(activate1)s"
             """) % {
+                'new_path': activator.pathsep_join(new_path_parts),
                 'activate1': activator.path_conversion(join(self.prefix, 'etc', 'conda', 'activate.d', 'activate1.xsh')),
                 'deactivate1': activator.path_conversion(join(self.prefix, 'etc', 'conda', 'deactivate.d', 'deactivate1.xsh')),
                 'native_prefix': self.prefix,
-                'new_path': activator.pathsep_join(new_path_parts),
             }
             assert reactivate_data == e_reactivate_data
 
             with captured() as c:
-                assert activate_main(['', 'shell.xonsh'] + deactivate_args) == 0
+                rc = activate_main(['', 'shell.xonsh'] + deactivate_args)
             assert not c.stderr
-            deactivate_result = c.stdout
-
-            with open(deactivate_result) as fh:
-                deactivate_data = fh.read()
-            rm_rf(deactivate_result)
+            assert rc == 0
+            deactivate_data = c.stdout
 
             new_path = activator.pathsep_join(activator._remove_prefix_from_path(self.prefix))
+            conda_exe_export, conda_exe_unset = activator.get_scripts_export_unset_vars()
             e_deactivate_data = dals("""
             $PATH = '%(new_path)s'
             source "%(deactivate1)s"