Index: Lib/os.py =================================================================== --- Lib/os.py (révision 80660) +++ Lib/os.py (copie de travail) @@ -355,8 +355,9 @@ return last_exc = saved_exc = None saved_tb = None + file = fsencode(file) for dir in get_exec_path(env): - fullname = path.join(dir, file) + fullname = path.join(fsencode(dir), file) try: exec_func(fullname, *argrest) except error as e: @@ -380,36 +381,39 @@ """ if env is None: env = environ - return env.get('PATH', defpath).split(pathsep) + path = env.get('PATH', defpath) + path = fsdecode(path) + return path.split(pathsep) # Change environ to automatically call putenv(), unsetenv if they exist. from _abcoll import MutableMapping # Can't use collections (bootstrap) class _Environ(MutableMapping): - def __init__(self, environ, keymap, putenv, unsetenv): - self.keymap = keymap + def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv): + self.encodekey = encodekey + self.decodekey = decodekey + self.encodevalue = encodevalue + self.decodevalue = decodevalue self.putenv = putenv self.unsetenv = unsetenv - self.data = data = {} - for key, value in environ.items(): - data[keymap(key)] = str(value) + self.data = data def __getitem__(self, key): - return self.data[self.keymap(key)] + value = self.data[self.encodekey(key)] + return self.decodevalue(value) def __setitem__(self, key, value): - value = str(value) self.putenv(key, value) - self.data[self.keymap(key)] = value + self.data[self.encodekey(key)] = self.encodevalue(value) def __delitem__(self, key): self.unsetenv(key) - del self.data[self.keymap(key)] + del self.data[self.encodekey(key)] def __iter__(self): for key in self.data: - yield key + yield self.decodekey(key) def __len__(self): return len(self.data) @@ -439,22 +443,56 @@ else: __all__.append("unsetenv") +def fsdecode(value): + """ + file system to unicode + """ + if isinstance(value, str): + return value + else: + return value.decode(sys.getfilesystemencoding(), 'surrogateescape') + +if name in ('os2', 'nt', 'ce'): + fsencode = fsdecode +else: + def fsencode(value): + """ + unicode to file system + """ + if isinstance(value, bytes): + return value + else: + return value.encode(sys.getfilesystemencoding(), 'surrogateescape') + if name in ('os2', 'nt'): # Where Env Var Names Must Be UPPERCASE - _keymap = lambda key: str(key.upper()) + def _keymap(key): + return fsencode(key).upper() else: # Where Env Var Names Can Be Mixed Case - _keymap = lambda key: str(key) + _keymap = fsencode -environ = _Environ(environ, _keymap, _putenv, _unsetenv) +data = {} +for key, value in environ.items(): + data[_keymap(key)] = fsencode(value) +# unicode environ +environ = _Environ(data, _keymap, fsdecode, fsencode, fsdecode, _putenv, _unsetenv) def getenv(key, default=None): """Get an environment variable, return None if it doesn't exist. The optional second argument can specify an alternate default.""" - if isinstance(key, bytes): - key = key.decode(sys.getfilesystemencoding(), "surrogateescape") return environ.get(key, default) __all__.append("getenv") +if name not in ('os2', 'nt'): + # bytes environ + environb = _Environ(data, _keymap, fsencode, fsencode, fsencode, _putenv, _unsetenv) + + def getenvb(key, default=None): + """Get an environment variable, return None if it doesn't exist. + The optional second argument can specify an alternate default.""" + return environb.get(key, default) + __all__.append("getenvb") + def _exists(name): return name in globals() Index: Lib/test/test_os.py =================================================================== --- Lib/test/test_os.py (révision 80660) +++ Lib/test/test_os.py (copie de travail) @@ -439,7 +439,22 @@ # Supplied PATH environment variable self.assertSequenceEqual(test_path, os.get_exec_path(test_env)) + @unittest.skipIf(sys.platform == "win32", "POSIX specific test") + def test_environb(self): + # os.environ -> os.environb + value = 'euro\u20ac' + os.environ['unicode'] = value + self.assertEquals(os.environ['unicode'], value) + value_bytes = os.fsencode(value) + self.assertEquals(os.environb[b'unicode'], value_bytes) + # os.environb -> os.environ + value = b'\xff' + os.environb[b'bytes'] = value + self.assertEquals(os.environb['bytes'], value) + value_str = os.fsdecode(value) + self.assertEquals(os.environ['bytes'], value_str) + class WalkTests(unittest.TestCase): """Tests for os.walk().""" Index: Lib/test/test_subprocess.py =================================================================== --- Lib/test/test_subprocess.py (révision 80660) +++ Lib/test/test_subprocess.py (copie de travail) @@ -827,7 +827,18 @@ stdout = stdout.rstrip(b'\n\r') self.assertEquals(stdout, value_repr) + def test_bytes_program(self): + path, program = os.path.split(sys.executable) + path = os.fsencode(path) + program = os.fsencode(program) + env = os.environ.copy() + env["PATH"] = path + exitcode = subprocess.call( + [program, "-c", "pass"], + env=env) + self.assertEquals(exitcode, 0) + @unittest.skipUnless(mswindows, "Windows specific tests") class Win32ProcessTestCase(BaseTestCase): Index: Lib/subprocess.py =================================================================== --- Lib/subprocess.py (révision 80660) +++ Lib/subprocess.py (copie de travail) @@ -1079,33 +1079,25 @@ self._set_cloexec_flag(errpipe_write) if _posixsubprocess: - fs_encoding = sys.getfilesystemencoding() - def fs_encode(s): - """Encode s for use in the env, fs or cmdline.""" - if isinstance(s, bytes): - return s - else: - return s.encode(fs_encoding, 'surrogateescape') - # We must avoid complex work that could involve # malloc or free in the child process to avoid # potential deadlocks, thus we do all this here. # and pass it to fork_exec() if env: - env_list = [fs_encode(k) + b'=' + fs_encode(v) + env_list = [os.fsencode(k) + b'=' + os.fsencode(v) for k, v in env.items()] else: env_list = None # Use execv instead of execve. if os.path.dirname(executable): - executable_list = (fs_encode(executable),) + executable_list = (os.fsencode(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(fs_encode(exe) - for exe in executable_list) + executable = os.fsencode(executable) + executable_list = tuple( + os.path.join(os.fsencode(dir), executable) + for dir in path_list) self.pid = _posixsubprocess.fork_exec( args, executable_list, close_fds, cwd, env_list, Index: Modules/posixmodule.c =================================================================== --- Modules/posixmodule.c (révision 80660) +++ Modules/posixmodule.c (copie de travail) @@ -493,14 +493,12 @@ char *p = strchr(*e, '='); if (p == NULL) continue; - k = PyUnicode_Decode(*e, (int)(p-*e), - Py_FileSystemDefaultEncoding, "surrogateescape"); + k = PyBytes_FromStringAndSize(*e, (int)(p-*e)); if (k == NULL) { PyErr_Clear(); continue; } - v = PyUnicode_Decode(p+1, strlen(p+1), - Py_FileSystemDefaultEncoding, "surrogateescape"); + v = PyBytes_FromStringAndSize(p+1, strlen(p+1)); if (v == NULL) { PyErr_Clear(); Py_DECREF(k);