Reputation: 6642
Is it possible to get command-line autocompletion of python -m package.subpackage.module
?
This is similar to, but not the same as, python ./package/subpackage/module.py
, which does autocomplete the directory and file paths. However with -m
, python runs the library's module as a script with the appropriate namespacing and import paths.
I'd like to be able to do python -m package.s[TAB]
and get autocompletion to subpackage
.
Is this feature built in somewhere, or how can I set it up?
Upvotes: 13
Views: 2220
Reputation: 131
Looking for this exact thing, @Amessihel's answer was a very useful start.
I'm posting my solution which built upon it as I believe it could help others, especially if you want:
# man page for "complete": https://manpages.ubuntu.com/manpages/noble/en/man7/bash-builtins.7.html
_python_target() {
local cur prev opts
# Retrieving the current typed argument
cur="${COMP_WORDS[COMP_CWORD]}"
# Retrieving the previous typed argument ("-m" for example)
prev="${COMP_WORDS[COMP_CWORD - 1]}"
# Preparing an array to store available list for completions
# COMREPLY will be checked to suggest the list
COMPREPLY=()
# Here, we only handle the case of "-m"
# we want to leave the autocomplete of the standard usage to the default,
# so COMREPLY stays an empty array and we fallback through "-o default"
if [[ "$prev" != "-m" ]]; then
return 0
fi
# Ensure package exists
PACKAGE_PATH=replace_this_by_your_package_path
if [[ ! -e "$PACKAGE_PATH" ]]; then
echo "$PACKAGE_PATH does not exist on your computer ?"
return 0
fi
# Otherwise, first we retrieve all paths of folder and .py files inside the <your_package> package,
# we keep only the package related section, remove the .py extension and convert their separators into dots
opts="$(find $PACKAGE_PATH/your_package -type d -o -regex ".*py" | sed "s|$PACKAGE_PATH||" | sed "s|\.py||" | sed -e 's+/+.+g' -e 's/^\.//')"
# Then we store the whole list by invoking "compgen" and filling COMREPLY with its output content.
# To mimick standard bash autocompletions we truncate autocomplete to the next folder (identified by dots)
COMPREPLY=($(compgen -W "$opts" -- "$cur" | sed "s|\($cur.[^.]*\).*|\1|" | uniq))
}
complete -F _python_target -o nospace -o bashdefault -o default python
# nospace disables printing of a space at the end of autocomplete,
# it allows to chain the autocomplete but:
# - removes the indication on end of chain that only one match was found.
# - removes the addition of the trailing / for standard python completion on folders
Upvotes: 0
Reputation: 6354
As said in the comment section, you need to extend the bash-completion tool. Then, you'll create a script which handles the cases you need (ie: when the last argument was -m
).
This little sample below shows a start for your custom completion script. Let's name it python_completion.sh
.
_python_target() {
local cur prev opts
# Retrieving the current typed argument
cur="${COMP_WORDS[COMP_CWORD]}"
# Retrieving the previous typed argument ("-m" for example)
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Preparing an array to store available list for completions
# COMREPLY will be checked to suggest the list
COMPREPLY=()
# Here, we'll only handle the case of "-m"
# Hence, the classic autocompletion is disabled
# (ie COMREPLY stays an empty array)
if [[ "$prev" != "-m" ]]
then
return 0
fi
# Retrieving paths and converts their separators into dots
# (if packages doesn't exist, same thing, empty array)
if [[ ! -e "./package" ]]
then
return 0
fi
# Otherwise, we retrieve first the paths starting with "./package"
# and converts their separators into dots
opts="$(find ./package -type d | sed -e 's+/+.+g' -e 's/^\.//' | head)"
# We store the whole list by invoking "compgen" and filling
# COMREPLY with its output content.
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
}
complete -F _python_target python
(Warning. This script has a flaw, it won't work with filenames containing spaces). To test it, run it in the current environnement:
. ./python_completion.sh
And test it:
python -m packag[TAB]
Here is a tutorial to continue in this way.
Upvotes: 2