diff --git a/click_completion/__init__.py b/click_completion/__init__.py index 907c96c..1877369 100644 --- a/click_completion/__init__.py +++ b/click_completion/__init__.py @@ -8,8 +8,8 @@ from click import ParamType from enum import Enum -from click_completion.core import completion_configuration, get_code, install, shells, resolve_ctx, get_choices, \ - startswith, Shell +from click_completion.core import completion_configuration, get_code, InstallConfiguration, install, \ + install_from_config, shells, resolve_ctx, get_choices, startswith, Shell from click_completion.lib import get_auto_shell from click_completion.patch import patch as _patch diff --git a/click_completion/core.py b/click_completion/core.py index 867085e..7f00cbd 100644 --- a/click_completion/core.py +++ b/click_completion/core.py @@ -307,6 +307,111 @@ def get_code(shell=None, prog_name=None, env_name=None, extra_env=None): return template.render(prog_name=prog_name, complete_var=env_name, extra_env=extra_env) +class InstallConfiguration(object): + """A class to hold the installation configuration for auto completion. + + Attributes + ---------- + prog_name : str + The program name on the command line. + shell : Shell + The shell type targeted. + path : str + The installation path of the code to be evaluated by the shell. + mode : str + Whether to append the content to the file or to override it. + code : str + The completion code to be written to the installation path. + """ + def __init__(self, shell=None, prog_name=None, env_name=None, path=None, append=None, extra_env=None): + """ + Parameters + ---------- + shell : Shell + The shell type targeted. It will be guessed with get_auto_shell() if the value is None (Default value = None) + prog_name : str + The program name on the command line. It will be automatically computed if the value is None + (Default value = None) + env_name : str + The environment variable name used to control the completion. It will be automatically computed if the value is + None (Default value = None) + path : str + The installation path of the code to be evaluated by the shell. The standard installation path is used if the + value is None (Default value = None) + append : bool + Whether to append the content to the file or to override it. The default behavior depends on the shell type + (Default value = None) + extra_env : dict + A set of environment variables and their values to be added to the generated code (Default value = None) + + Raises + ------- + click.ClickException + If the provided Shell isn't supported. + """ + self.prog_name = prog_name or click.get_current_context().find_root().info_name + self.shell = shell or get_auto_shell() + self.path, self.mode = self.__get_path_config(shell, prog_name, path, append) + self.code = get_code(shell, prog_name, env_name, extra_env) + + def __get_path_config(self, shell=None, prog_name=None, path=None, append=None): + """ + Determines the path and write mode for the given shell. + + Parameters + ---------- + shell : Shell + The shell type targeted. + prog_name : str + The program name on the command line. + path : str + The installation path of the code to be evaluated by the shell. + append : bool + Whether to append the content to the file or to override it. + + Returns + ------- + Tuple[str, str] + The path and write mode for writing the completion code. + + Raises + ------- + click.ClickException + If the provided Shell isn't supported. + + """ + if append is None and path is not None: + append = True + if append is not None: + mode = 'a' if append else 'w' + else: + mode = None + + if shell == 'fish': + path = path or os.path.expanduser('~') + '/.config/fish/completions/%s.fish' % prog_name + mode = mode or 'w' + elif shell == 'bash': + path = path or os.path.expanduser('~') + '/.bash_completion' + mode = mode or 'a' + elif shell == 'zsh': + path = path or os.path.expanduser('~') + '/.zshrc' + mode = mode or 'a' + elif shell == 'powershell': + subprocess.check_call(['powershell', 'Set-ExecutionPolicy Unrestricted -Scope CurrentUser']) + path = path or subprocess.check_output( + ['powershell', '-NoProfile', 'echo $profile']).strip() if install else '' + mode = mode or 'a' + else: + raise click.ClickException('%s is not supported.' % shell) + + if append is not None: + mode = 'a' if append else 'w' + else: + mode = mode + + return path, mode + + def install(shell=None, prog_name=None, env_name=None, path=None, append=None, extra_env=None): """Install the completion @@ -328,44 +433,42 @@ def install(shell=None, prog_name=None, env_name=None, path=None, append=None, e (Default value = None) extra_env : dict A set of environment variables and their values to be added to the generated code (Default value = None) + + Returns + ------- + Tuple[str, str] + The current shell and the path the code completion was written to. + + Raises + ------- + click.ClickException + If the provided Shell isn't supported. """ - prog_name = prog_name or click.get_current_context().find_root().info_name - shell = shell or get_auto_shell() - if append is None and path is not None: - append = True - if append is not None: - mode = 'a' if append else 'w' - else: - mode = None - - if shell == 'fish': - path = path or os.path.expanduser('~') + '/.config/fish/completions/%s.fish' % prog_name - mode = mode or 'w' - elif shell == 'bash': - path = path or os.path.expanduser('~') + '/.bash_completion' - mode = mode or 'a' - elif shell == 'zsh': - path = path or os.path.expanduser('~') + '/.zshrc' - mode = mode or 'a' - elif shell == 'powershell': - subprocess.check_call(['powershell', 'Set-ExecutionPolicy Unrestricted -Scope CurrentUser']) - path = path or subprocess.check_output(['powershell', '-NoProfile', 'echo $profile']).strip() if install else '' - mode = mode or 'a' - else: - raise click.ClickException('%s is not supported.' % shell) + install_config = InstallConfiguration(shell, prog_name, env_name, path, append, extra_env) + return install_from_config(install_config) - if append is not None: - mode = 'a' if append else 'w' - else: - mode = mode - d = os.path.dirname(path) + +def install_from_config(install_config): + """Install the auto completion from an InstallConfiguration object. + + Parameters + ---------- + install_config : InstallConfiguration + The object that holds the configuration with the auto completion settings. + + Returns + ------- + Tuple[str, str] + The current shell and the path the code completion was written to. + """ + d = os.path.dirname(install_config.path) if not os.path.exists(d): os.makedirs(d) - f = open(path, mode) - f.write(get_code(shell, prog_name, env_name, extra_env)) + f = open(install_config.path, install_config.mode) + f.write(install_config.code) f.write("\n") f.close() - return shell, path + return install_config.shell, install_config.path class Shell(Enum):