[Python-checkins] cpython: Issue #18283: shutil.which() now supports bytes argument, not only text

victor.stinner python-checkins at python.org
Mon Dec 16 22:49:10 CET 2013


http://hg.python.org/cpython/rev/a1a05e2724dd
changeset:   88011:a1a05e2724dd
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Mon Dec 16 22:48:48 2013 +0100
summary:
  Issue #18283: shutil.which() now supports bytes argument, not only text argument.

files:
  Doc/library/shutil.rst  |   4 ++++
  Lib/shutil.py           |  23 +++++++++++++++--------
  Lib/test/test_shutil.py |  13 +++++++++++--
  Misc/NEWS               |   2 ++
  4 files changed, 32 insertions(+), 10 deletions(-)


diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -352,6 +352,10 @@
 
    .. versionadded:: 3.3
 
+   .. versionchanged:: 3.4
+      The :class:`bytes` type is now accepted. If *cmd* type is :class:`bytes`,
+      the result type is also :class:`bytes`.
+
 
 .. exception:: Error
 
diff --git a/Lib/shutil.py b/Lib/shutil.py
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -1065,6 +1065,13 @@
 
     return os.terminal_size((columns, lines))
 
+# Check that a given file can be accessed with the correct mode.
+# Additionally check that `file` is not a directory, as on Windows
+# directories pass the os.access check.
+def _access_check(fn, mode):
+    return (os.path.exists(fn) and os.access(fn, mode)
+            and not os.path.isdir(fn))
+
 def which(cmd, mode=os.F_OK | os.X_OK, path=None):
     """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
@@ -1075,13 +1082,6 @@
     path.
 
     """
-    # Check that a given file can be accessed with the correct mode.
-    # Additionally check that `file` is not a directory, as on Windows
-    # directories pass the os.access check.
-    def _access_check(fn, mode):
-        return (os.path.exists(fn) and os.access(fn, mode)
-                and not os.path.isdir(fn))
-
     # If we're given a path with a directory part, look it up directly rather
     # than referring to PATH directories. This includes checking relative to the
     # current directory, e.g. ./script
@@ -1094,7 +1094,12 @@
         path = os.environ.get("PATH", os.defpath)
     if not path:
         return None
-    path = path.split(os.pathsep)
+    if isinstance(cmd, bytes):
+        path = os.fsencode(path)
+        path = path.split(os.fsencode(os.pathsep))
+    else:
+        path = os.fsdecode(path)
+        path = path.split(os.pathsep)
 
     if sys.platform == "win32":
         # The current directory takes precedence on Windows.
@@ -1103,6 +1108,8 @@
 
         # PATHEXT is necessary to check on Windows.
         pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
+        if isinstance(cmd, bytes):
+            pathext = map(os.fsencode, pathext)
         # See if the given file matches any of the expected path extensions.
         # This will allow us to short circuit when given "python.exe".
         # If it does match, only test that one, otherwise we have to try
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -1326,6 +1326,7 @@
         os.chmod(self.temp_file.name, stat.S_IXUSR)
         self.addCleanup(self.temp_file.close)
         self.dir, self.file = os.path.split(self.temp_file.name)
+        self.env_path = self.dir
 
     def test_basic(self):
         # Given an EXE in a directory, it should be returned.
@@ -1394,7 +1395,7 @@
 
     def test_environ_path(self):
         with support.EnvironmentVarGuard() as env:
-            env['PATH'] = self.dir
+            env['PATH'] = self.env_path
             rv = shutil.which(self.file)
             self.assertEqual(rv, self.temp_file.name)
 
@@ -1402,7 +1403,7 @@
         base_dir = os.path.dirname(self.dir)
         with support.change_cwd(path=self.dir), \
              support.EnvironmentVarGuard() as env:
-            env['PATH'] = self.dir
+            env['PATH'] = self.env_path
             rv = shutil.which(self.file, path='')
             self.assertIsNone(rv)
 
@@ -1413,6 +1414,14 @@
             self.assertIsNone(rv)
 
 
+class TestWhichBytes(TestWhich):
+    def setUp(self):
+        TestWhich.setUp(self)
+        self.dir = os.fsencode(self.dir)
+        self.file = os.fsencode(self.file)
+        self.temp_file.name = os.fsencode(self.temp_file.name)
+
+
 class TestMove(unittest.TestCase):
 
     def setUp(self):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -44,6 +44,8 @@
 Library
 -------
 
+- Issue #18283: shutil.which() now supports bytes argument, not only text argument.
+
 - Issue #19921: When Path.mkdir() is called with parents=True, any missing
   parent is created with the default permissions, ignoring the mode argument
   (mimicking the POSIX "mkdir -p" command).

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list