From 0a0044465c5318b0405f1ffe5c30a9b20b3331ea Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Nov 2024 10:33:33 +0100 Subject: [PATCH 1/6] gh-120057: Add os.reload_environ() function Replace the os.environ.refresh() method with a new os.reload_environ() function. --- Doc/library/os.rst | 20 ++++++++------- Doc/whatsnew/3.14.rst | 7 +++--- Lib/os.py | 25 +++++++++++-------- Lib/test/test_os.py | 16 ++++++------ ...-11-01-10-35-49.gh-issue-120057.YWy81Q.rst | 2 ++ 5 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index f9cded40c2c755..4e8517f1760a32 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -193,10 +193,6 @@ process and user. to the environment made after this time are not reflected in :data:`os.environ`, except for changes made by modifying :data:`os.environ` directly. - The :meth:`!os.environ.refresh` method updates :data:`os.environ` with - changes to the environment made by :func:`os.putenv`, by - :func:`os.unsetenv`, or made outside Python in the same process. - This mapping may be used to modify the environment as well as query the environment. :func:`putenv` will be called automatically when the mapping is modified. @@ -229,9 +225,6 @@ process and user. .. versionchanged:: 3.9 Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. - .. versionchanged:: 3.14 - Added the :meth:`!os.environ.refresh` method. - .. data:: environb @@ -249,6 +242,15 @@ process and user. Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. +.. function:: reload_environ() + + Update :data:`os.environ` and :data:`os.environb` with changes to the + environment made by :func:`os.putenv`, by :func:`os.unsetenv`, or made + outside Python in the same process. + + .. versionadded:: 3.14 + + .. function:: chdir(path) fchdir(fd) getcwd() @@ -568,7 +570,7 @@ process and user. of :data:`os.environ`. This also applies to :func:`getenv` and :func:`getenvb`, which respectively use :data:`os.environ` and :data:`os.environb` in their implementations. - See also the :data:`os.environ.refresh() ` method. + See also the :func:`os.reload_environ` function. .. note:: @@ -818,7 +820,7 @@ process and user. don't update :data:`os.environ`, so it is actually preferable to delete items of :data:`os.environ`. - See also the :data:`os.environ.refresh() ` method. + See also the :func:`os.reload_environ` function. .. audit-event:: os.unsetenv key os.unsetenv diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 48314f9c98c036..f4544ee9a83b06 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -361,9 +361,10 @@ operator os -- -* Add the :data:`os.environ.refresh() ` method to update - :data:`os.environ` with changes to the environment made by :func:`os.putenv`, - by :func:`os.unsetenv`, or made outside Python in the same process. +* Add the :func:`os.reload_environ` function to update :data:`os.environ` and + :data:`os.environb` with changes to the environment made by + :func:`os.putenv`, by :func:`os.unsetenv`, or made outside Python in the + same process. (Contributed by Victor Stinner in :gh:`120057`.) diff --git a/Lib/os.py b/Lib/os.py index aaa758d955fe4c..9c2258e6ccf5ba 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -765,17 +765,6 @@ def __ror__(self, other): new.update(self) return new - if _exists("_create_environ"): - def refresh(self): - data = _create_environ() - if name == 'nt': - data = {self.encodekey(key): value - for key, value in data.items()} - - # modify in-place to keep os.environb in sync - self._data.clear() - self._data.update(data) - def _create_environ_mapping(): if name == 'nt': # Where Env Var Names Must Be UPPERCASE @@ -810,6 +799,20 @@ def decode(value): del _create_environ_mapping +if _exists("_create_environ"): + def reload_environ(): + data = _create_environ() + if name == 'nt': + encodekey = environ.encodekey + data = {encodekey(key): value + for key, value in data.items()} + + # modify in-place to keep os.environb in sync + env_data = environ._data + env_data.clear() + env_data.update(data) + + 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. diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 307f0f11ddc33f..9a4be78556c648 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1298,8 +1298,8 @@ def test_ror_operator(self): self._test_underlying_process_env('_A_', '') self._test_underlying_process_env(overridden_key, original_value) - def test_refresh(self): - # Test os.environ.refresh() + def test_reload_environ(self): + # Test os.reload_environ() has_environb = hasattr(os, 'environb') # Test with putenv() which doesn't update os.environ @@ -1309,7 +1309,7 @@ def test_refresh(self): if has_environb: self.assertEqual(os.environb[b'test_env'], b'python_value') - os.environ.refresh() + os.reload_environ() self.assertEqual(os.environ['test_env'], 'new_value') if has_environb: self.assertEqual(os.environb[b'test_env'], b'new_value') @@ -1320,28 +1320,28 @@ def test_refresh(self): if has_environb: self.assertEqual(os.environb[b'test_env'], b'new_value') - os.environ.refresh() + os.reload_environ() self.assertNotIn('test_env', os.environ) if has_environb: self.assertNotIn(b'test_env', os.environb) if has_environb: - # test os.environb.refresh() with putenv() + # test reload_environ() on os.environb with putenv() os.environb[b'test_env'] = b'python_value2' os.putenv("test_env", "new_value2") self.assertEqual(os.environb[b'test_env'], b'python_value2') self.assertEqual(os.environ['test_env'], 'python_value2') - os.environb.refresh() + os.reload_environ() self.assertEqual(os.environb[b'test_env'], b'new_value2') self.assertEqual(os.environ['test_env'], 'new_value2') - # test os.environb.refresh() with unsetenv() + # test reload_environ() on os.environb with unsetenv() os.unsetenv('test_env') self.assertEqual(os.environb[b'test_env'], b'new_value2') self.assertEqual(os.environ['test_env'], 'new_value2') - os.environb.refresh() + os.reload_environ() self.assertNotIn(b'test_env', os.environb) self.assertNotIn('test_env', os.environ) diff --git a/Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst b/Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst new file mode 100644 index 00000000000000..ded60a3f57bca3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-01-10-35-49.gh-issue-120057.YWy81Q.rst @@ -0,0 +1,2 @@ +Replace the ``os.environ.refresh()`` method with a new +:func:`os.reload_environ` function. Patch by Victor Stinner. From ad1036616d897bd5dd7f517be9f0f5a3fe33e9bb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Nov 2024 12:21:59 +0100 Subject: [PATCH 2/6] Address review: complete doc --- Doc/library/os.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 4e8517f1760a32..1d65614ecdda13 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -245,8 +245,13 @@ process and user. .. function:: reload_environ() Update :data:`os.environ` and :data:`os.environb` with changes to the - environment made by :func:`os.putenv`, by :func:`os.unsetenv`, or made - outside Python in the same process. + current process environment made by :func:`os.putenv`, by + :func:`os.unsetenv`, or made outside Python in the same process. + + This function is not thread safe. Calling it while the environment is + modified in other thread has undefined behavior. Reading from + :data:`os.environ` or calling :func:`os.getenv` during reloading can return + empty result. .. versionadded:: 3.14 From 118b8c0fa7b302209457b00b2725fc0bd38ca2cd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Nov 2024 17:06:39 +0100 Subject: [PATCH 3/6] Add seealso to os.environ --- Doc/library/os.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 1d65614ecdda13..a70f88ae2a814a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -222,6 +222,10 @@ process and user. :data:`os.environ`, and when one of the :meth:`pop` or :meth:`clear` methods is called. + .. seealso:: + + The :func:`~os.reload_environ` function. + .. versionchanged:: 3.9 Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. From fde06e9337a00cb066e4ae646c330edfb009ee99 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Nov 2024 17:30:02 +0100 Subject: [PATCH 4/6] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/os.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index a70f88ae2a814a..caad2bf56c2413 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -224,7 +224,7 @@ process and user. .. seealso:: - The :func:`~os.reload_environ` function. + The :func:`os.reload_environ` function. .. versionchanged:: 3.9 Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. @@ -252,12 +252,12 @@ process and user. current process environment made by :func:`os.putenv`, by :func:`os.unsetenv`, or made outside Python in the same process. - This function is not thread safe. Calling it while the environment is - modified in other thread has undefined behavior. Reading from - :data:`os.environ` or calling :func:`os.getenv` during reloading can return - empty result. + This function is not thread-safe. Calling it while the environment is + being modified in an other thread is an undefined behavior. Reading from + :data:`os.environ` or :data:`os.environb`, or calling :func:`os.getenv` + while reloading, may return an empty result. - .. versionadded:: 3.14 + .. versionadded:: next .. function:: chdir(path) From ca90336dddcca82ef86484561efc0bf3c68080a1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Nov 2024 22:44:31 +0100 Subject: [PATCH 5/6] Update Doc/library/os.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/os.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index caad2bf56c2413..45792f58d8234f 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -248,14 +248,18 @@ process and user. .. function:: reload_environ() - Update :data:`os.environ` and :data:`os.environb` with changes to the - current process environment made by :func:`os.putenv`, by - :func:`os.unsetenv`, or made outside Python in the same process. - - This function is not thread-safe. Calling it while the environment is - being modified in an other thread is an undefined behavior. Reading from - :data:`os.environ` or :data:`os.environb`, or calling :func:`os.getenv` - while reloading, may return an empty result. + The :data:`os.environ` and :data:`os.environb` mappings are a cache of + environment variables at the time that Python started. + As such, changes to the current process environment are not reflected + if made outside Python, or by :func:`os.putenv` or :func:`os.unsetenv`. + Use :func:`!os.reload_environ` to update :data:`os.environ` and :data:`os.environb` + with any such changes to the current process environment. + + .. warning:: + This function is not thread-safe. Calling it while the environment is + being modified in an other thread is an undefined behavior. Reading from + :data:`os.environ` or :data:`os.environb`, or calling :func:`os.getenv` + while reloading, may return an empty result. .. versionadded:: next From 1b07ef64df77d5a54591f6baea5f5b1a1a8927e3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 2 Nov 2024 14:12:35 +0100 Subject: [PATCH 6/6] Update Doc/library/os.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/os.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 45792f58d8234f..c0354b2280c45c 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -248,7 +248,7 @@ process and user. .. function:: reload_environ() - The :data:`os.environ` and :data:`os.environb` mappings are a cache of + The :data:`os.environ` and :data:`os.environb` mappings are a cache of environment variables at the time that Python started. As such, changes to the current process environment are not reflected if made outside Python, or by :func:`os.putenv` or :func:`os.unsetenv`.