[Python-checkins] gh-104773: PEP 594: Remove the sndhdr module (#104774)

vstinner webhook-mailer at python.org
Wed May 24 05:32:26 EDT 2023


https://github.com/python/cpython/commit/7b00940f69ab26212ea375860a1956e157dd2c30
commit: 7b00940f69ab26212ea375860a1956e157dd2c30
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2023-05-24T09:32:18Z
summary:

gh-104773: PEP 594: Remove the sndhdr module (#104774)

Remove the Lib/test/sndhdrdata/ directory.

files:
A Misc/NEWS.d/next/Library/2023-05-23-01-37-40.gh-issue-104773.8c-GsG.rst
D Doc/library/sndhdr.rst
D Lib/sndhdr.py
D Lib/test/sndhdrdata/README
D Lib/test/sndhdrdata/sndhdr.8svx
D Lib/test/sndhdrdata/sndhdr.aifc
D Lib/test/sndhdrdata/sndhdr.aiff
D Lib/test/sndhdrdata/sndhdr.au
D Lib/test/sndhdrdata/sndhdr.hcom
D Lib/test/sndhdrdata/sndhdr.sndt
D Lib/test/sndhdrdata/sndhdr.voc
D Lib/test/sndhdrdata/sndhdr.wav
D Lib/test/test_sndhdr.py
M .gitattributes
M Doc/library/superseded.rst
M Doc/whatsnew/3.11.rst
M Doc/whatsnew/3.12.rst
M Doc/whatsnew/3.13.rst
M Makefile.pre.in
M Python/stdlib_module_names.h

diff --git a/.gitattributes b/.gitattributes
index 4ed95069442f..bab1ef0d0104 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -18,7 +18,6 @@
 *.zip binary
 
 # Specific binary files
-Lib/test/sndhdrdata/sndhdr.* binary
 PC/classicAppCompat.* binary
 
 # Text files that should not be subject to eol conversion
diff --git a/Doc/library/sndhdr.rst b/Doc/library/sndhdr.rst
deleted file mode 100644
index fa9323e18dc3..000000000000
--- a/Doc/library/sndhdr.rst
+++ /dev/null
@@ -1,104 +0,0 @@
-:mod:`sndhdr` --- Determine type of sound file
-==============================================
-
-.. module:: sndhdr
-   :synopsis: Determine type of a sound file.
-   :deprecated:
-
-.. sectionauthor:: Fred L. Drake, Jr. <fdrake at acm.org>
-.. Based on comments in the module source file.
-
-**Source code:** :source:`Lib/sndhdr.py`
-
-.. index::
-   single: A-LAW
-   single: u-LAW
-
-.. deprecated-removed:: 3.11 3.13
-   The :mod:`sndhdr` module is deprecated
-   (see :pep:`PEP 594 <594#sndhdr>` for details and alternatives).
-
---------------
-
-The :mod:`sndhdr` provides utility functions which attempt to determine the type
-of sound data which is in a file.  When these functions are able to determine
-what type of sound data is stored in a file, they return a
-:func:`~collections.namedtuple`, containing five attributes: (``filetype``,
-``framerate``, ``nchannels``, ``nframes``, ``sampwidth``). The value for *type*
-indicates the data type and will be one of the strings ``'aifc'``, ``'aiff'``,
-``'au'``, ``'hcom'``, ``'sndr'``, ``'sndt'``, ``'voc'``, ``'wav'``, ``'8svx'``,
-``'sb'``, ``'ub'``, or ``'ul'``.  The *sampling_rate* will be either the actual
-value or ``0`` if unknown or difficult to decode.  Similarly, *channels* will be
-either the number of channels or ``0`` if it cannot be determined or if the
-value is difficult to decode.  The value for *frames* will be either the number
-of frames or ``-1``.  The last item in the tuple, *bits_per_sample*, will either
-be the sample size in bits or ``'A'`` for A-LAW or ``'U'`` for u-LAW.
-
-
-.. function:: what(filename)
-
-   Determines the type of sound data stored in the file *filename* using
-   :func:`whathdr`.  If it succeeds, returns a namedtuple as described above, otherwise
-   ``None`` is returned.
-
-   .. versionchanged:: 3.5
-      Result changed from a tuple to a namedtuple.
-
-
-.. function:: whathdr(filename)
-
-   Determines the type of sound data stored in a file based on the file  header.
-   The name of the file is given by *filename*.  This function returns a namedtuple as
-   described above on success, or ``None``.
-
-   .. versionchanged:: 3.5
-      Result changed from a tuple to a namedtuple.
-
-The following sound header types are recognized, as listed below with the return value
-from :func:`whathdr`: and :func:`what`:
-
-+------------+------------------------------------+
-| Value      | Sound header format                |
-+============+====================================+
-| ``'aifc'`` | Compressed Audio Interchange Files |
-+------------+------------------------------------+
-| ``'aiff'`` | Audio Interchange Files            |
-+------------+------------------------------------+
-| ``'au'``   | Au Files                           |
-+------------+------------------------------------+
-| ``'hcom'`` | HCOM Files                         |
-+------------+------------------------------------+
-| ``'sndt'`` | Sndtool Sound Files                |
-+------------+------------------------------------+
-| ``'voc'``  | Creative Labs Audio Files          |
-+------------+------------------------------------+
-| ``'wav'``  | Waveform Audio File Format Files   |
-+------------+------------------------------------+
-| ``'8svx'`` | 8-Bit Sampled Voice Files          |
-+------------+------------------------------------+
-| ``'sb'``   | Signed Byte Audio Data Files       |
-+------------+------------------------------------+
-| ``'ub'``   | UB Files                           |
-+------------+------------------------------------+
-| ``'ul'``   | uLAW Audio Files                   |
-+------------+------------------------------------+
-
-.. data:: tests
-
-   A list of functions performing the individual tests.  Each function takes two
-   arguments: the byte-stream and an open file-like object. When :func:`what` is
-   called with a byte-stream, the file-like object will be ``None``.
-
-   The test function should return a string describing the image type if the test
-   succeeded, or ``None`` if it failed.
-
-Example:
-
-.. code-block:: pycon
-
-   >>> import sndhdr
-   >>> imghdr.what('bass.wav')
-   'wav'
-   >>> imghdr.whathdr('bass.wav')
-   'wav'
-
diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst
index a96d042f8374..cad4e9c50b69 100644
--- a/Doc/library/superseded.rst
+++ b/Doc/library/superseded.rst
@@ -22,7 +22,6 @@ backwards compatibility. They have been superseded by other modules.
    optparse.rst
    ossaudiodev.rst
    pipes.rst
-   sndhdr.rst
    spwd.rst
    sunau.rst
    uu.rst
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index d024b85059a8..4ad68463e0b5 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -1733,7 +1733,7 @@ Modules
   +---------------------+---------------------+---------------------+---------------------+---------------------+
   | :mod:`aifc`         | :mod:`chunk`        | :mod:`msilib`       | :mod:`pipes`        | :mod:`!telnetlib`   |
   +---------------------+---------------------+---------------------+---------------------+---------------------+
-  | :mod:`audioop`      | :mod:`crypt`        | :mod:`nis`          | :mod:`sndhdr`       | :mod:`uu`           |
+  | :mod:`audioop`      | :mod:`crypt`        | :mod:`nis`          | :mod:`!sndhdr`      | :mod:`uu`           |
   +---------------------+---------------------+---------------------+---------------------+---------------------+
   | :mod:`!cgi`         | :mod:`imghdr`       | :mod:`nntplib`      | :mod:`spwd`         | :mod:`xdrlib`       |
   +---------------------+---------------------+---------------------+---------------------+---------------------+
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 417da22ee045..50e758327b28 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -816,7 +816,7 @@ Modules (see :pep:`594`):
 * :mod:`nntplib`
 * :mod:`ossaudiodev`
 * :mod:`pipes`
-* :mod:`sndhdr`
+* :mod:`!sndhdr`
 * :mod:`spwd`
 * :mod:`sunau`
 * :mod:`!telnetlib`
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 4bd9a73e7aa7..1d4a4ca9614c 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -147,6 +147,12 @@ Removed
 
   (Contributed by Victor Stinner in :gh:`104773`.)
 
+* :pep:`594`: Remove the :mod:`!sndhdr` module, deprecated in Python 3.11: use
+  the projects `filetype <https://pypi.org/project/filetype/>`_, `puremagic
+  <https://pypi.org/project/puremagic/>`_, or `python-magic
+  <https://pypi.org/project/python-magic/>`_ instead.
+  (Contributed by Victor Stinner in :gh:`104773`.)
+
 
 Porting to Python 3.13
 ======================
diff --git a/Lib/sndhdr.py b/Lib/sndhdr.py
deleted file mode 100644
index 45def9ad16d3..000000000000
--- a/Lib/sndhdr.py
+++ /dev/null
@@ -1,271 +0,0 @@
-"""Routines to help recognizing sound files.
-
-Function whathdr() recognizes various types of sound file headers.
-It understands almost all headers that SOX can decode.
-
-The return tuple contains the following items, in this order:
-- file type (as SOX understands it)
-- sampling rate (0 if unknown or hard to decode)
-- number of channels (0 if unknown or hard to decode)
-- number of frames in the file (-1 if unknown or hard to decode)
-- number of bits/sample, or 'U' for U-LAW, or 'A' for A-LAW
-
-If the file doesn't have a recognizable type, it returns None.
-If the file can't be opened, OSError is raised.
-
-To compute the total time, divide the number of frames by the
-sampling rate (a frame contains a sample for each channel).
-
-Function what() calls whathdr().  (It used to also use some
-heuristics for raw data, but this doesn't work very well.)
-
-Finally, the function test() is a simple main program that calls
-what() for all files mentioned on the argument list.  For directory
-arguments it calls what() for all files in that directory.  Default
-argument is "." (testing all files in the current directory).  The
-option -r tells it to recurse down directories found inside
-explicitly given directories.
-"""
-
-import warnings
-
-warnings._deprecated(__name__, remove=(3, 13))
-
-# The file structure is top-down except that the test program and its
-# subroutine come last.
-
-__all__ = ['what', 'whathdr']
-
-from collections import namedtuple
-
-SndHeaders = namedtuple('SndHeaders',
-                        'filetype framerate nchannels nframes sampwidth')
-
-SndHeaders.filetype.__doc__ = ("""The value for type indicates the data type
-and will be one of the strings 'aifc', 'aiff', 'au','hcom',
-'sndr', 'sndt', 'voc', 'wav', '8svx', 'sb', 'ub', or 'ul'.""")
-SndHeaders.framerate.__doc__ = ("""The sampling_rate will be either the actual
-value or 0 if unknown or difficult to decode.""")
-SndHeaders.nchannels.__doc__ = ("""The number of channels or 0 if it cannot be
-determined or if the value is difficult to decode.""")
-SndHeaders.nframes.__doc__ = ("""The value for frames will be either the number
-of frames or -1.""")
-SndHeaders.sampwidth.__doc__ = ("""Either the sample size in bits or
-'A' for A-LAW or 'U' for u-LAW.""")
-
-def what(filename):
-    """Guess the type of a sound file."""
-    res = whathdr(filename)
-    return res
-
-
-def whathdr(filename):
-    """Recognize sound headers."""
-    with open(filename, 'rb') as f:
-        h = f.read(512)
-        for tf in tests:
-            res = tf(h, f)
-            if res:
-                return SndHeaders(*res)
-        return None
-
-
-#-----------------------------------#
-# Subroutines per sound header type #
-#-----------------------------------#
-
-tests = []
-
-def test_aifc(h, f):
-    """AIFC and AIFF files"""
-    with warnings.catch_warnings():
-        warnings.simplefilter('ignore', category=DeprecationWarning)
-        import aifc
-    if not h.startswith(b'FORM'):
-        return None
-    if h[8:12] == b'AIFC':
-        fmt = 'aifc'
-    elif h[8:12] == b'AIFF':
-        fmt = 'aiff'
-    else:
-        return None
-    f.seek(0)
-    try:
-        a = aifc.open(f, 'r')
-    except (EOFError, aifc.Error):
-        return None
-    return (fmt, a.getframerate(), a.getnchannels(),
-            a.getnframes(), 8 * a.getsampwidth())
-
-tests.append(test_aifc)
-
-
-def test_au(h, f):
-    """AU and SND files"""
-    if h.startswith(b'.snd'):
-        func = get_long_be
-    elif h[:4] in (b'\0ds.', b'dns.'):
-        func = get_long_le
-    else:
-        return None
-    filetype = 'au'
-    hdr_size = func(h[4:8])
-    data_size = func(h[8:12])
-    encoding = func(h[12:16])
-    rate = func(h[16:20])
-    nchannels = func(h[20:24])
-    sample_size = 1 # default
-    if encoding == 1:
-        sample_bits = 'U'
-    elif encoding == 2:
-        sample_bits = 8
-    elif encoding == 3:
-        sample_bits = 16
-        sample_size = 2
-    else:
-        sample_bits = '?'
-    frame_size = sample_size * nchannels
-    if frame_size:
-        nframe = data_size / frame_size
-    else:
-        nframe = -1
-    return filetype, rate, nchannels, nframe, sample_bits
-
-tests.append(test_au)
-
-
-def test_hcom(h, f):
-    """HCOM file"""
-    if h[65:69] != b'FSSD' or h[128:132] != b'HCOM':
-        return None
-    divisor = get_long_be(h[144:148])
-    if divisor:
-        rate = 22050 / divisor
-    else:
-        rate = 0
-    return 'hcom', rate, 1, -1, 8
-
-tests.append(test_hcom)
-
-
-def test_voc(h, f):
-    """VOC file"""
-    if not h.startswith(b'Creative Voice File\032'):
-        return None
-    sbseek = get_short_le(h[20:22])
-    rate = 0
-    if 0 <= sbseek < 500 and h[sbseek] == 1:
-        ratecode = 256 - h[sbseek+4]
-        if ratecode:
-            rate = int(1000000.0 / ratecode)
-    return 'voc', rate, 1, -1, 8
-
-tests.append(test_voc)
-
-
-def test_wav(h, f):
-    """WAV file"""
-    import wave
-    # 'RIFF' <len> 'WAVE' 'fmt ' <len>
-    if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ':
-        return None
-    f.seek(0)
-    try:
-        w = wave.open(f, 'r')
-    except (EOFError, wave.Error):
-        return None
-    return ('wav', w.getframerate(), w.getnchannels(),
-                   w.getnframes(), 8*w.getsampwidth())
-
-tests.append(test_wav)
-
-
-def test_8svx(h, f):
-    """8SVX file"""
-    if not h.startswith(b'FORM') or h[8:12] != b'8SVX':
-        return None
-    # Should decode it to get #channels -- assume always 1
-    return '8svx', 0, 1, 0, 8
-
-tests.append(test_8svx)
-
-
-def test_sndt(h, f):
-    """SNDT file"""
-    if h.startswith(b'SOUND'):
-        nsamples = get_long_le(h[8:12])
-        rate = get_short_le(h[20:22])
-        return 'sndt', rate, 1, nsamples, 8
-
-tests.append(test_sndt)
-
-
-def test_sndr(h, f):
-    """SNDR file"""
-    if h.startswith(b'\0\0'):
-        rate = get_short_le(h[2:4])
-        if 4000 <= rate <= 25000:
-            return 'sndr', rate, 1, -1, 8
-
-tests.append(test_sndr)
-
-
-#-------------------------------------------#
-# Subroutines to extract numbers from bytes #
-#-------------------------------------------#
-
-def get_long_be(b):
-    return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
-
-def get_long_le(b):
-    return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]
-
-def get_short_be(b):
-    return (b[0] << 8) | b[1]
-
-def get_short_le(b):
-    return (b[1] << 8) | b[0]
-
-
-#--------------------#
-# Small test program #
-#--------------------#
-
-def test():
-    import sys
-    recursive = 0
-    if sys.argv[1:] and sys.argv[1] == '-r':
-        del sys.argv[1:2]
-        recursive = 1
-    try:
-        if sys.argv[1:]:
-            testall(sys.argv[1:], recursive, 1)
-        else:
-            testall(['.'], recursive, 1)
-    except KeyboardInterrupt:
-        sys.stderr.write('\n[Interrupted]\n')
-        sys.exit(1)
-
-def testall(list, recursive, toplevel):
-    import sys
-    import os
-    for filename in list:
-        if os.path.isdir(filename):
-            print(filename + '/:', end=' ')
-            if recursive or toplevel:
-                print('recursing down:')
-                import glob
-                names = glob.glob(os.path.join(glob.escape(filename), '*'))
-                testall(names, recursive, 0)
-            else:
-                print('*** directory (use -r) ***')
-        else:
-            print(filename + ':', end=' ')
-            sys.stdout.flush()
-            try:
-                print(what(filename))
-            except OSError:
-                print('*** not found ***')
-
-if __name__ == '__main__':
-    test()
diff --git a/Lib/test/sndhdrdata/README b/Lib/test/sndhdrdata/README
deleted file mode 100644
index b2cb664615b9..000000000000
--- a/Lib/test/sndhdrdata/README
+++ /dev/null
@@ -1,5 +0,0 @@
-Sound file samples used by Lib/test/test_sndhdr.py and generated using the
-following commands:
-
-   dd if=/dev/zero of=sndhdr.raw bs=20 count=1
-   sox -s -2 -c 2 -r 44100 sndhdr.raw sndhdr.<format>
diff --git a/Lib/test/sndhdrdata/sndhdr.8svx b/Lib/test/sndhdrdata/sndhdr.8svx
deleted file mode 100644
index 8cd6cde5e09f..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.8svx and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.aifc b/Lib/test/sndhdrdata/sndhdr.aifc
deleted file mode 100644
index 8aae4e730bda..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.aifc and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.aiff b/Lib/test/sndhdrdata/sndhdr.aiff
deleted file mode 100644
index 8c279a762f1c..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.aiff and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.au b/Lib/test/sndhdrdata/sndhdr.au
deleted file mode 100644
index 67c9e8fdd995..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.au and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.hcom b/Lib/test/sndhdrdata/sndhdr.hcom
deleted file mode 100644
index debb02d9dd93..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.hcom and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.sndt b/Lib/test/sndhdrdata/sndhdr.sndt
deleted file mode 100644
index e1ca9cb185d1..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.sndt and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.voc b/Lib/test/sndhdrdata/sndhdr.voc
deleted file mode 100644
index 53a91fd1eae3..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.voc and /dev/null differ
diff --git a/Lib/test/sndhdrdata/sndhdr.wav b/Lib/test/sndhdrdata/sndhdr.wav
deleted file mode 100644
index 0dca36739cde..000000000000
Binary files a/Lib/test/sndhdrdata/sndhdr.wav and /dev/null differ
diff --git a/Lib/test/test_sndhdr.py b/Lib/test/test_sndhdr.py
deleted file mode 100644
index 4d97437f9072..000000000000
--- a/Lib/test/test_sndhdr.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import pickle
-import unittest
-from test.support import findfile
-from test.support import warnings_helper
-
-sndhdr = warnings_helper.import_deprecated("sndhdr")
-
-
-class TestFormats(unittest.TestCase):
-    def test_data(self):
-        for filename, expected in (
-            ('sndhdr.8svx', ('8svx', 0, 1, 0, 8)),
-            ('sndhdr.aifc', ('aifc', 44100, 2, 5, 16)),
-            ('sndhdr.aiff', ('aiff', 44100, 2, 5, 16)),
-            ('sndhdr.au', ('au', 44100, 2, 5.0, 16)),
-            ('sndhdr.hcom', ('hcom', 22050.0, 1, -1, 8)),
-            ('sndhdr.sndt', ('sndt', 44100, 1, 5, 8)),
-            ('sndhdr.voc', ('voc', 0, 1, -1, 8)),
-            ('sndhdr.wav', ('wav', 44100, 2, 5, 16)),
-        ):
-            filename = findfile(filename, subdir="sndhdrdata")
-            what = sndhdr.what(filename)
-            self.assertNotEqual(what, None, filename)
-            self.assertSequenceEqual(what, expected)
-            self.assertEqual(what.filetype, expected[0])
-            self.assertEqual(what.framerate, expected[1])
-            self.assertEqual(what.nchannels, expected[2])
-            self.assertEqual(what.nframes, expected[3])
-            self.assertEqual(what.sampwidth, expected[4])
-
-    def test_pickleable(self):
-        filename = findfile('sndhdr.aifc', subdir="sndhdrdata")
-        what = sndhdr.what(filename)
-        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
-            dump = pickle.dumps(what, proto)
-            self.assertEqual(pickle.loads(dump), what)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 091df3e4c0b8..033fdf98f1f7 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2119,7 +2119,6 @@ TESTSUBDIRS=	idlelib/idle_test \
 		test/imghdrdata \
 		test/leakers \
 		test/libregrtest \
-		test/sndhdrdata \
 		test/subprocessdata \
 		test/support \
 		test/support/_hypothesis_stubs \
diff --git a/Misc/NEWS.d/next/Library/2023-05-23-01-37-40.gh-issue-104773.8c-GsG.rst b/Misc/NEWS.d/next/Library/2023-05-23-01-37-40.gh-issue-104773.8c-GsG.rst
new file mode 100644
index 000000000000..228916158534
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-05-23-01-37-40.gh-issue-104773.8c-GsG.rst
@@ -0,0 +1,2 @@
+:pep:`594`: Remove the :mod:`!sndhdr` module, deprecated in Python 3.11.
+Patch by Victor Stinner.
diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h
index f5defe8923b2..245c3a1645f3 100644
--- a/Python/stdlib_module_names.h
+++ b/Python/stdlib_module_names.h
@@ -236,7 +236,6 @@ static const char* _Py_stdlib_module_names[] = {
 "signal",
 "site",
 "smtplib",
-"sndhdr",
 "socket",
 "socketserver",
 "spwd",



More information about the Python-checkins mailing list