summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2013-11-16 11:04:00 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2013-11-16 11:04:00 (GMT)
commit7714ebbe0e7556bbfa6b768e434042b55474939c (patch)
treeedfc3e4daeaca4f88a3efb384c1a34bdf7f808b1
parenta7a34a83f3f2b9b319cb6f379f6e189066233c76 (diff)
downloadcpython-7714ebbe0e7556bbfa6b768e434042b55474939c.zip
cpython-7714ebbe0e7556bbfa6b768e434042b55474939c.tar.gz
cpython-7714ebbe0e7556bbfa6b768e434042b55474939c.tar.bz2
Issue #5202: Added support for unseekable files in the wave module.
-rw-r--r--Doc/library/wave.rst10
-rw-r--r--Lib/test/audiotests.py60
-rw-r--r--Lib/test/test_aifc.py44
-rw-r--r--Lib/wave.py8
-rw-r--r--Misc/NEWS2
5 files changed, 90 insertions, 34 deletions
diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst
index 9d12455..189f21a 100644
--- a/Doc/library/wave.rst
+++ b/Doc/library/wave.rst
@@ -19,7 +19,7 @@ The :mod:`wave` module defines the following function and exception:
.. function:: open(file, mode=None)
If *file* is a string, open the file by that name, otherwise treat it as a
- seekable file-like object. *mode* can be:
+ file-like object. *mode* can be:
``'rb'``
Read only mode.
@@ -43,6 +43,8 @@ The :mod:`wave` module defines the following function and exception:
<wave.Wave_read.close>` or :meth:`Wave_write.close()
<wave.Wave_write.close()>` method is called.
+ .. versionchanged:: 3.4
+ Added support for unseekable files.
.. function:: openfp(file, mode)
@@ -154,7 +156,8 @@ Wave_write objects, as returned by :func:`.open`, have the following methods:
.. method:: Wave_write.close()
Make sure *nframes* is correct, and close the file if it was opened by
- :mod:`wave`. This method is called upon object collection.
+ :mod:`wave`. This method is called upon object collection. Can raise an
+ exception if *nframes* is not correct and a file is not seekable.
.. method:: Wave_write.setnchannels(n)
@@ -208,7 +211,8 @@ Wave_write objects, as returned by :func:`.open`, have the following methods:
.. method:: Wave_write.writeframes(data)
- Write audio frames and make sure *nframes* is correct.
+ Write audio frames and make sure *nframes* is correct. Can raise an
+ exception if a file is not seekable.
Note that it is invalid to set any parameters after calling :meth:`writeframes`
diff --git a/Lib/test/audiotests.py b/Lib/test/audiotests.py
index 7b39269..c22f0a1 100644
--- a/Lib/test/audiotests.py
+++ b/Lib/test/audiotests.py
@@ -21,6 +21,13 @@ def byteswap4(data):
a.byteswap()
return a.tobytes()
+class UnseekableIO(io.FileIO):
+ def tell(self):
+ raise io.UnsupportedOperation
+
+ def seek(self, *args, **kwargs):
+ raise io.UnsupportedOperation
+
class AudioTests:
close_fd = False
@@ -177,6 +184,59 @@ class AudioWriteTests(AudioTests):
self.assertEqual(testfile.read(13), b'ababagalamaga')
self.check_file(testfile, self.nframes, self.frames)
+ def test_unseekable_read(self):
+ with self.create_file(TESTFN) as f:
+ f.setnframes(self.nframes)
+ f.writeframes(self.frames)
+
+ with UnseekableIO(TESTFN, 'rb') as testfile:
+ self.check_file(testfile, self.nframes, self.frames)
+
+ def test_unseekable_write(self):
+ with UnseekableIO(TESTFN, 'wb') as testfile:
+ with self.create_file(testfile) as f:
+ f.setnframes(self.nframes)
+ f.writeframes(self.frames)
+
+ self.check_file(TESTFN, self.nframes, self.frames)
+
+ def test_unseekable_incompleted_write(self):
+ with UnseekableIO(TESTFN, 'wb') as testfile:
+ testfile.write(b'ababagalamaga')
+ f = self.create_file(testfile)
+ f.setnframes(self.nframes + 1)
+ try:
+ f.writeframes(self.frames)
+ except OSError:
+ pass
+ try:
+ f.close()
+ except OSError:
+ pass
+
+ with open(TESTFN, 'rb') as testfile:
+ self.assertEqual(testfile.read(13), b'ababagalamaga')
+ self.check_file(testfile, self.nframes + 1, self.frames)
+
+ def test_unseekable_overflowed_write(self):
+ with UnseekableIO(TESTFN, 'wb') as testfile:
+ testfile.write(b'ababagalamaga')
+ f = self.create_file(testfile)
+ f.setnframes(self.nframes - 1)
+ try:
+ f.writeframes(self.frames)
+ except OSError:
+ pass
+ try:
+ f.close()
+ except OSError:
+ pass
+
+ with open(TESTFN, 'rb') as testfile:
+ self.assertEqual(testfile.read(13), b'ababagalamaga')
+ framesize = self.nchannels * self.sampwidth
+ self.check_file(testfile, self.nframes - 1, self.frames[:-framesize])
+
class AudioTestsWithSourceFile(AudioTests):
diff --git a/Lib/test/test_aifc.py b/Lib/test/test_aifc.py
index 3e9a1d1..b4270bc 100644
--- a/Lib/test/test_aifc.py
+++ b/Lib/test/test_aifc.py
@@ -8,10 +8,17 @@ import struct
import aifc
-class AifcPCM8Test(audiotests.AudioWriteTests,
- audiotests.AudioTestsWithSourceFile,
- unittest.TestCase):
+class AifcTest(audiotests.AudioWriteTests,
+ audiotests.AudioTestsWithSourceFile):
module = aifc
+ close_fd = True
+ test_unseekable_read = None
+ test_unseekable_write = None
+ test_unseekable_incompleted_write = None
+ test_unseekable_overflowed_write = None
+
+
+class AifcPCM8Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm8.aiff'
sndfilenframes = 3307
nchannels = 2
@@ -26,13 +33,9 @@ class AifcPCM8Test(audiotests.AudioWriteTests,
11FA 3EFB BCFC 66FF CF04 4309 C10E 5112 EE17 8216 7F14 8012 \
490E 520D EF0F CE0F E40C 630A 080A 2B0B 510E 8B11 B60E 440A \
""")
- close_fd = True
-class AifcPCM16Test(audiotests.AudioWriteTests,
- audiotests.AudioTestsWithSourceFile,
- unittest.TestCase):
- module = aifc
+class AifcPCM16Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm16.aiff'
sndfilenframes = 3307
nchannels = 2
@@ -49,13 +52,9 @@ class AifcPCM16Test(audiotests.AudioWriteTests,
EEE21753 82071665 7FFF1443 8004128F 49A20EAF 52BB0DBA EFB40F60 CE3C0FBF \
E4B30CEC 63430A5C 08C80A20 2BBB0B08 514A0E43 8BCF1139 B6F60EEB 44120A5E \
""")
- close_fd = True
-class AifcPCM24Test(audiotests.AudioWriteTests,
- audiotests.AudioTestsWithSourceFile,
- unittest.TestCase):
- module = aifc
+class AifcPCM24Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm24.aiff'
sndfilenframes = 3307
nchannels = 2
@@ -78,13 +77,9 @@ class AifcPCM24Test(audiotests.AudioWriteTests,
E4B49C0CEA2D 6344A80A5A7C 08C8FE0A1FFE 2BB9860B0A0E \
51486F0E44E1 8BCC64113B05 B6F4EC0EEB36 4413170A5B48 \
""")
- close_fd = True
-class AifcPCM32Test(audiotests.AudioWriteTests,
- audiotests.AudioTestsWithSourceFile,
- unittest.TestCase):
- module = aifc
+class AifcPCM32Test(AifcTest, unittest.TestCase):
sndfilename = 'pluck-pcm32.aiff'
sndfilenframes = 3307
nchannels = 2
@@ -107,13 +102,9 @@ class AifcPCM32Test(audiotests.AudioWriteTests,
E4B49CC00CEA2D90 6344A8800A5A7CA0 08C8FE800A1FFEE0 2BB986C00B0A0E00 \
51486F800E44E190 8BCC6480113B0580 B6F4EC000EEB3630 441317800A5B48A0 \
""")
- close_fd = True
-class AifcULAWTest(audiotests.AudioWriteTests,
- audiotests.AudioTestsWithSourceFile,
- unittest.TestCase):
- module = aifc
+class AifcULAWTest(AifcTest, unittest.TestCase):
sndfilename = 'pluck-ulaw.aifc'
sndfilenframes = 3307
nchannels = 2
@@ -132,13 +123,9 @@ class AifcULAWTest(audiotests.AudioWriteTests,
""")
if sys.byteorder != 'big':
frames = audiotests.byteswap2(frames)
- close_fd = True
-class AifcALAWTest(audiotests.AudioWriteTests,
- audiotests.AudioTestsWithSourceFile,
- unittest.TestCase):
- module = aifc
+class AifcALAWTest(AifcTest, unittest.TestCase):
sndfilename = 'pluck-alaw.aifc'
sndfilenframes = 3307
nchannels = 2
@@ -157,7 +144,6 @@ class AifcALAWTest(audiotests.AudioWriteTests,
""")
if sys.byteorder != 'big':
frames = audiotests.byteswap2(frames)
- close_fd = True
class AifcMiscTest(audiotests.AudioTests, unittest.TestCase):
diff --git a/Lib/wave.py b/Lib/wave.py
index e723423..3d01817 100644
--- a/Lib/wave.py
+++ b/Lib/wave.py
@@ -491,14 +491,18 @@ class Wave_write:
if not self._nframes:
self._nframes = initlength // (self._nchannels * self._sampwidth)
self._datalength = self._nframes * self._nchannels * self._sampwidth
- self._form_length_pos = self._file.tell()
+ try:
+ self._form_length_pos = self._file.tell()
+ except (AttributeError, OSError):
+ self._form_length_pos = None
self._file.write(struct.pack('<L4s4sLHHLLHH4s',
36 + self._datalength, b'WAVE', b'fmt ', 16,
WAVE_FORMAT_PCM, self._nchannels, self._framerate,
self._nchannels * self._framerate * self._sampwidth,
self._nchannels * self._sampwidth,
self._sampwidth * 8, b'data'))
- self._data_length_pos = self._file.tell()
+ if self._form_length_pos is not None:
+ self._data_length_pos = self._file.tell()
self._file.write(struct.pack('<L', self._datalength))
self._headerwritten = True
diff --git a/Misc/NEWS b/Misc/NEWS
index 0cfdd13..9fd34c2 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -47,6 +47,8 @@ Core and Builtins
Library
-------
+- Issue #5202: Added support for unseekable files in the wave module.
+
- Issue #19544 and Issue #1180: Restore global option to ignore
~/.pydistutils.cfg in Distutils, accidentally removed in backout of
distutils2 changes.