Mercurial > repos > davidmurphy > codonlogo
comparison corebio/utils/_which.py @ 0:c55bdc2fb9fa
Uploaded
| author | davidmurphy |
|---|---|
| date | Thu, 27 Oct 2011 12:09:09 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:c55bdc2fb9fa |
|---|---|
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2002-2005 ActiveState Corp. | |
| 3 # See LICENSE.txt for license details. | |
| 4 # Author: | |
| 5 # Trent Mick (TrentM@ActiveState.com) | |
| 6 # Home: | |
| 7 # http://trentm.com/projects/which/ | |
| 8 | |
| 9 r"""Find the full path to commands. | |
| 10 | |
| 11 which(command, path=None, verbose=0, exts=None) | |
| 12 Return the full path to the first match of the given command on the | |
| 13 path. | |
| 14 | |
| 15 whichall(command, path=None, verbose=0, exts=None) | |
| 16 Return a list of full paths to all matches of the given command on | |
| 17 the path. | |
| 18 | |
| 19 whichgen(command, path=None, verbose=0, exts=None) | |
| 20 Return a generator which will yield full paths to all matches of the | |
| 21 given command on the path. | |
| 22 | |
| 23 By default the PATH environment variable is searched (as well as, on | |
| 24 Windows, the AppPaths key in the registry), but a specific 'path' list | |
| 25 to search may be specified as well. On Windows, the PATHEXT environment | |
| 26 variable is applied as appropriate. | |
| 27 | |
| 28 If "verbose" is true then a tuple of the form | |
| 29 (<fullpath>, <matched-where-description>) | |
| 30 is returned for each match. The latter element is a textual description | |
| 31 of where the match was found. For example: | |
| 32 from PATH element 0 | |
| 33 from HKLM\SOFTWARE\...\perl.exe | |
| 34 """ | |
| 35 | |
| 36 _cmdlnUsage = """ | |
| 37 Show the full path of commands. | |
| 38 | |
| 39 Usage: | |
| 40 which [<options>...] [<command-name>...] | |
| 41 | |
| 42 Options: | |
| 43 -h, --help Print this help and exit. | |
| 44 -V, --version Print the version info and exit. | |
| 45 | |
| 46 -a, --all Print *all* matching paths. | |
| 47 -v, --verbose Print out how matches were located and | |
| 48 show near misses on stderr. | |
| 49 -q, --quiet Just print out matches. I.e., do not print out | |
| 50 near misses. | |
| 51 | |
| 52 -p <altpath>, --path=<altpath> | |
| 53 An alternative path (list of directories) may | |
| 54 be specified for searching. | |
| 55 -e <exts>, --exts=<exts> | |
| 56 Specify a list of extensions to consider instead | |
| 57 of the usual list (';'-separate list, Windows | |
| 58 only). | |
| 59 | |
| 60 Show the full path to the program that would be run for each given | |
| 61 command name, if any. Which, like GNU's which, returns the number of | |
| 62 failed arguments, or -1 when no <command-name> was given. | |
| 63 | |
| 64 Near misses include duplicates, non-regular files and (on Un*x) | |
| 65 files without executable access. | |
| 66 """ | |
| 67 | |
| 68 __revision__ = "$Id: which.py 430 2005-08-20 03:11:58Z trentm $" | |
| 69 __version_info__ = (1, 1, 0) | |
| 70 __version__ = '.'.join(map(str, __version_info__)) | |
| 71 | |
| 72 import os | |
| 73 import sys | |
| 74 import getopt | |
| 75 import stat | |
| 76 | |
| 77 | |
| 78 #---- exceptions | |
| 79 | |
| 80 class WhichError(Exception): | |
| 81 pass | |
| 82 | |
| 83 | |
| 84 | |
| 85 #---- internal support stuff | |
| 86 | |
| 87 def _getRegisteredExecutable(exeName): | |
| 88 """Windows allow application paths to be registered in the registry.""" | |
| 89 registered = None | |
| 90 if sys.platform.startswith('win'): | |
| 91 if os.path.splitext(exeName)[1].lower() != '.exe': | |
| 92 exeName += '.exe' | |
| 93 import _winreg | |
| 94 try: | |
| 95 key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" +\ | |
| 96 exeName | |
| 97 value = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, key) | |
| 98 registered = (value, "from HKLM\\"+key) | |
| 99 except _winreg.error: | |
| 100 pass | |
| 101 if registered and not os.path.exists(registered[0]): | |
| 102 registered = None | |
| 103 return registered | |
| 104 | |
| 105 def _samefile(fname1, fname2): | |
| 106 if sys.platform.startswith('win'): | |
| 107 return ( os.path.normpath(os.path.normcase(fname1)) ==\ | |
| 108 os.path.normpath(os.path.normcase(fname2)) ) | |
| 109 else: | |
| 110 return os.path.samefile(fname1, fname2) | |
| 111 | |
| 112 def _cull(potential, matches, verbose=0): | |
| 113 """Cull inappropriate matches. Possible reasons: | |
| 114 - a duplicate of a previous match | |
| 115 - not a disk file | |
| 116 - not executable (non-Windows) | |
| 117 If 'potential' is approved it is returned and added to 'matches'. | |
| 118 Otherwise, None is returned. | |
| 119 """ | |
| 120 for match in matches: # don't yield duplicates | |
| 121 if _samefile(potential[0], match[0]): | |
| 122 if verbose: | |
| 123 sys.stderr.write("duplicate: %s (%s)\n" % potential) | |
| 124 return None | |
| 125 else: | |
| 126 if not stat.S_ISREG(os.stat(potential[0]).st_mode): | |
| 127 if verbose: | |
| 128 sys.stderr.write("not a regular file: %s (%s)\n" % potential) | |
| 129 elif not os.access(potential[0], os.X_OK): | |
| 130 if verbose: | |
| 131 sys.stderr.write("no executable access: %s (%s)\n"\ | |
| 132 % potential) | |
| 133 else: | |
| 134 matches.append(potential) | |
| 135 return potential | |
| 136 | |
| 137 | |
| 138 #---- module API | |
| 139 | |
| 140 def whichgen(command, path=None, verbose=0, exts=None): | |
| 141 """Return a generator of full paths to the given command. | |
| 142 | |
| 143 "command" is a the name of the executable to search for. | |
| 144 "path" is an optional alternate path list to search. The default it | |
| 145 to use the PATH environment variable. | |
| 146 "verbose", if true, will cause a 2-tuple to be returned for each | |
| 147 match. The second element is a textual description of where the | |
| 148 match was found. | |
| 149 "exts" optionally allows one to specify a list of extensions to use | |
| 150 instead of the standard list for this system. This can | |
| 151 effectively be used as an optimization to, for example, avoid | |
| 152 stat's of "foo.vbs" when searching for "foo" and you know it is | |
| 153 not a VisualBasic script but ".vbs" is on PATHEXT. This option | |
| 154 is only supported on Windows. | |
| 155 | |
| 156 This method returns a generator which yields either full paths to | |
| 157 the given command or, if verbose, tuples of the form (<path to | |
| 158 command>, <where path found>). | |
| 159 """ | |
| 160 matches = [] | |
| 161 if path is None: | |
| 162 usingGivenPath = 0 | |
| 163 path = os.environ.get("PATH", "").split(os.pathsep) | |
| 164 if sys.platform.startswith("win"): | |
| 165 path.insert(0, os.curdir) # implied by Windows shell | |
| 166 else: | |
| 167 usingGivenPath = 1 | |
| 168 | |
| 169 # Windows has the concept of a list of extensions (PATHEXT env var). | |
| 170 if sys.platform.startswith("win"): | |
| 171 if exts is None: | |
| 172 exts = os.environ.get("PATHEXT", "").split(os.pathsep) | |
| 173 # If '.exe' is not in exts then obviously this is Win9x and | |
| 174 # or a bogus PATHEXT, then use a reasonable default. | |
| 175 for ext in exts: | |
| 176 if ext.lower() == ".exe": | |
| 177 break | |
| 178 else: | |
| 179 exts = ['.COM', '.EXE', '.BAT'] | |
| 180 elif not isinstance(exts, list): | |
| 181 raise TypeError("'exts' argument must be a list or None") | |
| 182 else: | |
| 183 if exts is not None: | |
| 184 raise WhichError("'exts' argument is not supported on "\ | |
| 185 "platform '%s'" % sys.platform) | |
| 186 exts = [] | |
| 187 | |
| 188 # File name cannot have path separators because PATH lookup does not | |
| 189 # work that way. | |
| 190 if os.sep in command or os.altsep and os.altsep in command: | |
| 191 pass | |
| 192 else: | |
| 193 for i in range(len(path)): | |
| 194 dirName = path[i] | |
| 195 # On windows the dirName *could* be quoted, drop the quotes | |
| 196 if sys.platform.startswith("win") and len(dirName) >= 2\ | |
| 197 and dirName[0] == '"' and dirName[-1] == '"': | |
| 198 dirName = dirName[1:-1] | |
| 199 for ext in ['']+exts: | |
| 200 absName = os.path.abspath( | |
| 201 os.path.normpath(os.path.join(dirName, command+ext))) | |
| 202 if os.path.isfile(absName): | |
| 203 if usingGivenPath: | |
| 204 fromWhere = "from given path element %d" % i | |
| 205 elif not sys.platform.startswith("win"): | |
| 206 fromWhere = "from PATH element %d" % i | |
| 207 elif i == 0: | |
| 208 fromWhere = "from current directory" | |
| 209 else: | |
| 210 fromWhere = "from PATH element %d" % (i-1) | |
| 211 match = _cull((absName, fromWhere), matches, verbose) | |
| 212 if match: | |
| 213 if verbose: | |
| 214 yield match | |
| 215 else: | |
| 216 yield match[0] | |
| 217 match = _getRegisteredExecutable(command) | |
| 218 if match is not None: | |
| 219 match = _cull(match, matches, verbose) | |
| 220 if match: | |
| 221 if verbose: | |
| 222 yield match | |
| 223 else: | |
| 224 yield match[0] | |
| 225 | |
| 226 | |
| 227 def which(command, path=None, verbose=0, exts=None): | |
| 228 """Return the full path to the first match of the given command on | |
| 229 the path. | |
| 230 | |
| 231 "command" is a the name of the executable to search for. | |
| 232 "path" is an optional alternate path list to search. The default it | |
| 233 to use the PATH environment variable. | |
| 234 "verbose", if true, will cause a 2-tuple to be returned. The second | |
| 235 element is a textual description of where the match was found. | |
| 236 "exts" optionally allows one to specify a list of extensions to use | |
| 237 instead of the standard list for this system. This can | |
| 238 effectively be used as an optimization to, for example, avoid | |
| 239 stat's of "foo.vbs" when searching for "foo" and you know it is | |
| 240 not a VisualBasic script but ".vbs" is on PATHEXT. This option | |
| 241 is only supported on Windows. | |
| 242 | |
| 243 If no match is found for the command, a WhichError is raised. | |
| 244 """ | |
| 245 try: | |
| 246 match = whichgen(command, path, verbose, exts).next() | |
| 247 except StopIteration: | |
| 248 raise WhichError("Could not find '%s' on the path." % command) | |
| 249 return match | |
| 250 | |
| 251 | |
| 252 def whichall(command, path=None, verbose=0, exts=None): | |
| 253 """Return a list of full paths to all matches of the given command | |
| 254 on the path. | |
| 255 | |
| 256 "command" is a the name of the executable to search for. | |
| 257 "path" is an optional alternate path list to search. The default it | |
| 258 to use the PATH environment variable. | |
| 259 "verbose", if true, will cause a 2-tuple to be returned for each | |
| 260 match. The second element is a textual description of where the | |
| 261 match was found. | |
| 262 "exts" optionally allows one to specify a list of extensions to use | |
| 263 instead of the standard list for this system. This can | |
| 264 effectively be used as an optimization to, for example, avoid | |
| 265 stat's of "foo.vbs" when searching for "foo" and you know it is | |
| 266 not a VisualBasic script but ".vbs" is on PATHEXT. This option | |
| 267 is only supported on Windows. | |
| 268 """ | |
| 269 return list( whichgen(command, path, verbose, exts) ) | |
| 270 | |
| 271 | |
| 272 | |
| 273 #---- mainline | |
| 274 | |
| 275 def main(argv): | |
| 276 all = 0 | |
| 277 verbose = 0 | |
| 278 altpath = None | |
| 279 exts = None | |
| 280 try: | |
| 281 optlist, args = getopt.getopt(argv[1:], 'haVvqp:e:', | |
| 282 ['help', 'all', 'version', 'verbose', 'quiet', 'path=', 'exts=']) | |
| 283 except getopt.GetoptError, msg: | |
| 284 sys.stderr.write("which: error: %s. Your invocation was: %s\n"\ | |
| 285 % (msg, argv)) | |
| 286 sys.stderr.write("Try 'which --help'.\n") | |
| 287 return 1 | |
| 288 for opt, optarg in optlist: | |
| 289 if opt in ('-h', '--help'): | |
| 290 print _cmdlnUsage | |
| 291 return 0 | |
| 292 elif opt in ('-V', '--version'): | |
| 293 print "which %s" % __version__ | |
| 294 return 0 | |
| 295 elif opt in ('-a', '--all'): | |
| 296 all = 1 | |
| 297 elif opt in ('-v', '--verbose'): | |
| 298 verbose = 1 | |
| 299 elif opt in ('-q', '--quiet'): | |
| 300 verbose = 0 | |
| 301 elif opt in ('-p', '--path'): | |
| 302 if optarg: | |
| 303 altpath = optarg.split(os.pathsep) | |
| 304 else: | |
| 305 altpath = [] | |
| 306 elif opt in ('-e', '--exts'): | |
| 307 if optarg: | |
| 308 exts = optarg.split(os.pathsep) | |
| 309 else: | |
| 310 exts = [] | |
| 311 | |
| 312 if len(args) == 0: | |
| 313 return -1 | |
| 314 | |
| 315 failures = 0 | |
| 316 for arg in args: | |
| 317 #print "debug: search for %r" % arg | |
| 318 nmatches = 0 | |
| 319 for match in whichgen(arg, path=altpath, verbose=verbose, exts=exts): | |
| 320 if verbose: | |
| 321 print "%s (%s)" % match | |
| 322 else: | |
| 323 print match | |
| 324 nmatches += 1 | |
| 325 if not all: | |
| 326 break | |
| 327 if not nmatches: | |
| 328 failures += 1 | |
| 329 return failures | |
| 330 | |
| 331 | |
| 332 if __name__ == "__main__": | |
| 333 sys.exit( main(sys.argv) ) | |
| 334 | |
| 335 |
