bpo-31985: Deprecate openfp in aifc, sunau, and wave (#4344)

The openfp functions of aifp, sunau, and wave had pointed to the open
function of each module since 1993 as a matter of backwards
compatibility. In the case of aifc.openfp, it was both undocumented
and untested. This change begins the formal deprecation of those
openfp functions, with their removal coming in 3.9.

This additionally adds a TODO in test_pyclbr around using aifc.openfp,
though it shouldn't be changed until removal in 3.9.
diff --git a/Lib/aifc.py b/Lib/aifc.py
index 49a456a..e51e8f8 100644
--- a/Lib/aifc.py
+++ b/Lib/aifc.py
@@ -915,7 +915,10 @@
     else:
         raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
 
-openfp = open # B/W compatibility
+def openfp(f, mode=None):
+    warnings.warn("aifc.openfp is deprecated since Python 3.7. "
+                  "Use aifc.open instead.", DeprecationWarning, stacklevel=2)
+    return open(f, mode=mode)
 
 if __name__ == '__main__':
     import sys
diff --git a/Lib/sndhdr.py b/Lib/sndhdr.py
index 7ecafb4..5943531 100644
--- a/Lib/sndhdr.py
+++ b/Lib/sndhdr.py
@@ -160,7 +160,7 @@
         return None
     f.seek(0)
     try:
-        w = wave.openfp(f, 'r')
+        w = wave.open(f, 'r')
     except (EOFError, wave.Error):
         return None
     return ('wav', w.getframerate(), w.getnchannels(),
diff --git a/Lib/sunau.py b/Lib/sunau.py
index 0473e9e..dbad3db 100644
--- a/Lib/sunau.py
+++ b/Lib/sunau.py
@@ -104,6 +104,7 @@
 """
 
 from collections import namedtuple
+import warnings
 
 _sunau_params = namedtuple('_sunau_params',
                            'nchannels sampwidth framerate nframes comptype compname')
@@ -522,4 +523,7 @@
     else:
         raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
 
-openfp = open
+def openfp(f, mode=None):
+    warnings.warn("sunau.openfp is deprecated since Python 3.7. "
+                  "Use sunau.open instead.", DeprecationWarning, stacklevel=2)
+    return open(f, mode=mode)
diff --git a/Lib/test/audiotests.py b/Lib/test/audiotests.py
index d3e8e9e..0dad017 100644
--- a/Lib/test/audiotests.py
+++ b/Lib/test/audiotests.py
@@ -1,6 +1,7 @@
 from test.support import findfile, TESTFN, unlink
 import array
 import io
+from unittest import mock
 import pickle
 
 
@@ -49,6 +50,17 @@
             self.assertEqual(pickle.loads(dump), params)
 
 
+class AudioMiscTests(AudioTests):
+
+    def test_openfp_deprecated(self):
+        arg = "arg"
+        mode = "mode"
+        with mock.patch(f"{self.module.__name__}.open") as mock_open, \
+             self.assertWarns(DeprecationWarning):
+            self.module.openfp(arg, mode=mode)
+            mock_open.assert_called_with(arg, mode=mode)
+
+
 class AudioWriteTests(AudioTests):
 
     def create_file(self, testfile):
diff --git a/Lib/test/test_aifc.py b/Lib/test/test_aifc.py
index a731a51..a064a32 100644
--- a/Lib/test/test_aifc.py
+++ b/Lib/test/test_aifc.py
@@ -7,6 +7,7 @@
 import sys
 import struct
 import aifc
+import warnings
 
 
 class AifcTest(audiotests.AudioWriteTests,
@@ -144,7 +145,9 @@
         frames = byteswap(frames, 2)
 
 
-class AifcMiscTest(audiotests.AudioTests, unittest.TestCase):
+class AifcMiscTest(audiotests.AudioMiscTests, unittest.TestCase):
+    module = aifc
+
     def test_skipunknown(self):
         #Issue 2245
         #This file contains chunk types aifc doesn't recognize.
diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py
index 238eb71..eaab591 100644
--- a/Lib/test/test_pyclbr.py
+++ b/Lib/test/test_pyclbr.py
@@ -223,6 +223,8 @@
         cm('random', ignore=('Random',))  # from _random import Random as CoreGenerator
         cm('cgi', ignore=('log',))      # set with = in module
         cm('pickle', ignore=('partial',))
+        # TODO(briancurtin): openfp is deprecated as of 3.7.
+        # Update this once it has been removed.
         cm('aifc', ignore=('openfp', '_aifc_params'))  # set with = in module
         cm('sre_parse', ignore=('dump', 'groups', 'pos')) # from sre_constants import *; property
         cm('pdb')
diff --git a/Lib/test/test_sunau.py b/Lib/test/test_sunau.py
index bc1f46c..966224b 100644
--- a/Lib/test/test_sunau.py
+++ b/Lib/test/test_sunau.py
@@ -117,5 +117,9 @@
         frames = byteswap(frames, 2)
 
 
+class SunauMiscTests(audiotests.AudioMiscTests, unittest.TestCase):
+    module = sunau
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py
index 8666f72..c5d2e02 100644
--- a/Lib/test/test_wave.py
+++ b/Lib/test/test_wave.py
@@ -103,7 +103,9 @@
         frames = byteswap(frames, 4)
 
 
-class MiscTestCase(unittest.TestCase):
+class MiscTestCase(audiotests.AudioMiscTests, unittest.TestCase):
+    module = wave
+
     def test__all__(self):
         blacklist = {'WAVE_FORMAT_PCM'}
         support.check__all__(self, wave, blacklist=blacklist)
diff --git a/Lib/wave.py b/Lib/wave.py
index f71f7e5..cf94d5a 100644
--- a/Lib/wave.py
+++ b/Lib/wave.py
@@ -87,6 +87,7 @@
 import sys
 from chunk import Chunk
 from collections import namedtuple
+import warnings
 
 _wave_params = namedtuple('_wave_params',
                      'nchannels sampwidth framerate nframes comptype compname')
@@ -502,4 +503,7 @@
     else:
         raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
 
-openfp = open # B/W compatibility
+def openfp(f, mode=None):
+    warnings.warn("wave.openfp is deprecated since Python 3.7. "
+                  "Use wave.open instead.", DeprecationWarning, stacklevel=2)
+    return open(f, mode=mode)