Open dejw opened 11 years ago
I actually had an idea that sort of fell into this line of thinking, so I figured I'd post it here.
The idea was that by distributing shell scripts as vip's entry point instead of using the current vip.main:main entry point, you could actually handle the activation of the virtualenv in addition to the current functionality. The shell script could be fairly simple, as well. My idea was to have it do the following (replace the pre-existing command arguments here with whatever you decide to change them to in the end):
Unfortunately, for many of the posix-style shells, this would require adding a
vip() { source "$(which vip-script)"; }
or something similar to their shell profile. (Or so I think) Windows could handle this in bat scripts and PowerShell scripts.
Anyways, I went ahead and wrote a basic bash script to illustrate my point. (Again, sorry if my bash scripting is a bit rusty)
#!/bin/bash
# Four global variables that are unset before completion.
if [ "$_vip_activate_script" ]; then
# Make sure this starts unset.
unset -v _vip_activate_script
fi
_vip_do_deactivate=0
# Maybe have this set during installation.
_vip_python="$(which python)"
# TODO: Make this a part of the command-line.
_vip_verbose=0
_vip_error() {
local errmsg="$1"
local errcode=${2:-$?}
echo -en "\033[1m\e[31mError: $(tput sgr0)"
echo "$errmsg"
if [ "$_vip_activate_script" ]; then
unset -v _vip_activate_script
fi
unset -v _vip_python _vip_do_deactivate _vip_verbose
exit $errcode
}
_vip_log() {
if [ $_vip_verbose -eq 1 ]; then
echo -en "\033[1m\e[32mLOG: $(tput sgr0)"
echo "$@"
fi
}
_vip_maybe_activate() {
local target=${1:-'.'}
_vip_log "Checking for virtualenv at $target"
local vipdir=`$0 -l "$target"`
if [ $? -ne 0 ]; then
_vip_error "Not a virtualenv (or any of the parent directories): $target"
fi
# I don't remember if bash has scope issues with running
# environment-changing scripts from within the scope of a
# function, so instead of executing the here, we'll store
# it in our _vip_activate_script variable, and catch it
# on the way out.
_vip_activate_script="$vipdir/bin/activate"
# Originally had a check here for [ -x "$_vip_activate_script" ],
# but that doesn't work on cygwin.
if [ -e "$_vip_activate_script" ]; then
export _vip_activate_script
else
_vip_error "Found virtualenv, but no activate script at $_vip_activate_script" 1
fi
}
_vip_maybe_deactivate() {
type deactivate>/dev/null 2>&1
# See the comments in _vip_maybe_activate
if [ $? -eq 0 ]; then
_vip_log "Deactivating.."
export _vip_do_deactivate=1
fi
}
_vip_toggle_activation() {
_vip_log "Checking for activated virtualenv.."
if [ "$VIRTUAL_ENV" ]; then
_vip_log "Existing virtualenv detected at: $VIRTUAL_ENV"
_vip_maybe_deactivate
else
_vip_log "No existing virtualenv active."
_vip_maybe_activate $@
fi
}
_vip_process_args() {
# Check for a command that we handle, and pass
# anything else along to vip.
local args=("$_vip_python" "-m" "vip")
local passthru=1
local activation=0
while test $# -gt 0; do
local arg=$1
case "$arg" in
-a|--activate)
# TODO: Add verbosity to activation process.
activation=1
passthru=0
;;
-d|--deactivate)
# TODO: Add verbosity to deactivation process.
activation=2
passthru=0
;;
-i|--init)
activation=1
args=(${args[@]} "$arg")
;;
*)
args=(${args[@]} "$arg")
;;
esac
builtin shift
done
# First we'll run vip if we need to.
if [ $passthru -eq 1 ]; then
#_vip_process_args "Executing ${args[@]}"
eval "${args[@]}"
if [ $? -ne 0 ]; then
exit $?
fi
fi
# Could probably use a case structure here, but I don't
# know if I need to quote numbers or not.
if [ $activation -eq 1 ]; then
# We need to cleanup args first.
if [ ${#args[@]} -gt 3 ]; then
args=(${args[@]:3})
else
args=()
fi
declare -a scriptargs=("_vip_maybe_activate" ${args[@]/-*/})
eval "${scriptargs[@]}"
elif [ $activation -eq 2 ]; then
_vip_maybe_deactivate
fi
}
if [ $# -gt 1 ]; then
_vip_process_args $*
else
_vip_toggle_activation
fi
if [ $_vip_do_deactivate -eq 1 ]; then
_vip_log "Deactivating.."
deactivate
fi
if [ "$_vip_activate_script" ]; then
_vip_log "Activating.."
_vip_log "$_vip_activate_script"
source "$_vip_activate_script"
unset -v _vip_activate_script
fi
unset -v _vip_python _vip_do_deactivate _vip_verbose
Anyways, if it sounds like a good idea to you, I can go ahead and write the bat and PowerShell variations. I've also been meaning to give myself an excuse to play around with and learn fish shell, so I could take a shot at writing a shell script for that, as well.
Note - The renaming of the vip script to vip-script for posix shells is only because I wasn't sure what sort of priority functions have on the executable search strategy. If functions take priority over actual executables/scripts, the rename obviously isn't necessary.
Note 2 - This would also require adding a __main__.py
file to vip with the following:
# -*- coding: utf-8 -*-
from vip.main import main
if __name__ == "__main__":
main()
Edit - It might actually save some overhead to handle most of this logic on python side, and add an optional argument, like --shell=bash
that, if set, will silence or redirect all output to stderr, and if needed, output only the next command that the shell should execute in the dialect specified. (So on --init
, for instance, it would initialize the virutalenv, and then output, source path/to/activate
.
Since I used vip only to install (update, or remove) libraries and run something using installed Python, interface can be on even higher level than it is now.
Example:
vip install
- create a virtualenv and update its state - install, update, remove libraries listed inrequirements.txt
filevip test [filename]
- to run tests, using nose, py.test, unittest2 or simply executing python interpreter on given filesvip locate [binary]
- to locate binaries inside the virtualenv underneath