# 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