Home > other >  How to wrap the conda command in a fish function to initialize conda only on demand?
How to wrap the conda command in a fish function to initialize conda only on demand?

Time:12-03

From time to time I use the miniconda package manager. Normally, conda adds the following line to ~/.config/fish/config.fish upon installation:

eval /home/quappas/.apps/miniconda3/bin/conda "shell.fish" "hook" $argv | source

This line must be executed before conda can be used. However, it is quite slow to execute and having it in config.fish causes a significant startup delay every time I open a terminal. This is annoying because most of the time I don't even want to use conda when I open a terminal. So I decided to remove the line from config.fish and define a function conda.fish to wrap the conda command instead:

function conda --wraps 'conda'
    if not set -q CONDA_INITIALIZED
        echo 'Initializing conda...'
        eval /home/quappas/.apps/miniconda3/bin/conda "shell.fish" "hook" | source
        set -g CONDA_INITIALIZED 1
    end
    command conda $argv
end

With this function, for some reason I can do

conda
conda activate myenv

in a new terminal and it works just fine. However I do

conda activate myenv

directly in a new terminal, I get conda's "Your shell has not been properly configured to use 'conda activate'." error. Confusingly if I do

conda activate myenv
conda activate myenv

in a new terminal, the first command gives me the above error but the second one activates my environment successfully without complaint.

I'm not sure if this is a problem with my function, fish or conda. What can I do to be able to use the conda command normally, but only run the slow conda initialization script when I actually call conda?

CodePudding user response:

The issue is that the conda integration happens by defining a function called conda. So calling command conda is wrong, they want it to be called via a wrapper function.

See what happens after you run conda once and then use type conda to see the definition:

conda is a function with definition
# Defined via `source`
function conda
    set -l CONDA_EXE /opt/miniconda3/bin/conda
    if [ (count $argv) -lt 1 ]
        $CONDA_EXE
    else
        set -l cmd $argv[1]
        set -e argv[1]
        switch $cmd
            case activate deactivate
                eval ($CONDA_EXE shell.fish $cmd $argv)
            case install update upgrade remove uninstall
                $CONDA_EXE $cmd $argv
                and eval ($CONDA_EXE shell.fish reactivate)
            case '*'
                $CONDA_EXE $cmd $argv
        end
    end
end

You can either do that in your own function, or call conda $argv, which would ordinarily be an infinite loop.

Since that seems a bit awkward, how about this instead:

function conda --wraps 'conda'
    echo 'Initializing conda...'
    # We erase ourselves because conda defines a function of the same name.
    # This allows checking that that happened and can prevent infinite loops
    functions --erase conda
    /home/quappas/.apps/miniconda3/bin/conda "shell.fish" "hook" | source

    if not functions -q conda
        # If the function wasn't defined, we should not do the call below.
        echo 'Something went wrong initializing conda!' >&2
        return 1
    end
    
    # Now we can call `conda`, which is a function, but not this one (because we erased it),
    # so this is not an infinite loop.
    conda $argv
end

The variable is now unnecessary since this function is never called twice in the same shell, and I removed the dangerous and unnecessary eval.

  • Related