pyenv-virtualenv
pyenv-virtualenv copied to clipboard
Use chpwd hook for _pyenv_virtualenv_hook / internal hook system with pyenv
I wonder if it would be a good idea to use the chpwd hook (instead of precmd (for zsh)) with the hook that gets installed via pyenv virtualenv init -:
_pyenv_virtualenv_hook () {
if [ -n "$PYENV_ACTIVATE" ]
then
if [ "$(pyenv version-name)" = "system" ]
then
pyenv deactivate --no-error --verbose
return 0
fi
if [ "$PYENV_ACTIVATE" != "$(pyenv prefix)" ]
then
if pyenv deactivate --no-error --verbose
then
pyenv activate --no-error --verbose || unset PYENV_DEACTIVATE
else
pyenv activate --no-error --verbose
fi
fi
else
if [ -z "$VIRTUAL_ENV" ] && [ "$PYENV_DEACTIVATE" != "$(pyenv prefix)" ]
then
pyenv activate --no-error --verbose
fi
fi
}
Alternatively, pyenv itself could have a hook system, where changing the version (e.g. via pyenv shell) would notify the pyenv-virtualenv plugin and that could call activate then (based on some config).
Using chpwd might be better for zsh. I am happy if you sent me a patch, or I'll try it later.
As you pointed, pyenv has hook system. Though, it is enabled for pyenv-exec, pyenv-rehash and pyenv-which by default, not for pyenv-sh-shell. I have no idea to implement auto-activation system without modifying pyenv-init. If you have any idea on it, I'd like to see it.
I think the internal hook system would be better, instead of just improving it a bit for zsh (which would still be called to often in general then).
I have no idea about how to approach it though.
FWIW, I have currently disabled the pyenv-virtualenv hook, and use smartcd to activate the virtualenv with pyenv virtualenv foo (besides other things) when entering the project dir.
I didn't know cxreg/smartcd but it looks fine. I know similar tool named zimbatm/direnv to hook cd for some projects (mainly for golang projects).
Detecting version changing in pyenv is not so easy. There are 3 types of versions in pyenv; global, local and shell. To hook all version changing for all these types, we must hook these events:
- Every invocation of
pyenv global,pyenv localandpyenv shell - Every invocation of
cd - Every declaration of shell variable of
PYENV_VERSION
(1) can be implemented as pyenv's hook system. (2) can be implemented as similar as tools like smartcd or direnv is doing. (3) need to be implemented as something like zsh's precmd.
Currently, pyenv-virtualenv provides pyenv virtualenv-init - to inject shell function into user's shell to hook all those events.
I've thought there was maybe some central point where pyenv determined which version should be used. Then there could a check get added which compares it with the last used version.
I do not mean that a virtualenv would get activated when changing into a specific directory, but only when pyenv reads the .python-version file from there, through its shims.
(smartcd is very nice in that it has support for leaving a directory and nice helpers to automatically shadow vars like $PATH for the time you're inside the dir. I've found it after looking for another tool, which name I couldn't remember and stumbled upon a few weeks/months ago.)
@blueyed thanks for mentioning direnv. I was about to start using zsh-autoenv, but this makes me believe that direnv is superior. direnv has support for unloading the changed environment as well.
@obestwalter
I would recommend https://github.com/Tarrasch/zsh-autoenv by now, after having a lot of features to it a while ago. It also supports unloading (even very comfortably via autostash).
I hope this not getting too off topic here but I would be interested why you prefer zsh-autoenv now. Direnv seems to cover all bases and comes as a fast go executable, so this looked pretty neat to me.
Yeah, it's pretty off topic, but interesting in general.
direnv looks really nice, and I would have probably used it instead of rewriting/enhancing zsh-autoenv, if I've known about it already. The stdlib is awesome, e.g. the wrapper for Python environments.
I've added direnv to the related-projects section on zsh-autoenv (https://github.com/Tarrasch/zsh-autoenv/commit/489bbb46fdaaf1363b152c68c68c16f40847a856) and created a PR for a backlink (https://github.com/direnv/direnv/pull/189).
Some features I like about zsh-autoenv:
- displays the file to be confirmed, which can be done interactively - although I like the idea of explicitly calling the
allowcommand (oredit). - the concept of "leave" events in seperate or the same file: direnv appears to only care about the state of exported vars?!
Regarding performance, eval "$(direnv hook zsh)" is twice as fast as source ~/.dotfiles/lib/zsh-autoenv/autoenv.zsh (given 100 iterations).
But then the chpwd hook is 2.5x slower with direnv (0.191 vs 0.074); eval "$(direnv export zsh)" vs _autoenv_chpwd_handler - at least something.. ;)
@blueyed
the concept of "leave" events in seperate or the same file: direnv appears to only care about the state of exported vars?!
Leave events and autostashing in zsh-autoenv look definitely more sophisticated than what direnv has to offer, but I'll keep playing around with direnv for a while as I had set it up already right after reading about it here.
Speed is not really a topic for me at the moment as new shells open instantaneously with my current setup (i3wm + roxterm with quite a lot zsh goodness).
Enough with the off-topicism then :)
Hi, direnv author here. Nice work with zsh-autoenv. I also like what you did with displaying the unallowed file directly in the shell and might steal that idea :)