Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert scripts to the format used by console_scripts #493

Merged
merged 4 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added argcomplete/scripts/__init__.py
Empty file.
166 changes: 166 additions & 0 deletions argcomplete/scripts/activate_global_python_argcomplete.py
Original file line number Diff line number Diff line change
@@ -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())
Original file line number Diff line number Diff line change
@@ -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 <TAB> in your shell.

Usage:
python-argcomplete-check-easy-install-script <input executable file>
"""

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())
79 changes: 79 additions & 0 deletions argcomplete/scripts/register_python_argcomplete.py
Original file line number Diff line number Diff line change
@@ -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())
Loading
Loading