diff --git a/argcomplete/scripts/__init__.py b/argcomplete/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/argcomplete/scripts/activate_global_python_argcomplete.py b/argcomplete/scripts/activate_global_python_argcomplete.py new file mode 100755 index 0000000..01142fe --- /dev/null +++ b/argcomplete/scripts/activate_global_python_argcomplete.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK + +# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. +# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. + +""" +Activate the generic bash-completion script or zsh completion autoload function for the argcomplete module. +""" + +import argparse +import os +import shutil +import site +import subprocess +import sys + +import argcomplete + +# PEP 366 +__package__ = "argcomplete.scripts" + +zsh_shellcode = """ +# Begin added by argcomplete +fpath=( {zsh_fpath} "${{fpath[@]}}" ) +# End added by argcomplete +""" + +bash_shellcode = """ +# Begin added by argcomplete +source "{activator}" +# End added by argcomplete +""" + +parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) +parser.add_argument("-y", "--yes", help="automatically answer yes for all questions", action="store_true") +parser.add_argument("--dest", help='Specify the shell completion modules directory to install into, or "-" for stdout') +parser.add_argument("--user", help="Install into user directory", action="store_true") +argcomplete.autocomplete(parser) +args = None + + +def get_local_dir(): + try: + return subprocess.check_output(["brew", "--prefix"]).decode().strip() + except (FileNotFoundError, subprocess.CalledProcessError): + return "/usr/local" + + +def get_zsh_system_dir(): + return f"{get_local_dir()}/share/zsh/site-functions" + + +def get_bash_system_dir(): + if "BASH_COMPLETION_COMPAT_DIR" in os.environ: + return os.environ["BASH_COMPLETION_COMPAT_DIR"] + elif sys.platform == "darwin": + return f"{get_local_dir()}/etc/bash_completion.d" # created by homebrew + else: + return "/etc/bash_completion.d" # created by bash-completion + + +def get_activator_dir(): + return os.path.join(os.path.abspath(os.path.dirname(argcomplete.__file__)), "bash_completion.d") + + +def get_activator_path(): + return os.path.join(get_activator_dir(), "_python-argcomplete") + + +def install_to_destination(dest): + activator = get_activator_path() + if dest == "-": + with open(activator) as fh: + sys.stdout.write(fh.read()) + return + destdir = os.path.dirname(dest) + if not os.path.exists(destdir): + try: + os.makedirs(destdir, exist_ok=True) + except Exception as e: + parser.error(f"path {destdir} does not exist and could not be created: {e}") + try: + print(f"Installing {activator} to {dest}...", file=sys.stderr) + shutil.copy(activator, dest) + print("Installed.", file=sys.stderr) + except Exception as e: + parser.error( + f"while installing to {dest}: {e}. Please run this command using sudo, or see --help for more options." + ) + + +def get_consent(): + if args.yes is True: + return True + while True: + res = input("OK to proceed? [y/n] ") + if res.lower() not in {"y", "n", "yes", "no"}: + print('Please answer "yes" or "no".', file=sys.stderr) + elif res.lower() in {"y", "yes"}: + return True + else: + return False + + +def append_to_config_file(path, shellcode): + if os.path.exists(path): + with open(path, 'r') as fh: + if shellcode in fh.read(): + print(f"The code already exists in the file {path}.", file=sys.stderr) + return + print(f"argcomplete needs to append to the file {path}. The following code will be appended:", file=sys.stderr) + for line in shellcode.splitlines(): + print(">", line, file=sys.stderr) + if not get_consent(): + print("Not added.", file=sys.stderr) + return + print(f"Adding shellcode to {path}...", file=sys.stderr) + with open(path, "a") as fh: + fh.write(shellcode) + print("Added.", file=sys.stderr) + + +def link_user_rcfiles(): + # TODO: warn if running as superuser + zsh_rcfile = os.path.join(os.path.expanduser(os.environ.get("ZDOTDIR", "~")), ".zshenv") + append_to_config_file(zsh_rcfile, zsh_shellcode.format(zsh_fpath=get_activator_dir())) + + bash_completion_user_file = os.path.expanduser("~/.bash_completion") + append_to_config_file(bash_completion_user_file, bash_shellcode.format(activator=get_activator_path())) + + +def main(): + global args + args = parser.parse_args() + + destinations = [] + + if args.dest: + if args.dest != "-" and not os.path.exists(args.dest): + parser.error(f"directory {args.dest} was specified via --dest, but it does not exist") + destinations.append(args.dest) + elif site.ENABLE_USER_SITE and site.USER_SITE in argcomplete.__file__: + print( + "Argcomplete was installed in the user site local directory. Defaulting to user installation.", file=sys.stderr + ) + link_user_rcfiles() + elif sys.prefix != sys.base_prefix: + print("Argcomplete was installed in a virtual environment. Defaulting to user installation.", file=sys.stderr) + link_user_rcfiles() + elif args.user: + link_user_rcfiles() + else: + print("Defaulting to system-wide installation.", file=sys.stderr) + destinations.append(f"{get_zsh_system_dir()}/_python-argcomplete") + destinations.append(f"{get_bash_system_dir()}/python-argcomplete") + + for destination in destinations: + install_to_destination(destination) + + if args.dest is None: + print("Please restart your shell or source the installed file to activate it.", file=sys.stderr) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/argcomplete/scripts/python_argcomplete_check_easy_install_script.py b/argcomplete/scripts/python_argcomplete_check_easy_install_script.py new file mode 100755 index 0000000..b74a745 --- /dev/null +++ b/argcomplete/scripts/python_argcomplete_check_easy_install_script.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. +# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. + +""" +This script is part of the Python argcomplete package (https://github.com/kislyuk/argcomplete). +It is used to check if an EASY-INSTALL-SCRIPT wrapper redirects to a script that contains the string +"PYTHON_ARGCOMPLETE_OK". If you have enabled global completion in argcomplete, the completion hook will run it every +time you press in your shell. + +Usage: + python-argcomplete-check-easy-install-script +""" + +import sys + +# PEP 366 +__package__ = "argcomplete.scripts" + +def main(): + if len(sys.argv) != 2: + sys.exit(__doc__) + + sys.tracebacklimit = 0 + + with open(sys.argv[1]) as fh: + line1, head = fh.read(1024).split("\n", 1)[:2] + if line1.startswith("#") and ("py" in line1 or "Py" in line1): + import re + + lines = head.split("\n", 12) + for line in lines: + if line.startswith("# EASY-INSTALL-SCRIPT"): + import pkg_resources + + dist, script = re.match("# EASY-INSTALL-SCRIPT: '(.+)','(.+)'", line).groups() + if "PYTHON_ARGCOMPLETE_OK" in pkg_resources.get_distribution(dist).get_metadata("scripts/" + script): + return 0 + elif line.startswith("# EASY-INSTALL-ENTRY-SCRIPT"): + dist, group, name = re.match("# EASY-INSTALL-ENTRY-SCRIPT: '(.+)','(.+)','(.+)'", line).groups() + import pkgutil + + import pkg_resources + + module_name = pkg_resources.get_distribution(dist).get_entry_info(group, name).module_name + with open(pkgutil.get_loader(module_name).get_filename()) as mod_fh: + if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): + return 0 + elif line.startswith("# EASY-INSTALL-DEV-SCRIPT"): + for line2 in lines: + if line2.startswith("__file__"): + filename = re.match("__file__ = '(.+)'", line2).group(1) + with open(filename) as mod_fh: + if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): + return 0 + elif line.startswith("# PBR Generated"): + module = re.search("from (.*) import", head).groups()[0] + import pkgutil + + import pkg_resources + + with open(pkgutil.get_loader(module).get_filename()) as mod_fh: + if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): + return 0 + + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/argcomplete/scripts/register_python_argcomplete.py b/argcomplete/scripts/register_python_argcomplete.py new file mode 100755 index 0000000..d2f6ae9 --- /dev/null +++ b/argcomplete/scripts/register_python_argcomplete.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK + +# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. +# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. + +""" +Register a Python executable for use with the argcomplete module. + +To perform the registration, source the output of this script in your bash shell +(quote the output to avoid interpolation). + +Example: + + $ eval "$(register-python-argcomplete my-favorite-script.py)" + +For Tcsh + + $ eval `register-python-argcomplete --shell tcsh my-favorite-script.py` + +For Fish + + $ register-python-argcomplete --shell fish my-favourite-script.py > ~/.config/fish/my-favourite-script.py.fish +""" + +import argparse +import sys + +import argcomplete + +# PEP 366 +__package__ = "argcomplete.scripts" + +def main(): + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument( + "--no-defaults", + dest="use_defaults", + action="store_false", + default=True, + help="when no matches are generated, do not fallback to readline's default completion (affects bash only)", + ) + parser.add_argument( + "--complete-arguments", + nargs=argparse.REMAINDER, + help="arguments to call complete with; use of this option discards default options (affects bash only)", + ) + parser.add_argument( + "-s", + "--shell", + choices=("bash", "zsh", "tcsh", "fish", "powershell"), + default="bash", + help="output code for the specified shell", + ) + parser.add_argument( + "-e", "--external-argcomplete-script", help="external argcomplete script for auto completion of the executable" + ) + + parser.add_argument("executable", nargs="+", help="executable to completed (when invoked by exactly this name)") + + argcomplete.autocomplete(parser) + + if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + + args = parser.parse_args() + + + sys.stdout.write( + argcomplete.shellcode( + args.executable, args.use_defaults, args.shell, args.complete_arguments, args.external_argcomplete_script + ) + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/activate-global-python-argcomplete b/scripts/activate-global-python-argcomplete old mode 100755 new mode 100644 index 96017cb..0b661a3 --- a/scripts/activate-global-python-argcomplete +++ b/scripts/activate-global-python-argcomplete @@ -1,154 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK # Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. -""" -Activate the generic bash-completion script or zsh completion autoload function for the argcomplete module. -""" - -import argparse -import os -import shutil -import site -import subprocess import sys - -import argcomplete - -zsh_shellcode = """ -# Begin added by argcomplete -fpath=( {zsh_fpath} "${{fpath[@]}}" ) -# End added by argcomplete -""" - -bash_shellcode = """ -# Begin added by argcomplete -source "{activator}" -# End added by argcomplete -""" - - -def get_local_dir(): - try: - return subprocess.check_output(["brew", "--prefix"]).decode().strip() - except (FileNotFoundError, subprocess.CalledProcessError): - return "/usr/local" - - -def get_zsh_system_dir(): - return f"{get_local_dir()}/share/zsh/site-functions" - - -def get_bash_system_dir(): - if "BASH_COMPLETION_COMPAT_DIR" in os.environ: - return os.environ["BASH_COMPLETION_COMPAT_DIR"] - elif sys.platform == "darwin": - return f"{get_local_dir()}/etc/bash_completion.d" # created by homebrew - else: - return "/etc/bash_completion.d" # created by bash-completion - - -def get_activator_dir(): - return os.path.join(os.path.abspath(os.path.dirname(argcomplete.__file__)), "bash_completion.d") - - -def get_activator_path(): - return os.path.join(get_activator_dir(), "_python-argcomplete") - - -def install_to_destination(dest): - activator = get_activator_path() - if dest == "-": - with open(activator) as fh: - sys.stdout.write(fh.read()) - return - destdir = os.path.dirname(dest) - if not os.path.exists(destdir): - try: - os.makedirs(destdir, exist_ok=True) - except Exception as e: - parser.error(f"path {destdir} does not exist and could not be created: {e}") - try: - print(f"Installing {activator} to {dest}...", file=sys.stderr) - shutil.copy(activator, dest) - print("Installed.", file=sys.stderr) - except Exception as e: - parser.error( - f"while installing to {dest}: {e}. Please run this command using sudo, or see --help for more options." - ) - - -def get_consent(): - if args.yes is True: - return True - while True: - res = input("OK to proceed? [y/n] ") - if res.lower() not in {"y", "n", "yes", "no"}: - print('Please answer "yes" or "no".', file=sys.stderr) - elif res.lower() in {"y", "yes"}: - return True - else: - return False - - -def append_to_config_file(path, shellcode): - if os.path.exists(path): - with open(path, 'r') as fh: - if shellcode in fh.read(): - print(f"The code already exists in the file {path}.", file=sys.stderr) - return - print(f"argcomplete needs to append to the file {path}. The following code will be appended:", file=sys.stderr) - for line in shellcode.splitlines(): - print(">", line, file=sys.stderr) - if not get_consent(): - print("Not added.", file=sys.stderr) - return - print(f"Adding shellcode to {path}...", file=sys.stderr) - with open(path, "a") as fh: - fh.write(shellcode) - print("Added.", file=sys.stderr) - - -def link_user_rcfiles(): - # TODO: warn if running as superuser - zsh_rcfile = os.path.join(os.path.expanduser(os.environ.get("ZDOTDIR", "~")), ".zshenv") - append_to_config_file(zsh_rcfile, zsh_shellcode.format(zsh_fpath=get_activator_dir())) - - bash_completion_user_file = os.path.expanduser("~/.bash_completion") - append_to_config_file(bash_completion_user_file, bash_shellcode.format(activator=get_activator_path())) - - -parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) -parser.add_argument("-y", "--yes", help="automatically answer yes for all questions", action="store_true") -parser.add_argument("--dest", help='Specify the shell completion modules directory to install into, or "-" for stdout') -parser.add_argument("--user", help="Install into user directory", action="store_true") -argcomplete.autocomplete(parser) -args = parser.parse_args() -destinations = [] - -if args.dest: - if args.dest != "-" and not os.path.exists(args.dest): - parser.error(f"directory {args.dest} was specified via --dest, but it does not exist") - destinations.append(args.dest) -elif site.ENABLE_USER_SITE and site.USER_SITE in argcomplete.__file__: - print( - "Argcomplete was installed in the user site local directory. Defaulting to user installation.", file=sys.stderr - ) - link_user_rcfiles() -elif sys.prefix != sys.base_prefix: - print("Argcomplete was installed in a virtual environment. Defaulting to user installation.", file=sys.stderr) - link_user_rcfiles() -elif args.user: - link_user_rcfiles() -else: - print("Defaulting to system-wide installation.", file=sys.stderr) - destinations.append(f"{get_zsh_system_dir()}/_python-argcomplete") - destinations.append(f"{get_bash_system_dir()}/python-argcomplete") - -for destination in destinations: - install_to_destination(destination) - -if args.dest is None: - print("Please restart your shell or source the installed file to activate it.", file=sys.stderr) +from argcomplete.scripts import activate_global_python_argcomplete +sys.exit(activate_global_python_argcomplete()) diff --git a/scripts/python-argcomplete-check-easy-install-script b/scripts/python-argcomplete-check-easy-install-script old mode 100755 new mode 100644 index 07961a2..a3b499a --- a/scripts/python-argcomplete-check-easy-install-script +++ b/scripts/python-argcomplete-check-easy-install-script @@ -1,63 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. -""" -This script is part of the Python argcomplete package (https://github.com/kislyuk/argcomplete). -It is used to check if an EASY-INSTALL-SCRIPT wrapper redirects to a script that contains the string -"PYTHON_ARGCOMPLETE_OK". If you have enabled global completion in argcomplete, the completion hook will run it every -time you press in your shell. - -Usage: - python-argcomplete-check-easy-install-script -""" - import sys - -if len(sys.argv) != 2: - sys.exit(__doc__) - -sys.tracebacklimit = 0 - -with open(sys.argv[1]) as fh: - line1, head = fh.read(1024).split("\n", 1)[:2] - if line1.startswith("#") and ("py" in line1 or "Py" in line1): - import re - - lines = head.split("\n", 12) - for line in lines: - if line.startswith("# EASY-INSTALL-SCRIPT"): - import pkg_resources - - dist, script = re.match("# EASY-INSTALL-SCRIPT: '(.+)','(.+)'", line).groups() - if "PYTHON_ARGCOMPLETE_OK" in pkg_resources.get_distribution(dist).get_metadata("scripts/" + script): - exit(0) - elif line.startswith("# EASY-INSTALL-ENTRY-SCRIPT"): - dist, group, name = re.match("# EASY-INSTALL-ENTRY-SCRIPT: '(.+)','(.+)','(.+)'", line).groups() - import pkgutil - - import pkg_resources - - module_name = pkg_resources.get_distribution(dist).get_entry_info(group, name).module_name - with open(pkgutil.get_loader(module_name).get_filename()) as mod_fh: - if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): - exit(0) - elif line.startswith("# EASY-INSTALL-DEV-SCRIPT"): - for line2 in lines: - if line2.startswith("__file__"): - filename = re.match("__file__ = '(.+)'", line2).group(1) - with open(filename) as mod_fh: - if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): - exit(0) - elif line.startswith("# PBR Generated"): - module = re.search("from (.*) import", head).groups()[0] - import pkgutil - - import pkg_resources - - with open(pkgutil.get_loader(module).get_filename()) as mod_fh: - if "PYTHON_ARGCOMPLETE_OK" in mod_fh.read(1024): - exit(0) - -exit(1) +from argcomplete.scripts import python_argcomplete_check_easy_install_script +sys.exit(python_argcomplete_check_easy_install_script()) diff --git a/scripts/register-python-argcomplete b/scripts/register-python-argcomplete old mode 100755 new mode 100644 index d95eb3f..ac376c6 --- a/scripts/register-python-argcomplete +++ b/scripts/register-python-argcomplete @@ -1,71 +1,6 @@ -#!/usr/bin/env python3 -# PYTHON_ARGCOMPLETE_OK - # Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. -""" -Register a Python executable for use with the argcomplete module. - -To perform the registration, source the output of this script in your bash shell -(quote the output to avoid interpolation). - -Example: - - $ eval "$(register-python-argcomplete my-favorite-script.py)" - -For Tcsh - - $ eval `register-python-argcomplete --shell tcsh my-favorite-script.py` - -For Fish - - $ register-python-argcomplete --shell fish my-favourite-script.py > ~/.config/fish/my-favourite-script.py.fish -""" - -import argparse import sys - -import argcomplete - -parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - -parser.add_argument( - "--no-defaults", - dest="use_defaults", - action="store_false", - default=True, - help="when no matches are generated, do not fallback to readline's default completion (affects bash only)", -) -parser.add_argument( - "--complete-arguments", - nargs=argparse.REMAINDER, - help="arguments to call complete with; use of this option discards default options (affects bash only)", -) -parser.add_argument( - "-s", - "--shell", - choices=("bash", "zsh", "tcsh", "fish", "powershell"), - default="bash", - help="output code for the specified shell", -) -parser.add_argument( - "-e", "--external-argcomplete-script", help="external argcomplete script for auto completion of the executable" -) - -parser.add_argument("executable", nargs="+", help="executable to completed (when invoked by exactly this name)") - -argcomplete.autocomplete(parser) - -if len(sys.argv) == 1: - parser.print_help() - sys.exit(1) - -args = parser.parse_args() - - -sys.stdout.write( - argcomplete.shellcode( - args.executable, args.use_defaults, args.shell, args.complete_arguments, args.external_argcomplete_script - ) -) +from argcomplete.scripts import register_python_argcomplete +sys.exit(register_python_argcomplete())