From fff3930ab9a3c931b4a58efbd816fcd476b4df19 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 00:30:29 +0000 Subject: [PATCH 1/9] refactor: Add get_pyaudio_v2 with test --- speech_recognition/__init__.py | 12 ++++++++++++ tests/test_microphone.py | 9 +++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/test_microphone.py diff --git a/speech_recognition/__init__.py b/speech_recognition/__init__.py index 253ab0fe..add8e6df 100644 --- a/speech_recognition/__init__.py +++ b/speech_recognition/__init__.py @@ -21,6 +21,7 @@ import time import uuid import wave +from typing import TYPE_CHECKING from urllib.error import HTTPError, URLError from urllib.parse import urlencode from urllib.request import Request, urlopen @@ -34,6 +35,9 @@ WaitTimeoutError, ) +if TYPE_CHECKING: + import pyaudio + __author__ = "Anthony Zhang (Uberi)" __version__ = "3.11.0" __license__ = "BSD" @@ -105,6 +109,14 @@ def get_pyaudio(): raise AttributeError("Could not find PyAudio; check installation") return pyaudio + @staticmethod + def get_pyaudio_v2() -> pyaudio.PyAudio: + try: + import pyaudio + except ImportError: + raise AttributeError("Could not find PyAudio; check installation") + return pyaudio.PyAudio() + @staticmethod def list_microphone_names(): """ diff --git a/tests/test_microphone.py b/tests/test_microphone.py new file mode 100644 index 00000000..af812b84 --- /dev/null +++ b/tests/test_microphone.py @@ -0,0 +1,9 @@ +from unittest.mock import patch + +from speech_recognition import Microphone + +class TestPyAudioWrapper: + @patch("pyaudio.PyAudio") + def test_get_pyaudio(self, PyAudio): + assert Microphone.get_pyaudio_v2() == PyAudio.return_value + PyAudio.assert_called_once_with() From 28673164cbaeef2b6ee20088f4806f6a5d67d516 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 12:50:30 +0000 Subject: [PATCH 2/9] refactor: Replace with v2 --- speech_recognition/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/speech_recognition/__init__.py b/speech_recognition/__init__.py index add8e6df..b9981250 100644 --- a/speech_recognition/__init__.py +++ b/speech_recognition/__init__.py @@ -124,7 +124,7 @@ def list_microphone_names(): The index of each microphone's name in the returned list is the same as its device index when creating a ``Microphone`` instance - if you want to use the microphone at index 3 in the returned list, use ``Microphone(device_index=3)``. """ - audio = Microphone.get_pyaudio().PyAudio() + audio = Microphone.get_pyaudio_v2() try: result = [] for i in range(audio.get_device_count()): From ff0b8f44e2f0ccdfe409c8a945c436f00d21b2d0 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 13:06:09 +0000 Subject: [PATCH 3/9] refactor: Extract list_microphone_names() --- speech_recognition/__init__.py | 29 --------------------------- speech_recognition/microphone.py | 34 ++++++++++++++++++++++++++++++++ tests/test_microphone.py | 5 +++-- 3 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 speech_recognition/microphone.py diff --git a/speech_recognition/__init__.py b/speech_recognition/__init__.py index b9981250..f9515174 100644 --- a/speech_recognition/__init__.py +++ b/speech_recognition/__init__.py @@ -21,7 +21,6 @@ import time import uuid import wave -from typing import TYPE_CHECKING from urllib.error import HTTPError, URLError from urllib.parse import urlencode from urllib.request import Request, urlopen @@ -35,9 +34,6 @@ WaitTimeoutError, ) -if TYPE_CHECKING: - import pyaudio - __author__ = "Anthony Zhang (Uberi)" __version__ = "3.11.0" __license__ = "BSD" @@ -109,31 +105,6 @@ def get_pyaudio(): raise AttributeError("Could not find PyAudio; check installation") return pyaudio - @staticmethod - def get_pyaudio_v2() -> pyaudio.PyAudio: - try: - import pyaudio - except ImportError: - raise AttributeError("Could not find PyAudio; check installation") - return pyaudio.PyAudio() - - @staticmethod - def list_microphone_names(): - """ - Returns a list of the names of all available microphones. For microphones where the name can't be retrieved, the list entry contains ``None`` instead. - - The index of each microphone's name in the returned list is the same as its device index when creating a ``Microphone`` instance - if you want to use the microphone at index 3 in the returned list, use ``Microphone(device_index=3)``. - """ - audio = Microphone.get_pyaudio_v2() - try: - result = [] - for i in range(audio.get_device_count()): - device_info = audio.get_device_info_by_index(i) - result.append(device_info.get("name")) - finally: - audio.terminate() - return result - @staticmethod def list_working_microphones(): """ diff --git a/speech_recognition/microphone.py b/speech_recognition/microphone.py new file mode 100644 index 00000000..6b7206eb --- /dev/null +++ b/speech_recognition/microphone.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import audioop +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import pyaudio + + +class PyAudioWrapper: + @staticmethod + def get_pyaudio_v2() -> pyaudio.PyAudio: + try: + import pyaudio + except ImportError: + raise AttributeError("Could not find PyAudio; check installation") + return pyaudio.PyAudio() + + @staticmethod + def list_microphone_names(): + """ + Returns a list of the names of all available microphones. For microphones where the name can't be retrieved, the list entry contains ``None`` instead. + + The index of each microphone's name in the returned list is the same as its device index when creating a ``Microphone`` instance - if you want to use the microphone at index 3 in the returned list, use ``Microphone(device_index=3)``. + """ + audio = PyAudioWrapper.get_pyaudio_v2() + try: + result = [] + for i in range(audio.get_device_count()): + device_info = audio.get_device_info_by_index(i) + result.append(device_info.get("name")) + finally: + audio.terminate() + return result diff --git a/tests/test_microphone.py b/tests/test_microphone.py index af812b84..95f953ea 100644 --- a/tests/test_microphone.py +++ b/tests/test_microphone.py @@ -1,9 +1,10 @@ from unittest.mock import patch -from speech_recognition import Microphone +from speech_recognition.microphone import PyAudioWrapper + class TestPyAudioWrapper: @patch("pyaudio.PyAudio") def test_get_pyaudio(self, PyAudio): - assert Microphone.get_pyaudio_v2() == PyAudio.return_value + assert PyAudioWrapper.get_pyaudio_v2() == PyAudio.return_value PyAudio.assert_called_once_with() From 87db628722b9d8e667426ebff6aff839babe5f40 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 13:08:12 +0000 Subject: [PATCH 4/9] refactor: Rename --- speech_recognition/microphone.py | 4 ++-- tests/test_microphone.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/speech_recognition/microphone.py b/speech_recognition/microphone.py index 6b7206eb..ed4855da 100644 --- a/speech_recognition/microphone.py +++ b/speech_recognition/microphone.py @@ -9,7 +9,7 @@ class PyAudioWrapper: @staticmethod - def get_pyaudio_v2() -> pyaudio.PyAudio: + def get_pyaudio() -> pyaudio.PyAudio: try: import pyaudio except ImportError: @@ -23,7 +23,7 @@ def list_microphone_names(): The index of each microphone's name in the returned list is the same as its device index when creating a ``Microphone`` instance - if you want to use the microphone at index 3 in the returned list, use ``Microphone(device_index=3)``. """ - audio = PyAudioWrapper.get_pyaudio_v2() + audio = PyAudioWrapper.get_pyaudio() try: result = [] for i in range(audio.get_device_count()): diff --git a/tests/test_microphone.py b/tests/test_microphone.py index 95f953ea..ea399145 100644 --- a/tests/test_microphone.py +++ b/tests/test_microphone.py @@ -6,5 +6,5 @@ class TestPyAudioWrapper: @patch("pyaudio.PyAudio") def test_get_pyaudio(self, PyAudio): - assert PyAudioWrapper.get_pyaudio_v2() == PyAudio.return_value + assert PyAudioWrapper.get_pyaudio() == PyAudio.return_value PyAudio.assert_called_once_with() From affab62092539ce43660bca78d815eafc8b67c65 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 13:10:37 +0000 Subject: [PATCH 5/9] docs: PyAudioWrapper.get_pyaudio() --- speech_recognition/microphone.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/speech_recognition/microphone.py b/speech_recognition/microphone.py index ed4855da..1b50b302 100644 --- a/speech_recognition/microphone.py +++ b/speech_recognition/microphone.py @@ -10,10 +10,16 @@ class PyAudioWrapper: @staticmethod def get_pyaudio() -> pyaudio.PyAudio: + """Returns pyaudio.PyAudio instance. + + Checks pyaudio's installation, throws exceptions if pyaudio can't be found + """ try: import pyaudio except ImportError: - raise AttributeError("Could not find PyAudio; check installation") + raise AttributeError( + "Could not find PyAudio; Run `pip install SpeechRecognition[audio]`" + ) return pyaudio.PyAudio() @staticmethod From 1fb4c0c21dc9a99dd070e87deb7fcdcf949be713 Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 13:15:53 +0000 Subject: [PATCH 6/9] bugfix: Fix flake8 F401 --- speech_recognition/microphone.py | 1 - 1 file changed, 1 deletion(-) diff --git a/speech_recognition/microphone.py b/speech_recognition/microphone.py index 1b50b302..0e6079b4 100644 --- a/speech_recognition/microphone.py +++ b/speech_recognition/microphone.py @@ -1,6 +1,5 @@ from __future__ import annotations -import audioop from typing import TYPE_CHECKING if TYPE_CHECKING: From 5cc9ceedbcf6713cd82a2117d650c93bf715390e Mon Sep 17 00:00:00 2001 From: nikkie Date: Tue, 5 Nov 2024 23:58:16 +0000 Subject: [PATCH 7/9] bugfix: Skip PyAudio tests on WIndows --- tests/test_microphone.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_microphone.py b/tests/test_microphone.py index ea399145..e738a4f4 100644 --- a/tests/test_microphone.py +++ b/tests/test_microphone.py @@ -1,8 +1,12 @@ +import sys from unittest.mock import patch +import pytest + from speech_recognition.microphone import PyAudioWrapper +@pytest.mark.skip(sys.platform.startswith("win"), "skip on Windows") class TestPyAudioWrapper: @patch("pyaudio.PyAudio") def test_get_pyaudio(self, PyAudio): From ca695b0b8e888127a98f83352318610b4d23716e Mon Sep 17 00:00:00 2001 From: nikkie Date: Wed, 6 Nov 2024 09:20:04 +0000 Subject: [PATCH 8/9] bugfix: Fix typo (skipif) --- tests/test_microphone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_microphone.py b/tests/test_microphone.py index e738a4f4..1f659eef 100644 --- a/tests/test_microphone.py +++ b/tests/test_microphone.py @@ -6,7 +6,7 @@ from speech_recognition.microphone import PyAudioWrapper -@pytest.mark.skip(sys.platform.startswith("win"), "skip on Windows") +@pytest.mark.skipif(sys.platform.startswith("win"), "skip on Windows") class TestPyAudioWrapper: @patch("pyaudio.PyAudio") def test_get_pyaudio(self, PyAudio): From 2cdbd5a3deeee165f4c3aa2e5898e462030da14f Mon Sep 17 00:00:00 2001 From: nikkie Date: Wed, 6 Nov 2024 11:59:24 +0000 Subject: [PATCH 9/9] bugfix: reason is keyword parameter --- tests/test_microphone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_microphone.py b/tests/test_microphone.py index 1f659eef..b9e3d394 100644 --- a/tests/test_microphone.py +++ b/tests/test_microphone.py @@ -6,7 +6,7 @@ from speech_recognition.microphone import PyAudioWrapper -@pytest.mark.skipif(sys.platform.startswith("win"), "skip on Windows") +@pytest.mark.skipif(sys.platform.startswith("win"), reason="skip on Windows") class TestPyAudioWrapper: @patch("pyaudio.PyAudio") def test_get_pyaudio(self, PyAudio):