on master help sign in
[enh] utils/lib.sh - commands pyenv, pyenv.drop pyenv.(un)install
ce9312dMarkus Heiser
utils/lib.sh+211−4
@@ -86,7 +86,7 @@ set_terminal_colors() {
86 _Red='\e[0;31m'
86 _Red='\e[0;31m'
87 _Green='\e[0;32m'
87 _Green='\e[0;32m'
88 _Yellow='\e[0;33m'
88 _Yellow='\e[0;33m'
89− _Blue='\e[0;34m'
89+ _Blue='\e[0;94m'
90 _Violet='\e[0;35m'
90 _Violet='\e[0;35m'
91 _Cyan='\e[0;36m'
91 _Cyan='\e[0;36m'
92
92
@@ -95,12 +95,12 @@ set_terminal_colors() {
95 _BRed='\e[1;31m'
95 _BRed='\e[1;31m'
96 _BGreen='\e[1;32m'
96 _BGreen='\e[1;32m'
97 _BYellow='\e[1;33m'
97 _BYellow='\e[1;33m'
98− _BBlue='\e[1;34m'
98+ _BBlue='\e[1;94m'
99 _BPurple='\e[1;35m'
99 _BPurple='\e[1;35m'
100 _BCyan='\e[1;36m'
100 _BCyan='\e[1;36m'
101}
101}
102
102
103−if [ ! -p /dev/stdout ]; then
103+if [ ! -p /dev/stdout ] && [ ! "$TERM" = 'dumb' ] && [ ! "$TERM" = 'unknown' ]; then
104 set_terminal_colors
104 set_terminal_colors
105fi
105fi
106
106
@@ -152,6 +152,12 @@ err_msg() { echo -e "${_BRed}ERROR:${_creset} $*" >&2; }
152warn_msg() { echo -e "${_BBlue}WARN:${_creset} $*" >&2; }
152warn_msg() { echo -e "${_BBlue}WARN:${_creset} $*" >&2; }
153info_msg() { echo -e "${_BYellow}INFO:${_creset} $*" >&2; }
153info_msg() { echo -e "${_BYellow}INFO:${_creset} $*" >&2; }
154
154
155+build_msg() {
156+ local tag="$1 "
157+ shift
158+ echo -e "${_Blue}${tag:0:10}${_creset}$*"
159+}
160+
155clean_stdin() {
161clean_stdin() {
156 if [[ $(uname -s) != 'Darwin' ]]; then
162 if [[ $(uname -s) != 'Darwin' ]]; then
157 while read -r -n1 -t 0.1; do : ; done
163 while read -r -n1 -t 0.1; do : ; done
@@ -496,6 +502,203 @@ service_is_available() {
496 return "$exit_val"
502 return "$exit_val"
497}
503}
498
504
505+# python
506+# ------
507+
508+PY="${PY:=3}"
509+PYTHON="${PYTHON:=python$PY}"
510+PY_ENV="${PY_ENV:=local/py${PY}}"
511+PY_ENV_BIN="${PY_ENV}/bin"
512+PY_ENV_REQ="${PY_ENV_REQ:=${REPO_ROOT}/requirements*.txt}"
513+
514+# List of python packages (folders) or modules (files) installed by command:
515+# pyenv.install
516+PYOBJECTS="${PYOBJECTS:=.}"
517+
518+# folder where the python distribution takes place
519+PYDIST="${PYDIST:=dist}"
520+
521+# folder where the intermediate build files take place
522+PYBUILD="${PYBUILD:=build/py${PY}}"
523+
524+# https://www.python.org/dev/peps/pep-0508/#extras
525+#PY_SETUP_EXTRAS='[develop,test]'
526+PY_SETUP_EXTRAS="${PY_SETUP_EXTRAS:=[develop,test]}"
527+
528+PIP_BOILERPLATE=( pip wheel setuptools )
529+
530+# shellcheck disable=SC2120
531+pyenv() {
532+
533+ # usage: pyenv [vtenv_opts ...]
534+ #
535+ # vtenv_opts: see 'pip install --help'
536+ #
537+ # Builds virtualenv with 'requirements*.txt' (PY_ENV_REQ) installed. The
538+ # virtualenv will be reused by validating sha256sum of the requirement
539+ # files.
540+
541+ required_commands \
542+ sha256sum "${PYTHON}" \
543+ || exit
544+
545+ local pip_req=()
546+
547+ if ! pyenv.OK > /dev/null; then
548+ rm -f "${PY_ENV}/${PY_ENV_REQ}.sha256"
549+ pyenv.drop > /dev/null
550+ build_msg PYENV "[virtualenv] installing ${PY_ENV_REQ} into ${PY_ENV}"
551+
552+ "${PYTHON}" -m venv "$@" "${PY_ENV}"
553+ "${PY_ENV_BIN}/python" -m pip install -U "${PIP_BOILERPLATE[@]}"
554+
555+ for i in ${PY_ENV_REQ}; do
556+ pip_req=( "${pip_req[@]}" "-r" "$i" )
557+ done
558+
559+ (
560+ [ "$VERBOSE" = "1" ] && set -x
561+ # shellcheck disable=SC2086
562+ "${PY_ENV_BIN}/python" -m pip install "${pip_req[@]}" \
563+ && sha256sum ${PY_ENV_REQ} > "${PY_ENV}/requirements.sha256"
564+ )
565+ fi
566+ pyenv.OK
567+}
568+
569+_pyenv_OK=''
570+pyenv.OK() {
571+
572+ # probes if pyenv exists and runs the script from pyenv.check
573+
574+ [ "$_pyenv_OK" == "OK" ] && return 0
575+
576+ if [ ! -f "${PY_ENV_BIN}/python" ]; then
577+ build_msg PYENV "[virtualenv] missing ${PY_ENV_BIN}/python"
578+ return 1
579+ fi
580+
581+ if [ ! -f "${PY_ENV}/requirements.sha256" ] \
582+ || ! sha256sum --check --status <"${PY_ENV}/requirements.sha256" 2>/dev/null; then
583+ build_msg PYENV "[virtualenv] requirements.sha256 failed"
584+ sed 's/^/ [virtualenv] - /' <"${PY_ENV}/requirements.sha256"
585+ return 1
586+ fi
587+
588+ pyenv.check \
589+ | "${PY_ENV_BIN}/python" 2>&1 \
590+ | prefix_stdout "${_Blue}PYENV ${_creset}[check] "
591+
592+ local err=${PIPESTATUS[1]}
593+ if [ "$err" -ne "0" ]; then
594+ build_msg PYENV "[check] python test failed"
595+ return "$err"
596+ fi
597+
598+ build_msg PYENV "OK"
599+ _pyenv_OK="OK"
600+ return 0
601+}
602+
603+pyenv.drop() {
604+
605+ build_msg PYENV "[virtualenv] drop ${PY_ENV}"
606+ rm -rf "${PY_ENV}"
607+ _pyenv_OK=''
608+
609+}
610+
611+pyenv.check() {
612+
613+ # Prompts a python script with additional checks. Used by pyenv.OK to check
614+ # if virtualenv is ready to install python objects. This function should be
615+ # overwritten by the application script.
616+
617+ local imp=""
618+
619+ for i in "${PIP_BOILERPLATE[@]}"; do
620+ imp="$imp, $i"
621+ done
622+
623+ cat <<EOF
624+import ${imp#,*}
625+
626+EOF
627+}
628+
629+pyenv.install() {
630+
631+ if ! pyenv.OK; then
632+ py.clean > /dev/null
633+ fi
634+ if ! pyenv.install.OK > /dev/null; then
635+ build_msg PYENV "[install] ${PYOBJECTS}"
636+ if ! pyenv.OK >/dev/null; then
637+ pyenv
638+ fi
639+ for i in ${PYOBJECTS}; do
640+ build_msg PYENV "[install] pip install -e '$i${PY_SETUP_EXTRAS}'"
641+ "${PY_ENV_BIN}/python" -m pip install -e "$i${PY_SETUP_EXTRAS}"
642+ done
643+ fi
644+ pyenv.install.OK
645+}
646+
647+_pyenv_install_OK=''
648+pyenv.install.OK() {
649+
650+ [ "$_pyenv_install_OK" == "OK" ] && return 0
651+
652+ local imp=""
653+ local err=""
654+
655+ if [ "." = "${PYOBJECTS}" ]; then
656+ imp="import $(basename "$(pwd)")"
657+ else
658+ # shellcheck disable=SC2086
659+ for i in ${PYOBJECTS}; do imp="$imp, $i"; done
660+ imp="import ${imp#,*} "
661+ fi
662+ (
663+ [ "$VERBOSE" = "1" ] && set -x
664+ "${PY_ENV_BIN}/python" -c "import sys; sys.path.pop(0); $imp;" 2>/dev/null
665+ )
666+
667+ err=$?
668+ if [ "$err" -ne "0" ]; then
669+ build_msg PYENV "[install] python installation test failed"
670+ return "$err"
671+ fi
672+
673+ build_msg PYENV "[install] OK"
674+ _pyenv_install_OK="OK"
675+ return 0
676+}
677+
678+pyenv.uninstall() {
679+
680+ build_msg PYENV "[uninstall] ${PYOBJECTS}"
681+
682+ if [ "." = "${PYOBJECTS}" ]; then
683+ pyenv.cmd python setup.py develop --uninstall 2>&1 \
684+ | prefix_stdout "${_Blue}PYENV ${_creset}[pyenv.uninstall] "
685+ else
686+ pyenv.cmd python -m pip uninstall --yes ${PYOBJECTS} 2>&1 \
687+ | prefix_stdout "${_Blue}PYENV ${_creset}[pyenv.uninstall] "
688+ fi
689+}
690+
691+
692+pyenv.cmd() {
693+ pyenv.install
694+ ( set -e
695+ # shellcheck source=/dev/null
696+ source "${PY_ENV_BIN}/activate"
697+ [ "$VERBOSE" = "1" ] && set -x
698+ "$@"
699+ )
700+}
701+
499# golang
702# golang
500# ------
703# ------
501
704
@@ -1250,7 +1453,7 @@ pkg_install() {
1250 centos)
1453 centos)
1251 # shellcheck disable=SC2068
1454 # shellcheck disable=SC2068
1252 yum install -y $@
1455 yum install -y $@
1253− ;;
1456+ ;;
1254 esac
1457 esac
1255}
1458}
1256
1459
@@ -1382,6 +1585,10 @@ LXC_ENV_FOLDER=
1382if in_container; then
1585if in_container; then
1383 # shellcheck disable=SC2034
1586 # shellcheck disable=SC2034
1384 LXC_ENV_FOLDER="lxc-env/$(hostname)/"
1587 LXC_ENV_FOLDER="lxc-env/$(hostname)/"
1588+ PY_ENV="${LXC_ENV_FOLDER}${PY_ENV}"
1589+ PY_ENV_BIN="${LXC_ENV_FOLDER}${PY_ENV_BIN}"
1590+ PYDIST="${LXC_ENV_FOLDER}${PYDIST}"
1591+ PYBUILD="${LXC_ENV_FOLDER}${PYBUILD}"
1385fi
1592fi
1386
1593
1387lxc_init_container_env() {
1594lxc_init_container_env() {