Index: Doc/library/os.rst =================================================================== --- Doc/library/os.rst (révision 81236) +++ Doc/library/os.rst (copie de travail) @@ -142,7 +142,7 @@ synchronized (modify :data:`environb` updates :data:`environ`, and vice versa). - Availability: Unix. + :data:`environb` is available if :data:`supports_bytes_environ` is True. .. versionadded:: 3.2 @@ -457,6 +457,12 @@ Availability: Unix, Windows. +.. data:: supports_bytes_environ + + True if the native OS type of the environment is bytes (eg. False on + Windows). :data:`environb` is only available if + :data:`supports_bytes_environ` is True. + .. function:: umask(mask) Set the current numeric umask and return the previous umask. Index: Lib/os.py =================================================================== --- Lib/os.py (révision 81236) +++ Lib/os.py (copie de travail) @@ -355,7 +355,11 @@ return last_exc = saved_exc = None saved_tb = None - for dir in get_exec_path(env): + path_list = get_exec_path(env) + if name != 'nt': + file = fsencode(file) + path_list = map(fsencode, path_list) + for dir in path_list: fullname = path.join(dir, file) try: exec_func(fullname, *argrest) @@ -380,7 +384,24 @@ """ if env is None: env = environ - return env.get('PATH', defpath).split(pathsep) + try: + path_list = env.get('PATH') + except TypeError: + path_list = None + if supports_bytes_environ: + try: + path_listb = env[b'PATH'] + except (KeyError, TypeError): + pass + else: + if path_list is not None: + raise ValueError( + "env contains two PATH variables of different types") + path_list = path_listb.decode(sys.getfilesystemencoding(), + 'surrogateescape') + if path_list is None: + path_list = defpath + return path_list.split(pathsep) # Change environ to automatically call putenv(), unsetenv if they exist. @@ -482,9 +503,11 @@ The optional second argument can specify an alternate default. key, default and the result are str.""" return environ.get(key, default) -__all__.append("getenv") -if name not in ('os2', 'nt'): +supports_bytes_environ = name not in ('os2', 'nt') +__all__.extend(("getenv", "supports_bytes_environ")) + +if supports_bytes_environ: def _check_bytes(value): if not isinstance(value, bytes): raise TypeError("bytes expected, not %s" % type(value).__name__) Index: Lib/test/test_subprocess.py =================================================================== --- Lib/test/test_subprocess.py (révision 81236) +++ Lib/test/test_subprocess.py (copie de travail) @@ -825,7 +825,23 @@ stdout = stdout.rstrip(b'\n\r') self.assertEquals(stdout.decode('ascii'), repr(value)) + def test_bytes_program(self): + path, program = os.path.split(sys.executable) + program = os.fsencode(program) + # bytes program, unicode PATH + env = os.environ.copy() + env["PATH"] = path + exitcode = subprocess.call([program, "-c", "pass"], env=env) + self.assertEquals(exitcode, 0) + + # bytes program, bytes PATH + envb = os.environb.copy() + envb[b"PATH"] = os.fsencode(path) + exitcode = subprocess.call([program, "-c", "pass"], env=envb) + self.assertEquals(exitcode, 0) + + @unittest.skipUnless(mswindows, "Windows specific tests") class Win32ProcessTestCase(BaseTestCase): Index: Lib/subprocess.py =================================================================== --- Lib/subprocess.py (révision 81236) +++ Lib/subprocess.py (copie de travail) @@ -1096,15 +1096,14 @@ for k, v in env.items()] else: env_list = None # Use execv instead of execve. + executable = os.fsencode(executable) if os.path.dirname(executable): - executable_list = (os.fsencode(executable),) + executable_list = (executable,) else: # This matches the behavior of os._execvpe(). - path_list = os.get_exec_path(env) - executable_list = (os.path.join(dir, executable) - for dir in path_list) - executable_list = tuple(os.fsencode(exe) - for exe in executable_list) + executable_list = tuple( + os.path.join(os.fsencode(dir), executable) + for dir in os.get_exec_path(env)) self.pid = _posixsubprocess.fork_exec( args, executable_list, close_fds, cwd, env_list,