jenkins-bot has submitted this change and it was merged.
Change subject: [FIX] open_compressed: Wrap BZ2File in Py 2.7 ......................................................................
[FIX] open_compressed: Wrap BZ2File in Py 2.7
Although the documentation specifies that BZ2File is compatible with the with-statement since Python 2.7 and the code for both gzip and BZ2File were changed in the same commit in cpython, only since Python 2.7.1 this is supported.
It also separates the tests so it's easier to see if only one specific compressor has a problem. It also tests that the wrapper does not block any exception.
The docstring is also improved to reflect the use_extension parameter.
Bug: T101649 Change-Id: I3f7024746164cc1bb1d89f2ac52dfa41ca08a3a6 --- M pywikibot/tools/__init__.py M tests/tools_tests.py 2 files changed, 48 insertions(+), 28 deletions(-)
Approvals: Merlijn van Deen: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py index 9636c46..8d9c4ff 100644 --- a/pywikibot/tools/__init__.py +++ b/pywikibot/tools/__init__.py @@ -750,22 +750,25 @@ they are always available. 7zip is only available when a 7za program is available.
- The compression is selected via the file ending. + The compression is either selected via the magic number or file ending.
@param filename: The filename. @type filename: str + @param use_extension: Use the file extension instead of the magic number + to determine the type of compression (default False). + @type use_extension: bool @raises ValueError: When 7za is not available. @raises OSError: When it's not a 7z archive but the file extension is 7z. It is also raised by bz2 when its content is invalid. gzip does not immediately raise that error but only on reading it. - @return: A file like object returning the uncompressed data in binary mode. - Before Python 2.7 it's wrapping the object returned by BZ2File and gzip - in a ContextManagerWrapper so it's advantages/disadvantages apply there. - @rtype: file like object + @return: A file-like object returning the uncompressed data in binary mode. + Before Python 2.7 the GzipFile object and before 2.7.1 the BZ2File are + wrapped in a ContextManagerWrapper with its advantages/disadvantages. + @rtype: file-like object """ - def wrap(wrapped): + def wrap(wrapped, sub_ver): """Wrap in a wrapper when this is below Python version 2.7.""" - if sys.version_info < (2, 7): + if sys.version_info < (2, 7, sub_ver): return ContextManagerWrapper(wrapped) else: return wrapped @@ -787,9 +790,9 @@ extension = ''
if extension == 'bz2': - return wrap(bz2.BZ2File(filename)) + return wrap(bz2.BZ2File(filename), 1) elif extension == 'gz': - return wrap(gzip.open(filename)) + return wrap(gzip.open(filename), 0) elif extension == '7z': try: process = subprocess.Popen(['7za', 'e', '-bd', '-so', filename], diff --git a/tests/tools_tests.py b/tests/tools_tests.py index ceb42cb..718f589 100644 --- a/tests/tools_tests.py +++ b/tests/tools_tests.py @@ -24,26 +24,26 @@
"""Test that ContextManagerWrapper is working correctly."""
+ class DummyClass(object): + + """A dummy class which has some values and a close method.""" + + class_var = 42 + + def __init__(self): + """Create instance with dummy values.""" + self.instance_var = 1337 + self.closed = False + + def close(self): + """Just store that it has been closed.""" + self.closed = True + net = False
def test_wrapper(self): """Create a test instance and verify the wrapper redirects.""" - class DummyClass(object): - - """A dummy class which has some values and a close method.""" - - class_var = 42 - - def __init__(self): - """Create instance with dummy values.""" - self.instance_var = 1337 - self.closed = False - - def close(self): - """Just store that it has been closed.""" - self.closed = True - - obj = DummyClass() + obj = self.DummyClass() wrapped = tools.ContextManagerWrapper(obj) self.assertIs(wrapped.class_var, obj.class_var) self.assertIs(wrapped.instance_var, obj.instance_var) @@ -52,7 +52,18 @@ with wrapped as unwrapped: self.assertFalse(obj.closed) self.assertIs(unwrapped, obj) + unwrapped.class_var = 47 self.assertTrue(obj.closed) + self.assertEqual(wrapped.class_var, 47) + + def test_exec_wrapper(self): + """Check that the wrapper permits exceptions.""" + wrapper = tools.ContextManagerWrapper(self.DummyClass()) + self.assertFalse(wrapper.closed) + with self.assertRaises(ZeroDivisionError): + with wrapper: + 1 / 0 + self.assertTrue(wrapper.closed)
class OpenCompressedTestCase(TestCase): @@ -82,13 +93,19 @@ with tools.open_compressed(*args) as f: return f.read()
- def test_open_compressed(self): - """Test open_compressed with all compressors in the standard library.""" + def test_open_compressed_normal(self): + """Test open_compressed with no compression in the standard library.""" self.assertEqual(self._get_content(self.base_file), self.original_content) + + def test_open_compressed_bz2(self): + """Test open_compressed with bz2 compressor in the standard library.""" self.assertEqual(self._get_content(self.base_file + '.bz2'), self.original_content) - self.assertEqual(self._get_content(self.base_file + '.gz'), self.original_content) self.assertEqual(self._get_content(self.base_file + '.bz2', True), self.original_content)
+ def test_open_compressed_gz(self): + """Test open_compressed with gz compressor in the standard library.""" + self.assertEqual(self._get_content(self.base_file + '.gz'), self.original_content) + def test_open_compressed_7z(self): """Test open_compressed with 7za if installed.""" try: