├── .github
└── CODEOWNERS
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bin
├── help.config
├── help.links
├── help.overview
├── install
├── list-all
├── list-legacy-filenames
└── utils.sh
├── renovate.json
└── shims
└── pip
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @asdf-community/asdf-python
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /pyenv
2 | /pyenv_last_update
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: c
2 | script: asdf plugin-test python https://github.com/danhper/asdf-python.git
3 | before_script:
4 | - git clone https://github.com/asdf-vm/asdf.git asdf
5 | - . asdf/asdf.sh
6 | os:
7 | - linux
8 | - osx
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Daniel Perez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # asdf-python
2 |
3 | [](https://travis-ci.org/danhper/asdf-python)
4 |
5 | Python plugin for [asdf](https://github.com/asdf-vm/asdf) version manager
6 |
7 | ## Install
8 |
9 | ```
10 | asdf plugin-add python
11 | ```
12 |
13 | ### Install with `--patch`
14 |
15 | > Enable to fix macOS 11 issues
16 |
17 | You can use environment variable `ASDF_PYTHON_PATCH_URL` to install with `--patch` like that:
18 |
19 | ```
20 | export ASDF_PYTHON_PATCH_URL="https://github.com/python/cpython/commit/8ea6353.patch?full_index=1"
21 | asdf install python 3.6.12
22 | ```
23 |
24 | or use environment variable `ASDF_PYTHON_PATCHES_DIRECTORY`.
25 |
26 | ## Use
27 |
28 | Check [asdf](https://github.com/asdf-vm/asdf) readme for instructions on how to install & manage versions of Python.
29 | Please make sure you have the required [system dependencies](https://github.com/pyenv/pyenv/wiki#suggested-build-environment) installed before trying to install Python.
30 |
31 | Under the hood, asdf-python uses [pyenv python-build](https://github.com/yyuu/pyenv/tree/master/plugins/python-build)
32 | to build and install Python, check its [README](https://github.com/yyuu/pyenv/tree/master/plugins/python-build)
33 | for more information about build options and the [common build problems](https://github.com/pyenv/pyenv/wiki/Common-build-problems) wiki page for any issues encountered
34 | during installation of python versions. You may also want to check [Python's official setup building section](https://devguide.python.org/getting-started/setup-building/#install-dependencies) for latest version dependencies.
35 |
36 | ## Using multiple versions of Python
37 |
38 | A common request for Python is being able to use the `python2` and `python3` commands without needing to switch version.
39 | This can be achieved by setting multiple versions of Python, for example with
40 |
41 | ```
42 | asdf global python 3.6.2 2.7.13
43 | ```
44 |
45 | Executables in the first version will take priority over the executables in the next one. Note that you can use an arbitrary number over versions, if needed.
46 | With the above example, `python` will therefore use the `python` executable found in version 3.6.2.
47 | However, as the `python2` does not exist in Python 3.6.2, `python2` will use the `python2` executable found in version 2.7.13.
48 |
49 | ```
50 | python -V
51 | Python 3.6.2
52 |
53 | python3 -V
54 | Python 3.6.2
55 |
56 | python2 -V
57 | Python 2.7.13
58 | ```
59 |
60 | ## Pip installed modules and binaries
61 |
62 | If you use pip to install a module like ipython that has binaries. You will need to run `asdf reshim python` for the binary to be in your path.
63 |
64 | ## Default Python packages
65 |
66 | asdf-python can automatically install a default set of Python packages with pip right after installing a Python version. To enable this feature, provide a `$HOME/.default-python-packages` file that lists one package per line, for example:
67 |
68 | ```
69 | ansible
70 | pipenv
71 | ```
72 |
73 | You can specify a non-default location of this file by setting a `ASDF_PYTHON_DEFAULT_PACKAGES_FILE` variable.
74 |
--------------------------------------------------------------------------------
/bin/help.config:
--------------------------------------------------------------------------------
1 | # Output should be freeform text. asdf decides how to reformat it.
2 |
3 | echo 'asdf-python can automatically install a default set of Python packages with pip right after installing a Python version. To enable this feature, provide a $HOME/.default-python-packages file that lists one package per line, for example:
4 |
5 | ansible
6 | pipenv
7 |
8 | You can specify a non-default location of this file by setting a ASDF_PYTHON_DEFAULT_PACKAGES_FILE variable.
9 | '
10 |
--------------------------------------------------------------------------------
/bin/help.links:
--------------------------------------------------------------------------------
1 | # Output should be
:
2 |
3 | echo 'home-page: https://github.com/danhper/asdf-python
4 | dependencies: https://github.com/pyenv/pyenv/wiki#suggested-build-environment
5 | troubleshooting: https://github.com/pyenv/pyenv/wiki/Common-build-problems
6 | '
7 |
--------------------------------------------------------------------------------
/bin/help.overview:
--------------------------------------------------------------------------------
1 | # Output should be freeform text. asdf decides how to reformat it.
2 |
3 | echo 'Basic usage:
4 |
5 | asdf install python 3.6.12
6 | asdf global python 3.6.12
7 | python -V
8 | Python 3.6.12
9 |
10 | Check the asdf documentation for more instructions on how to install & manage versions of Python. Please make sure you have the required system dependencies installed before trying to install Python.
11 |
12 | If you use pip to install a module like `ipython` that has binaries, you will need to run `asdf reshim python` for the binary to be in your path.
13 |
14 | Under the hood, asdf-python uses python-build (the same backend of pyenv) to build and install Python. Check its readme for more information about build options and the common build problems wiki page for any issues encountered during installation of python versions.
15 | '
16 |
--------------------------------------------------------------------------------
/bin/install:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | source "$(dirname "$0")/utils.sh"
6 |
7 | install_python() {
8 | local install_type=$1
9 | local version=$2
10 | local install_path=$3
11 |
12 | if [ "$install_type" != "version" ]; then
13 | echoerr "Cannot install specific ref from source, sorry."
14 | echoerr "For a list of available versions, see \`asdf list-all python\`."
15 | exit 1
16 | fi
17 | install_or_update_python_build
18 |
19 | if [[ -n "${ASDF_PYTHON_PATCH_URL:-}" ]]; then
20 | echo "python-build --patch $version $install_path"
21 | echo "with patch file from: $ASDF_PYTHON_PATCH_URL"
22 | $(python_build_path) --patch "$version" "$install_path" < <(curl -sSL "$ASDF_PYTHON_PATCH_URL")
23 | elif [[ -n "${ASDF_PYTHON_PATCHES_DIRECTORY:-}" ]] && [[ -f ${ASDF_PYTHON_PATCHES_DIRECTORY}/${version}.patch ]]; then
24 | local patch_file=${ASDF_PYTHON_PATCHES_DIRECTORY}/${version}.patch
25 | echo "python-build $version $install_path -p < $patch_file"
26 | $(python_build_path) "$version" "$install_path" -p < $patch_file
27 | else
28 | echo "python-build $version $install_path"
29 | $(python_build_path) "$version" "$install_path"
30 | fi
31 | }
32 |
33 | install_default_python_packages() {
34 | local packages_file="${ASDF_PYTHON_DEFAULT_PACKAGES_FILE:-$HOME/.default-python-packages}"
35 |
36 | if [ -f "$packages_file" ]; then
37 | echo -ne "\nInstalling default python packages..."
38 | PATH="$ASDF_INSTALL_PATH/bin:$PATH" pip install -U -r "$packages_file"
39 | fi
40 | }
41 |
42 | ensure_python_build_installed
43 | install_python "$ASDF_INSTALL_TYPE" "$ASDF_INSTALL_VERSION" "$ASDF_INSTALL_PATH"
44 | install_default_python_packages
45 |
--------------------------------------------------------------------------------
/bin/list-all:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source "$(dirname "$0")/utils.sh"
4 |
5 | list_all() {
6 | install_or_update_python_build
7 | $(python_build_path) --definitions | tr '\n' ' '
8 | }
9 |
10 | list_all
11 |
--------------------------------------------------------------------------------
/bin/list-legacy-filenames:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo ".python-version"
4 |
--------------------------------------------------------------------------------
/bin/utils.sh:
--------------------------------------------------------------------------------
1 | echoerr() {
2 | printf "\033[0;31m%s\033[0m" "$1" >&2
3 | }
4 |
5 | ensure_python_build_installed() {
6 | if [ ! -f "$(python_build_path)" ]; then
7 | download_python_build
8 | fi
9 | }
10 |
11 | download_python_build() {
12 | echo "Downloading python-build..." >&2
13 | local pyenv_url="https://github.com/pyenv/pyenv.git"
14 | git clone $pyenv_url "$(pyenv_path)"
15 | }
16 |
17 | python_build_path() {
18 | echo "$(pyenv_path)/plugins/python-build/bin/python-build"
19 | }
20 |
21 | update_python_build() {
22 | cd "$(pyenv_path)" && git fetch && git reset --hard origin/master > /dev/null 2>&1
23 | }
24 |
25 | pyenv_path() {
26 | echo "$(dirname $(dirname $0))/pyenv"
27 | }
28 |
29 | pyenv_update_timestamp_path() {
30 | echo "$(dirname $(dirname "$0"))/pyenv_last_update"
31 | }
32 |
33 | pyenv_should_update() {
34 | update_timeout=3600
35 | update_timestamp_path=$(pyenv_update_timestamp_path)
36 |
37 | if [ ! -f "$update_timestamp_path" ]; then
38 | return 0
39 | fi
40 |
41 | last_update=$(cat "$update_timestamp_path")
42 | current_timestamp=$(date +%s)
43 | invalidated_at=$(($last_update + $update_timeout))
44 |
45 | [ $invalidated_at -lt $current_timestamp ]
46 | }
47 |
48 | install_or_update_python_build() {
49 | if [ ! -f "$(python_build_path)" ]; then
50 | download_python_build
51 | elif pyenv_should_update; then
52 | update_python_build
53 | date +%s > "$(pyenv_update_timestamp_path)"
54 | fi
55 | }
56 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/shims/pip:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | # This script wraps pip to run `asdf reshim` after installs and uninstalls.
4 | # Any other cases are passed-through to pip.
5 | #
6 | # Inspired by the npm shim: https://github.com/asdf-vm/asdf-nodejs/blob/b2d06a768d9a14186db72/shims/npm
7 |
8 | set -euo pipefail
9 |
10 | this_dir=$(dirname "${BASH_SOURCE[0]}")
11 | this_dir=$(cd "$this_dir" && pwd -P) # Normalizes the directory; see https://stackoverflow.com/a/7666/2308068
12 | plugin_name=$(basename "$(dirname "$this_dir")")
13 |
14 | should_reshim() {
15 | if [ "${ASDF_PYTHON_SKIP_RESHIM:-}" ]; then
16 | return 1
17 | fi
18 |
19 | for arg; do
20 | case "$arg" in
21 | install|uninstall)
22 | return 0
23 | ;;
24 | esac
25 | done
26 |
27 | return 1
28 | }
29 |
30 | resolve_pip() {
31 | local pip_location="${ASDF_PYTHON_CANON_PIP_PATH:-$(search_pip_bin)}"
32 |
33 | if ! [ "$pip_location" ]; then
34 | echo "asdf-python couldn't find a suitable pip executable"
35 | echo "This is probably a problem with the plugin, please report this issue"
36 | exit 1
37 | fi
38 |
39 | echo "$pip_location"
40 | }
41 |
42 | search_pip_bin() {
43 | local probably_pip="$(asdf where python)/bin/pip"
44 |
45 | if [ -x "$probably_pip" ]; then
46 | echo "$probably_pip"
47 | return 0
48 | fi
49 |
50 | return 1
51 | }
52 |
53 | wrap_pip() {
54 | local pip=$(resolve_pip)
55 |
56 | if should_reshim "$@"; then
57 | "$pip" "$@"
58 | echo "Reshimming asdf $plugin_name..."
59 | asdf reshim "$plugin_name"
60 | else
61 | exec "$pip" "$@"
62 | fi
63 | }
64 |
65 | wrap_pip "$@"
66 |
67 |
--------------------------------------------------------------------------------