From 35e9d41a9cc3999672ba7440847b16ec71bd9ddd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 11 Oct 2025 22:58:43 +0200 Subject: [PATCH] gh-139482: Add `posix._clearenv()` function (#139965) --- Lib/os.py | 11 ++++++++ Lib/test/test_os/test_os.py | 8 ++++++ ...-10-11-20-03-13.gh-issue-139482.du2Stg.rst | 3 +++ Modules/clinic/posixmodule.c.h | 27 ++++++++++++++++++- Modules/posixmodule.c | 20 ++++++++++++++ configure | 6 +++++ configure.ac | 5 ++-- pyconfig.h.in | 3 +++ 8 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst diff --git a/Lib/os.py b/Lib/os.py index 710d6f8cfcdf..328d13c303b5 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -58,6 +58,11 @@ if 'posix' in _names: __all__.append('_exit') except ImportError: pass + try: + from posix import _clearenv + __all__.append('_clearenv') + except ImportError: + pass import posixpath as path try: @@ -768,6 +773,12 @@ class _Environ(MutableMapping): new.update(self) return new + if _exists("_clearenv"): + def clear(self): + _clearenv() + self._data.clear() + + def _create_environ_mapping(): if name == 'nt': # Where Env Var Names Must Be UPPERCASE diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index e074858fe2ad..86880a6d2813 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -1494,6 +1494,14 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol): self.assertNotIn(b'test_env', os.environb) self.assertNotIn('test_env', os.environ) + def test_clearenv(self): + os.environ['REMOVEME'] = '1' + os.environ.clear() + self.assertEqual(os.environ, {}) + + self.assertRaises(TypeError, os.environ.clear, None) + + class WalkTests(unittest.TestCase): """Tests for os.walk().""" is_fwalk = False diff --git a/Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst b/Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst new file mode 100644 index 000000000000..4edd3d238bf2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-11-20-03-13.gh-issue-139482.du2Stg.rst @@ -0,0 +1,3 @@ +Optimize :data:`os.environ.clear() ` by calling +:manpage:`clearenv(3)` when this function is available. +Patch by Victor Stinner. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 3d9863ad179d..71f87ac8ec7c 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -9539,6 +9539,27 @@ exit: #endif /* !defined(MS_WINDOWS) */ +#if defined(HAVE_CLEARENV) + +PyDoc_STRVAR(os__clearenv__doc__, +"_clearenv($module, /)\n" +"--\n" +"\n"); + +#define OS__CLEARENV_METHODDEF \ + {"_clearenv", (PyCFunction)os__clearenv, METH_NOARGS, os__clearenv__doc__}, + +static PyObject * +os__clearenv_impl(PyObject *module); + +static PyObject * +os__clearenv(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return os__clearenv_impl(module); +} + +#endif /* defined(HAVE_CLEARENV) */ + PyDoc_STRVAR(os_strerror__doc__, "strerror($module, code, /)\n" "--\n" @@ -13292,6 +13313,10 @@ exit: #define OS_UNSETENV_METHODDEF #endif /* !defined(OS_UNSETENV_METHODDEF) */ +#ifndef OS__CLEARENV_METHODDEF + #define OS__CLEARENV_METHODDEF +#endif /* !defined(OS__CLEARENV_METHODDEF) */ + #ifndef OS_WCOREDUMP_METHODDEF #define OS_WCOREDUMP_METHODDEF #endif /* !defined(OS_WCOREDUMP_METHODDEF) */ @@ -13447,4 +13472,4 @@ exit: #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=47ace1528820858b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67f0df7cd5a7de20 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 8278902cbeb3..38ddc3ec4ffc 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -13201,6 +13201,25 @@ os_unsetenv_impl(PyObject *module, PyObject *name) #endif /* !MS_WINDOWS */ +#ifdef HAVE_CLEARENV +/*[clinic input] +os._clearenv +[clinic start generated code]*/ + +static PyObject * +os__clearenv_impl(PyObject *module) +/*[clinic end generated code: output=2d6705d62c014b51 input=47d2fa7f323c43ca]*/ +{ + errno = 0; + int err = clearenv(); + if (err) { + return posix_error(); + } + Py_RETURN_NONE; +} +#endif + + /*[clinic input] os.strerror @@ -17167,6 +17186,7 @@ static PyMethodDef posix_methods[] = { OS_POSIX_FADVISE_METHODDEF OS_PUTENV_METHODDEF OS_UNSETENV_METHODDEF + OS__CLEARENV_METHODDEF OS_STRERROR_METHODDEF OS_FCHDIR_METHODDEF OS_FSYNC_METHODDEF diff --git a/configure b/configure index d80340e3015b..211f84399064 100755 --- a/configure +++ b/configure @@ -19225,6 +19225,12 @@ if test "x$ac_cv_func_chown" = xyes then : printf "%s\n" "#define HAVE_CHOWN 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "clearenv" "ac_cv_func_clearenv" +if test "x$ac_cv_func_clearenv" = xyes +then : + printf "%s\n" "#define HAVE_CLEARENV 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "clock" "ac_cv_func_clock" if test "x$ac_cv_func_clock" = xyes diff --git a/configure.ac b/configure.ac index 1e0c0f71b7c2..35bf153a8987 100644 --- a/configure.ac +++ b/configure.ac @@ -5226,7 +5226,8 @@ fi # checks for library functions AC_CHECK_FUNCS([ \ - accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ + accept4 alarm bind_textdomain_codeset chmod chown clearenv \ + clock closefrom close_range confstr \ copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ @@ -8173,7 +8174,7 @@ PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_d # Determine JIT stencils header files based on target platform JIT_STENCILS_H="" -AS_VAR_IF([enable_experimental_jit], [no], +AS_VAR_IF([enable_experimental_jit], [no], [], [case "$host" in aarch64-apple-darwin*) diff --git a/pyconfig.h.in b/pyconfig.h.in index 60bff4a9f263..72870411bc08 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -141,6 +141,9 @@ /* Define if you have the 'chroot' function. */ #undef HAVE_CHROOT +/* Define to 1 if you have the 'clearenv' function. */ +#undef HAVE_CLEARENV + /* Define to 1 if you have the 'clock' function. */ #undef HAVE_CLOCK -- 2.47.3