-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcore.py
166 lines (129 loc) · 5.55 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import os
import platform
from typing import TYPE_CHECKING, Any, Callable, Optional, Set, TypeVar, Union
from click import Command, Context, Parameter, option
from .constants import ShellType
from .exceptions import ShellEnvVarNotFoundError, ShellTypeNotSupportedError
from .utils import add_shell_configuration, create_file, detect_shell
if TYPE_CHECKING:
import typing_extensions as te
R = TypeVar("R")
T = TypeVar("T")
_AnyCallable = Callable[..., Any]
_Decorator: "te.TypeAlias" = Callable[[T], T]
FC = TypeVar("FC", bound=Union[_AnyCallable, Command])
def enable_click_shell_completion(
program_name: str,
shells: Optional[Set[ShellType]] = None,
verbose: Optional[bool] = False,
) -> None:
"""
Enable tab completion for Click's supported shell types.
If ``shells`` is not provided, `auto-click-auto` will attempt to detect the
shell type the user is currently running the program on.
See https://click.palletsprojects.com/en/latest/shell-completion.
`auto-click-auto` is using the `eval` command implementation suggested from
Click.
:param program_name: The program name for which we enable shell completion,
also described as the executable name.
:param shells: The shell types for which we want to add tab completion
support.
:param verbose: `True` to print more details regarding the enabling,
`False` otherwise. If this function is called on every run of the CLI
program it might be better to set to `False`.
:raise NotImplementedError: When ``shells`` option is not supported.
"""
# Check that the program is run on one of the supported Operating Systems
supported_os = ("Linux", "MacOS", "Darwin")
os_name = platform.system()
if os_name not in supported_os:
if verbose is True:
print(
f"{os_name} is not one of the supported Operating Systems "
f"({supported_os}) of `auto-click-auto`."
)
return None
click_env_var = f"_{program_name.upper().replace('-', '_')}_COMPLETE"
if shells is None:
try:
shells = {detect_shell()}
except (ShellTypeNotSupportedError, ShellEnvVarNotFoundError) as err:
if verbose is True:
print(err)
return None
for shell in shells:
if shell in (ShellType.BASH, ShellType.ZSH):
shell_config_file = os.path.expanduser(f"~/.{shell}rc")
# Completion implementation: `eval` command in shell configuration
eval_command = (
f'eval \"$({click_env_var}={shell.value}_source '
f'{program_name})\"'
)
add_shell_configuration(
shell_config_file=shell_config_file,
config_string=eval_command,
verbose=verbose,
)
elif shell == ShellType.FISH:
completer_script_path = os.path.expanduser(
f"~/.config/fish/completions/{program_name}.{shell}"
)
# bash and zsh config files are generic, so we can assume the user
# already has created them. fish's shell configuration file for
# custom completions is specific to the program name, so we will
# create it if it doesn't already exist.
create_file(file_path=completer_script_path)
command = (
f"{click_env_var}={shell.value}_source {program_name} | source"
)
add_shell_configuration(
shell_config_file=completer_script_path,
config_string=command,
verbose=verbose,
)
else:
raise NotImplementedError
def enable_click_shell_completion_option(
*param_decls: str,
program_name: Optional[str] = None,
shells: Optional[Set[ShellType]] = None,
**kwargs: Any,
) -> _Decorator[FC]:
"""
Add a ``--autocomplete`` option which enables tab completion and exits the
program. This function can be used as a decorator in a Click command.
If ``program_name`` is not provided, it will be detected from the command.
If ``shells`` is not provided, `auto-click-auto` will attempt to detect the
shell type the user is currently running the program on.
Uses Click's `option` function. It is possible to pass the relevant
function arguments to override the defaults ones, e.g., ``expose_value``,
``help``, etc.
:param param_decls: One or more option names. Defaults to the single value
``"--autocomplete"``.
:param program_name: The program name for which we enable shell completion,
also described as the executable name.
:param shells: The shell types for which we want to add tab completion
support.
:param kwargs: Extra arguments are passed to :func:`option`.
"""
def callback(ctx: Context, param: Parameter, value: bool) -> None:
if not value or ctx.resilient_parsing:
return
nonlocal program_name
nonlocal shells
if program_name is None:
program_name = ctx.find_root().info_name
assert program_name is not None
enable_click_shell_completion(
program_name=program_name, shells=shells, verbose=True
)
ctx.exit()
return None
if not param_decls:
param_decls = ("--autocomplete",)
kwargs.setdefault("is_flag", True)
kwargs.setdefault("expose_value", False)
kwargs.setdefault("is_eager", True)
kwargs.setdefault("help", "Enable tab autocompletion and exit.")
kwargs["callback"] = callback
return option(*param_decls, **kwargs)