diff -r 9d0f4da4d531 Lib/os.py --- a/Lib/os.py Sat Sep 17 01:30:48 2016 +0300 +++ b/Lib/os.py Sat Sep 17 01:13:24 2016 +0000 @@ -785,28 +785,25 @@ __all__.append("unsetenv") def _createenviron(): + def _check_str(value): + if not isinstance(value, str): + raise TypeError("str expected, not %s" % type(value).__name__) + return value if name == 'nt': # Where Env Var Names Must Be UPPERCASE - def check_str(value): - if not isinstance(value, str): - raise TypeError("str expected, not %s" % type(value).__name__) - return value - encode = check_str + encode = _check_str decode = str def encodekey(key): return encode(key).upper() - data = {} - for key, value in environ.items(): - data[encodekey(key)] = value + data = {encodekey(k):v for k, v in environ.items()} else: # Where Env Var Names Can Be Mixed Case encoding = sys.getfilesystemencoding() + errors = sys.getfilesystemencodeerrors() def encode(value): - if not isinstance(value, str): - raise TypeError("str expected, not %s" % type(value).__name__) - return value.encode(encoding, 'surrogateescape') + return _check_str(value).encode(encoding, errors) def decode(value): - return value.decode(encoding, 'surrogateescape') + return value.decode(encoding, errors) encodekey = encode data = environ return _Environ(data, @@ -818,36 +815,52 @@ environ = _createenviron() del _createenviron - 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. key, default and the result are str.""" return environ.get(key, default) -supports_bytes_environ = (name != 'nt') -__all__.extend(("getenv", "supports_bytes_environ")) +__all__.extend(("getenv",)) -if supports_bytes_environ: +supports_bytes_environ = True + +def _createenvironb(): def _check_bytes(value): if not isinstance(value, bytes): raise TypeError("bytes expected, not %s" % type(value).__name__) return value + if name == 'nt': + # Where Env Var Names Must Be UPPERCASE + encoding = sys.getfilesystemencoding() + errors = sys.getfilesystemencodeerrors() + def encode(value): + return _check_bytes(value).decode(encoding, errors) + def decode(value): + return value.encode(encoding, errors) + def encodekey(key): + return encode(key).upper() + else: + # Where Env Var Names Can Be Mixed Case + encode = _check_bytes + decode = bytes + encodekey = encode + return _Environ(environ._data, + encodekey, decode, + encode, decode, + _putenv, _unsetenv) - # bytes environ - environb = _Environ(environ._data, - _check_bytes, bytes, - _check_bytes, bytes, - _putenv, _unsetenv) - del _check_bytes +# bytes environ +environb = _createenvironb() +del _createenvironb - 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. - key, default and the result are bytes.""" - return environb.get(key, default) - - __all__.extend(("environb", "getenvb")) +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. + key, default and the result are bytes.""" + return environb.get(key, default) + +__all__.extend(("supports_bytes_environ", "environb", "getenvb")) def _fscodec(): encoding = sys.getfilesystemencoding() diff -r 9d0f4da4d531 Lib/test/test_os.py --- a/Lib/test/test_os.py Sat Sep 17 01:30:48 2016 +0300 +++ b/Lib/test/test_os.py Sat Sep 17 01:13:24 2016 +0000 @@ -762,25 +762,24 @@ @unittest.skipUnless(os.supports_bytes_environ, "os.environb required for this test.") def test_environb(self): - # os.environ -> os.environb + encoding = sys.getfilesystemencoding() + errors = sys.getfilesystemencodeerrors() value = 'euro\u20ac' try: - value_bytes = value.encode(sys.getfilesystemencoding(), - 'surrogateescape') + value_bytes = value.encode(encoding, errors) except UnicodeEncodeError: - msg = "U+20AC character is not encodable to %s" % ( - sys.getfilesystemencoding(),) + msg = "U+20AC character is not encodable to %s" % (encoding,) self.skipTest(msg) + + # os.environ -> os.environb os.environ['unicode'] = value self.assertEqual(os.environ['unicode'], value) self.assertEqual(os.environb[b'unicode'], value_bytes) # os.environb -> os.environ - value = b'\xff' - os.environb[b'bytes'] = value - self.assertEqual(os.environb[b'bytes'], value) - value_str = value.decode(sys.getfilesystemencoding(), 'surrogateescape') - self.assertEqual(os.environ['bytes'], value_str) + os.environb[b'bytes'] = value_bytes + self.assertEqual(os.environb[b'bytes'], value_bytes) + self.assertEqual(os.environ['bytes'], value) # On FreeBSD < 7 and OS X < 10.6, unsetenv() doesn't return a value (issue # #13415). diff -r 9d0f4da4d531 Modules/clinic/posixmodule.c.h --- a/Modules/clinic/posixmodule.c.h Sat Sep 17 01:30:48 2016 +0300 +++ b/Modules/clinic/posixmodule.c.h Sat Sep 17 01:13:24 2016 +0000 @@ -4289,16 +4289,21 @@ os_putenv(PyObject *module, PyObject *args) { PyObject *return_value = NULL; - PyObject *name; - PyObject *value; - - if (!PyArg_ParseTuple(args, "UU:putenv", - &name, &value)) { + PyObject *name = NULL; + PyObject *value = NULL; + + if (!PyArg_ParseTuple(args, "O&O&:putenv", + PyUnicode_FSDecoder, &name, PyUnicode_FSDecoder, &value)) { goto exit; } return_value = os_putenv_impl(module, name, value); exit: + /* Cleanup for name */ + Py_XDECREF(name); + /* Cleanup for value */ + Py_XDECREF(value); + return return_value; } @@ -6148,4 +6153,4 @@ #ifndef OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF #endif /* !defined(OS_GETRANDOM_METHODDEF) */ -/*[clinic end generated code: output=b9ed5703d2feb0d9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3e1176a6adc72af6 input=a9049054013a1b77]*/ diff -r 9d0f4da4d531 Modules/posixmodule.c --- a/Modules/posixmodule.c Sat Sep 17 01:30:48 2016 +0300 +++ b/Modules/posixmodule.c Sat Sep 17 01:13:24 2016 +0000 @@ -2325,6 +2325,17 @@ def cleanup(self): return "Py_XDECREF(" + self.name + ");\n" +class FSDecoder_converter(CConverter): + type = 'PyObject *' + converter = 'PyUnicode_FSDecoder' + def converter_init(self): + if self.default is not unspecified: + fail("FSDecoder_converter does not support default values") + self.c_default = 'NULL' + + def cleanup(self): + return "Py_XDECREF(" + self.name + ");\n" + class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "' @@ -2364,7 +2375,7 @@ impl_by_reference = True; [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=418fce0e01144461]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=cdf0eb96bcdcbf4f]*/ /*[clinic input] @@ -8761,8 +8772,8 @@ /*[clinic input] os.putenv - name: unicode - value: unicode + name: FSDecoder + value: FSDecoder / Change or add an environment variable. @@ -8770,7 +8781,7 @@ static PyObject * os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) -/*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/ +/*[clinic end generated code: output=d29a567d6b2327d2 input=3d4ced5099273829]*/ { const wchar_t *env;