bpo-39413: Implement os.unsetenv() on Windows (GH-18163)
The os.unsetenv() function is now also available on Windows.
This commit is contained in:
parent
2d5097663d
commit
161e7b36b1
|
@ -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:
|
||||
|
||||
|
|
|
@ -224,6 +224,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
|
||||
------
|
||||
|
||||
|
|
|
@ -953,17 +953,44 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
|
|||
value_str = value.decode(sys.getfilesystemencoding(), 'surrogateescape')
|
||||
self.assertEqual(os.environ['bytes'], value_str)
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'putenv'), "Test needs os.putenv()")
|
||||
@unittest.skipUnless(hasattr(os, 'unsetenv'), "Test needs os.unsetenv()")
|
||||
def test_putenv_unsetenv(self):
|
||||
name = "PYTHONTESTVAR"
|
||||
value = "testvalue"
|
||||
code = f'import os; print(repr(os.environ.get({name!r})))'
|
||||
|
||||
with support.EnvironmentVarGuard() as env:
|
||||
env.pop(name, None)
|
||||
|
||||
os.putenv(name, value)
|
||||
proc = subprocess.run([sys.executable, '-c', code], check=True,
|
||||
stdout=subprocess.PIPE, text=True)
|
||||
self.assertEqual(proc.stdout.rstrip(), repr(value))
|
||||
|
||||
os.unsetenv(name)
|
||||
proc = subprocess.run([sys.executable, '-c', code], check=True,
|
||||
stdout=subprocess.PIPE, text=True)
|
||||
self.assertEqual(proc.stdout.rstrip(), repr(None))
|
||||
|
||||
# On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415).
|
||||
@support.requires_mac_ver(10, 6)
|
||||
def test_unset_error(self):
|
||||
@unittest.skipUnless(hasattr(os, 'putenv'), "Test needs os.putenv()")
|
||||
@unittest.skipUnless(hasattr(os, 'unsetenv'), "Test needs os.unsetenv()")
|
||||
def test_putenv_unsetenv_error(self):
|
||||
# Empty variable name is invalid.
|
||||
# "=" and null character are not allowed in a variable name.
|
||||
for name in ('', '=name', 'na=me', 'name=', 'name\0', 'na\0me'):
|
||||
self.assertRaises((OSError, ValueError), os.putenv, name, "value")
|
||||
self.assertRaises((OSError, ValueError), os.unsetenv, name)
|
||||
|
||||
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)
|
||||
# On Windows, an environment variable string ("name=value" string)
|
||||
# is limited to 32,767 characters
|
||||
longstr = 'x' * 32_768
|
||||
self.assertRaises(ValueError, os.putenv, longstr, "1")
|
||||
self.assertRaises(ValueError, os.putenv, "X", longstr)
|
||||
self.assertRaises(ValueError, os.unsetenv, longstr)
|
||||
|
||||
def test_key_type(self):
|
||||
missing = 'missingkey'
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
The :func:`os.unsetenv` function is now also available on Windows.
|
|
@ -6125,7 +6125,43 @@ exit:
|
|||
|
||||
#endif /* ((defined(HAVE_SETENV) || 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=6f42d8be634f5942 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=0348cbdff48691e3 input=a9049054013a1b77]*/
|
||||
|
|
|
@ -10082,6 +10082,64 @@ posix_putenv_dict_setitem(PyObject *name, PyObject *value)
|
|||
#endif /* PY_PUTENV_DICT */
|
||||
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
static PyObject*
|
||||
win32_putenv(PyObject *name, PyObject *value)
|
||||
{
|
||||
/* Search from index 1 because on Windows starting '=' is allowed for
|
||||
defining hidden environment variables. */
|
||||
if (PyUnicode_GET_LENGTH(name) == 0 ||
|
||||
PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
|
||||
return NULL;
|
||||
}
|
||||
PyObject *unicode;
|
||||
if (value != NULL) {
|
||||
unicode = PyUnicode_FromFormat("%U=%U", name, value);
|
||||
}
|
||||
else {
|
||||
unicode = PyUnicode_FromFormat("%U=", name);
|
||||
}
|
||||
if (unicode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_ssize_t size;
|
||||
/* PyUnicode_AsWideCharString() rejects embedded null characters */
|
||||
wchar_t *env = PyUnicode_AsWideCharString(unicode, &size);
|
||||
Py_DECREF(unicode);
|
||||
|
||||
if (env == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (size > _MAX_ENV) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"the environment variable is longer than %u characters",
|
||||
_MAX_ENV);
|
||||
PyMem_Free(env);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* _wputenv() and SetEnvironmentVariableW() update the environment in the
|
||||
Process Environment Block (PEB). _wputenv() also updates CRT 'environ'
|
||||
and '_wenviron' variables, whereas SetEnvironmentVariableW() does not.
|
||||
|
||||
Prefer _wputenv() to be compatible with C libraries using CRT
|
||||
variables and CRT functions using these variables (ex: getenv()). */
|
||||
int err = _wputenv(env);
|
||||
PyMem_Free(env);
|
||||
|
||||
if (err) {
|
||||
posix_error();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
/*[clinic input]
|
||||
os.putenv
|
||||
|
@ -10097,47 +10155,7 @@ static PyObject *
|
|||
os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
|
||||
/*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/
|
||||
{
|
||||
const wchar_t *env;
|
||||
Py_ssize_t size;
|
||||
|
||||
/* Search from index 1 because on Windows starting '=' is allowed for
|
||||
defining hidden environment variables. */
|
||||
if (PyUnicode_GET_LENGTH(name) == 0 ||
|
||||
PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
|
||||
return NULL;
|
||||
}
|
||||
PyObject *unicode = PyUnicode_FromFormat("%U=%U", name, value);
|
||||
if (unicode == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env = PyUnicode_AsUnicodeAndSize(unicode, &size);
|
||||
if (env == NULL)
|
||||
goto error;
|
||||
if (size > _MAX_ENV) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"the environment variable is longer than %u characters",
|
||||
_MAX_ENV);
|
||||
goto error;
|
||||
}
|
||||
if (wcslen(env) != (size_t)size) {
|
||||
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_wputenv(env)) {
|
||||
posix_error();
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(unicode);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
|
||||
error:
|
||||
Py_DECREF(unicode);
|
||||
return NULL;
|
||||
return win32_putenv(name, value);
|
||||
}
|
||||
/* repeat !defined(MS_WINDOWS) to workaround an Argument Clinic issue */
|
||||
#elif (defined(HAVE_SETENV) || defined(HAVE_PUTENV)) && !defined(MS_WINDOWS)
|
||||
|
@ -10186,7 +10204,23 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
|
|||
#endif /* defined(HAVE_SETENV) || defined(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]*/
|
||||
{
|
||||
return win32_putenv(name, NULL);
|
||||
}
|
||||
/* repeat !defined(MS_WINDOWS) to workaround an Argument Clinic issue */
|
||||
#elif defined(HAVE_UNSETENV) && !defined(MS_WINDOWS)
|
||||
/*[clinic input]
|
||||
os.unsetenv
|
||||
name: FSConverter
|
||||
|
@ -10199,16 +10233,13 @@ static PyObject *
|
|||
os_unsetenv_impl(PyObject *module, PyObject *name)
|
||||
/*[clinic end generated code: output=54c4137ab1834f02 input=2bb5288a599c7107]*/
|
||||
{
|
||||
#ifndef HAVE_BROKEN_UNSETENV
|
||||
int err;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BROKEN_UNSETENV
|
||||
unsetenv(PyBytes_AS_STRING(name));
|
||||
#else
|
||||
err = unsetenv(PyBytes_AS_STRING(name));
|
||||
if (err)
|
||||
int err = unsetenv(PyBytes_AS_STRING(name));
|
||||
if (err) {
|
||||
return posix_error();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PY_PUTENV_DICT
|
||||
|
|
Loading…
Reference in New Issue