├── README.md └── share └── chpython └── chpython.sh /README.md: -------------------------------------------------------------------------------- 1 | # chpython 2 | 3 | Changes the current Python. 4 | 5 | ## What is it? 6 | 7 | It's a way to seamlessly switch between multiple Python installations. It's 8 | mostly designed for setting a "default" Python interpreter for running scripts 9 | or providing an interactive shell, rather than setting a Python interpreter 10 | for a specific project (virtualenv and friends is a better tool for 11 | per-project Python installations). 12 | 13 | ## What is it not? 14 | 15 | **chpython** is not a tool to: 16 | 17 | * Replace virtualenv. **chpython** is not designed to switch between Pythons 18 | on a per-project basis. 19 | * Install Python. I do not plan to add support for downloading and compiling 20 | Python to **chpython**. It is expressly for _managing_ installations. 21 | 22 | ## What does it do? 23 | 24 | **chpython** lets you switch easily between different versions of Python that 25 | you have installed in `~/.pythons`. It switches between them by: 26 | 27 | * Updating `$PATH`. 28 | * Aliasing `*3` bins to their basename (e.g., `python3` can be called using 29 | `python`) _(not implemented yet)_. 30 | * Calling `hash -r` to update the command-lookup hashtable. 31 | 32 | You can return to the default (system) Python by calling: 33 | 34 | $ chpython system 35 | 36 | Calling `chpython system` will completely restore the environment you had 37 | before calling `chpython VERSION`. 38 | 39 | You can also execute a command using any installed version of Python, without 40 | updating `$PATH` or any other environment variables, by calling: 41 | 42 | $ chpython exec VERSION args... 43 | 44 | For example, you can call `python -V` on an ancient version of Python you have 45 | installed for some reason: 46 | 47 | $ chpython exec 2.5 python -V 48 | 49 | **chpython** supports fuzzy matching of Python versions, so `chpython 2.7` 50 | will execute the latest version of Python 2.7 it can find. You can also 51 | specify a full version: `chpython 2.7.10`. 52 | 53 | ## What does it not do? 54 | 55 | **chpython** does not: 56 | 57 | * Hook into `cd`. 58 | * Install executable shims (like [pyenv]). 59 | * Does not muck with your prompt or anything but the `$PATH` variable. 60 | 61 | ## What does it require? 62 | 63 | * bash (it may work with zsh, but I haven't tested it) 64 | 65 | ## How do I install it? 66 | 67 | 1. Clone the repository (`git clone 68 | https://github.com/mdippery/chpython.git`). 69 | 2. Copy or symlink `share/chpython/chpython.sh` to 70 | `/usr/local/share/chpython`, or anywhere else you feel like putting it. Or 71 | keep it where it is after cloning the repo, I don't care. 72 | 3. Add `source /where/you/put/share/chpython/chpython.sh` somewhere in 73 | `~/.bashrc`. 74 | 4. Optionally, set a Python version by adding `chpython VERSION` to your 75 | `~/.bashrc` file. If you don't do this, your system's Python will be used 76 | by default, but you can use a different Python on a per-shell basis by 77 | calling `chpython VERSION` manually. 78 | 79 | 80 | ## Why not use... 81 | 82 | ### ...virtualenv? 83 | 84 | Virtualenv is a great solution for setting up a specific Python version to use 85 | on a per-project basis, but I have never liked using it to set a default 86 | python to use for "everyday" scripts in my shell, since it involves setting up 87 | a "global" virtualenv anyway. I also don't like how the default virtualenv 88 | `activate` scripts mucks with things in my environment, such as `$PS1` (and 89 | virtualenv's default `$PS1` is _ugly_). Generally speaking, pointing your 90 | shell to a "new" Python is as simple as adding its `bin` directory to your 91 | `$PATH`; nothing else is needed if you installed it normally. 92 | 93 | I compile versions of Python to use by default in my shell to `~/.pythons`. I 94 | wanted a quick way to switch between different versions, and was inspired by 95 | [chruby] to write a bash script to handle that switching seamlessly for me. 96 | 97 | ### ...Conda (Anaconda, Miniconda)? 98 | 99 | Conda and its various incarnations could be used for a similar purpose, but 100 | personally I find them to be far too heavyweight for the relatively simple 101 | tasking of providing and managing default Python installations. 102 | 103 | [chruby]: https://github.com/postmodern/chruby 104 | [pyenv]: https://github.com/yyuu/pyenv 105 | -------------------------------------------------------------------------------- /share/chpython/chpython.sh: -------------------------------------------------------------------------------- 1 | CHPYTHON_VERSION='0.1.0.dev' 2 | PYTHONS=() 3 | 4 | [ -d "${HOME}/.pythons" ] && PYTHONS+=("${HOME}/.pythons"/*) 5 | 6 | function _chpython_exec { 7 | local python 8 | python=$1 9 | shift 10 | # TODO: Clear path so invoking `chpython 3.5 python -V` when 11 | # $CHPYTHON_VERSION is set to a 2.x Python gives a command not 12 | # found error instead of running $CHPYTHON_VERSION/bin/python. 13 | env PATH="$python/bin:$PATH" $* 14 | } 15 | 16 | function _chpython_reset { 17 | [ -z "$CHPYTHON_ROOT" ] && return 18 | PATH=":$PATH:" 19 | PATH=${PATH//:$CHPYTHON_ROOT\/bin:/:} 20 | PATH=${PATH#:}; PATH=${PATH%:} 21 | unset CHPYTHON_ROOT 22 | hash -r 23 | type python || type python3 24 | } 25 | 26 | function _chpython_select { 27 | local dir python match 28 | for dir in ${PYTHONS[@]}; do 29 | dir=${dir%%/} 30 | python=${dir##*/} 31 | case "$python" in 32 | "$1") 33 | # Match using 3-part version number, e.g. `chruby 2.7.10` 34 | match=$dir && break 35 | ;; 36 | "$1"*) 37 | # Match best using 2-part version number, e.g., `chpython 2.7` 38 | match=$dir 39 | ;; 40 | esac 41 | done 42 | echo "$match" 43 | } 44 | 45 | function _chpython_use { 46 | if [[ ! -x "$1/bin/python" && ! -x "$1/bin/python3" ]]; then 47 | echo "chpython: neither $1/bin/python nor $1/bin/python3 are executable" >&2 48 | return 1 49 | fi 50 | 51 | # TODO: Alias *3 stuff to base name, so, e.g., `python` can be called 52 | # instead of `python3` 53 | 54 | export CHPYTHON_ROOT=$1 55 | export PATH="${CHPYTHON_ROOT}/bin:${PATH}" 56 | hash -r 57 | type python || type python3 58 | } 59 | 60 | function chpython { 61 | case "$1" in 62 | -h|--help) 63 | cat <&2 78 | return 1 79 | fi 80 | 81 | shift 82 | _chpython_exec $match $* 83 | ;; 84 | system) 85 | _chpython_reset 86 | ;; 87 | '') 88 | local dir python 89 | for dir in ${PYTHONS[@]}; do 90 | dir="${dir%%/}" 91 | python=${dir##*/} 92 | if [[ "$dir" == "$CHPYTHON_ROOT" ]]; then 93 | echo " * ${python}" 94 | else 95 | echo " ${python}" 96 | fi 97 | done 98 | ;; 99 | *) 100 | local match 101 | match=$(_chpython_select $1) 102 | if [ -z "$match" ]; then 103 | echo "chpython: unknown Python: $1" >&2 104 | return 1 105 | fi 106 | 107 | shift 108 | _chpython_use $match $* 109 | ;; 110 | esac 111 | } 112 | --------------------------------------------------------------------------------