diff -r 340f45374cf5 Doc/whatsnew/3.4.rst --- a/Doc/whatsnew/3.4.rst Thu Aug 29 12:37:28 2013 +0300 +++ b/Doc/whatsnew/3.4.rst Thu Aug 29 22:48:24 2013 +0300 @@ -362,6 +362,13 @@ (Contributed by Antoine Pitrou in :issue:`17804`.) +sunau +---- + +:meth:`sunau.open` now supports the context manager protocol. (Contributed +by Serhiy Storchaka in :issue:`18878`.) + + urllib ------ diff -r 340f45374cf5 Lib/sunau.py --- a/Lib/sunau.py Thu Aug 29 12:37:28 2013 +0300 +++ b/Lib/sunau.py Thu Aug 29 22:48:24 2013 +0300 @@ -163,6 +163,12 @@ if self._file: self.close() + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + def initfp(self, file): self._file = file self._soundpos = 0 @@ -298,6 +304,12 @@ self.close() self._file = None + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + def initfp(self, file): self._file = file self._framerate = 0 @@ -405,14 +417,17 @@ self._patchheader() def close(self): - self._ensure_header_written() - if self._nframeswritten != self._nframes or \ - self._datalength != self._datawritten: - self._patchheader() - self._file.flush() - if self._opened and self._file: - self._file.close() - self._file = None + if self._file: + try: + self._ensure_header_written() + if self._nframeswritten != self._nframes or \ + self._datalength != self._datawritten: + self._patchheader() + self._file.flush() + finally: + if self._opened and self._file: + self._file.close() + self._file = None # # private methods diff -r 340f45374cf5 Lib/test/test_sunau.py --- a/Lib/test/test_sunau.py Thu Aug 29 12:37:28 2013 +0300 +++ b/Lib/test/test_sunau.py Thu Aug 29 22:48:24 2013 +0300 @@ -1,4 +1,4 @@ -from test.support import run_unittest, TESTFN +from test.support import TESTFN, unlink import unittest import os @@ -17,10 +17,7 @@ def tearDown(self): if self.f is not None: self.f.close() - try: - os.remove(TESTFN) - except OSError: - pass + unlink(TESTFN) def test_lin(self): self.f = sunau.open(TESTFN, 'w') @@ -62,9 +59,49 @@ self.assertEqual(self.f.readframes(nframes), output) self.f.close() + def test_write_context_manager_calls_close(self): + # Close checks for a minimum header and will raise an error + # if it is not set, so this proves that close is called. + with self.assertRaises(sunau.Error): + with sunau.open(TESTFN, 'wb') as f: + pass + with self.assertRaises(sunau.Error): + with open(TESTFN, 'wb') as testfile: + with sunau.open(testfile): + pass -def test_main(): - run_unittest(SunAUTest) + def test_context_manager_with_open_file(self): + with open(TESTFN, 'wb') as testfile: + with sunau.open(testfile) as f: + f.setnchannels(nchannels) + f.setsampwidth(sampwidth) + f.setframerate(framerate) + self.assertFalse(testfile.closed) + with open(TESTFN, 'rb') as testfile: + with sunau.open(testfile) as f: + self.assertFalse(f.getfp().closed) + params = f.getparams() + self.assertEqual(params[0], nchannels) + self.assertEqual(params[1], sampwidth) + self.assertEqual(params[2], framerate) + self.assertIsNone(f.getfp()) + self.assertFalse(testfile.closed) -if __name__ == "__main__": + def test_context_manager_with_filename(self): + # If the file doesn't get closed, this test won't fail, but it will + # produce a resource leak warning. + with sunau.open(TESTFN, 'wb') as f: + f.setnchannels(nchannels) + f.setsampwidth(sampwidth) + f.setframerate(framerate) + with sunau.open(TESTFN) as f: + self.assertFalse(f.getfp().closed) + params = f.getparams() + self.assertEqual(params[0], nchannels) + self.assertEqual(params[1], sampwidth) + self.assertEqual(params[2], framerate) + self.assertIsNone(f.getfp()) + + +if __name__ == '__main__': unittest.main() diff -r 340f45374cf5 Misc/NEWS --- a/Misc/NEWS Thu Aug 29 12:37:28 2013 +0300 +++ b/Misc/NEWS Thu Aug 29 22:48:24 2013 +0300 @@ -51,6 +51,8 @@ Library ------- +- Issue #18878: sunau.open now supports the context manager protocol. + - Issue #17974: Switch unittest from using getopt to using argparse. - Issue #11798: TestSuite now drops references to own tests after execution.