shutil_which.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. """Backport of shutil.which from Python 3.5
  2. The function is included unmodified from Python stdlib 3.5.1,
  3. and is (C) Python
  4. """
  5. from __future__ import absolute_import # Import system's os, not backports.os.
  6. import os
  7. import sys
  8. __version__ = '3.5.1'
  9. def backport_which(cmd, mode=os.F_OK | os.X_OK, path=None):
  10. """Given a command, mode, and a PATH string, return the path which
  11. conforms to the given mode on the PATH, or None if there is no such
  12. file.
  13. `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
  14. of os.environ.get("PATH"), or can be overridden with a custom search
  15. path.
  16. """
  17. # Check that a given file can be accessed with the correct mode.
  18. # Additionally check that `file` is not a directory, as on Windows
  19. # directories pass the os.access check.
  20. def _access_check(fn, mode):
  21. return (os.path.exists(fn) and os.access(fn, mode)
  22. and not os.path.isdir(fn))
  23. # If we're given a path with a directory part, look it up directly rather
  24. # than referring to PATH directories. This includes checking relative to the
  25. # current directory, e.g. ./script
  26. if os.path.dirname(cmd):
  27. if _access_check(cmd, mode):
  28. return cmd
  29. return None
  30. if path is None:
  31. path = os.environ.get("PATH", os.defpath)
  32. if not path:
  33. return None
  34. path = path.split(os.pathsep)
  35. if sys.platform == "win32":
  36. # The current directory takes precedence on Windows.
  37. if not os.curdir in path:
  38. path.insert(0, os.curdir)
  39. # PATHEXT is necessary to check on Windows.
  40. pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
  41. # See if the given file matches any of the expected path extensions.
  42. # This will allow us to short circuit when given "python.exe".
  43. # If it does match, only test that one, otherwise we have to try
  44. # others.
  45. if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
  46. files = [cmd]
  47. else:
  48. files = [cmd + ext for ext in pathext]
  49. else:
  50. # On other platforms you don't have things like PATHEXT to tell you
  51. # what file suffixes are executable, so just pass on cmd as-is.
  52. files = [cmd]
  53. seen = set()
  54. for dir in path:
  55. normdir = os.path.normcase(dir)
  56. if not normdir in seen:
  57. seen.add(normdir)
  58. for thefile in files:
  59. name = os.path.join(dir, thefile)
  60. if _access_check(name, mode):
  61. return name
  62. return None
  63. try:
  64. from shutil import which
  65. except ImportError:
  66. which = backport_which