justinmayer / virtualfish

Fish shell tool for managing Python virtual environments
MIT License
1.06k stars 100 forks source link

Add a version flag to vf new as a shorthand for pyenv/pythonz pythons #114

Closed daisylb closed 4 years ago

daisylb commented 7 years ago

So, instead of vf new -p (pyenv root)/versions/3.5.1/bin/python foo, users could do e.g. vf new -v 3.5.1 foo. We'd then detect if pyenv/pythonz is installed and if the appropriate version is present, convert this to the appropriate-p arg, and invoke virtualenv appropriately.

Obviously this would depend on #71. I'm 50/50 on whether I want to break the current status quo where vf new arguments work more or less the same as virtualenv arguments, though, and I'd love to hear opinions on the matter. (We already sort of violate this with VIRTUALFISH_DEFAULT_PYTHON, but this is a step further.)

Eduard-gan commented 5 years ago

Yeah, supplying a full path every time is a bit annoying but how about a less generic key, say, something like "vf new --pyenv 3.7.1 foo"?

cecep2 commented 4 years ago

For others who were searching for a solution and found this issue: I made a cheap workaround by creating my own 'mkvenv' function. It's very simple and uses the currently active pyenv version to create a new virtualenv using python venv. Sharing it here because it might be useful for others until this issue is resolved:

function mkvenv --description 'Create new Python virtual env in ~/.venv. Uses python venv and current pyenv version (if installed).'
    if test (count $argv) -eq 1
        # Check which Python version to use
        # Using 'python3' to make sure this never uses Mac OS system Python 2
        if type -q pyenv
            set PYTHON_VERSION (pyenv which python3)
        else
            set PYTHON_VERSION (which python3)
        end
        # Set folder to store new venv
        set VENV_HOME "$HOME/.venvs/$argv"
        # Make sure virtual env doesn't already exist before creating new one
        if test -d $VENV_HOME 
            echo "Virtual env with that name already exists."
        else
            echo "Creating venv in: $VENV_HOME"
            echo "Using Python executable: $PYTHON_VERSION"
            command $PYTHON_VERSION -m venv "$VENV_HOME"
            echo "Upgrading pip and setuptools to latest versions..."
            "$VENV_HOME/bin/pip" install --upgrade pip setuptools
        end
    else
        # Print help message if no, or more than one argv were provided
        echo "Usage: mkvenv <name of venv>"
    end
end

As a side note: now that Python 2 is officially unsupported, I think it would be nice if virtualfish could use python venv and drop the virtualenv dependency?

justinmayer commented 4 years ago

@cecep2 said:

now that Python 2 is officially unsupported, I think it would be nice if virtualfish could use python venv and drop the virtualenv dependency?

I agree. See #158.

justinmayer commented 4 years ago

I put a lot of thought into this, and then I implemented a preliminary solution based on the following analysis…

--pyenv, --pythonz, etc.

Regarding the less-generic vf new --pyenv 3.7.1 foo idea, my main problems with that are:

  1. We’d have to implement option flag handlers for each of the tools that provide alternate Python interpreters. That list could include Pyenv, asdf, Pythonz, Homebrew keg-only versioned Python formulae… At some point the option flag list gets unwieldy.

  2. Some of those option flags (e.g., --pyenv and --pythonz) are going to “compete” with --python for completions, resulting in extra typing to disambiguate.

  3. Most users are only going to use one of the aforementioned alternative Python interpreter installer tools, so folks will be typing things like --pythonz 3.7.1 every time, even though there’s no need for them to disambiguate from, say, Pyenv, which they are very unlikely to also have installed.

-v / --version

The concern with the original -v / --version idea was that it would break the status quo where vf new arguments work more-or-less the same as virtualenv arguments. I’ve already proposed that we switch from Virtualenv to Python’s built-in venv module, which if adopted would mean that status quo is going to break anyway. But I think the original thought process is sound: it would be nice if vf new arguments could work more-or-less the same as python -m venv arguments.

The good news is that arguably the most useful option flags are already consistent between the two virtual environment creation tools:

The most important CLI invocation “signature” difference between the tools is that venv forgoes Virtualenv’s -p / --python option flag in favor of building virtual environments with whichever Python interpreter invoked venv to begin with. I think the best way to handle this is to continue using the vf new -p / --python convention even if we switch to the venv module.

I think my main concern with -v / --version is that the distinction is… muddy. If I want to specify a Homebrew keg-only versioned Python interpreter, do I use -p or -v, and why? Ultimately, all of these are simply Python interpreters, so what puts some of them in the -p camp and others in the -v camp? Whether they’re on the user’s PATH or not? I dunno… The more I thought about it, the less clear it all seemed to me.

Hybrid Approach

So for my proof-of-concept implementation, I decided to test how it might work if we used -p / --python to select any and all interpreters, checking the parsed argument in the following order of priority:

  1. If the argument is a full filesystem path to a Python interpreter (such as /usr/bin/python3 or a bare Python executable on PATH (such as pypy), use it.
  2. If the argument looks like python3.8, check for found interpreters in the following order of priority:
    1. Homebrew keg-only versioned Python executable (e.g., 3.8) found at: /usr/local/opt/python@3.8/bin/python3.8
    2. asdf Python plugin is installed and has built the specified Python version.
    3. Pyenv is installed and has built the specified Python version.
    4. Pythonz is installed and has built the specified Python version.

As a bonus, for the latter class of cases, I also accepted -p 3.8 and -p 3.9.0a4. Why make users type -p python3.9.0a4 when it’s clear what they want to do?

I tried to think of use cases that would be confounded by such an implementation, but so far I haven’t come up with any. That said, I imagine such cases must exist, which is why I took the time to write up this detailed explanation, so other people can ponder on it and point out potential problems. (Had to end on a high-alliteration note.) ✨

justinmayer commented 4 years ago

I have pushed a PR that implements the above hybrid approach, among other features, while sticking with Virtualenv due to its much faster speed: https://github.com/justinmayer/virtualfish/pull/159

justinmayer commented 4 years ago

Above PR has been merged into master, so this feature has been implemented as outlined above.