-
-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-31412: wave.open takes a path-like object #3484
Changes from 5 commits
f8fd6f7
dd997b7
83ecd48
d4e19dd
51aa76a
5e5bf41
189c4e2
e56e644
e2cea59
4a82d65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,8 +19,8 @@ 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 | ||
file-like object. *mode* can be: | ||
If *file* is a string or :term:`path-like object`, open the file | ||
by that name, otherwise treat it as a file-like object. *mode* can be: | ||
|
||
``'rb'`` | ||
Read only mode. | ||
|
@@ -47,6 +47,9 @@ The :mod:`wave` module defines the following function and exception: | |
.. versionchanged:: 3.4 | ||
Added support for unseekable files. | ||
|
||
.. versionchanged:: 3.7 | ||
Added support for :term:`path-like object`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or |
||
|
||
.. function:: openfp(file, mode) | ||
|
||
A synonym for :func:`.open`, maintained for backwards compatibility. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,12 @@ | |
from test import audiotests | ||
from test import support | ||
from audioop import byteswap | ||
import io | ||
import pathlib | ||
import os | ||
import sys | ||
import wave | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please revert this change. |
||
class WaveTest(audiotests.AudioWriteTests, | ||
audiotests.AudioTestsWithSourceFile): | ||
module = wave | ||
|
@@ -108,6 +110,26 @@ def test__all__(self): | |
blacklist = {'WAVE_FORMAT_PCM'} | ||
support.check__all__(self, wave, blacklist=blacklist) | ||
|
||
def test_open(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rename this to something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test checks only failed cases. It would be better to test successful opens. Test not only reading, but writing too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it acceptable to add a wave file to the test directory? I didn't test on a .wav file since even a small one can be 20-30k -- if that's okay then I will add one and test that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are sample wave files in the audiodata directory. See how other tests use them. |
||
# test that a.wave file could in theory be opened. | ||
# For file size reasons, we do not have an actual .wav file, | ||
# so we will pass in this file, but check that a | ||
# a wave.Error (invalid data) will be raised, | ||
# and not a TypeError or AttributeError | ||
# showing that the opening was actually completed. | ||
this_file = os.path.abspath(__file__) | ||
|
||
with self.assertRaises(wave.Error): | ||
wave.open(this_file) | ||
|
||
this_file_as_path = pathlib.Path(this_file) | ||
with self.assertRaises(wave.Error): | ||
wave.open(this_file_as_path) | ||
|
||
this_string_io = io.StringIO() | ||
with self.assertRaises(EOFError): | ||
wave.open(this_string_io) | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
"""Stuff to parse WAVE files. | ||
"""Module for parsing WAVE audio files. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is out of scope for this issue. |
||
|
||
Usage. | ||
|
||
Reading WAVE files: | ||
f = wave.open(file, 'r') | ||
where file is either the name of a file or an open file pointer. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you create a separate PR for these changes? We've removed duplicate docstrings from several modules (e.g. the venv module) to avoid future maintenance costs. I think we can also drop this giant docstring too. |
||
The open file pointer must have methods read(), seek(), and close(). | ||
When the setpos() and rewind() methods are not used, the seek() | ||
method is not necessary. | ||
where file is either a str, bytes, or Pathlike object | ||
representing a file or an open file pointer. | ||
The open file pointer must have methods read(), and close(). | ||
If the setpos() and rewind() methods are used, then a seek() | ||
method is also necessary. | ||
|
||
This returns an instance of a class with the following public methods: | ||
getnchannels() -- returns number of audio channels (1 for | ||
|
@@ -37,9 +38,11 @@ | |
|
||
Writing WAVE files: | ||
f = wave.open(file, 'w') | ||
where file is either the name of a file or an open file pointer. | ||
The open file pointer must have methods write(), tell(), seek(), and | ||
close(). | ||
where file is either a str, bytes, or Pathlike object | ||
representing a file or an open file pointer. | ||
The open file pointer must have methods write(), tell(), and | ||
close(). If the pointer does not have a seek() method, then | ||
setnframes() must be given an accurate value | ||
|
||
This returns an instance of a class with the following public methods: | ||
setnchannels(n) -- set the number of channels | ||
|
@@ -83,6 +86,7 @@ class Error(Exception): | |
_array_fmts = None, 'b', 'h', None, 'i' | ||
|
||
import audioop | ||
import os | ||
import struct | ||
import sys | ||
from chunk import Chunk | ||
|
@@ -495,6 +499,13 @@ def open(f, mode=None): | |
mode = f.mode | ||
else: | ||
mode = 'rb' | ||
try: | ||
f = os.fspath(f) | ||
except TypeError: | ||
if not hasattr(f, 'read') or not hasattr(f, 'close'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd also check for a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is enough to check for a Note that in |
||
raise TypeError('open() takes str, bytes, a PathLike object, ' | ||
+ f'or an open filehandle, not {type(f)}') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually bytes are not supported with current implementation. See the comment above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we also need to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "open filehandle" -> file-like object |
||
|
||
if mode in ('r', 'rb'): | ||
return Wave_read(f) | ||
elif mode in ('w', 'wb'): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
`wave.open()` now accepts PathLike objects. Contributed by Michael Scott | ||
methane marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PathLike -> :class:`os.PathLike` There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or just
methane marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Cuthbert. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a reference to the "file-like object" term?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked in the glossary and "file-like object" redirects to "file object" so I used that term instead. Hope that that's okay.