Search code examples
pythonanacondacondazshpyenv

PATH="/custom/dir:$PATH" prepending instead of appending - Conda vs Pyenv


I have a Mac (latest software update) with Pyenv and Anaconda. I manage packages with Homebrew and have installed Python 3 with it. When I echo $PATH I get the following:

/Library/Frameworks/Python.framework/Versions/3.8/bin:
\ /usr/local/bin:
\ /usr/bin:
\ /bin:
\ /usr/local/sbin:
\ /usr/sbin:
\ /sbin:
\ /Library/TeX/texbin:
\ /Users/luca/.pyenv/versions/anaconda3-2020.02/condabin:
\ /Users/luca/.pyenv/versions/3.8.5/bin:
\ /Users/luca/.pyenv/bin
  1. I do not know what sets up /Library/Frameworks/Python.framework/Versions/3.8/bin: this directory is non-existent on my Mac. I had previously installed Python without Homebrew that is why the directory was created. I did remove that, but there is still something that exports that line in $PATH, but I cannot find it! Does someone have a guess? I did try and grep -r /* it, but that is too much of a search for my laptop to finish.

  2. I set up in my .zshenv the code for initialising pyenv and conda. Of course, I did write PATH="/dir/to/conda/bin:$PATH" and the same for pyenv (see code below). I do not understand why, but they end up at the end of $PATH. Does someone know why? Is it because of eval "$(pyenv init -)" being evaluated before conda?

# >>> pyenv initialize
export PYENV_SHELL=$SHELL
export PATH="/Users/luca/.pyenv/bin:$PATH"
export PATH="/Users/luca/.pyenv/versions/3.8.5/bin:$PATH" #export Python 3.8 bin directory
if command -v pyenv 1>/dev/null 2>&1; then
    eval "$(pyenv init -)"
fi
# <<< pyenv initialize <<<

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/Users/luca/.pyenv/versions/anaconda3-2020.02/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/Users/luca/.pyenv/versions/anaconda3-2020.02/etc/profile.d/conda.sh" ]; then
        . "/Users/luca/.pyenv/versions/anaconda3-2020.02/etc/profile.d/conda.sh"
    else
        export PATH="/Users/luca/.pyenv/versions/anaconda3-2020.02/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<

Solution

  • TL;DR

    In macOS, if you place any code that modifies $PATH on macOS inside .zshenv, it will be overridden. You should put it in .zshrc or somewhere else which is then sourced by your .zshrc.

    Complete explanation

    As answered on Unix & Linux Stack Exchange, when zsh is sourced, files are read in the following order:

    etc/zshenv/ -> $ZDOTDIR/.zshenv/ -> etc/zprofile/ -> $ZDOTDIR/.zprofile/ -> etc/zshrc/ -> $ZDOTDIR/.zshrc/ -> etc/zlogin/ -> $ZDOTDIR/.zlogin/

    In macOS's /etc/zprofile, a script is sourced that overrides the $PATH according to the contents of these files:

    /etc/paths
    /etc/paths.d
    /etc/manpaths
    /etc/manpaths.d
    

    So you should put not put any line that changes $PATH in your $ZDOTDIR/.zshenv, but rather in any file sourced after that (e.g. $ZDOTDIR/.zshrc). Do not edit files in /etc/!