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

Feature: Config class for getting installation config without running the installation process #39

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions click_completion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
167 changes: 135 additions & 32 deletions click_completion/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +407 to +412
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fully aware that this overrides the previous configuration setting on the lines above, but since this was the behaviour of the old way I don't want to break it with this PR.



def install(shell=None, prog_name=None, env_name=None, path=None, append=None, extra_env=None):
"""Install the completion

Expand All @@ -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):
Expand Down