168 lines
5.2 KiB
Python
168 lines
5.2 KiB
Python
import os
|
|
import subprocess
|
|
import re
|
|
|
|
if os.name == 'nt':
|
|
from ctypes import windll, create_unicode_buffer
|
|
|
|
from .console_write import console_write
|
|
from .unicode import unicode_from_os
|
|
from .show_error import show_error
|
|
|
|
try:
|
|
# Python 2
|
|
str_cls = unicode
|
|
except (NameError):
|
|
# Python 3
|
|
str_cls = str
|
|
|
|
|
|
def create_cmd(args, basename_binary=False):
|
|
"""
|
|
Takes an array of strings to be passed to subprocess.Popen and creates
|
|
a string that can be pasted into a terminal
|
|
|
|
:param args:
|
|
The array containing the binary name/path and all arguments
|
|
|
|
:param basename_binary:
|
|
If only the basename of the binary should be disabled instead of the full path
|
|
|
|
:return:
|
|
The command string
|
|
"""
|
|
|
|
if basename_binary:
|
|
args[0] = os.path.basename(args[0])
|
|
|
|
if os.name == 'nt':
|
|
return subprocess.list2cmdline(args)
|
|
else:
|
|
escaped_args = []
|
|
for arg in args:
|
|
if re.search('^[a-zA-Z0-9/_^\\-\\.:=]+$', arg) == None:
|
|
arg = u"'" + arg.replace(u"'", u"'\\''") + u"'"
|
|
escaped_args.append(arg)
|
|
return u' '.join(escaped_args)
|
|
|
|
|
|
class Cli(object):
|
|
"""
|
|
Base class for running command line apps
|
|
|
|
:param binary:
|
|
The full filesystem path to the executable for the version control
|
|
system. May be set to None to allow the code to try and find it.
|
|
"""
|
|
|
|
cli_name = None
|
|
|
|
def __init__(self, binary, debug):
|
|
self.binary = binary
|
|
self.debug = debug
|
|
|
|
def execute(self, args, cwd, input=None):
|
|
"""
|
|
Creates a subprocess with the executable/args
|
|
|
|
:param args:
|
|
A list of the executable path and all arguments to it
|
|
|
|
:param cwd:
|
|
The directory in which to run the executable
|
|
|
|
:param input:
|
|
The input text to send to the program
|
|
|
|
:return: A string of the executable output
|
|
"""
|
|
|
|
startupinfo = None
|
|
if os.name == 'nt':
|
|
startupinfo = subprocess.STARTUPINFO()
|
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
|
|
# Make sure the cwd is ascii
|
|
try:
|
|
cwd.encode('ascii')
|
|
except UnicodeEncodeError:
|
|
buf = create_unicode_buffer(512)
|
|
if windll.kernel32.GetShortPathNameW(cwd, buf, len(buf)):
|
|
cwd = buf.value
|
|
|
|
if self.debug:
|
|
console_write(u"Trying to execute command %s" % create_cmd(args), True)
|
|
|
|
try:
|
|
proc = subprocess.Popen(args, stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
|
startupinfo=startupinfo, cwd=cwd)
|
|
|
|
if input and isinstance(input, str_cls):
|
|
input = input.encode('utf-8')
|
|
output, _ = proc.communicate(input)
|
|
output = output.decode('utf-8')
|
|
output = output.replace('\r\n', '\n').rstrip(' \n\r')
|
|
|
|
return output
|
|
|
|
except (OSError) as e:
|
|
cmd = create_cmd(args)
|
|
error = unicode_from_os(e)
|
|
message = u"Error executing: %s\n%s\n\nTry checking your \"%s_binary\" setting?" % (cmd, error, self.cli_name)
|
|
show_error(message)
|
|
return False
|
|
|
|
def find_binary(self, name):
|
|
"""
|
|
Locates the executable by looking in the PATH and well-known directories
|
|
|
|
:param name:
|
|
The string filename of the executable
|
|
|
|
:return: The filesystem path to the executable, or None if not found
|
|
"""
|
|
|
|
if self.binary:
|
|
if self.debug:
|
|
error_string = u"Using \"%s_binary\" from settings \"%s\"" % (
|
|
self.cli_name, self.binary)
|
|
console_write(error_string, True)
|
|
return self.binary
|
|
|
|
# Try the path first
|
|
for dir_ in os.environ['PATH'].split(os.pathsep):
|
|
path = os.path.join(dir_, name)
|
|
if os.path.exists(path):
|
|
if self.debug:
|
|
console_write(u"Found %s at \"%s\"" % (self.cli_name, path), True)
|
|
return path
|
|
|
|
# This is left in for backwards compatibility and for windows
|
|
# users who may have the binary, albeit in a common dir that may
|
|
# not be part of the PATH
|
|
if os.name == 'nt':
|
|
dirs = ['C:\\Program Files\\Git\\bin',
|
|
'C:\\Program Files (x86)\\Git\\bin',
|
|
'C:\\Program Files\\TortoiseGit\\bin',
|
|
'C:\\Program Files\\Mercurial',
|
|
'C:\\Program Files (x86)\\Mercurial',
|
|
'C:\\Program Files (x86)\\TortoiseHg',
|
|
'C:\\Program Files\\TortoiseHg',
|
|
'C:\\cygwin\\bin']
|
|
else:
|
|
# ST seems to launch with a minimal set of environmental variables
|
|
# on OS X, so we add some common paths for it
|
|
dirs = ['/usr/local/git/bin', '/usr/local/bin']
|
|
|
|
for dir_ in dirs:
|
|
path = os.path.join(dir_, name)
|
|
if os.path.exists(path):
|
|
if self.debug:
|
|
console_write(u"Found %s at \"%s\"" % (self.cli_name, path), True)
|
|
return path
|
|
|
|
if self.debug:
|
|
console_write(u"Could not find %s on your machine" % self.cli_name, True)
|
|
return None
|