Files
pre-commit/pre_commit/prefixed_command_runner.py

87 lines
2.6 KiB
Python

import os
import os.path
import subprocess
class CalledProcessError(RuntimeError):
def __init__(self, returncode, cmd, expected_returncode, output=None):
self.returncode = returncode
self.cmd = cmd
self.expected_returncode = expected_returncode
self.output = output
def __str__(self):
return (
'Command: {0!r}\n'
'Return code: {1}\n'
'Expected return code {2}\n',
'Output: {3!r}\n'.format(
self.cmd,
self.returncode,
self.expected_returncode,
self.output,
),
)
def _replace_cmd(cmd, **kwargs):
return [part.format(**kwargs) for part in cmd]
class PrefixedCommandRunner(object):
"""A PrefixedCommandRunner allows you to run subprocess commands with
comand substitution.
For instance:
PrefixedCommandRunner('/tmp/foo').run(['{prefix}foo.sh', 'bar', 'baz'])
will run ['/tmpl/foo/foo.sh', 'bar', 'baz']
"""
def __init__(self, prefix_dir, popen=subprocess.Popen, makedirs=os.makedirs):
self.prefix_dir = prefix_dir.rstrip(os.sep) + os.sep
self.__popen = popen
self.__makedirs = makedirs
def _create_path_if_not_exists(self):
if not os.path.exists(self.prefix_dir):
self.__makedirs(self.prefix_dir)
def run(self, cmd, retcode=0, stdin=None, **kwargs):
self._create_path_if_not_exists()
replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)
proc = self.__popen(
replaced_cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
**kwargs
)
stdout, stderr = proc.communicate(stdin)
returncode = proc.returncode
if retcode is not None and retcode != returncode:
raise CalledProcessError(
returncode, replaced_cmd, retcode, output=(stdout, stderr),
)
return proc.returncode, stdout, stderr
def path(self, *parts):
path = os.path.join(self.prefix_dir, *parts)
return os.path.normpath(path)
def exists(self, *parts):
return os.path.exists(self.path(*parts))
@classmethod
def from_command_runner(cls, command_runner, path_end):
"""Constructs a new command runner from an existing one by appending
`path_end` to the command runner's prefix directory.
"""
return cls(
command_runner.path(path_end),
popen=command_runner.__popen,
makedirs=command_runner.__makedirs,
)