Repository: pypa/pip

Issue:
Pip crashes on non-ASCII symbol in lsb_release output
* Pip version: 9.0.0
* Python version: CPython 3.5.2
* Operating System: Fedora release 19 (Schrödinger’s Cat)

### Description:

According to https://github.com/pypa/pip/blob/c8e8a99b7a6f9404536bc9d895a1a42a060f7f91/pip/_vendor/distro.py#L922 pip wants ```lsb_release -a``` output to be strictly in ascii only, while there can be any unicode symbol. Because of that it crashes on ```Fedora release 19 (Schrödinger’s Cat)```  with traceback
```
Traceback (most recent call last):
  File "/home/username/.virtualenvs/powerdns-cpython3.5/bin/pip", line 11, in <module>
    sys.exit(main())
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/__init__.py", line 233, in main
    return command.main(cmd_args)
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/basecommand.py", line 251, in main
    timeout=min(5, options.timeout)) as session:
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/basecommand.py", line 72, in _build_session
    insecure_hosts=options.trusted_hosts,
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/download.py", line 329, in __init__
    self.headers["User-Agent"] = user_agent()
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/download.py", line 93, in user_agent
    from pip._vendor import distro
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/_vendor/distro.py", line 1051, in <module>
    _distro = LinuxDistribution()
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/_vendor/distro.py", line 594, in __init__
    if include_lsb else {}
  File "/home/username/.virtualenvs/powerdns-cpython3.5/lib/python3.5/site-packages/pip/_vendor/distro.py", line 922, in _lsb_release_info
    stdout, stderr = stdout.decode('ascii'), stderr.decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 250: ordinal not in range(128)
```

### What I've run:

```sh
pip install .
```



Discussion:
Fixed in distro module in https://github.com/nir0s/distro/pull/144


Resolving patch:
diff --git a/pip/_vendor/distro.py b/pip/_vendor/distro.py
--- a/pip/_vendor/distro.py
+++ b/pip/_vendor/distro.py
@@ -589,12 +589,14 @@ def __init__(self,
         self.os_release_file = os_release_file or \
             os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME)
         self.distro_release_file = distro_release_file or ''  # updated later
-        self._os_release_info = self._os_release_info()
-        self._lsb_release_info = self._lsb_release_info() \
+        self._os_release_info = self._get_os_release_info()
+        self._lsb_release_info = self._get_lsb_release_info() \
             if include_lsb else {}
-        self._distro_release_info = self._distro_release_info()
+        self._distro_release_info = self._get_distro_release_info()
 
     def __repr__(self):
+        """Return repr of all info
+        """
         return \
             "LinuxDistribution(" \
             "os_release_file={0!r}, " \
@@ -623,12 +625,10 @@ def linux_distribution(self, full_distribution_name=True):
         )
 
     def id(self):
-        """
-        Return the distro ID of the Linux distribution, as a string.
+        """Return the distro ID of the Linux distribution, as a string.
 
         For details, see :func:`distro.id`.
         """
-
         def normalize(distro_id, table):
             distro_id = distro_id.lower().replace(' ', '_')
             return table.get(distro_id, distro_id)
@@ -833,7 +833,7 @@ def distro_release_attr(self, attribute):
         """
         return self._distro_release_info.get(attribute, '')
 
-    def _os_release_info(self):
+    def _get_os_release_info(self):
         """
         Get the information items from the specified os-release file.
 
@@ -905,7 +905,7 @@ def _parse_os_release_content(lines):
                 pass
         return props
 
-    def _lsb_release_info(self):
+    def _get_lsb_release_info(self):
         """
         Get the information items from the lsb_release command output.
 
@@ -919,7 +919,7 @@ def _lsb_release_info(self):
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE)
         stdout, stderr = process.communicate()
-        stdout, stderr = stdout.decode('ascii'), stderr.decode('ascii')
+        stdout, stderr = stdout.decode('utf-8'), stderr.decode('utf-8')
         code = process.returncode
         if code == 0:
             content = stdout.splitlines()
@@ -950,8 +950,7 @@ def _parse_lsb_release_content(lines):
         """
         props = {}
         for line in lines:
-            if isinstance(line, bytes):
-                line = line.decode('utf-8')
+            line = line.decode('utf-8') if isinstance(line, bytes) else line
             kv = line.strip('\n').split(':', 1)
             if len(kv) != 2:
                 # Ignore lines without colon.
@@ -960,7 +959,7 @@ def _parse_lsb_release_content(lines):
             props.update({k.replace(' ', '_').lower(): v.strip()})
         return props
 
-    def _distro_release_info(self):
+    def _get_distro_release_info(self):
         """
         Get the information items from the specified distro release file.
 
@@ -1067,15 +1066,15 @@ def main():
     args = parser.parse_args()
 
     if args.json:
-        logger.info(json.dumps(info()))
+        logger.info(json.dumps(info(), indent=4, sort_keys=True))
     else:
-        logger.info('Name: {0}'.format(name(pretty=True)))
+        logger.info('Name: %s', name(pretty=True))
         distribution_version = version(pretty=True)
         if distribution_version:
-            logger.info('Version: {0}'.format(distribution_version))
+            logger.info('Version: %s', distribution_version)
         distribution_codename = codename()
         if distribution_codename:
-            logger.info('Codename: {0}'.format(distribution_codename))
+            logger.info('Codename: %s', distribution_codename)
 
 
 if __name__ == '__main__':


Test patch: