Repository: pypa/pip

Issue:
Whitespace in entry_points.txt is suddenly significant when installíng with wheels
It seems that whitespace in `entry_points.txt` (possibly at other places too) has become significant with `pip` and wheels:

```
$ pip uninstall gunicorn
Cannot uninstall requirement gunicorn, not installed
$ pip wheel gunicorn
[....]
$ pip install wheelhouse/gunicorn-18.0-py26-none-any.whl 
Unpacking ./wheelhouse/gunicorn-18.0-py26-none-any.whl
Installing collected packages: gunicorn
Cleaning up...
Exception:
Traceback (most recent call last):
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/basecommand.py", line 121, in main
    status = self.run(options, args)
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/commands/install.py", line 259, in run
    requirement_set.install(install_options, global_options, root=options.root_path)
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/req.py", line 1322, in install
    requirement.install(install_options, global_options, *args, **kwargs)
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/req.py", line 615, in install
    self.move_wheel_files(self.source_dir, root=root)
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/req.py", line 839, in move_wheel_files
    root=root,
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/wheel.py", line 193, in move_wheel_files
    console, gui = get_entrypoints(ep_file)
  File "/home/rassie/.virtualenvs/test/lib/python2.6/site-packages/pip/wheel.py", line 112, in get_entrypoints
    cp.read(filename)
  File "/usr/lib64/python2.6/ConfigParser.py", line 286, in read
    self._read(fp, filename)
  File "/usr/lib64/python2.6/ConfigParser.py", line 482, in _read
    raise MissingSectionHeaderError(fpname, lineno, line)
MissingSectionHeaderError: File contains no section headers.
file: /home/rassie/.virtualenvs/test/lib64/python2.6/site-packages/gunicorn-18.0.dist-info/entry_points.txt, line: 3
'    [console_scripts]\n'

Storing debug log for failure in /home/rassie/.pip/pip.log
```

The whitespace does indeed come from the [source](https://github.com/benoitc/gunicorn/blob/master/setup.py), and `ConfigParser` really can't parse that, so the actual error is on `gunicorn`'s side. However, this behaviour differs from normal `pip install` and also from `easy_install` when using `egg`s, so something has to be done.



Discussion:
So the problem is https://github.com/benoitc/gunicorn/blob/master/setup.py#L85-L105

We can probably just strip the whitespace on reach line before reading the entrypoints file.


Resolving patch:
diff --git a/pip/wheel.py b/pip/wheel.py
--- a/pip/wheel.py
+++ b/pip/wheel.py
@@ -13,7 +13,7 @@
 import sys
 from base64 import urlsafe_b64encode
 
-from pip.backwardcompat import ConfigParser
+from pip.backwardcompat import ConfigParser, StringIO
 from pip.exceptions import InvalidWheelFilename
 from pip.locations import distutils_scheme
 from pip.log import logger
@@ -106,11 +106,25 @@ def root_is_purelib(name, wheeldir):
                         return True
     return False
 
+
 def get_entrypoints(filename):
     if not os.path.exists(filename):
         return {}, {}
+
+    # This is done because you can pass a string to entry_points wrappers which
+    # means that they may or may not be valid INI files. The attempt here is to
+    # strip leading and trailing whitespace in order to make them valid INI
+    # files.
+    with open(filename) as fp:
+        data = StringIO()
+        for line in fp:
+            data.write(line.strip())
+            data.write("\n")
+        data.seek(0)
+
     cp = ConfigParser.RawConfigParser()
-    cp.read(filename)
+    cp.readfp(data)
+
     console = {}
     gui = {}
     if cp.has_section('console_scripts'):
@@ -119,6 +133,7 @@ def get_entrypoints(filename):
         gui = dict(cp.items('gui_scripts'))
     return console, gui
 
+
 def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None):
     """Install a wheel"""
 


Test patch:
diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py
--- a/tests/unit/test_wheel.py
+++ b/tests/unit/test_wheel.py
@@ -11,6 +11,19 @@
 from tests.lib import assert_raises_regexp
 
 
+def test_get_entrypoints(tmpdir):
+    with open(str(tmpdir.join("entry_points.txt")), "w") as fp:
+        fp.write("""
+            [console_scripts]
+            pip = pip.main:pip
+        """)
+
+    assert wheel.get_entrypoints(str(tmpdir.join("entry_points.txt"))) == (
+        {"pip": "pip.main:pip"},
+        {},
+    )
+
+
 def test_uninstallation_paths():
     class dist(object):
         def get_metadata_lines(self, record):