diff -r 5c09e1c57200 Lib/platform.py --- a/Lib/platform.py Wed Mar 20 20:16:47 2013 +0100 +++ b/Lib/platform.py Mon Mar 25 11:57:44 2013 -0700 @@ -129,6 +129,9 @@ # Standard Unix uses /dev/null DEV_NULL = '/dev/null' +# Directory to search for configuration information on Unix +_UNIXCONFDIR = '/etc' + ### Platform specific APIs _libc_search = re.compile(b'(__libc_init)' @@ -315,15 +318,17 @@ """ try: - etc = os.listdir('/etc') + etc = os.listdir(_UNIXCONFDIR) except OSError: # Probably not a Unix system - return distname,version,id + return distname, version, id + + # Try to find a file containing a distribution's release information etc.sort() - for file in etc: - m = _release_filename.match(file) + for filename in etc: + m = _release_filename.match(filename) if m is not None: - _distname,dummy = m.groups() + _distname, dummy = m.groups() if _distname in supported_dists: distname = _distname break @@ -331,7 +336,8 @@ return _dist_try_harder(distname,version,id) # Read the first line - with open('/etc/'+file, 'r') as f: + with open(os.path.join(_UNIXCONFDIR, filename), 'r', encoding='utf-8', + errors='surrogateescape') as f: firstline = f.readline() _distname, _version, _id = _parse_release_file(firstline) diff -r 5c09e1c57200 Lib/test/test_platform.py --- a/Lib/test/test_platform.py Wed Mar 20 20:16:47 2013 +0100 +++ b/Lib/test/test_platform.py Mon Mar 25 11:57:44 2013 -0700 @@ -1,10 +1,14 @@ +import locale import os import platform import subprocess import sys +import tempfile import unittest import warnings +from unittest.mock import MagicMock, patch + from test import support class PlatformTest(unittest.TestCase): @@ -37,6 +41,55 @@ for terse in (False, True): res = platform.platform(aliased, terse) + def test_dist_with_utf8(self): + # Test reading the platform information with a variety of "odd" locale + # settings and utf-8 encodings of the files. + + # Expectation: + # utf8_data will come out as utf8 + # locale will have no effect on the outcome + + with tempfile.TemporaryDirectory() as d: + utf8_data_directory = d + f = open(os.path.join(d, 'fedora-release'), 'wb') + f.write(self.release_string.encode('utf-8')) + f.close() + + # The linux_distribution function reads from a file so we have to + # mock out the directory to look for that file in + with patch('platform._UNIXCONFDIR', d): + for temp_locale in ('C', 'pt_BR.UTF8', 'pt_BR.ISO8859-1'): + locale.setlocale(locale.LC_ALL, temp_locale) + self.assertEqual(platform.dist(), + self.parsed_release_string) + + locale.resetlocale() + + def test_dist_with_latin1(self): + # Test reading the platform information with a variety of "odd" locale + # settings and latin1 encodings of the files. (Want to catch + # tracebacks in unicode handling and unknown encodings of that file) + + # Expectations: + # latin1_data will be mangled with some surrogateescapes + # locale will have no effect on the outcome + + with tempfile.TemporaryDirectory() as d: + latin1_data_directory = d + f = open(os.path.join(d, 'fedora-release'), 'wb') + f.write(self.release_string.encode('latin-1')) + f.close() + + # The linux_distribution function reads from a file so we have to + # mock out the file that will be found by that + with patch('platform._UNIXCONFDIR', d): + for temp_locale in ('C', 'pt_BR.UTF8', 'pt_BR.ISO8859-1'): + locale.setlocale(locale.LC_ALL, temp_locale) + self.assertTrue(platform.dist(), + self.parsed_mangled_latin1_release_as_utf8) + + locale.resetlocale() + def test_system(self): res = platform.system() @@ -60,6 +113,12 @@ self.save_mercurial = sys._mercurial self.save_platform = sys.platform + # data for testing dist with non-ascii encodings + self.release_string = "Fedora release 19 (Schrödinger's Cat)\n" + self.parsed_release_string = ("fedora", "19", "Schrödinger's Cat") + self.parsed_mangled_latin1_release_as_utf8 = ("fedora", "19", + "Schr\udcf6dinger's_Cat") + def tearDown(self): sys.version = self.save_version sys._mercurial = self.save_mercurial diff -r 5c09e1c57200 Misc/NEWS --- a/Misc/NEWS Wed Mar 20 20:16:47 2013 +0100 +++ b/Misc/NEWS Mon Mar 25 11:57:44 2013 -0700 @@ -292,6 +292,9 @@ Library ------- +- Issue #17429: Fix a UnicodeDecodeError when platform.platform() reads the os + release information from a file that contains non-ascii characters. + - Issue #16997: unittest.TestCase now provides a subTest() context manager to procedurally generate, in an easy way, small test instances.