134 lines
4.1 KiB
Python
134 lines
4.1 KiB
Python
# vim:fileencoding=utf-8:noet
|
||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||
|
||
import sys
|
||
import os
|
||
|
||
from subprocess import Popen, PIPE
|
||
from functools import partial
|
||
|
||
from powerline.lib.encoding import get_preferred_input_encoding, get_preferred_output_encoding
|
||
|
||
|
||
if sys.platform.startswith('win32'):
|
||
# Prevent windows from launching consoles when calling commands
|
||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||
Popen = partial(Popen, creationflags=0x08000000)
|
||
|
||
|
||
def run_cmd(pl, cmd, stdin=None, strip=True):
|
||
'''Run command and return its stdout, stripped
|
||
|
||
If running command fails returns None and logs failure to ``pl`` argument.
|
||
|
||
:param PowerlineLogger pl:
|
||
Logger used to log failures.
|
||
:param list cmd:
|
||
Command which will be run.
|
||
:param str stdin:
|
||
String passed to command. May be None.
|
||
:param bool strip:
|
||
True if the result should be stripped.
|
||
'''
|
||
try:
|
||
p = Popen(cmd, shell=False, stdout=PIPE, stdin=PIPE)
|
||
except OSError as e:
|
||
pl.exception('Could not execute command ({0}): {1}', e, cmd)
|
||
return None
|
||
else:
|
||
stdout, err = p.communicate(
|
||
stdin if stdin is None else stdin.encode(get_preferred_output_encoding()))
|
||
stdout = stdout.decode(get_preferred_input_encoding())
|
||
return stdout.strip() if strip else stdout
|
||
|
||
|
||
def asrun(pl, ascript):
|
||
'''Run the given AppleScript and return the standard output and error.'''
|
||
return run_cmd(pl, ['osascript', '-'], ascript)
|
||
|
||
|
||
def readlines(cmd, cwd):
|
||
'''Run command and read its output, line by line
|
||
|
||
:param list cmd:
|
||
Command which will be run.
|
||
:param str cwd:
|
||
Working directory of the command which will be run.
|
||
'''
|
||
p = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, cwd=cwd)
|
||
encoding = get_preferred_input_encoding()
|
||
p.stderr.close()
|
||
with p.stdout:
|
||
for line in p.stdout:
|
||
yield line[:-1].decode(encoding)
|
||
|
||
|
||
try:
|
||
from shutil import which
|
||
except ImportError:
|
||
# shutil.which was added in python-3.3. Here is what was added:
|
||
# Lib/shutil.py, commit 5abe28a9c8fe701ba19b1db5190863384e96c798
|
||
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
||
'''Given a command, mode, and a PATH string, return the path which
|
||
conforms to the given mode on the PATH, or None if there is no such
|
||
file.
|
||
|
||
``mode`` defaults to os.F_OK | os.X_OK. ``path`` defaults to the result
|
||
of ``os.environ.get('PATH')``, or can be overridden with a custom search
|
||
path.
|
||
'''
|
||
# Check that a given file can be accessed with the correct mode.
|
||
# Additionally check that `file` is not a directory, as on Windows
|
||
# directories pass the os.access check.
|
||
def _access_check(fn, mode):
|
||
return (
|
||
os.path.exists(fn)
|
||
and os.access(fn, mode)
|
||
and not os.path.isdir(fn)
|
||
)
|
||
|
||
# If we’re given a path with a directory part, look it up directly rather
|
||
# than referring to PATH directories. This includes checking relative to the
|
||
# current directory, e.g. ./script
|
||
if os.path.dirname(cmd):
|
||
if _access_check(cmd, mode):
|
||
return cmd
|
||
return None
|
||
|
||
if path is None:
|
||
path = os.environ.get('PATH', os.defpath)
|
||
if not path:
|
||
return None
|
||
path = path.split(os.pathsep)
|
||
|
||
if sys.platform == 'win32':
|
||
# The current directory takes precedence on Windows.
|
||
if os.curdir not in path:
|
||
path.insert(0, os.curdir)
|
||
|
||
# PATHEXT is necessary to check on Windows.
|
||
pathext = os.environ.get('PATHEXT', '').split(os.pathsep)
|
||
# See if the given file matches any of the expected path extensions.
|
||
# This will allow us to short circuit when given 'python.exe'.
|
||
# If it does match, only test that one, otherwise we have to try
|
||
# others.
|
||
if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
|
||
files = [cmd]
|
||
else:
|
||
files = [cmd + ext for ext in pathext]
|
||
else:
|
||
# On other platforms you don’t have things like PATHEXT to tell you
|
||
# what file suffixes are executable, so just pass on cmd as-is.
|
||
files = [cmd]
|
||
|
||
seen = set()
|
||
for dir in path:
|
||
normdir = os.path.normcase(dir)
|
||
if normdir not in seen:
|
||
seen.add(normdir)
|
||
for thefile in files:
|
||
name = os.path.join(dir, thefile)
|
||
if _access_check(name, mode):
|
||
return name
|
||
return None
|