classification
Title: os.getenv() not updated after external module uses C putenv()
Type: behavior Stage:
Components: Documentation, Library (Lib) Versions: Python 3.3, Python 3.4, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Arfrever, amaury.forgeotdarc, docs@python, draghuram, jaraco, loewis, martin.panter, pythonmeister, robert.ancell, terry.reedy
Priority: normal Keywords:

Created on 2007-09-13 07:22 by robert.ancell, last changed 2014-09-22 02:44 by martin.panter.

Files
File name Uploaded Description Edit
environmodule.c robert.ancell, 2007-09-14 04:42
Messages (14)
msg55881 - (view) Author: Robert Ancell (robert.ancell) Date: 2007-09-13 07:22
The Python os.getenv() function accesses an Python dictionary which is
mirroring the process environment. This dictionary is populated when the
interpreter starts and updated when os.environ.__setitem__() or
os.putenv() are called. However if the python program imports an
extension module that uses the system putenv() then the changes cannot
be accessed using the Python standard library.

This has been a problem for us as we have created Python bindings to an
existing C based library that modifies the environment dynamically (not
the best design decision...). The workaround we are using is to create
our own wrapper to the system (Solaris/Linux) getenv().

A potential solution could be to make environ a class where
os.environ.__setitem__() calls putenv(), os.environ.__getitem__() calls
getenv() and os.environ.keys()/items()/iter*() uses **environ (or other
appropriate system call). This does however have undefined issues on how
the environment behaves on various systems (memory leaks etc).
msg55883 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2007-09-13 07:35
I can't see a bug here. If you want the current C library value of the
environment variable, just use os.getenv, not os.environ.
msg55884 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2007-09-13 07:49
Ignore my comment - I see now that you are talking about os.getenv.
msg55893 - (view) Author: Raghuram Devarakonda (draghuram) (Python triager) Date: 2007-09-13 16:20
On 9/13/07, Robert Ancell <report@bugs.python.org> wrote:

> The Python os.getenv() function accesses an Python dictionary which is
> mirroring the process environment. This dictionary is populated when the
> interpreter starts and updated when os.environ.__setitem__() or
> os.putenv() are called. However if the python program imports an

As per the document and my simple test (on Linux), os.putenv() does
not update os.environ. I think, it should update it.
msg55894 - (view) Author: Stefan Sonnenberg-Carstens (pythonmeister) Date: 2007-09-13 17:43
> As per the document and my simple test (on Linux), os.putenv() does
> not update os.environ. I think, it should update it.
What would be the benefit ?
msg55895 - (view) Author: Raghuram Devarakonda (draghuram) (Python triager) Date: 2007-09-13 17:58
> Stefan Sonnenberg-Carstens added the comment:
>
> > As per the document and my simple test (on Linux), os.putenv() does
> > not update os.environ. I think, it should update it.
> What would be the benefit ?

Symmetrical behaviour. When os.getenv() returns the value from
os.environ, one would expect, os.putenv to store the value there (At
least, I did). On the other hand, it is also ok for both os.getenv and
os.putenv get/set the environment directly instead of going through
os.environ. I am sure there was some reason for the current behaviour
of os.putenv. Perhaps, because putenv is supposedly not available on
all platforms?

Any way, I think the OP was asking to always "get" the value
dynamically when ever os.environ['VAR'] or os.getenv['VAR'] is done. I
don't see any problem with that approach. How ever, if it is
considered backwards incompatible, I guess an optional parameter can
be added to os.getenv.
msg55897 - (view) Author: Stefan Sonnenberg-Carstens (pythonmeister) Date: 2007-09-13 19:45
I'd like to see perl/ruby behaviour:
an dict (os.environ), nothing more (perl %ENV,ruby $ENV).
Get rid of setenv/putenv at all.
3.0a1 has even more:
There is os.environ (a dict), os.[put|get]env() and os.environ.putenv()
msg55905 - (view) Author: Robert Ancell (robert.ancell) Date: 2007-09-14 00:20
draghuram, unfortunately while os.putenv() can be fixed to be
symmetrical any putenv call from a C module cannot, for example:

If you make an extension:
#include <stdlib.h>
PyObject *putenvC(PyObject *module, PyObject *args)
{
    int result;

    if (!PyArg_ParseTuple(args, ""))
        return 0;

    result = putenv("FOO=BAR");

    return Py_BuildValue("i", result);
}

The following behaviour will occur:
$ python
>>> import putenv
>>> putenv.putenvC()
>>> assert(os.getenv('FOO') == None)
>>> assert(os.environ.get('FOO') == None)

This is because the os.environ dictionary will never be updated:
From Lib/os.py:
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."""
    return environ.get(key, default)
msg55907 - (view) Author: Robert Ancell (robert.ancell) Date: 2007-09-14 04:42
I've attached proof-of-concept showing how os.environ would ideally
work. It'll only work in Posix, etc etc.

Reading into it more there are a lot of general issues with environments
and memory allocation which is why I suspect Python doesn't use
putenv... See putenv(3) for details.

Compile with:
gcc -shared -o environmodule.so -g -Wall -I /usr/include/python2.5
environmodule.c
msg55912 - (view) Author: Raghuram Devarakonda (draghuram) (Python triager) Date: 2007-09-14 15:37
> Robert Ancell added the comment:
>
> draghuram, unfortunately while os.putenv() can be fixed to be
> symmetrical any putenv call from a C module cannot, for example:

Hi Robert, I understood the problem from your very first report. I
brought up putenv() not updating os.environ only because you mentioned
in the original report that it does. All you need is a way to get the
current value of an environment variable. The situation is a bit
complicated since a cache is in the picture (os.environ). I would
suggest that you bring up the topic for discussion on python-dev and
once a consensus is reached, some one can come up with a patch (I can
try).
msg81660 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) Date: 2009-02-11 19:47
As a workaround, could you use ctypes to pull the environment back into
the python context?  For example:

http://paste.turbogears.org/paste/34734
msg116776 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2010-09-18 13:35
Can someone please comment on whether or not this issue is still valid.
msg191432 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2013-06-18 20:28
The problem is the 'putenv' and 'getenv' appear to be parallel, and seem to be documented as being parallel
"Set the environment variable named key to the string value."
"Return the value of the environment variable key if it exists,"
but they are not. Getenv actually looks up the key in the internal os.environ copy while putenv puts to the actual external environment.

This tripped-up someone today on python-list, who did putenv(key, val); getenv(key) and wondered why putenv seemed to have no effect.

I think the solution for this should be to document the asymmetry by adding ' in os.environ' after 'key' in the getenv doc.

--
putenv should not also update os.environ because one can already do that with "os.environ[key] = value" (as recommended) and because the latter uses putenv *if available* to also update the external environment *if possible*. Note that at the time this system was designed, not all systems supported putenv (and perhaps not 'true' getenv either).

getenv(key) is not the same as os.environ[key] because the former has an optional 'default' parameter that defaults to None. So aside from back-compatibility, I do not think the behavior of existing code should change.

A new parameter might be possible. To implement it, one would have to augment the underlying, undocumented, C-coded (for CPython) os-specific module --  posix, nt, os2, ce -- to define a new os-specific getenv function that parallels the os-specific putenv function.

Adding os.environ.update (or .synchronize) to resynchronize os.environ with the external environment would also require a new os-specific function. Currently, the original os.environ is imported as a *data* attribute of the os-specific module.

However, neither of these changes are needed for python code that used os.environ as intended. I don't think we should necessarily cater to badly written C libraries that modify the enviroment in a way that cannot be easily intercepted or controlled. So after making a doc change, I would be inclined to close this pending a python-ideas discussion that supported a new feature.
msg191454 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2013-06-19 08:07
FYI, PyPy recently got bitten by this: https://bugs.pypy.org/issue1518
A posix.libc_getenv() function could be a solution.
History
Date User Action Args
2014-11-26 13:41:51amaury.forgeotdarclinkissue4887 superseder
2014-09-22 02:44:01martin.pantersetnosy: + martin.panter
2014-02-03 19:19:42Arfreversetnosy: + Arfrever
2014-02-03 17:07:43BreamoreBoysetnosy: - BreamoreBoy
2013-06-19 08:07:25amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg191454
2013-06-18 20:28:38terry.reedysetversions: + Python 2.7, Python 3.3, Python 3.4, - Python 2.6
nosy: + terry.reedy, docs@python

messages: + msg191432

assignee: docs@python
components: + Documentation
2010-09-18 13:35:38BreamoreBoysetresolution: postponed ->

messages: + msg116776
nosy: + BreamoreBoy
2009-02-11 19:47:20jaracosetnosy: + jaraco
messages: + msg81660
2007-09-18 12:24:54jafosetpriority: normal
resolution: postponed
2007-09-14 15:37:33draghuramsetmessages: + msg55912
2007-09-14 04:42:18robert.ancellsetfiles: + environmodule.c
messages: + msg55907
2007-09-14 00:20:26robert.ancellsetmessages: + msg55905
2007-09-13 19:45:01pythonmeistersetmessages: + msg55897
2007-09-13 17:58:13draghuramsetmessages: + msg55895
2007-09-13 17:43:52pythonmeistersetnosy: + pythonmeister
messages: + msg55894
2007-09-13 16:20:30draghuramsetmessages: + msg55893
2007-09-13 15:56:48draghuramsetnosy: + draghuram
2007-09-13 07:49:42loewissetmessages: + msg55884
2007-09-13 07:35:46loewissetnosy: + loewis
messages: + msg55883
2007-09-13 07:22:07robert.ancellcreate