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.
(cmd, mode=os.F_OK | os.X_OK, path=None)
| 401 | |
| 402 | # `shutils.which` function copied verbatim from the Python-3.3 source. |
| 403 | def which(cmd, mode=os.F_OK | os.X_OK, path=None): |
| 404 | """Given a command, mode, and a PATH string, return the path which |
| 405 | conforms to the given mode on the PATH, or None if there is no such |
| 406 | file. |
| 407 | `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result |
| 408 | of os.environ.get("PATH"), or can be overridden with a custom search |
| 409 | path. |
| 410 | """ |
| 411 | |
| 412 | # Check that a given file can be accessed with the correct mode. |
| 413 | # Additionally check that `file` is not a directory, as on Windows |
| 414 | # directories pass the os.access check. |
| 415 | def _access_check(fn, mode): |
| 416 | return (os.path.exists(fn) and os.access(fn, mode) and |
| 417 | not os.path.isdir(fn)) |
| 418 | |
| 419 | # Short circuit. If we're given a full path which matches the mode |
| 420 | # and it exists, we're done here. |
| 421 | if _access_check(cmd, mode): |
| 422 | return cmd |
| 423 | |
| 424 | path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep) |
| 425 | |
| 426 | if sys.platform == "win32": |
| 427 | # The current directory takes precedence on Windows. |
| 428 | if os.curdir not in path: |
| 429 | path.insert(0, os.curdir) |
| 430 | |
| 431 | # PATHEXT is necessary to check on Windows. |
| 432 | pathext = os.environ.get("PATHEXT", "").split(os.pathsep) |
| 433 | # See if the given file matches any of the expected path extensions. |
| 434 | # This will allow us to short circuit when given "python.exe". |
| 435 | matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())] |
| 436 | # If it does match, only test that one, otherwise we have to try |
| 437 | # others. |
| 438 | files = [cmd] if matches else [cmd + ext.lower() for ext in pathext] |
| 439 | else: |
| 440 | # On other platforms you don't have things like PATHEXT to tell you |
| 441 | # what file suffixes are executable, so just pass on cmd as-is. |
| 442 | files = [cmd] |
| 443 | |
| 444 | seen = set() |
| 445 | for dir in path: |
| 446 | dir = os.path.normcase(dir) |
| 447 | if dir not in seen: |
| 448 | seen.add(dir) |
| 449 | for thefile in files: |
| 450 | name = os.path.join(dir, thefile) |
| 451 | if _access_check(name, mode): |
| 452 | return name |
| 453 | return None |
| 454 | |
| 455 | |
| 456 | # --------------------------------------------------------------------------- |