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)
| 77 | |
| 78 | # shutil.which from Python 3.4 |
| 79 | def _shutil_which(cmd, mode=os.F_OK | os.X_OK, path=None): |
| 80 | """Given a command, mode, and a PATH string, return the path which |
| 81 | conforms to the given mode on the PATH, or None if there is no such |
| 82 | file. |
| 83 | |
| 84 | `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result |
| 85 | of os.environ.get("PATH"), or can be overridden with a custom search |
| 86 | path. |
| 87 | |
| 88 | This is a backport of shutil.which from Python 3.4 |
| 89 | """ |
| 90 | # Check that a given file can be accessed with the correct mode. |
| 91 | # Additionally check that `file` is not a directory, as on Windows |
| 92 | # directories pass the os.access check. |
| 93 | def _access_check(fn, mode): |
| 94 | return (os.path.exists(fn) and os.access(fn, mode) |
| 95 | and not os.path.isdir(fn)) |
| 96 | |
| 97 | # If we're given a path with a directory part, look it up directly rather |
| 98 | # than referring to PATH directories. This includes checking relative to the |
| 99 | # current directory, e.g. ./script |
| 100 | if os.path.dirname(cmd): |
| 101 | if _access_check(cmd, mode): |
| 102 | return cmd |
| 103 | return None |
| 104 | |
| 105 | if path is None: |
| 106 | path = os.environ.get("PATH", os.defpath) |
| 107 | if not path: |
| 108 | return None |
| 109 | path = path.split(os.pathsep) |
| 110 | |
| 111 | if sys.platform == "win32": |
| 112 | # The current directory takes precedence on Windows. |
| 113 | if not os.curdir in path: |
| 114 | path.insert(0, os.curdir) |
| 115 | |
| 116 | # PATHEXT is necessary to check on Windows. |
| 117 | pathext = os.environ.get("PATHEXT", "").split(os.pathsep) |
| 118 | # See if the given file matches any of the expected path extensions. |
| 119 | # This will allow us to short circuit when given "python.exe". |
| 120 | # If it does match, only test that one, otherwise we have to try |
| 121 | # others. |
| 122 | if any(cmd.lower().endswith(ext.lower()) for ext in pathext): |
| 123 | files = [cmd] |
| 124 | else: |
| 125 | files = [cmd + ext for ext in pathext] |
| 126 | else: |
| 127 | # On other platforms you don't have things like PATHEXT to tell you |
| 128 | # what file suffixes are executable, so just pass on cmd as-is. |
| 129 | files = [cmd] |
| 130 | |
| 131 | seen = set() |
| 132 | for dir in path: |
| 133 | normdir = os.path.normcase(dir) |
| 134 | if not normdir in seen: |
| 135 | seen.add(normdir) |
| 136 | for thefile in files: |
nothing calls this directly
no test coverage detected