[pyenv] 2. 项目实现

一个优秀的项目在定位和思惟上都是非常值的借鉴的. 在pyenv我们除了可以学习到shell的一些高级使用技巧外, 还有其框架设计思路.

  1. PS4 变量, 让运行bug定位更简单.

  2. compgen 命令补齐

# 代码在 completions/pyenv.bash

_pyenv() {
  COMPREPLY=()
  local word="${COMP_WORDS[COMP_CWORD]}"

  if [ "$COMP_CWORD" -eq 1 ]; then
    COMPREPLY=( $(compgen -W "$(pyenv commands)" -- "$word") )
  else
    local words=("${COMP_WORDS[@]}")
    unset words[0]
    unset words[$COMP_CWORD]
    local completions=$(pyenv completions "${words[@]}")
    COMPREPLY=( $(compgen -W "$completions" -- "$word") )
  fi
}

complete -F _pyenv pyenv

shims 指令拦截
在pyenv中, 由于设置的命令路径在PATH变量的最前方, 所以想当与做了一个命令拦截, 拦截的命令在shims 中, 大致格式都是

#!/usr/bin/env bash
set -e
[ -n "$PYENV_DEBUG" ] && set -x

program="${0##*/}"

export PYENV_ROOT="/home/lhpc04/workspace/program/pyenv"
exec "/home/lhpc04/workspace/program/pyenv/libexec/pyenv" exec "$program" "$@"

相当于使用pyenv exec做个判断是否继续执行, 在环境判断不能通过, 直接进行拦截和提示.

pyenv 节选脚本注释

... ...
if enable -f "${BASH_SOURCE%/*}"/../libexec/pyenv-realpath.dylib realpath 2>/dev/null; then   # dylib 是 iOS 系统的动态库, 
# 如果是苹果系统,将引入 pyenv-realpath.dylib 
# 定义 abs_dirname 找路径中目录的绝对路径函数
  abs_dirname() {
    local path
    path="$(realpath "$1")"
    echo "${path%/*}"
  }
else
# 如果不是 iOS 系统, 默认为linux系统, 使用系统下的 readlink 命令.
  [ -z "$PYENV_NATIVE_EXT" ] || abort "failed to load \`realpath' builtin"

  READLINK=$(type -P readlink) 
  # 如果没找到 readlink, 就退出
  [ -n "$READLINK" ] || abort "cannot find readlink - are you missing GNU coreutils?"
  echo $READLINK
  resolve_link() {
    $READLINK "$1"
  }
# 定义 abs_dirname 找路径中目录的绝对路径函数
  abs_dirname() {
    local path="$1"

    # Use a subshell to avoid changing the current path
    (
    while [ -n "$path" ]; do
      cd_path="${path%/*}"
      if [[ "$cd_path" != "$path" ]]; then
        cd "$cd_path"
      fi
      name="${path##*/}"
      path="$(resolve_link "$name" || true)"
    done

    echo "$PWD"
    )
  }
fi

  • 加载插件, 默认有 pyenv-doctor pyenv-update pyenv-virtualenv python-build 几个插件.
... ...

#  
bin_path="$(abs_dirname "$0")"
for plugin_bin in "${bin_path%/*}"/plugins/*/bin; do
  PATH="${plugin_bin}:${PATH}"
done
# PYENV_ROOT can be set to anything, so it may happen to be equal to the base path above,
# resulting in duplicate PATH entries
if [ "${bin_path%/*}" != "$PYENV_ROOT" ]; then
  for plugin_bin in "${PYENV_ROOT}"/plugins/*/bin; do
    PATH="${plugin_bin}:${PATH}"
  done
fi


  • 加载回调, 如果命令在回调中, 则先执行回调函数, 默认回调 在 pyenv.d 目录中.


PYENV_HOOK_PATH="${PYENV_HOOK_PATH}:${PYENV_ROOT}/pyenv.d"
if [ "${bin_path%/*}" != "$PYENV_ROOT" ]; then
  # Add pyenv's own `pyenv.d` unless pyenv was cloned to PYENV_ROOT
  PYENV_HOOK_PATH="${PYENV_HOOK_PATH}:${bin_path%/*}/pyenv.d"
fi
PYENV_HOOK_PATH="${PYENV_HOOK_PATH}:/usr/local/etc/pyenv.d:/etc/pyenv.d:/usr/lib/pyenv/hooks"
for plugin_hook in "${PYENV_ROOT}/plugins/"*/etc/pyenv.d; do
  PYENV_HOOK_PATH="${PYENV_HOOK_PATH}:${plugin_hook}"
done
PYENV_HOOK_PATH="${PYENV_HOOK_PATH#:}"
export PYENV_HOOK_PATH



  • 解析并执行指令
command="$1"
case "$command" in
"" )
  { pyenv---version
    pyenv-help
  } | abort
  ;;
-v | --version )
  exec pyenv---version
  ;;
-h | --help )
  exec pyenv-help
  ;;
* )
  command_path="$(command -v "pyenv-$command" || true)"
  if [ -z "$command_path" ]; then
    if [ "$command" == "shell" ]; then
      abort "shell integration not enabled. Run \`pyenv init' for instructions."
    else
      abort "no such command \`$command'"
    fi
  fi

  shift 1 
  if [ "$1" = --help ]; then
    if [[ "$command" == "sh-"* ]]; then
      echo "pyenv help \"$command\""
    else
      exec pyenv-help "$command"
    fi
  else
    exec "$command_path" "$@"
  fi
  ;;
esac

pyenv 的不足:

  1. 没有做包列表统一管理, 每个环境都是独立安传包, 这可能会有一些冗余.
  2. 虚拟环境之间没有继承关系, 每次搭建环境都需要重新安装所有包,
  3. 虚拟环境没有提供重命名等管理.
  4. shell 实现, 项目上手需要些积累.

参考

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值