← Back to team overview

configglue team mailing list archive

[Merge] lp:~ricardokirkner/configglue/1196754-non-utf8-locale into lp:configglue

 

Ricardo Kirkner has proposed merging lp:~ricardokirkner/configglue/1196754-non-utf8-locale into lp:configglue.

Commit message:
always open file using explicit encoding

Requested reviews:
  Configglue developers (configglue)
Related bugs:
  Bug #1196754 in configglue: "Tests fail in Python 3 with non UTF-8 locale"
  https://bugs.launchpad.net/configglue/+bug/1196754

For more details, see:
https://code.launchpad.net/~ricardokirkner/configglue/1196754-non-utf8-locale/+merge/173236

Fix bug #1196754 by always opening files using an explicit encoding
-- 
https://code.launchpad.net/~ricardokirkner/configglue/1196754-non-utf8-locale/+merge/173236
Your team Configglue developers is requested to review the proposed merge of lp:~ricardokirkner/configglue/1196754-non-utf8-locale into lp:configglue.
=== modified file 'configglue/parser.py'
--- configglue/parser.py	2013-05-31 15:26:42 +0000
+++ configglue/parser.py	2013-07-05 15:58:29 +0000
@@ -20,7 +20,6 @@
 import logging
 import os
 import re
-from io import TextIOWrapper
 
 from configparser import (
     DEFAULTSECT,
@@ -31,7 +30,7 @@
 )
 from functools import reduce
 
-from configglue._compat import text_type, string_types
+from configglue._compat import PY2, text_type, string_types
 
 
 __all__ = [
@@ -50,6 +49,14 @@
 logger.addHandler(NullHandler())
 
 
+def open_file(filename, mode='r', encoding=None):
+    if PY2:
+        fp = codecs.open(filename, mode, encoding=encoding)
+    else:
+        fp = open(filename, mode, encoding=encoding)
+    return fp
+
+
 class SchemaValidationError(Exception):
     """Exception class raised for any schema validation error."""
 
@@ -256,7 +263,7 @@
             if path in already_read:
                 continue
             try:
-                fp = codecs.open(path, 'r', CONFIG_FILE_ENCODING)
+                fp = open_file(path, 'r', encoding=CONFIG_FILE_ENCODING)
             except IOError:
                 logger.warn(
                     'File {0} could not be read. Skipping.'.format(path))
@@ -592,10 +599,7 @@
         """
         if fp is not None:
             if isinstance(fp, string_types):
-                fp = open(fp, 'w')
-            if not isinstance(fp, TextIOWrapper):
-                # write to a specific file
-                fp = codecs.getwriter(CONFIG_FILE_ENCODING)(fp)
+                fp = open_file(fp, 'w', encoding=CONFIG_FILE_ENCODING)
             self.write(fp)
         else:
             # write to the original files
@@ -621,7 +625,8 @@
                         parser.set(section, option, value)
 
                 # write to new file
-                parser.write(open("%s.new" % filename, 'w'))
+                parser.write(open_file("%s.new" % filename, 'w',
+                                       encoding=CONFIG_FILE_ENCODING))
                 # rename old file
                 if os.path.exists(filename):
                     os.rename(filename, "%s.old" % filename)

=== modified file 'configglue/tests/test_parser.py'
--- configglue/tests/test_parser.py	2013-05-26 19:07:46 +0000
+++ configglue/tests/test_parser.py	2013-07-05 15:58:29 +0000
@@ -16,7 +16,6 @@
 ###############################################################################
 from __future__ import unicode_literals
 
-import codecs
 import os
 import shutil
 import tempfile
@@ -37,12 +36,13 @@
     patch,
 )
 
-from configglue._compat import PY2, iteritems
+from configglue._compat import iteritems
 from configglue.parser import (
     CONFIG_FILE_ENCODING,
     NoOptionError,
     SchemaConfigParser,
     SchemaValidationError,
+    open_file,
 )
 from configglue.schema import (
     BoolOption,
@@ -93,9 +93,9 @@
         self.assertEqual(expected_location, location)
 
     @patch('configglue.parser.logger.warn')
-    @patch('configglue.parser.codecs.open')
-    def test_read_ioerror(self, mock_open, mock_warn):
-        mock_open.side_effect = IOError
+    @patch('configglue.parser.open_file')
+    def test_read_ioerror(self, mock_open_file, mock_warn):
+        mock_open_file.side_effect = IOError
 
         parser = SchemaConfigParser(self.schema)
         read_ok = parser.read(self.name)
@@ -110,20 +110,24 @@
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = open_file("%s/first.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1\nincludes=second.cfg")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = open_file("%s/second.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbar=2\nincludes=sub/third.cfg")
             f.close()
 
             os.mkdir("%s/sub" % folder)
-            f = open("%s/sub/third.cfg" % folder, 'w')
+            f = open_file("%s/sub/third.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nincludes=../fourth.cfg")
             f.close()
 
-            f = open("%s/fourth.cfg" % folder, 'w')
+            f = open_file("%s/fourth.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbaz=3")
             f.close()
 
@@ -157,11 +161,13 @@
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = open_file("%s/first.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1\nbar=2\nincludes=second.cfg")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = open_file("%s/second.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbaz=3")
             f.close()
 
@@ -876,11 +882,13 @@
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = open_file("%s/first.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=foo")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = open_file("%s/second.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=bar")
             f.close()
 
@@ -902,12 +910,9 @@
         fp, filename = tempfile.mkstemp()
 
         try:
-            if PY2:
-                f = open(filename, 'w')
-                f.write('[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
-            else:
-                f = open(filename, 'w', encoding=CONFIG_FILE_ENCODING)
-                f.write('[__main__]\nfoo=€')
+            f = open_file(filename, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
+            f.write('[__main__]\nfoo=€')
             f.close()
 
             self.parser.read(filename)
@@ -971,8 +976,10 @@
         # create config file
         fp, filename = tempfile.mkstemp()
         try:
-            parser.write(open(filename, 'w'))
-            result = open(filename, 'r').read().strip()
+            parser.write(open_file(filename, 'w',
+                                   encoding=CONFIG_FILE_ENCODING))
+            result = open_file(filename, 'r',
+                               encoding=CONFIG_FILE_ENCODING).read().strip()
             self.assertEqual(result, expected)
         finally:
             # remove the file
@@ -989,8 +996,10 @@
         # create config file
         fp, filename = tempfile.mkstemp()
         try:
-            parser.write(open(filename, 'w'))
-            result = open(filename, 'r').read().strip()
+            parser.write(open_file(filename, 'w',
+                                   encoding=CONFIG_FILE_ENCODING))
+            result = open_file(filename, 'r',
+                               encoding=CONFIG_FILE_ENCODING).read().strip()
             self.assertEqual(result, expected)
         finally:
             # remove the file
@@ -1012,14 +1021,15 @@
         # create config file
         fp, filename = tempfile.mkstemp()
         try:
-            self.parser.save(open(filename, 'w'))
-            result = codecs.open(filename, 'r',
-                                 encoding=CONFIG_FILE_ENCODING).read().strip()
+            self.parser.save(open_file(filename, 'w',
+                                       encoding=CONFIG_FILE_ENCODING))
+            result = open_file(filename, 'r',
+                               encoding=CONFIG_FILE_ENCODING).read().strip()
             self.assertEqual(result, expected)
 
             self.parser.save(filename)
-            result = codecs.open(filename, 'r',
-                                 encoding=CONFIG_FILE_ENCODING).read().strip()
+            result = open_file(filename, 'r',
+                               encoding=CONFIG_FILE_ENCODING).read().strip()
             self.assertEqual(result, expected)
         finally:
             # remove the file
@@ -1043,11 +1053,13 @@
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = open_file("%s/first.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = open_file("%s/second.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbar=2")
             f.close()
 
@@ -1069,12 +1081,14 @@
         self.parser.save()
 
         # test the changes were correctly saved
-        data = open("%s/first.cfg" % folder).read()
+        data = open_file("%s/first.cfg" % folder,
+                         encoding=CONFIG_FILE_ENCODING).read()
         self.assertTrue('foo = 42' in data)
         self.assertFalse('bar = 42' in data)
         # new value goes into last read config file
         self.assertFalse('baz = 42' in data)
-        data = open("%s/second.cfg" % folder).read()
+        data = open_file("%s/second.cfg" % folder,
+                         encoding=CONFIG_FILE_ENCODING).read()
         self.assertFalse('foo = 42' in data)
         self.assertTrue('bar = 42' in data)
         # new value goes into last read config file
@@ -1090,15 +1104,18 @@
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = open_file("%s/first.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = open_file("%s/second.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbar=2\nincludes = third.cfg")
             f.close()
 
-            f = open("%s/third.cfg" % folder, 'w')
+            f = open_file("%s/third.cfg" % folder, 'w',
+                          encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=3")
             f.close()
 
@@ -1120,11 +1137,14 @@
         self.parser.save()
 
         # test the changes were correctly saved
-        data = open("%s/first.cfg" % folder).read()
+        data = open_file("%s/first.cfg" % folder,
+                         encoding=CONFIG_FILE_ENCODING).read()
         self.assertEqual(data.strip(), '[__main__]\nfoo=1')
-        data = open("%s/third.cfg" % folder).read()
+        data = open_file("%s/third.cfg" % folder,
+                         encoding=CONFIG_FILE_ENCODING).read()
         self.assertEqual(data.strip(), '[__main__]\nfoo = 42')
-        data = open("%s/second.cfg" % folder).read()
+        data = open_file("%s/second.cfg" % folder,
+                         encoding=CONFIG_FILE_ENCODING).read()
         self.assertTrue('bar = 42' in data)
         # new value goes into last read config file
         # not in the last included config file


Follow ups