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 |