From 440332072706c5e422e6c54a2ec0ebb88e09c85c Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sun, 24 Apr 2022 15:50:07 -0700 Subject: Rewrite audio.py to jive with image.py (#91886) Similar to the rewrite of email/mime/image.py and associated test after the deprecation of imghdr.py, thisrewrites email/mime/audio.py and associated tests after the deprecation of sndhdr.py. Closes #91885 --- Lib/email/mime/audio.py | 108 +++++++++++++++++----------------- Lib/test/test_email/data/audiotest.au | Bin 28144 -> 0 bytes Lib/test/test_email/data/sndhdr.aifc | Bin 0 -> 106 bytes Lib/test/test_email/data/sndhdr.aiff | Bin 0 -> 108 bytes Lib/test/test_email/data/sndhdr.au | Bin 0 -> 28144 bytes Lib/test/test_email/data/sndhdr.wav | Bin 0 -> 64 bytes Lib/test/test_email/test_email.py | 34 +++++++---- 7 files changed, 78 insertions(+), 64 deletions(-) delete mode 100644 Lib/test/test_email/data/audiotest.au create mode 100644 Lib/test/test_email/data/sndhdr.aifc create mode 100644 Lib/test/test_email/data/sndhdr.aiff create mode 100644 Lib/test/test_email/data/sndhdr.au create mode 100644 Lib/test/test_email/data/sndhdr.wav diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py index e859c2e..8815f5c 100644 --- a/Lib/email/mime/audio.py +++ b/Lib/email/mime/audio.py @@ -11,58 +11,6 @@ from email import encoders from email.mime.nonmultipart import MIMENonMultipart -_tests = [] - -def _test_aifc_aiff(h, f): - if not h.startswith(b'FORM'): - return None - if h[8:12] in {b'AIFC', b'AIFF'}: - return 'x-aiff' - else: - return None - -_tests.append(_test_aifc_aiff) - - -def _test_au(h, f): - if h.startswith(b'.snd'): - return 'basic' - else: - return None - -_tests.append(_test_au) - - -def _test_wav(h, f): - import wave - # 'RIFF' 'WAVE' 'fmt ' - if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ': - return None - else: - return "x-wav" - -_tests.append(_test_wav) - - -# There are others in sndhdr that don't have MIME types. :( -# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? -def _whatsnd(data): - """Try to identify a sound file type. - - sndhdr.what() has a pretty cruddy interface, unfortunately. This is why - we re-do it here. It would be easier to reverse engineer the Unix 'file' - command and use the standard 'magic' file, as shipped with a modern Unix. - """ - hdr = data[:512] - fakefile = BytesIO(hdr) - for testfn in _tests: - res = testfn(hdr, fakefile) - if res is not None: - return res - else: - return None - - class MIMEAudio(MIMENonMultipart): """Class for generating audio/* MIME documents.""" @@ -89,10 +37,64 @@ class MIMEAudio(MIMENonMultipart): header. """ if _subtype is None: - _subtype = _whatsnd(_audiodata) + _subtype = _what(_audiodata) if _subtype is None: raise TypeError('Could not find audio MIME subtype') MIMENonMultipart.__init__(self, 'audio', _subtype, policy=policy, **_params) self.set_payload(_audiodata) _encoder(self) + + +_rules = [] + + +# Originally from the sndhdr module. +# +# There are others in sndhdr that don't have MIME types. :( +# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? +def _what(data): + # Try to identify a sound file type. + # + # sndhdr.what() had a pretty cruddy interface, unfortunately. This is why + # we re-do it here. It would be easier to reverse engineer the Unix 'file' + # command and use the standard 'magic' file, as shipped with a modern Unix. + hdr = data[:512] + fakefile = BytesIO(hdr) + for testfn in _rules: + if res := testfn(hdr, fakefile): + return res + else: + return None + + +def rule(rulefunc): + _rules.append(rulefunc) + return rulefunc + + +@rule +def _aiff(h, f): + if not h.startswith(b'FORM'): + return None + if h[8:12] in {b'AIFC', b'AIFF'}: + return 'x-aiff' + else: + return None + + +@rule +def _au(h, f): + if h.startswith(b'.snd'): + return 'basic' + else: + return None + + +@rule +def _wav(h, f): + # 'RIFF' 'WAVE' 'fmt ' + if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ': + return None + else: + return "x-wav" diff --git a/Lib/test/test_email/data/audiotest.au b/Lib/test/test_email/data/audiotest.au deleted file mode 100644 index f76b050..0000000 Binary files a/Lib/test/test_email/data/audiotest.au and /dev/null differ diff --git a/Lib/test/test_email/data/sndhdr.aifc b/Lib/test/test_email/data/sndhdr.aifc new file mode 100644 index 0000000..8aae4e7 Binary files /dev/null and b/Lib/test/test_email/data/sndhdr.aifc differ diff --git a/Lib/test/test_email/data/sndhdr.aiff b/Lib/test/test_email/data/sndhdr.aiff new file mode 100644 index 0000000..8c279a7 Binary files /dev/null and b/Lib/test/test_email/data/sndhdr.aiff differ diff --git a/Lib/test/test_email/data/sndhdr.au b/Lib/test/test_email/data/sndhdr.au new file mode 100644 index 0000000..f76b050 Binary files /dev/null and b/Lib/test/test_email/data/sndhdr.au differ diff --git a/Lib/test/test_email/data/sndhdr.wav b/Lib/test/test_email/data/sndhdr.wav new file mode 100644 index 0000000..0dca367 Binary files /dev/null and b/Lib/test/test_email/data/sndhdr.wav differ diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 6ead594..933aa4c 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1515,37 +1515,49 @@ Blah blah blah # Test the basic MIMEAudio class class TestMIMEAudio(unittest.TestCase): - def setUp(self): - with openfile('audiotest.au', 'rb') as fp: + def _make_audio(self, ext): + with openfile(f'sndhdr.{ext}', 'rb') as fp: self._audiodata = fp.read() self._au = MIMEAudio(self._audiodata) def test_guess_minor_type(self): - self.assertEqual(self._au.get_content_type(), 'audio/basic') + for ext, subtype in { + 'aifc': 'x-aiff', + 'aiff': 'x-aiff', + 'wav': 'x-wav', + 'au': 'basic', + }.items(): + self._make_audio(ext) + subtype = ext if subtype is None else subtype + self.assertEqual(self._au.get_content_type(), f'audio/{subtype}') def test_encoding(self): + self._make_audio('au') payload = self._au.get_payload() self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), - self._audiodata) + self._audiodata) def test_checkSetMinor(self): + self._make_audio('au') au = MIMEAudio(self._audiodata, 'fish') self.assertEqual(au.get_content_type(), 'audio/fish') def test_add_header(self): + self._make_audio('au') eq = self.assertEqual self._au.add_header('Content-Disposition', 'attachment', - filename='audiotest.au') + filename='sndhdr.au') eq(self._au['content-disposition'], - 'attachment; filename="audiotest.au"') + 'attachment; filename="sndhdr.au"') eq(self._au.get_params(header='content-disposition'), - [('attachment', ''), ('filename', 'audiotest.au')]) + [('attachment', ''), ('filename', 'sndhdr.au')]) eq(self._au.get_param('filename', header='content-disposition'), - 'audiotest.au') + 'sndhdr.au') missing = [] eq(self._au.get_param('attachment', header='content-disposition'), '') - self.assertIs(self._au.get_param('foo', failobj=missing, - header='content-disposition'), missing) + self.assertIs(self._au.get_param( + 'foo', failobj=missing, + header='content-disposition'), missing) # Try some missing stuff self.assertIs(self._au.get_param('foobar', missing), missing) self.assertIs(self._au.get_param('attachment', missing, @@ -3462,7 +3474,7 @@ multipart/report self.assertEqual(s.getvalue(), msgtxt) def test_mime_classes_policy_argument(self): - with openfile('audiotest.au', 'rb') as fp: + with openfile('sndhdr.au', 'rb') as fp: audiodata = fp.read() with openfile('python.gif', 'rb') as fp: bindata = fp.read() -- cgit v0.12