1try: 2 from shutil import which # Python >= 3.3 3except ImportError: 4 import os, sys 5 6 # This is copied from Python 3.4.1 7 def which(cmd, mode=os.F_OK | os.X_OK, path=None): 8 """Given a command, mode, and a PATH string, return the path which 9 conforms to the given mode on the PATH, or None if there is no such 10 file. 11 12 `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result 13 of os.environ.get("PATH"), or can be overridden with a custom search 14 path. 15 16 """ 17 # Check that a given file can be accessed with the correct mode. 18 # Additionally check that `file` is not a directory, as on Windows 19 # directories pass the os.access check. 20 def _access_check(fn, mode): 21 return (os.path.exists(fn) and os.access(fn, mode) 22 and not os.path.isdir(fn)) 23 24 # If we're given a path with a directory part, look it up directly rather 25 # than referring to PATH directories. This includes checking relative to the 26 # current directory, e.g. ./script 27 if os.path.dirname(cmd): 28 if _access_check(cmd, mode): 29 return cmd 30 return None 31 32 if path is None: 33 path = os.environ.get("PATH", os.defpath) 34 if not path: 35 return None 36 path = path.split(os.pathsep) 37 38 if sys.platform == "win32": 39 # The current directory takes precedence on Windows. 40 if not os.curdir in path: 41 path.insert(0, os.curdir) 42 43 # PATHEXT is necessary to check on Windows. 44 pathext = os.environ.get("PATHEXT", "").split(os.pathsep) 45 # See if the given file matches any of the expected path extensions. 46 # This will allow us to short circuit when given "python.exe". 47 # If it does match, only test that one, otherwise we have to try 48 # others. 49 if any(cmd.lower().endswith(ext.lower()) for ext in pathext): 50 files = [cmd] 51 else: 52 files = [cmd + ext for ext in pathext] 53 else: 54 # On other platforms you don't have things like PATHEXT to tell you 55 # what file suffixes are executable, so just pass on cmd as-is. 56 files = [cmd] 57 58 seen = set() 59 for dir in path: 60 normdir = os.path.normcase(dir) 61 if not normdir in seen: 62 seen.add(normdir) 63 for thefile in files: 64 name = os.path.join(dir, thefile) 65 if _access_check(name, mode): 66 return name 67 return None 68 69 70class PtyProcessError(Exception): 71 """Generic error class for this package.""" 72