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
(cmd, mode=os.F_OK | os.X_OK, path=None)
| 1478 | |
| 1479 | |
| 1480 | def which(cmd, mode=os.F_OK | os.X_OK, path=None): |
| 1481 | """Given a command, mode, and a PATH string, return the path which |
| 1482 | conforms to the given mode on the PATH, or None if there is no such |
| 1483 | file. |
| 1484 | |
| 1485 | `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result |
| 1486 | of os.environ.get("PATH"), or can be overridden with a custom search |
| 1487 | path. |
| 1488 | |
| 1489 | """ |
| 1490 | # If we're given a path with a directory part, look it up directly rather |
| 1491 | # than referring to PATH directories. This includes checking relative to the |
| 1492 | # current directory, e.g. ./script |
| 1493 | if os.path.dirname(cmd): |
| 1494 | if _access_check(cmd, mode): |
| 1495 | return cmd |
| 1496 | return None |
| 1497 | |
| 1498 | use_bytes = isinstance(cmd, bytes) |
| 1499 | |
| 1500 | if path is None: |
| 1501 | path = os.environ.get("PATH", None) |
| 1502 | if path is None: |
| 1503 | try: |
| 1504 | path = os.confstr("CS_PATH") |
| 1505 | except (AttributeError, ValueError): |
| 1506 | # os.confstr() or CS_PATH is not available |
| 1507 | path = os.defpath |
| 1508 | # bpo-35755: Don't use os.defpath if the PATH environment variable is |
| 1509 | # set to an empty string |
| 1510 | |
| 1511 | # PATH='' doesn't match, whereas PATH=':' looks in the current directory |
| 1512 | if not path: |
| 1513 | return None |
| 1514 | |
| 1515 | if use_bytes: |
| 1516 | path = os.fsencode(path) |
| 1517 | path = path.split(os.fsencode(os.pathsep)) |
| 1518 | else: |
| 1519 | path = os.fsdecode(path) |
| 1520 | path = path.split(os.pathsep) |
| 1521 | |
| 1522 | if sys.platform == "win32": |
| 1523 | # The current directory takes precedence on Windows. |
| 1524 | curdir = os.curdir |
| 1525 | if use_bytes: |
| 1526 | curdir = os.fsencode(curdir) |
| 1527 | if curdir not in path: |
| 1528 | path.insert(0, curdir) |
| 1529 | |
| 1530 | # PATHEXT is necessary to check on Windows. |
| 1531 | pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT |
| 1532 | pathext = [ext for ext in pathext_source.split(os.pathsep) if ext] |
| 1533 | |
| 1534 | if use_bytes: |
| 1535 | pathext = [os.fsencode(ext) for ext in pathext] |
| 1536 | # See if the given file matches any of the expected path extensions. |
| 1537 | # This will allow us to short circuit when given "python.exe". |