Open twiddles opened 8 months ago
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! .
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.
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.
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}"
}
$ uvsh
: this will activate .venv
by default$ uvsh .my_venv
: this will activate .my_venv
$ deactivate
: exit the current venvI 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 :(
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?)
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
.
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)]$
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.
Unfortunately you can't source
things for someone from another process.
I think there are two separate feature requests that are being combined in this issue:
pipenv shell
, and poetry shell
do.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.
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
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.]
.
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:
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.uv
behind-the-scenes. (best, since we will not need to toggle between multiple commands for venv related tasks)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.).
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.
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 ?
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 usinguv
.We have two options:
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.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
.
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.
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.
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
?
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 https://github.com/direnv/direnv/issues/1250.
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.
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
Instead of activating the environment, I personally find it helpful instead to
uv run <script>
to run a script directlyuv run python
to start the repl backed by the virtual environmentI'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
:grin:
EDIT: direnv
now has a working uv
script I've just tried and it works perfectly: https://github.com/direnv/direnv/wiki/Python#uv
(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
.
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
What I do to get around this for now, is use the
python
plugin inoh-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 :rofl: - that works just prefectly and utterly transparent :+1:
Doesn't seem to interfere with folders where i'm using the direnv
method too.
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
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
Poetry's poetry shell
has been brought up several times in this thread but poetry 2.0 has removed poetry shell
in https://github.com/python-poetry/poetry/pull/9763 and replaced it with a command that simply prints the activate command (similar to pdm's solution).
Not sure why they did this, the shell
command is easy, intuitive and does the expected job.
Note that poetry shell
still exists here https://github.com/python-poetry/poetry-plugin-shell.
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:
https://github.com/python-poetry/poetry/issues/9136 https://github.com/python-poetry/poetry-plugin-shell/issues/18
It's been mentioned before, but seriously, wouldn't an implementation similar to pdm's be a good compromise here?
It has 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.
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 thepoetry 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.