Skip to content

bpo-39395: os.putenv() is now always available #18135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 19 additions & 24 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ process and user.
to the environment made after this time are not reflected in ``os.environ``,
except for changes made by modifying ``os.environ`` directly.

If the platform supports the :func:`putenv` function, 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.
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.

On Unix, keys and values use :func:`sys.getfilesystemencoding` and
``'surrogateescape'`` error handler. Use :data:`environb` if you would like
Expand All @@ -130,14 +130,10 @@ process and user.
cause memory leaks. Refer to the system documentation for
:c:func:`putenv`.

If :func:`putenv` is not provided, a modified copy of this mapping may be
passed to the appropriate process-creation functions to cause child processes
to use a modified environment.

If the platform supports the :func:`unsetenv` function, you can delete items in
this mapping to unset environment variables. :func:`unsetenv` will be called
automatically when an item is deleted from ``os.environ``, and when
one of the :meth:`pop` or :meth:`clear` methods is called.
You can delete items in this mapping to unset environment variables.
:func:`unsetenv` will be called automatically when an item is deleted from
``os.environ``, and when one of the :meth:`pop` or :meth:`clear` methods is
called.


.. data:: environb
Expand Down Expand Up @@ -439,17 +435,18 @@ process and user.
changes to the environment affect subprocesses started with :func:`os.system`,
:func:`popen` or :func:`fork` and :func:`execv`.

.. availability:: most flavors of Unix, Windows.
Assignments to items in ``os.environ`` are automatically translated into
corresponding calls to :func:`putenv`; however, calls to :func:`putenv`
don't update ``os.environ``, so it is actually preferable to assign to items
of ``os.environ``.

.. note::

On some platforms, including FreeBSD and Mac OS X, setting ``environ`` may
cause memory leaks. Refer to the system documentation for putenv.
cause memory leaks. Refer to the system documentation for :c:func:`putenv`.

When :func:`putenv` is supported, assignments to items in ``os.environ`` are
automatically translated into corresponding calls to :func:`putenv`; however,
calls to :func:`putenv` don't update ``os.environ``, so it is actually
preferable to assign to items of ``os.environ``.
.. versionchanged:: 3.9
The function is now always available.


.. function:: setegid(egid)
Expand Down Expand Up @@ -638,15 +635,13 @@ process and user.
environment affect subprocesses started with :func:`os.system`, :func:`popen` or
:func:`fork` and :func:`execv`.

When :func:`unsetenv` is supported, deletion of items in ``os.environ`` is
automatically translated into a corresponding call to :func:`unsetenv`; however,
calls to :func:`unsetenv` don't update ``os.environ``, so it is actually
preferable to delete items of ``os.environ``.

.. availability:: most flavors of Unix, Windows.
Deletion of items in ``os.environ`` is automatically translated into a
corresponding call to :func:`unsetenv`; however, calls to :func:`unsetenv`
don't update ``os.environ``, so it is actually preferable to delete items of
``os.environ``.

.. versionchanged:: 3.9
The function is now also available on Windows.
The function is now always available and is also available on Windows.


.. _os-newstreams:
Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ descriptors.
The :func:`os.unsetenv` function is now also available on Windows.
(Contributed by Victor Stinner in :issue:`39413`.)

The :func:`os.putenv` and :func:`os.unsetenv` functions are now always
available.
(Contributed by Victor Stinner in :issue:`39395`.)

poplib
------

Expand Down Expand Up @@ -331,6 +335,10 @@ Build and C API Changes
Python 3.0, it has been ignored and unused.
(Contributed by Jeroen Demeyer in :issue:`36974`.)

* On non-Windows platforms, the :c:func:`setenv` and :c:func:`unsetenv`
functions are now required to build Python.
(Contributed by Victor Stinner in :issue:`39395`.)


Deprecated
==========
Expand Down
32 changes: 6 additions & 26 deletions Lib/os.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,17 +654,15 @@ def get_exec_path(env=None):
return path_list.split(pathsep)


# Change environ to automatically call putenv(), unsetenv if they exist.
# Change environ to automatically call putenv() and unsetenv()
from _collections_abc import MutableMapping

class _Environ(MutableMapping):
def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv):
def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue):
self.encodekey = encodekey
self.decodekey = decodekey
self.encodevalue = encodevalue
self.decodevalue = decodevalue
self.putenv = putenv
self.unsetenv = unsetenv
self._data = data

def __getitem__(self, key):
Expand All @@ -678,12 +676,12 @@ def __getitem__(self, key):
def __setitem__(self, key, value):
key = self.encodekey(key)
value = self.encodevalue(value)
self.putenv(key, value)
putenv(key, value)
self._data[key] = value

def __delitem__(self, key):
encodedkey = self.encodekey(key)
self.unsetenv(encodedkey)
unsetenv(encodedkey)
try:
del self._data[encodedkey]
except KeyError:
Expand Down Expand Up @@ -712,22 +710,6 @@ def setdefault(self, key, value):
self[key] = value
return self[key]

try:
_putenv = putenv
except NameError:
_putenv = lambda key, value: None
else:
if "putenv" not in __all__:
__all__.append("putenv")

try:
_unsetenv = unsetenv
except NameError:
_unsetenv = lambda key: _putenv(key, "")
else:
if "unsetenv" not in __all__:
__all__.append("unsetenv")

def _createenviron():
if name == 'nt':
# Where Env Var Names Must Be UPPERCASE
Expand Down Expand Up @@ -755,8 +737,7 @@ def decode(value):
data = environ
return _Environ(data,
encodekey, decode,
encode, decode,
_putenv, _unsetenv)
encode, decode)

# unicode environ
environ = _createenviron()
Expand All @@ -781,8 +762,7 @@ def _check_bytes(value):
# bytes environ
environb = _Environ(environ._data,
_check_bytes, bytes,
_check_bytes, bytes,
_putenv, _unsetenv)
_check_bytes, bytes)
del _check_bytes

def getenvb(key, default=None):
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,8 +953,6 @@ def test_environb(self):
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"
Expand All @@ -975,8 +973,6 @@ def test_putenv_unsetenv(self):

# On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415).
@support.requires_mac_ver(10, 6)
@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.
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,6 @@ def test_environ(self):
self.assertEqual(type(k), item_type)
self.assertEqual(type(v), item_type)

@unittest.skipUnless(hasattr(os, "putenv"), "requires os.putenv()")
def test_putenv(self):
with self.assertRaises(ValueError):
os.putenv('FRUIT\0VEGETABLE', 'cabbage')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
On non-Windows platforms, the :c:func:`setenv` and :c:func:`unsetenv` functions
are now required to build Python.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The :func:`os.putenv` and :func:`os.unsetenv` functions are now always
available.
10 changes: 5 additions & 5 deletions Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading