Source code for pyscaffold.shell

# -*- coding: utf-8 -*-
"""
Shell commands like git, django-admin etc.
"""

import functools
import shutil
import subprocess
import sys

from .exceptions import ShellCommandException
from .log import logger


[docs]class ShellCommand(object): """Shell command that can be called with flags like git('add', 'file') Args: command (str): command to handle shell (bool): run the command in the shell cwd (str): current working dir to run the command The produced command can be called with the following keyword arguments: - **log** (*bool*): log activity when true. ``False`` by default. - **pretend** (*bool*): skip execution (but log) when pretending. ``False`` by default. The positional arguments are passed to the underlying shell command. """ def __init__(self, command, shell=True, cwd=None): self._command = command self._shell = shell self._cwd = cwd def __call__(self, *args, **kwargs): """Execute command with the given arguments.""" command = "{cmd} {args}".format( cmd=self._command, args=subprocess.list2cmdline(args) ) should_pretend = kwargs.get("pretend") should_log = kwargs.get("log", should_pretend) # ^ When pretending, automatically output logs # (after all, this is the primary purpose of pretending) if should_log: logger.report("run", command, context=self._cwd) if should_pretend: output = "" else: try: output = subprocess.check_output( command, shell=self._shell, cwd=self._cwd, stderr=subprocess.STDOUT, universal_newlines=True, ) except subprocess.CalledProcessError as e: raise ShellCommandException(e.output) from e return (line for line in output.splitlines())
[docs]def shell_command_error2exit_decorator(func): """Decorator to convert given ShellCommandException to an exit message This avoids displaying nasty stack traces to end-users """ @functools.wraps(func) def func_wrapper(*args, **kwargs): try: func(*args, **kwargs) except ShellCommandException as e: e = e.__cause__ print("{err}:\n{msg}".format(err=str(e), msg=e.output)) sys.exit(1) return func_wrapper
[docs]def get_git_cmd(**args): """Retrieve the git shell command depending on the current platform Args: **args: additional keyword arguments to :obj:`~.ShellCommand` """ if sys.platform == "win32": for cmd in ["git.cmd", "git.exe"]: git = ShellCommand(cmd, **args) try: git("--version") except ShellCommandException: continue return git else: return None else: git = ShellCommand("git", **args) try: git("--version") except ShellCommandException: return None return git
[docs]def command_exists(cmd): """Check if command exists Args: cmd: executable name """ if shutil.which(cmd) is None: return False else: return True
#: Command for git git = get_git_cmd() #: Command for django-admin django_admin = ShellCommand("django-admin")