From 56cd3710a1ea3ba872d345ea1bebc86ed08bc8b8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 21 Jan 2020 16:13:09 +0100 Subject: [PATCH] bpo-39413: Implement os.unsetenv() on Windows (GH-18104) The os.unsetenv() function is now also available on Windows. It is implemented with SetEnvironmentVariableW(name, NULL). --- Doc/library/os.rst | 3 ++ Doc/whatsnew/3.9.rst | 3 ++ Lib/test/test_os.py | 11 ++--- .../2020-01-21-15-48-35.bpo-39413.7XYDM8.rst | 1 + Modules/clinic/posixmodule.c.h | 42 ++++++++++++++++-- Modules/posixmodule.c | 44 ++++++++++++++++++- 6 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-01-21-15-48-35.bpo-39413.7XYDM8.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 4fec647828e..de3e5603e10 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -645,6 +645,9 @@ process and user. .. availability:: most flavors of Unix, Windows. + .. versionchanged:: 3.9 + The function is now also available on Windows. + .. _os-newstreams: diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index f40685c9327..ab27c488ea3 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -216,6 +216,9 @@ Exposed the Linux-specific :func:`os.pidfd_open` (:issue:`38692`) and :data:`os.P_PIDFD` (:issue:`38713`) for process management with file descriptors. +The :func:`os.unsetenv` function is now also available on Windows. +(Contributed by Victor Stinner in :issue:`39413`.) + poplib ------ diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 82c441c2048..50535da0a2b 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -956,14 +956,9 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol): # On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415). @support.requires_mac_ver(10, 6) def test_unset_error(self): - if sys.platform == "win32": - # an environment variable is limited to 32,767 characters - key = 'x' * 50000 - self.assertRaises(ValueError, os.environ.__delitem__, key) - else: - # "=" is not allowed in a variable name - key = 'key=' - self.assertRaises(OSError, os.environ.__delitem__, key) + # "=" is not allowed in a variable name + key = 'key=' + self.assertRaises(OSError, os.environ.__delitem__, key) def test_key_type(self): missing = 'missingkey' diff --git a/Misc/NEWS.d/next/Library/2020-01-21-15-48-35.bpo-39413.7XYDM8.rst b/Misc/NEWS.d/next/Library/2020-01-21-15-48-35.bpo-39413.7XYDM8.rst new file mode 100644 index 00000000000..a185ab5efe2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-21-15-48-35.bpo-39413.7XYDM8.rst @@ -0,0 +1 @@ +The :func:`os.unsetenv` function is now also available on Windows. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index aa4756a620a..661d91afb7e 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -6125,7 +6125,43 @@ exit: #endif /* defined(HAVE_PUTENV) && !defined(MS_WINDOWS) */ -#if defined(HAVE_UNSETENV) +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os_unsetenv__doc__, +"unsetenv($module, name, /)\n" +"--\n" +"\n" +"Delete an environment variable."); + +#define OS_UNSETENV_METHODDEF \ + {"unsetenv", (PyCFunction)os_unsetenv, METH_O, os_unsetenv__doc__}, + +static PyObject * +os_unsetenv_impl(PyObject *module, PyObject *name); + +static PyObject * +os_unsetenv(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *name; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("unsetenv", "argument", "str", arg); + goto exit; + } + if (PyUnicode_READY(arg) == -1) { + goto exit; + } + name = arg; + return_value = os_unsetenv_impl(module, name); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if (defined(HAVE_UNSETENV) && !defined(MS_WINDOWS)) PyDoc_STRVAR(os_unsetenv__doc__, "unsetenv($module, name, /)\n" @@ -6157,7 +6193,7 @@ exit: return return_value; } -#endif /* defined(HAVE_UNSETENV) */ +#endif /* (defined(HAVE_UNSETENV) && !defined(MS_WINDOWS)) */ PyDoc_STRVAR(os_strerror__doc__, "strerror($module, code, /)\n" @@ -8773,4 +8809,4 @@ exit: #ifndef OS__REMOVE_DLL_DIRECTORY_METHODDEF #define OS__REMOVE_DLL_DIRECTORY_METHODDEF #endif /* !defined(OS__REMOVE_DLL_DIRECTORY_METHODDEF) */ -/*[clinic end generated code: output=51ba5b9536420cea input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6e739a2715712e88 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 322c2159812..d2047d9c2ac 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -10163,7 +10163,49 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) #endif /* HAVE_PUTENV */ -#ifdef HAVE_UNSETENV +#ifdef MS_WINDOWS +/*[clinic input] +os.unsetenv + name: unicode + / + +Delete an environment variable. +[clinic start generated code]*/ + +static PyObject * +os_unsetenv_impl(PyObject *module, PyObject *name) +/*[clinic end generated code: output=54c4137ab1834f02 input=4d6a1747cc526d2f]*/ +{ + /* PyUnicode_AsWideCharString() rejects embedded null characters */ + wchar_t *name_str = PyUnicode_AsWideCharString(name, NULL); + if (name_str == NULL) { + return NULL; + } + + BOOL ok = SetEnvironmentVariableW(name_str, NULL); + PyMem_Free(name_str); + + if (!ok) { + return PyErr_SetFromWindowsErr(0); + } + + /* Remove the key from posix_putenv_garbage; + * this will cause it to be collected. This has to + * happen after the real unsetenv() call because the + * old value was still accessible until then. + */ + if (PyDict_DelItem(_posixstate(module)->posix_putenv_garbage, name)) { + /* really not much we can do; just leak */ + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + return NULL; + } + PyErr_Clear(); + } + + Py_RETURN_NONE; +} +/* repeat !defined(MS_WINDOWS) to workaround an Argument Clinic issue */ +#elif defined(HAVE_UNSETENV) && !defined(MS_WINDOWS) /*[clinic input] os.unsetenv name: FSConverter