Hi, some time ago I've adopted an existing completion script for pip, which has been removed from https://github.com/zsh-users/zsh-completions (in https://github.com/zsh-users/zsh-completions/commit/890f3701). I've also looked at oh-my-zsh's plugin (https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/pip/_pip), where I've adopted the idea to provide the list of installable packages from. I've enhanced it to provide completion for installable packages (both using the PyPI's simple and XMLRPC interface), and made it use pip's completion interface to get the list of un-installable packages. There are other fixes, like for completing filenames with "pip install -r". It is currently maintained at: https://gist.github.com/blueyed/54a257c411310a28805a I am attaching the current revision (cbd8ec9a). I would appreciate feedback on it and then think it would be nice to have it included in Zsh itself. zsh-users/zsh-completions's policy is to not provide completion scripts, if upstream does so - but pip's distributed completion is very limited: % pip completion --zsh # pip zsh completion start function _pip_completion { local words cword read -Ac words read -cn cword reply=( $( COMP_WORDS="$words[*]" \ COMP_CWORD=$(( cword-1 )) \ PIP_AUTO_COMPLETE=1 $words[1] ) ) } compctl -K _pip_completion pip # pip zsh completion end Thanks, Daniel.
#compdef -P pip[0-9.]# # # Completion script for pip (http://pypi.python.org/pypi/pip). # # Taken from https://github.com/zsh-users/zsh-completions/commit/890f3701 # (where it got removed). Original source: # https://github.com/technolize/zsh-completion-funcs. # # Currently maintained in https://gist.github.com/blueyed/54a257c411310a28805a. local ret=1 state # Setup $python, based on pip version from word[1]. # Must get done early, otherwise $words might have been changed already. local python pip pip=${words[1]} python=${${pip}/pip/python} [[ $python == $pip ]] && python=python # The index(es) to use. The simple index only provides a full list, while # the xmlrpc index can be queried by "last updated in". # This should be probably made configurable through zstyle and then be used # in the cache name. # For the xmlrpc index, the "days" could be made configurable typeset -a ZSH_PIP_INDEXES ZSH_PIP_XMLRPC_INDEXES # ZSH_PIP_INDEXES=(https://pypi.python.org/simple/) ZSH_PIP_XMLRPC_INDEXES=(https://pypi.python.org/pypi) local ZSH_PIP_XMLRPC_INDEX_DAYS=365 # Get all packages from (remote) ZSH_PIP_INDEXES. _pip_all() { _pip_set_cache_policy if ( ! (( $+_zsh_all_pkgs )) || _cache_invalid pip_allpkgs ) && ! _retrieve_cache pip_allpkgs; then typeset -a -g _zsh_all_pkgs _zsh_all_pkgs=() # Build XMLPC request to pypi.python.org to get a list of packages updated # in the last year. This is meant to reduce the number of packages. local ts_1year=$(date +%s -d @$(( $(date +%s) - 86400 * ZSH_PIP_XMLRPC_INDEX_DAYS ))) local xml_req="<?xml version='1.0'?><methodCall><methodName>updated_releases</methodName><params><param><value><int>${ts_1year}</int></value></param></params></methodCall>" for xmlrpc_index in $ZSH_PIP_XMLRPC_INDEXES; do _zsh_all_pkgs+=( $(echo $xml_req | curl -s -d @- -H "Content-Type: text/xml" $xmlrpc_index \ | sed -n '/^<value><string>/p' | sed -n '1~2 s/^<value><string>//p' | sed -n 's/<\/string><\/value>$//p' \ | tr '\n' ' ') ) done for index in $ZSH_PIP_INDEXES; do # All packages, more than 54000! _zsh_all_pkgs+=( $(curl -s $index \ | sed -n '/<a href/ s/.*>\([^<]\{1,\}\).*/\1/p' \ | tr '\n' ' ') ) done _store_cache pip_allpkgs _zsh_all_pkgs fi } # Get installed packages, using pips completion interface. _pip_installed() { _pip_set_cache_policy if ( ! (( $+_zsh_installed_pkgs )) || _cache_invalid pip_installedpkgs ) && ! _retrieve_cache pip_installedpkgs; then typeset -a -g _zsh_installed_pkgs # _zsh_installed_pkgs=($($pip freeze | cut -d '=' -f 1)) _zsh_installed_pkgs=($(COMP_WORDS="pip uninstall" COMP_CWORD="2" PIP_AUTO_COMPLETE=1 $pip)) _store_cache pip_installedpkgs _zsh_installed_pkgs fi } _pip_set_cache_policy() { local cache_policy zstyle -s ":completion:${curcontext}:" cache-policy cache_policy if [[ -z "$cache_policy" ]]; then zstyle ":completion:${curcontext}:" cache-policy _pip_caching_policy fi } _pip_caching_policy() { if [[ ${1:t} == pip_allpkgs ]]; then # rebuild if cache is more than two weeks old local -a oldp oldp=( "$1"(Nm+14) ) (( $#oldp )) else # pip_installedpkgs # Compare cache file's timestamp to the most recently modified sys.path entry. # This gets changed/touched when installing removing packages. local newest_sys_path=$($python -c ' import sys from os.path import exists, getmtime print(sorted(sys.path, key=lambda x: exists(x) and getmtime(x))[-1])') [[ $newest_sys_path -nt $1 ]] fi } local -a common_ops common_ops=( {-h,--help}"[show help]" {-v,--verbose}"[give more output]" {-V,--version}"[show version and exit]" {-q,--quiet}"[give less output]" "--log=[log file where a complete record will be kept]:file" "--proxy=[specify a proxy in the form user:passwd@proxy.server:port]:proxy" "--timeout=[set the socket timeout (default 15 seconds)]:second" "--exists-action=[default action when a path already exists: (s)witch, (i)gnore, (w)ipe, (b)ackup]:action" "--cert=[path to alternate CA bundle]:path" ) _directories () { _wanted directories expl directory _path_files -/ "$@" - } _requirements_file () { # Prefer requirement* and *.txt pattern. This should fall back to "all files", according to the file-patterns style. _wanted files expl file _files -g 'requirement*' -g '*.txt' "$@" - } typeset -A opt_args _arguments \ ':subcommand:->subcommand' \ $common_ops \ '*::options:->options' && ret=0 case $state in subcommand) local -a subcommands subcommands=( "install:install packages" "uninstall:uninstall packages" "freeze:put all currently installed packages" "list:list installed packages" "show:show information about installed packages" "search:search pypi" "wheel:build wheels from your requirements" "zip:zip dividual packages" "unzip:unzip undividual packages" "bundle:create pybundle" "help:show available commands" ) _describe -t subcommands 'pip subcommand' subcommands && ret=0 ;; options) local -a args args=( $common_ops ) local -a pi_ops pi_ops=( {-i,--index-url=}"[base URL of Python Package Index]:URL" "--extra-index-url=[extra URLs of package indexes to use in addition to --index-url]:URL" "--no-index[ignore package index (only looking at --find-links URLs instead)]" {-f,--find-links=}"[URL to look for packages at]:URL" {-M,--use-mirrors}"[use the PyPI mirrors as a fallback in case the main index is down]" "--mirrors=[specific mirror URLs to query when --use-mirrors is used]:URL" "--allow-external=[allow the installation of externally hosted files]:package" "--allow-all-external[allow the installation of all externally hosted files]" "--no-allow-external[disallow the installation of all externally hosted files]" "--allow-insecure=[allow the installation of insecure and unverifiable files]:package" "--no-allow-insecure[disallow the installation of insecure and unverifiable files]" ) case $words[1] in install | bundle) args+=( {-e,--editable=}"[install a package directly from a checkout]:directory or VCS+REPOS_URL[@REV]#egg=PACKAGE:_files -/" {-r,--requirement=}"[install all the packages listed in the given requirements file]:requirements file:_requirements_file" {-b,--build=}"[unpack packages into DIR]:directory:_directories" {-t,--target=}"[install packages into DIR]:directory:_directories" {-d,--download=}"[download packages into DIR instead of installing them]:directory:_directories" "--download-cache=[cache downloaded packages in DIR]:directory:_directories" "--src=[check out --editable packages into DIR]:directory:_directories" {-U,--upgrade}"[upgrade all packages to the newest available version]" "--force-reinstall[when upgrading, reinstall all packages even if they are already up-to-date]" {-I,--ignore-installed}"[ignore the installed packages]" "--no-deps[don't install package dependencies]" "--no-install[download and unpack all packages, but don't actually install them]" "--no-download[don't download any packages, just install the ones already downloaded]" "--install-option=[extra arguments to be supplied to the setup.py install command]:options" "--global-option=[Extra global options to be supplied to the setup.py call before the install command]:options" "--user[install using the user scheme]" "--egg[install as self contained egg file, like easy_install does]" "--root=[Install everything relative to this alternate root directory]:directory:_directories" "--use-wheel[find and prefer wheel archives when searching indexes and find-links locations]" "--pre[include pre-release and development versions]" "--no-clean[don't clean up build directories]" ':package name:->pkgs_all' $pi_ops ) ;; uninstall) args+=( {-r,--requirement=}"[install all the packages listed in the given requirements file]::requirements file:_requirements_file" {-y,--yes}"[don't ask for confirmation of uninstall deletions]" ':installed package:->pkgs_installed' ) ;; freeze) args+=( {-r,--requirement=}"[install all the packages listed in the given requirements file]::requirements file:_requirements_file" {-f,--find-links=}"[URL to look for packages at]:URL" {-l,--local}"[If in a virtualenv that has global access, do not list globally-installed packages]" ) ;; list) args+=( {-o,--outdated}"[list outdated packages (excluding editables)]" {-u,--uptodated}"[list uptodated packages (excluding editables)]" {-e,--editable}"[list editable projects]" {-l,--local}"[If in a virtualenv that has global access, do not list globally-installed packages]" "--pre[include pre-release and development versions]" $pi_ops ) ;; show) args+=( {-f,--files}"[show the full list of installed files for each package]" ':installed package:->pkgs_installed' ) ;; search) args+=( "--index[base URL of Python Package Index]:URL" ) ;; wheel) args+=( {-w,--wheel-dir=}"[build wheels into DIR, where the default is '<cwd>/wheelhouse']:directory:_directories" "--use-wheel[find and prefer wheel archives when searching indexes and find-links locations]" "--build-option=[extra arguments to be supplied to 'setup.py bdist_wheel']:options" {-r,--requirement=}"[install all the packages listed in the given requirements file]::requirements file:_requirements_file" "--download-cache=[cache downloaded packages in DIR]:directory:_directories" "--no-deps[don't install package dependencies]" {-b,--build=}"[directory to unpack packages into and build in]:directory:_directories" "--global-option=[extra global options to be supplied to the setup.py call before the 'bdist_wheel' command]:options" "--pre[include pre-release and development versions]" "--no-clean[don't clean up build directories]" $pi_ops ) ;; unzip | zip) args+=( "--unzip[unzip a package]" "--no-pyc[do not include .pyc files in zip files]" {-l,--list}"[list the packages available, and their zip status]" "--sort-files[with --list, sort packages according to how many files they contain]" "--path=[restrict operation to the given paths]:paths" {-n,--simulate}"[do not actually perform the zip/unzip operation]" ) ;; esac _arguments $args && ret=0 # Additional state for expensive actions. case $state in pkgs_all) _pip_all _wanted _zsh_all_pkgs expl 'packages' compadd -a _zsh_all_pkgs && ret=0 ;; pkgs_installed) _pip_installed _wanted _zsh_installed_pkgs expl 'installed packages' compadd -a _zsh_installed_pkgs && ret=0 ;; esac ;; esac return ret # Local Variables: # mode: Shell-Script # sh-indentation: 2 # indent-tabs-mode: nil # sh-basic-offset: 2 # End: # vim: ft=zsh sw=2 ts=2 et
Attachment:
signature.asc
Description: OpenPGP digital signature