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

Feature Request: uv shell #1910

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

Feature Request: uv shell #1910

twiddles opened this issue Feb 23, 2024 · 45 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

@wlnirvana
Copy link

Instead of activating the environment, I personally find it helpful instead to

  1. either uv run <script> to run a script directly
  2. or uv run python to start the repl backed by the virtual environment

@seapagan
Copy link

seapagan commented Sep 17, 2024

I'll be honest, I originally saw the lack of a 'shell' command as a deal-breaker since I am so used to Poetry. When I stepped back for a sec I realise I can get by with a couple of shell aliases (Linux or Mac):

# uv aliases
alias va='source .venv/bin/activate'
alias vd='deactivate'

This is from my zsh config but all shells offer similar. Windows users can use a Doskey or powershell alias.

Now, I just enter my project folder and type va and ready to go, actually faster that the Poetry equivalent.

I've got my GitHub actions working again, and changed to Renovate from Dependabot so I think I'm ready to go full on uv 😁

EDIT: direnv now has a working uv script I've just tried and it works perfectly: https://github.com/direnv/direnv/wiki/Python#uv

@luxedo
Copy link

luxedo commented Sep 23, 2024

(Grabs 🍿)

I like uv shell concept because of consistency. No need to run anything else other than uv tooling. Newcomers won't even have to worry about what is a venv.

Although I like very much using uv run, uv pip, ... I think that uv shell could make it easier for them to migrate to uv .

@MrHamel
Copy link

MrHamel commented Oct 5, 2024

What I do to get around this for now, is use the python plugin in oh-my-zsh to load/unload the environment based on the directory I'm in, by setting this in my .zshrc file:

plugins=(... python)

export PYTHON_VENV_NAME=".venv"
export PYTHON_AUTO_VRUN=true

@seapagan
Copy link

seapagan commented Oct 5, 2024

What I do to get around this for now, is use the python plugin in oh-my-zsh to load/unload the environment based on the directory I'm in, by setting this in my .zshrc file:

plugins=(... python)

export PYTHON_VENV_NAME=".venv"
export PYTHON_AUTO_VRUN=true

OMG, I really need to RTFM more often, i've been using omz for years 🤣 - that works just prefectly and utterly transparent 👍

Doesn't seem to interfere with folders where i'm using the direnv method too.

@richin13
Copy link

richin13 commented Oct 9, 2024

I've had this in my .zshrc for ages and never have to worry about activating a virtualenv

export DEFAULT_PYTHON_VENV_DIR=.venv

function _py_venv_activation_hook() {
  local ret=$?

  if [ -n "$VIRTUAL_ENV" ]; then
    source $DEFAULT_PYTHON_VENV_DIR/bin/activate 2>/dev/null || deactivate || true
  else
    source $DEFAULT_PYTHON_VENV_DIR/bin/activate 2>/dev/null || true
  fi

  return $ret
}

typeset -g -a precmd_functions
if [[ -z $precmd_functions[(r)_py_venv_activation_hook] ]]; then
  precmd_functions=(_py_venv_activation_hook $precmd_functions);
fi

@ultrabear
Copy link

ultrabear commented Oct 21, 2024

Just wanted to leave my two cents, as I see this is a more complicated issue than I first thought (started learning uv, missed poetry shell, yknow the deal)

fish has a flag to run a command before starting interactivity (-C) which I will use going forwards, hope that helps anyone who is (like me) too conservative to use a direnv type functionality:

function uvenv
    fish -C "source .venv/bin/activate.fish"
end

@Ravencentric
Copy link

Poetry's poetry shell has been brought up several times in this thread but poetry 2.0 has removed poetry shell in python-poetry/poetry#9763 and replaced it with a command that simply prints the activate command (similar to pdm's solution).

@cosmincatalin
Copy link

Not sure why they did this, the shell command is easy, intuitive and does the expected job.

@thernstig
Copy link

Note that poetry shell still exists here https://github.com/python-poetry/poetry-plugin-shell.

@Ravencentric
Copy link

Not sure why they did this, the shell command is easy, intuitive and does the expected job.

The issues with poetry shell are extensively discussed in:

python-poetry/poetry#9136
python-poetry/poetry-plugin-shell#18

@dpprdan
Copy link
Contributor

dpprdan commented Nov 13, 2024

It's been mentioned before, but seriously, wouldn't an implementation similar to pdm's be a good compromise here?
It generates a platform independent activation command, allows for a custom uv shell command if one prefers, and a uv venv activate would fit nicely in the uv pip/venv interface.

@alberto-gomezcasado-AP
Copy link

Writing from windows, I have just tried uv run cmd.exe and uv run powershell from the project folder root and they seem to do exactly what I would expect. Am I missing something here?

@anddam
Copy link

anddam commented Nov 20, 2024

Writing from windows, I have just tried uv run cmd.exe and uv run powershell from the project folder root and they seem to do exactly what I would expect. Am I missing something here?

Well, the request was about activating the environment, thus modifying the shell process the user was already in.
In your case you are spawning a new process.

@DonDebonair
Copy link

Writing from windows, I have just tried uv run cmd.exe and uv run powershell from the project folder root and they seem to do exactly what I would expect. Am I missing something here?

Well, the request was about activating the environment, thus modifying the shell process the user was already in. In your case you are spawning a new process.

That's the thing: this issue is about mimicking poetry shell, which does in fact spawn a new process. What you're referring to (modifying the shell process the user is already in), is basically activating the venv.

@anddam
Copy link

anddam commented Nov 23, 2024

I see, I am unfamiliar with poetry and was only following OP's "Proposed Solution" bit.

I am personally not a fan of spawning a new shell just in order to have the environment automatically activated, at that point I can setup a quick, unobtrusive way to activate the shell and use that in plave of uv shell, say an function called uva (uv activate).

@astrojuanlu
Copy link

FYI, you don't need to activate environments anymore. uv run, uv add, uv pip etc pick up the appropriate env based on cwd.

@bluss
Copy link
Contributor

bluss commented Nov 24, 2024

I think easy virtual environment activation is great to have. IMO uv has not made it obsolete, uv has based all its functionality on virtual environments, so they continue to be relevant.

Virtual environment activation needs shell integration. I understand if uv doesn't want to build this, but users need a place to go where they can install good existing solutions for this kind of shell integration. Most(?) experienced python developers already have some solution for this, and they vary: simple shell aliases, shell functions, direnv, etc.

  • We need visibility of existing or future solutions - users need a place to go to get these shell integrations, so that not every developer writes their own shell functions

@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.

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