Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a command to activate the virtual environment, e.g., uv shell #1910

Open
twiddles opened this issue Feb 23, 2024 · 62 comments
Open

Add a command to activate the virtual environment, e.g., uv shell #1910

twiddles opened this issue Feb 23, 2024 · 62 comments
Labels
cli Related to the command line interface virtualenv Related to virtual environments

Comments

@twiddles
Copy link

Summary
I propose the introduction of a new command, uv shell. This command would provide users with a quick and straightforward way to enter the associated Python virtual environment (venv) for their projects.

Motivation
The inspiration for uv shell comes from the convenience and simplicity offered by the poetry shell command in Poetry. Having worked with poetry on multiple OSs over the last year, I miss this intuitive approach to environment management. I propose introducing a similar capability into uv.

Proposed Solution
The uv shell command would activate the project's Python virtual environment (basically a shorthand for .venv/bin/activate), allowing users to immediately start working within the context of that environment OR return an error message if the venv is missing. This feature would abstract away the need to manually source the venv activation script, thus providing a more seamless development workflow across operating systems.

@zanieb zanieb added projects Related to project management capabilities cli Related to the command line interface virtualenv Related to virtual environments and removed projects Related to project management capabilities labels Feb 23, 2024
@mgaitan
Copy link

mgaitan commented Feb 24, 2024

This is somewhat similar to the workon <venv> command of virtualenvwrapper (a tool I have been using for many years), except that here each virtualenv is referrer by a name and not its path (its not stored inside the project directory but in as a subidir of a command path defined by $WORKON_HOME . like ~/.venvs)

Also, a functionality that I find useful of virtualenvwrapper are the hooks (pre/post hooks on venv activation and venv creation)

If you can include some of these features in uv many of us would be grateful! .

@rgilton
Copy link

rgilton commented Feb 24, 2024

I frequently use pipenv shell, and the killer feature of this is that it starts a new shell. Whereas activating a virtual environment modifies the currently running shell. This means that one can exit the shell without exiting the terminal window one is running the shell in. It would be great if uv shell could replicate this behaviour.

@jfcherng
Copy link
Contributor

jfcherng commented Feb 25, 2024

Would like to have an even shorter alias at the same time such as uv sh (just like uv v for uv venv).


Fwiw, this is what I have for my Bash shell on both Windows (git-bash) and Linux right now.

Bash Script

You can add this into your .bashrc / .bash_aliases or whatever auto sourced bash file.

uvsh() {
    local venv_name=${1:-'.venv'}
    venv_name=${venv_name//\//} # remove trailing slashes (Linux)
    venv_name=${venv_name//\\/} # remove trailing slashes (Windows)

    local venv_bin=
    if [[ -d ${WINDIR} ]]; then
        venv_bin='Scripts/activate'
    else
        venv_bin='bin/activate'
    fi

    local activator="${venv_name}/${venv_bin}"
    echo "[INFO] Activate Python venv: ${venv_name} (via ${activator})"

    if [[ ! -f ${activator} ]]; then
        echo "[ERROR] Python venv not found: ${venv_name}"
        return
    fi

    # shellcheck disable=SC1090
    . "${activator}"
}

Usage

  • $ uvsh: this will activate .venv by default
  • $ uvsh .my_venv: this will activate .my_venv
  • $ deactivate: exit the current venv

@mitsuhiko
Copy link
Contributor

I had this in rye and backed it out because there is just no non hacky way to make this work. I think the desire is real, but the experience just will always have holes. You basically need to allocate a pty, hook it up with the parent pty and then send key presses into it to manipulate the shell environment. It's pretty hacky and breaks stuff in subtle ways :(

@rgilton
Copy link

rgilton commented Feb 26, 2024

You basically need to allocate a pty, hook it up with the parent pty and then send key presses into it to manipulate the shell environment. It's pretty hacky and breaks stuff in subtle ways :(

Is it not just a case of exec'ing $SHELL, after modifying the environment as required? (Or exec'ing $SHELL with an argument to make it source the venv's activate script on start-up?)

@NMertsch
Copy link

For work I'm switching between Windows and Linux a lot. Having one command for activating would be nice to have, so I don't have to deal with .venv\Scripts\activate.bat vs . .venv/bin/activate.

@rgilton
Copy link

rgilton commented Feb 28, 2024

Here's the script that I am currently using as a stand-in for uv shell. Currently specific to bash, but that fits my use-case. Making it work on other platforms/shells looks reasonably tractable as demonstrated in pipenv.

Quick demo of it in operation:

[rob@zem foo-project(main)]$ uv venv
Using Python 3.12.1 interpreter at /usr/bin/python3
Creating virtualenv at: .venv
Activate with: source .venv/bin/activate
[rob@zem foo-project(main)]$ cd src
[rob@zem src(main)]$ uvs
Entering virtualenv /home/rob/tmp/foo-project/.venv
(foo-project) [rob@zem src(main)]$ 
(foo-project) [rob@zem src(main)]$ uvs
Error: Already within virtualenv
(foo-project) [rob@zem src(main)]$ 
exit
[rob@zem src(main)]$ 

@tiagofassoni
Copy link

What about if, instead of hooking up pty's, we just do the bare minimum? We detect the shell (whether zsh, bash, nushell or powershell), the OS, and just call the source file.

Although I'm afraid @mitsuhiko thought of this and decided not to do it, so perhaps it just isn't feasible.

@zanieb
Copy link
Member

zanieb commented Apr 8, 2024

Unfortunately you can't source things for someone from another process.

@rgilton
Copy link

rgilton commented Apr 8, 2024

I think there are two separate feature requests that are being combined in this issue:

  1. The ability to spawn a new shell as a new process with the venv activated within it. This is what pipenv shell, and poetry shell do.
  2. The ability to activate the venv within the current shell. As running source .venv/bin/activate would do.

I think the talk of injecting keypresses into a shell via a pty is about the second item, whereas the original request in this issue was about the first. My preference is for the first, as it is straightforward to 'exit' the spawned shell without exiting the terminal emulator/window, whereas if the current shell is modified then it is not easy to 'unmodify' it.

@samypr100
Copy link
Collaborator

I think the first option somewhat reflects what pixi does, https://github.com/prefix-dev/pixi/blob/main/src/cli/shell.rs

I gave their impl a quick whirl on uv to test, and seems to works pretty well

ps_venv_shell

@patrick-kidger
Copy link

FWIW I've always considered poetry shell a misfeature from a UX perspective. It's too easy to drop into a shell, then change directory, and lose track of which venv you're actually working in.

So for my team I prefer to recommend always using poetry run (often plus some shell aliases to minimise typing). I think that would make better sense as a first-class citizen for uv. And then if someone still really really wants a shell then this is still accomplishable via uv run [bash|fish|etc.].

@zefr0x
Copy link

zefr0x commented May 16, 2024

Unfortunately you can't source things for someone from another process.

What about providing a wrapper shell script to do the sourcing part?

Just like virtualfish, but simple and using uv.

We have two options:

  1. uv will manage every thing from creating to deleting venvs in a global directory. But for the sourcing part the shell script will do the task.
  2. The script will be a wrapper for all related venv management tasks using uv behind-the-scenes. (best, since we will not need to toggle between multiple commands for venv related tasks)

@lmanc
Copy link

lmanc commented May 18, 2024

I don't know if this is relevant here, but sometimes I have difficulties understanding which venv I'm actually using and what's happening under the hood (and why). I'm not sure if there's already a way to figure out these things and I'm just missing it.

Consider this scenario: I have a project in VSCode without a venv. I open the terminal and run uv pip list, which outputs:

Package    Version
---------- -------
pip        23.0.1
setuptools 65.5.0

This output reflects my clean, system environment.

Next, I run uv venv. According to the documentation:

By default, `uv` lists packages in the currently activated virtual environment, or a virtual environment (`.venv`) located in the current working directory or any parent directory, falling back to the system Python if no virtual environment is found.

So, I'm expecting uv to pick the local .venv without activation, and it does, even if the venv is not activated.

Now, if I close VSCode and reopen it, VSCode continues with its "implicit" activation of .venv in every terminal I open from now on.

If, for some reason, I now delete .venv and run uv pip list, I get this error:

$ uv pip list
error: failed to canonicalize path `<path>`
  Caused by: The system cannot find the file specified. (os error 2)

While I expected the same output (the system environment) as before.

I think it's VSCode's fault because I guess it somehow implicitly runs something like source .venv/bin/activate if I open it with a venv already created. However, since the venv is now activated, I think this "overrides" uv's standard venv detection flow when none is activated.

So my point is: it would be nice to have some mechanism inside of uv to check which venv it is currently selecting and why (e.g., "explicit activation of <path>", "implicit use since no other venvs are activated and there's a .venv in the current directory", "implicit system environment use because no venv is activated and no .venv in the current directory," etc.).

@zanieb
Copy link
Member

zanieb commented May 18, 2024

Hi @lmanc — thanks for the write up!

I'm actually adding tracking of all of these sources at #3266 so I think we'll have better logging of that in the near future.

@NikosAlexandris
Copy link

Just a kind reminder of the existence of https://github.com/direnv/direnv. It would be actually nice to learn from it and explore integration with it ?

@zefr0x
Copy link

zefr0x commented Jul 29, 2024

Unfortunately you can't source things for someone from another process.

What about providing a wrapper shell script to do the sourcing part?

Just like virtualfish, but simple and using uv.

We have two options:

  1. uv will manage every thing from creating to deleting venvs in a global directory. But for the sourcing part the shell script will do the task.

  2. The script will be a wrapper for all related venv management tasks using uv behind-the-scenes. (best, since we will not need to toggle between multiple commands for venv related tasks)

I wrote a simple function for the Fish shell: https://github.com/zefr0x/dotfiles/blob/f3f20a4ab91c198674211c76ad431ef8260cfe10/utils/fish/functions/vf.fish

It does store all venvs in a global dir, and provides some commands to manage them.

It address the problem for my workflow, and can be used as a reference to implement a general solution that gets packaged with uv.

@mitsuhiko
Copy link
Contributor

I think it's worth looking at this from the lense of what the world might look like in two years. A lot of the reasons people want to "activate" virtualenvs is that this is how we used to do things, not necessarily how we would do things in a slightly changed world.

Even with rye today the need to actually activate a virtualenv is largely gone as the python executable always does the right thing, and rye run does too. So it primarily are things created in a world that assumed that virtualenvs are for activating that need this.

npm got away without any equivalent of "activation" and I think a future Python ecosystem will also no longer find much use in virtualenv activation.

@anddam
Copy link

anddam commented Aug 6, 2024

What about providing a wrapper shell script to do the sourcing part?

I just threw this in my ~/.bashrc (MSYS2 on win)

uv () {
  [ -n "$2" ] && d="$2" || d=".venv"
  [ "$1" = "venv" ] && [ -d "$d" ] \
    && source "./${d}/Scripts/activate" \
    || command uv "$@"
}

this mimics the same venv function I have had for years for creating/listing/activating venvs.

Albeit feeling the need for a quicker way to activate the env while using uv I agree with the sentiment that it's not the called process role to alter the parent's environment.

@chrisrodrigue
Copy link

chrisrodrigue commented Aug 8, 2024

It's tricky enough that other projects like pdm have avoided adding this feature. Too many broken corner cases.

See the green note here: Looking for pdm shell?

@thernstig
Copy link

thernstig commented Sep 11, 2024

Just a kind reminder of the existence of https://github.com/direnv/direnv. It would be actually nice to learn from it and explore integration with it ?

There is a thread about integration that might go official sometime, see direnv/direnv#1250.

@pkucmus
Copy link

pkucmus commented Sep 12, 2024

uv shell (I do like it in Poetry) or not, I would like to have a uv venv --activate or even uv activate something that would set the project's Python interpreter for my current shell.

@tanloong
Copy link

tanloong commented Sep 12, 2024

And it would be great if uv activate could activate the environment from any subfolder, not just the root. Here is my function to do the subfolder activation in fish:

function activate
    set --function cwd (pwd)
    set --function home (dirname (realpath $HOME))
    set --function venv_path ""
    # Recursive search upward for .venv directory
    while test -n $cwd -a $home != $cwd
        if test -e $cwd/.venv
            set --function venv_path (realpath $cwd/.venv)
            break
        end
        set --function cwd (dirname $cwd)
    end
    if test -n $venv_path
        if test -d $venv_path
            source $venv_path/bin/activate.fish
        else
            echo "Found .venv at $venv_path, but it is not a valid directory"
        end
    else
        echo "Could not find .venv directory"
    end
end

@Ravencentric
Copy link

venvs made by uv have the activation scripts, so source .venv/bin/activate (maybe an alias if you find that too verbose). Poetry did the poetry shell and had to get rid of it. PDM decided from the get-to that pdm shell would be too problematic for how little of benefit it brings. Rye decided against it too. At one point I thought poetry shell (or an equivalent) is important but after a while of using just poetry run and uv run, I've come to enjoy them. uv run also has the benefit of guaranteeing a proper up-to-date environment on every run.

@Ravencentric
Copy link

Ravencentric commented Nov 24, 2024

After realising that $tool shell is obviously problematic, I did think about proposing the alternative of a command that simply prints that activate command, after all the commands are different on Windows (.venv/scripts/activate) and Linux (.venv/bin/activate):

$ uv shell
source .venv/bin/activate
$ eval $(uv shell)

This sounds nice on paper (cross platform command!) but it's not actually cross platform. On Windows you would need to iex (uv shell) while on Linux you would need to eval $(uv shell). So in the end, the benefits are negligible. You save a few characters while still needing to remember some minor differences. I haven't really needed a reason to activate the venv either, after all, uv run/add/pip/etc do the right thing and are cross platform while also avoiding the foot gun of "oops I forgot to activate the venv and installed something in my global environment". VS Code and Pycharm also pick the in project venv without any activation. On the rare occasion where I do need to activate the venv source .venv/bin/activate works fine.

@luxedo
Copy link

luxedo commented Nov 24, 2024

hatch shell also spawns a new process and I really like it. https://hatch.pypa.io/1.9/environment/#entering-environments

Also, quitting the venv with Ctrl+D feels better than using deactivate for me.

@mitsuhiko
Copy link
Contributor

mitsuhiko commented Nov 25, 2024

@luxedo that is exactly what everybody else is doing. You don't spawn the shell, you pexpect into a shell with all the complexities and bugs that this causes.

https://github.com/pypa/hatch/blob/6bc4bbeff1be8226958874e43698f5bf3b8ea339/src/hatch/utils/shells.py#L109-L141

There is no way to do this with shells today without having to remote control it and send key presses into it.

I really believe the best one can achieve here is that someone runs uv shell and it prints out an informational text with something you can copy paste to source it. I think the goal should be to just stop activating virtualenvs and do everything to kill all uses cases that currently require virtualenv activation in other ways.

@chrisrodrigue
Copy link

Opinions ahead…

I kind of agree with @mitsuhiko.

virtualenv activation adds even more state to an already stateful shell, in the name of saving a few keystrokes. An activated virtual environment makes things available on the PATH that otherwise shouldn’t be there. The fact that a different activation script is needed for each platform is yet another code smell.

Maybe uvr could be the final answer to the virtual environment activation problem?

@zanieb zanieb changed the title Feature Request: uv shell A command to activate the virtual environment, e.g., uv shell Nov 26, 2024
@zanieb zanieb changed the title A command to activate the virtual environment, e.g., uv shell Add a command to activate the virtual environment, e.g., uv shell Nov 27, 2024
@sebastian-correa
Copy link

The uv OhMyZSH plugin has the uvr alias for uv run.

Disclaimer: I built the plugin.

@anddam
Copy link

anddam commented Nov 28, 2024

I think the goal should be to just stop activating virtualenvs and do everything to kill all uses cases that currently require virtualenv activation in other ways.

I might be missing something, what's the beef with virtualenv activation here?

My use case for venv usage is you activate one, start exploring what you need and add package interactively along the way, then at a certain point you solidify (i.e. "freeze") the requirements. It's kinda the REPL concept.

@rgilton
Copy link

rgilton commented Nov 28, 2024

I might be missing something, what's the beef with virtualenv activation here?

That was my thought too. When I went back and read through what @mitsuhiko said, I think the general aim here is to remove the need to "active" a virtualenv. If all the tooling just does the "right thing" based on the working directory (and, perhaps some other environment variables, for some rare cases, I assume), then there is no longer any need to 'activate'.

My use case for venv usage is you activate one, start exploring what you need and add package interactively along the way, then at a certain point you solidify (i.e. "freeze") the requirements

Don't uv pip install and uv pip freeze fulfil your needs here?

@rumpelsepp
Copy link
Contributor

rumpelsepp commented Nov 28, 2024

Fish has a useful flag for this (-C: Evaluate specified commands after reading the configuration but before executing command specified by -c or reading interactive input.)

I have a qick 'n' dirty script uv-shell which looks like this:

#!/usr/bin/bash

set -eu

activation_script=".venv/bin/activate.fish"

if [[ ! -r "$activation_script" ]]; then
    activation_script="$(git rev-parse --show-toplevel)/.venv/bin/activate.fish"
fi

fish -C "source $activation_script"

I can imagine, that a potential uv shell requires such functionality from the shell to do it without problematic pty code.

@anddam
Copy link

anddam commented Nov 28, 2024

Don't uv pip install and uv pip freeze fulfil your needs here?

Likely yes, but my digital (in the finger meaning) habits come from before uv.

@rgilton
Copy link

rgilton commented Nov 29, 2024

I can imagine, that a potential uv shell requires such functionality from the shell to do it without problematic pty code.

Agreed -- I don't think there's really any shell that requires such problematic code. Any shell that supports having an init file you can spec via command-line (or env-var, I guess!) can do similar.

@krystofbe
Copy link

The lack of a uv shell command is currently the only reason preventing me from switching away from Poetry. Having a built-in way to activate the virtual environment is such a convenience in my workflow.

@glukicov
Copy link

glukicov commented Dec 30, 2024

Hey @krystofbe and others, don't wait too long to switch to uv 🙌 The migration has been great for me: dev groups, container building with dependencies, pulling packages from our private registry all works as expected, and the project build is so much faster. The workaround to activate the venv that I use is:

from subprocess import run

def activate():
    """Activate uv virtual environment 🐍."""
    cmd = f"source .venv/bin/activate"
    run(args=["/bin/bash", "-c", f"{cmd}; exec $SHELL"], check=True)

This can be wrapped in a typer / click CLI command and incorporated with other uv commands like sync or uv python pin to have one single command that installs and activates a Python project.

@kaine-bruce-dmt
Copy link

kaine-bruce-dmt commented Jan 2, 2025

As a previous user of pipenv, I thought I would miss pipenv shell... But I've just been hitting . and then autocompleting .venv/scripts/activate. It doesn't slow me down.

@tringenbach
Copy link

I scrolled through the comments and didn't see this suggested yet (but maybe I missed it, or maybe it's just a terrible idea):

uv super environment

  • source it once in your .zshrc / .bash_profile / etc
  • Replace python with a a shell script that sets VIRTUAL_ENV and runs the real python
    • Or it could be a symlink to uv itself, which would check argv[0], set the env var and exec python (if it's faster to implement in Rust than in shell)
  • Add a "super environment"/bin directory to PATH, with files for all entrypoints in all uv managed environments
    • they would be shims or symlinks that check the dynamic virtual environment and forward to the right command
    • commands that don't exist in that environment would fail

Your virtualenv would be dynamic based on the CWD.

One down side, unless there's a clever way around this, is that you would get tab completion for commands not installed in the active virtualenv. You'd also want to try to detect if a virtual environment was manually activated and disable "super environment" mode, which might get tricky.

I could also see it being frowned upon to add a layer in front of python like this.

@mortenb-buypass
Copy link

Another scenario:

Us coming from pyenv with the uv install python <pythonver> --default --preview would really like to have this feature. From pyenv we have a standard ~/.bash_profile that works like this: (scenario is a pod running a not root service that need a dedicated python environment):

uv venv 
source ~/.venv/bin/activate
uv pip install -e .

# And I append this file:
cat ~/.bash_profile
export UV_ROOT=~/.local/bin
export PATH="$UV_ROOT:$PATH"
if [ -f ~/.venv/bin/activate ]; then
        source ~/.venv/bin/activate
fi

I use this in all my containers. UV has removed more than 200MB from the container compared to pyenv which needs to build from source. We never use sudo apt-get install python3 because we like to control everything ourselves and patch independently of ubuntu. and once pyenv had a new python ready (usually max 5-10hours after python.org release) Our CICD system had build and tested.

Add this source file based on what shell you use to give you a default python on login.

uv shell ~/.venv --shell=bash

@krystofbe
Copy link

Turns out I’ve been overreacting about the lack of uv shell. Poetry removed their poetry shell command in 2.0.0, so I guess manual source .venv/bin/activate is the hot new lifestyle.

@Noobzik
Copy link

Noobzik commented Feb 13, 2025

I came from conda and I am transitionning to uv.

I have a project that I needed to share with someone. So I zipped my project, and then I noticed that I forgot the .venv is here. This is something I didn't need to worry when I was in miniconda since the environnements are all located at the same path. I think this is an additional argument to back up this feature request.

Right now, i need to go inside my project, select all needed files to be zipped. Or delete the .venv folder before I go back to one level, file zip a single folder containing my project.

It's an additional step compared to conda since it doesn't store the venv inside the project folder...

@alberto-gomezcasado-AP
Copy link

I came from conda and I am transitionning to uv.

I have a project that I needed to share with someone. So I zipped my project, and then I noticed that I forgot the .venv is here. This is something I didn't need to worry when I was in miniconda since the environnements are all located at the same path. I think this is an additional argument to back up this feature request.

Right now, i need to go inside my project, select all needed files to be zipped. Or delete the .venv folder before I go back to one level, file zip a single folder containing my project.

It's an additional step compared to conda since it doesn't store the venv inside the project folder...

FYI: UV stores the venv wherever you tell it to do so, https://docs.astral.sh/uv/pip/environments/#using-arbitrary-python-environments and will search for it, even will try automatically one folder up https://docs.astral.sh/uv/pip/environments/#discovery-of-python-environments.

But I don't see how any of this is relevant to the activation, and the existence of a command to do so, so maybe you are in the wrong issue?

@elisiariocouto
Copy link

@Noobzik there's an issue tracking the problem you're describing: #1495

@anddam
Copy link

anddam commented Feb 14, 2025

I have a project that I needed to share with someone. So I zipped my project, and then I noticed that I forgot the .venv is here. This is something I didn't need to worry when I was in miniconda

Or if you use git-archive or equivalent, with the additional bonus that you don't carry build products along.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cli Related to the command line interface virtualenv Related to virtual environments
Projects
None yet
Development

No branches or pull requests