classification
Title: os.environ.clear() fails with empty keys (posix.unsetenv)
Type: Stage: test needed
Components: Interpreter Core Versions: Python 3.3, Python 3.4, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Arfrever, akira, blueyed, ned.deily, pefu, serhiy.storchaka, vstinner
Priority: normal Keywords: patch

Created on 2014-02-17 13:06 by blueyed, last changed 2015-10-05 08:15 by pefu.

Files
File name Uploaded Description Edit
test_empty_env_var.py vstinner, 2014-02-18 11:53
reject_empty_env_var.patch vstinner, 2014-02-18 11:53 review
Messages (13)
msg211415 - (view) Author: daniel hahler (blueyed) * Date: 2014-02-17 13:06
posix.unsetenv fails to clear the environment if there's an entry with an empty key.

TEST CASE:

    Python 2.7.6 (default, Jan  6 2014, 17:05:19) 
    [GCC 4.8.1] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import os
    >>> os.environ['']='foo'
    >>> os.environ.clear()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/path/to/python/2.7.6/lib/python2.7/os.py", line 499, in clear
        unsetenv(key)
    OSError: [Errno 22] Invalid argument

I believe that os.environ.clear() via unsetenv should handle empty keys.
msg211450 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2014-02-17 22:23
According to the Open Group Base Specification (Issue 7 2013 Issue): 

"The setenv() function shall fail if:

[EINVAL]
The envname argument points to an empty string or points to a string containing an '=' character."

So it seems to me that the issue here is that os.environ[''] attempts to allow you to create a environment variable with a null key.
msg211451 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2014-02-17 22:27
OTOH, the specification for putenv, which is what is actually used by posixmodule.c, does not contain that requirement.

http://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html
http://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html
msg211508 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-02-18 11:53
putenv("=value") does nothing: it doesn't create a variable with an empty name. You can test with the attach test_empty_env_var.py script (written for Linux).

Attached reject_empty_env_var.patch patch modifies posix.putenv() to raise a ValueError if the name is empty.

The patch is incomplete, the Windows part should also be modified. But I didn't test yet if Windows supports variables with empty name.
msg211509 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-02-18 11:53
The workaround of this bug is to avoid "os.environ['']=value".
msg211612 - (view) Author: daniel hahler (blueyed) * Date: 2014-02-19 13:21
Please note that I have noticed this not because of setting it via `os.environ`, but because a program using `os.environ.clear()` crashed:
https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1281086

I cannot say how this unusual entry was added to the environment, but it showed up in `env` and therefore it is possible somehow.
msg211613 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-02-19 13:27
"Please note that I have noticed this not because of setting it via `os.environ`, but because a program using `os.environ.clear()` crashed:
https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1281086"

The bug is not os.environ.clear(), but that os.environ contains an empty string.

It would help to know if the key was set manually by apport, or if it comes from the real environment. The environment looks correct:
https://launchpadlibrarian.net/166517334/ProcEnviron.txt
msg211615 - (view) Author: daniel hahler (blueyed) * Date: 2014-02-19 13:32
> It would help to know if the key was set manually by apport, or if it comes from the real environment. The environment looks correct:

It comes from the real environment.

I wanted to use apport, but could not from the current shell, because of this bug.

I had then looked at the output of `env` in this shell, and it contained just "=" (IIRC, but an empty name/key for sure). This was in a tmux shell/pane, which I have closed already.
msg211617 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-02-19 13:38
> It comes from the real environment.

Oh. It looks possible to define an environment variable with an empty key, but not to remove it:

$ env -i =value python -c 'import pprint, os; pprint.pprint(os.environ); del os.environ[""]'
environ({'': 'value'})
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "Lib/os.py", line 662, in __delitem__
    self.unsetenv(encodedkey)
OSError: [Errno 22] Invalid argument
msg211619 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-02-19 13:41
By the way, Python allows also to set an environment with a name containing "=", whereas getenv/setenv/unsetenv doesn't.

$ env -i python -c 'import pprint, posix, os; os.environ["a="]="1"; print(os.environ); posix.unsetenv("a=")'

environ({'a=': '1'})
Traceback (most recent call last):
  File "<string>", line 1, in <module>
OSError: [Errno 22] Invalid argument
msg211622 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2014-02-19 14:39
Are there supported platforms on which setenv() is not supported? When we will migrate to setenv(), an argument check will be not needed.

In any case I think we should wrap unsetenv() in os.environ.clear() so that it should try to remove all environment variables even if some calls of unsetenv() fails.
msg211624 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-02-19 15:06
In fact, there is also a clearenv() function which could be used by os.environ.clear().

"The clearenv() function clears the environment of all name-value pairs and sets the value of the external variable environ to NULL."

It looks like supported names depends a lot on the platform and platform version. Extract of Linux manual pages:

setenv:
---
BUGS:

POSIX.1-2001  specifies  that  if  name  contains an '=' character, then setenv() should fail with the error EINVAL; however, versions of glibc before 2.3.4 allowed an '=' sign in name.
---

clearenv:
---
CONFORMING TO

Various  UNIX variants (DG/UX, HP-UX, QNX, ...).  POSIX.9 (bindings for FORTRAN77).  POSIX.1-1996 did not accept clearenv() and putenv(3), but changed its mind and scheduled these functions for some later issue of this standard (cf. B.4.6.1).  However, POSIX.1-2001 adds only putenv(3),  and  rejected clearenv().
---

> In any case I think we should wrap unsetenv() in os.environ.clear() so that it should try to remove all environment variables even if some calls of unsetenv() fails.

os.environ.clear() may tries to remove as much keys as possible, but keep keys for which unsetenv raised an error and raise a global error in this case.
msg212405 - (view) Author: Akira Li (akira) * Date: 2014-02-28 05:02
Related: issue4926 "putenv() accepts names containing '=', return value of unsetenv() not checked" 

`os.environ.clear()` also fails if the environment contains names with `=`:

  >>> import os
  >>> os.environ['a=b'] = 'c'
  >>> os.environ.clear()
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/.../lib/python3.4/_collections_abc.py", line 558, in clear
      self.popitem()
    File "/.../lib/python3.4/_collections_abc.py", line 551, in popitem
      del self[key]
    File "/.../lib/python3.4/os.py", line 662, in __delitem__
      self.unsetenv(encodedkey)
  OSError: [Errno 22] Invalid argument
History
Date User Action Args
2015-10-05 08:15:29pefusetnosy: + pefu
2014-02-28 05:02:09akirasetnosy: + akira
messages: + msg212405
2014-02-20 03:04:16Arfreversetnosy: + Arfrever
2014-02-19 15:06:54vstinnersetmessages: + msg211624
2014-02-19 14:39:24serhiy.storchakasetmessages: + msg211622
2014-02-19 14:34:56serhiy.storchakasetmessages: - msg211621
2014-02-19 14:34:32serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg211621
2014-02-19 13:41:49vstinnersetmessages: + msg211619
2014-02-19 13:38:41vstinnersetmessages: + msg211617
2014-02-19 13:32:15blueyedsetmessages: + msg211615
2014-02-19 13:27:28vstinnersetmessages: + msg211613
2014-02-19 13:21:18blueyedsetmessages: + msg211612
2014-02-18 11:53:49vstinnersetmessages: + msg211509
2014-02-18 11:53:10vstinnersetfiles: + reject_empty_env_var.patch
keywords: + patch
2014-02-18 11:53:04vstinnersetfiles: + test_empty_env_var.py
nosy: + vstinner
messages: + msg211508

2014-02-17 22:27:10ned.deilysetmessages: + msg211451
2014-02-17 22:23:17ned.deilysetversions: + Python 3.4
nosy: + ned.deily

messages: + msg211450

type: crash ->
stage: test needed
2014-02-17 13:06:08blueyedcreate