diff --git a/pyproject.toml b/pyproject.toml index 08dbf69f..7836d3f2 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,9 @@ lint.select = ["ALL"] "docs/source/*.ipynb" = [ "E402", # Module level import not at top of cell in doc notebooks ] +"*" = [ + "ANN401" # typing.Any type hint for *args and **kwargs +] [tool.pytest.ini_options] diff --git a/src/osekit/audio_backend/audio_file_manager.py b/src/osekit/audio_backend/audio_file_manager.py index da0bbdbb..8b18c448 100644 --- a/src/osekit/audio_backend/audio_file_manager.py +++ b/src/osekit/audio_backend/audio_file_manager.py @@ -85,9 +85,6 @@ def read( """ _, frames, _ = self.info(path) - if stop is None: - stop = frames - if stop is None: stop = frames diff --git a/tests/conftest.py b/tests/conftest.py index 70663b2b..9180c581 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,11 +7,9 @@ from pathlib import Path from unittest.mock import MagicMock -import numpy as np import pandas as pd import pytest import soundfile as sf -from pandas import Timestamp from osekit import config from osekit.audio_backend.soundfile_backend import SoundFileBackend @@ -19,7 +17,6 @@ TIMESTAMP_FORMAT_EXPORTED_FILES_LOCALIZED, TIMESTAMP_FORMAT_EXPORTED_FILES_UNLOCALIZED, ) -from osekit.core.audio_data import AudioData from osekit.core.audio_file import AudioFile from osekit.utils.audio import generate_sample_audio @@ -110,7 +107,7 @@ def patch_filehandlers( if "allow_log_write_to_file" in request.keywords: return - def disabled_filewrite(self: any, record: any) -> None: + def disabled_filewrite(self: typing.Any, record: typing.Any) -> None: """Prevent the logger from actually writing files.""" monkeypatch.setattr(logging.FileHandler, "emit", disabled_filewrite) @@ -167,55 +164,6 @@ def mock_open(self: SoundFileBackend, path: Path) -> None: return opened_files -@pytest.fixture -def patch_audio_data(monkeypatch: pytest.MonkeyPatch) -> None: - original_init = AudioData.__init__ - original_get_raw_value = AudioData.get_raw_value - original_length = AudioData.length - - def mocked_init( - self: AudioData, - *args: list, - mocked_value: list[float] | np.ndarray | None = None, - **kwargs: dict, - ) -> None: - defaults = { - "begin": Timestamp("2000-01-01 00:00:00"), - "end": Timestamp("2000-01-01 00:00:01"), - "sample_rate": 48000, - } - for key, value in defaults.items(): - if key not in kwargs: - kwargs.update(**{key: value}) - - original_init(self, *args, **kwargs) - if mocked_value is not None: - self.mocked_value = mocked_value - if type(mocked_value) is list or len(mocked_value.shape) == 1: - self.mocked_value = np.array(self.mocked_value).reshape( - len(mocked_value), - 1, - ) - - def mocked_length(self: AudioData) -> int: - if hasattr(self, "mocked_value"): - return len(self.mocked_value) - return original_length.fget(self) - - def mocked_get_raw_value(self: AudioData) -> np.ndarray: - if hasattr(self, "mocked_value"): - return self.mocked_value - return original_get_raw_value(self) - - monkeypatch.setattr(AudioData, "__init__", mocked_init) - monkeypatch.setattr(AudioData, "length", property(mocked_length)) - monkeypatch.setattr( - AudioData, - "get_raw_value", - mocked_get_raw_value, - ) - - @pytest.fixture(autouse=True) def restore_config() -> typing.Generator: resample_quality_settings = {**config.resample_quality_settings} diff --git a/tests/helpers/audio.py b/tests/helpers/audio.py new file mode 100644 index 00000000..726126a8 --- /dev/null +++ b/tests/helpers/audio.py @@ -0,0 +1,39 @@ +import typing + +import numpy as np +from pandas import Timestamp + +from osekit.core.audio_data import AudioData + + +class MockedAudioData(AudioData): + def __init__( + self, + mocked_value: list[float] | np.ndarray, + *args: typing.Any, + **kwargs: typing.Any, + ) -> None: + defaults = { + "begin": Timestamp("2000-01-01 00:00:00"), + "end": Timestamp("2000-01-01 00:00:01"), + "sample_rate": 48000, + } + for key, value in defaults.items(): + if key not in kwargs: + kwargs.update(**{key: value}) + + super().__init__(*args, **kwargs) + if mocked_value is not None: + self.mocked_value = mocked_value + if type(mocked_value) is list or len(mocked_value.shape) == 1: + self.mocked_value = np.array(self.mocked_value).reshape( + len(mocked_value), + 1, + ) + + @property + def length(self) -> int: + return len(self.mocked_value) + + def get_raw_value(self) -> np.ndarray: + return self.mocked_value diff --git a/tests/test_audio.py b/tests/test_audio.py index c15422b9..37374847 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -26,15 +26,16 @@ from osekit.core.instrument import Instrument from osekit.utils import audio from osekit.utils.audio import Normalization, generate_sample_audio, normalize +from tests.helpers.audio import MockedAudioData -def test_patch_audio_data(patch_audio_data: None) -> None: +def test_mocked_audio_data() -> None: mocked_value = [1.0, 2.0, 3.0] - audio_data = AudioData( - mocked_value=mocked_value, # Type: ignore # Unexpected argument + audio_data = MockedAudioData( + mocked_value=mocked_value, ) assert np.array_equal( - audio_data.mocked_value[:, 0], # Type: ignore # Unresolved attribute + audio_data.mocked_value[:, 0], mocked_value, ) @@ -1534,10 +1535,16 @@ def test_audio_dataset_from_folder_errors_warnings( assert all(f in caplog.text for f in corrupted_audio_files) -def test_audio_dataset_instrument(patch_audio_data: None) -> None: +def test_audio_dataset_instrument() -> None: ad = [ - AudioData(mocked_value=[1, 2, 3], instrument=Instrument(end_to_end_db=150.0)), - AudioData(mocked_value=[4, 5, 6], instrument=Instrument(end_to_end_db=150.0)), + MockedAudioData( + mocked_value=[1, 2, 3], + instrument=Instrument(end_to_end_db=150.0), + ), + MockedAudioData( + mocked_value=[4, 5, 6], + instrument=Instrument(end_to_end_db=150.0), + ), ] ads = AudioDataset(data=ad) @@ -1807,9 +1814,8 @@ def test_split_data( assert subsubdata.normalization == subdata.normalization -def test_split_data_normalization_pass(patch_audio_data: None) -> None: - ad = AudioData() - ad.mocked_value = [1, 2, 3] +def test_split_data_normalization_pass() -> None: + ad = MockedAudioData(mocked_value=[1, 2, 3]) original_normalization_values = ad.get_normalization_values() assert all(original_normalization_values.values()) @@ -1926,9 +1932,9 @@ def test_split_data_frames( assert np.array_equal(ad.get_value()[:, 0], expected_data) -def test_split_frames_errors(patch_audio_data: None) -> None: +def test_split_frames_errors() -> None: mocked_value = [1, 2, 3] - ad = AudioData(mocked_value=mocked_value) + ad = MockedAudioData(mocked_value=mocked_value) error_msgs = [ "Start_frame must be greater than or equal to 0.", "Stop_frame must be lower than the length of the data.", diff --git a/tests/test_spectro.py b/tests/test_spectro.py index 55c30913..9fc38ae3 100644 --- a/tests/test_spectro.py +++ b/tests/test_spectro.py @@ -32,6 +32,7 @@ from osekit.core.spectro_file import SpectroFile from osekit.core.spectro_item import SpectroItem from osekit.utils.audio import Normalization, generate_sample_audio +from tests.helpers.audio import MockedAudioData from tests.helpers.dummy import DummyFile @@ -84,9 +85,9 @@ def test_spectrogram_shape( assert spectro.nb_bytes == nb_points * 8 -def test_spectro_data_sx_dtype(patch_audio_data: None) -> None: +def test_spectro_data_sx_dtype() -> None: sd = SpectroData.from_audio_data( - data=AudioData(mocked_value=[0.0 for _ in range(48_000)]), + data=MockedAudioData(mocked_value=[0.0 for _ in range(48_000)]), fft=ShortTimeFFT(hamming(1024), 512, 48_000), ) assert sd.sx_dtype is complex @@ -97,11 +98,10 @@ def test_spectro_data_sx_dtype(patch_audio_data: None) -> None: def test_spectro_data_db_ref( - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: sd = SpectroData.from_audio_data( - data=AudioData(mocked_value=[0.0 for _ in range(48_000)]), + data=MockedAudioData(mocked_value=[0.0 for _ in range(48_000)]), fft=ShortTimeFFT(hamming(1024), 512, 48_000), ) @@ -991,7 +991,6 @@ def test_link_audio_data( ], ) def test_link_audio_dataset( - patch_audio_data: None, audio_data_params: list[ tuple[Timestamp, Timestamp, float] ], # begin, end, sample_rate @@ -1005,7 +1004,7 @@ def test_link_audio_dataset( ) -> None: ads_origin = AudioDataset( [ - AudioData( + MockedAudioData( begin=sd_params[0], end=sd_params[1], sample_rate=sd_params[2], @@ -1017,7 +1016,7 @@ def test_link_audio_dataset( ads_dest = AudioDataset( [ - AudioData( + MockedAudioData( begin=ad_params[0], end=ad_params[1], sample_rate=ad_params[2], @@ -1349,11 +1348,11 @@ def test_ltas(audio_files: tuple[list[AudioFile], None], tmp_path: Path) -> None assert np.array_equal(ltas_ds.data[0].get_value(), ltas_ds2.data[0].get_value()) -def test_ltas_dataset(patch_audio_data: None) -> None: +def test_ltas_dataset() -> None: ads = AudioDataset( [ - AudioData(mocked_value=[0] * 48_000), - AudioData(mocked_value=[0] * 48_000), + MockedAudioData(mocked_value=[0] * 48_000), + MockedAudioData(mocked_value=[0] * 48_000), ], ) @@ -1504,11 +1503,10 @@ def patch_collect() -> None: def test_spectrodataset_scale( tmp_path: Path, - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: - ad = AudioData( - mocked_value=np.linspace(0.0, 1.0, 1000), # Type: ignore # Unexpected argument + ad = MockedAudioData( + mocked_value=np.linspace(0.0, 1.0, 1000), ) fft = ShortTimeFFT(win=hamming(512), hop=128, fs=ad.sample_rate) @@ -1543,9 +1541,9 @@ def mock_empty_method(*args: list[...], **kwargs: dict) -> None: ltas_ds.save_all(tmp_path, tmp_path) -def test_spectro_dataset_properties_propagate(patch_audio_data: None) -> None: - ad1, ad2 = AudioData( - mocked_value=[0.0 for _ in range(48_000)], # Type: ignore # Unexpected argument +def test_spectro_dataset_properties_propagate() -> None: + ad1, ad2 = MockedAudioData( + mocked_value=[0.0 for _ in range(48_000)], ).split() fft = ShortTimeFFT(win=hamming(512), hop=128, fs=ad1.sample_rate) @@ -1567,11 +1565,10 @@ def test_spectro_dataset_properties_propagate(patch_audio_data: None) -> None: def test_spectro_dataset_folder_moves_files( - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: - ad1, ad2 = AudioData( - mocked_value=[0.0 for _ in range(48_000)], # Type: ignore # Unexpected argument + ad1, ad2 = MockedAudioData( + mocked_value=[0.0 for _ in range(48_000)], ).split() fft = ShortTimeFFT(win=hamming(512), hop=128, fs=ad1.sample_rate) @@ -1648,9 +1645,10 @@ def patch_sd_from_dict(dictionary: dict, *args: list, **kwargs: dict) -> str: def test_spectro_multichannel_audio_file( monkeypatch: pytest.MonkeyPatch, - patch_audio_data: pytest.MonkeyPatch, ) -> None: - ad = AudioData(mocked_value=np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])) + ad = MockedAudioData( + mocked_value=np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]), + ) sft = ShortTimeFFT(win=hamming(512), hop=128, fs=48_000) @@ -1706,7 +1704,6 @@ def mocked_ad_init( def test_spectro_write_welch( - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: created_dir_calls = [] @@ -1732,7 +1729,7 @@ def mocked_savez(**kwargs: dict) -> None: monkeypatch.setattr(np, "savez", mocked_savez) sd = SpectroData.from_audio_data( - data=AudioData(mocked_value=[0.0 for _ in range(48_000)]), + data=MockedAudioData(mocked_value=[0.0 for _ in range(48_000)]), fft=ShortTimeFFT(win=hamming(512), hop=128, fs=48_000), ) @@ -1748,7 +1745,6 @@ def mocked_savez(**kwargs: dict) -> None: def test_spectro_get_value_from_items_errors( - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: def mocked_read_metadata(self: SpectroFile, *args: list, **kwargs: dict) -> None: @@ -1795,7 +1791,6 @@ def mocked_read_metadata(self: SpectroFile, *args: list, **kwargs: dict) -> None def test_spectro_populated_duration( - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: ad_sentinel = object() @@ -1820,7 +1815,7 @@ def test_spectro_populated_duration( ) assert ( SpectroData.from_audio_data( - data=AudioData(mocked_value=[0, 1, 2]), + data=MockedAudioData(mocked_value=[0, 1, 2]), fft=sft, ).populated_duration == bd_sentinel @@ -1830,7 +1825,7 @@ def test_spectro_populated_duration( monkeypatch.setattr(SpectroData, "files", property(lambda _: None)) assert ( SpectroData.from_audio_data( - data=AudioData(mocked_value=[0, 1, 2]), + data=MockedAudioData(mocked_value=[0, 1, 2]), fft=sft, ).populated_duration == ad_sentinel @@ -1838,7 +1833,7 @@ def test_spectro_populated_duration( # SD With no files or audio data return 0. sd = SpectroData.from_audio_data( - data=AudioData(mocked_value=[0, 1, 2]), + data=MockedAudioData(mocked_value=[0, 1, 2]), fft=sft, ) sd.audio_data = None @@ -1846,7 +1841,6 @@ def test_spectro_populated_duration( def test_spectro_get_db_value( - patch_audio_data: None, monkeypatch: pytest.MonkeyPatch, ) -> None: get_value_calls = [] @@ -1862,7 +1856,7 @@ def mock_to_db(self: SpectroData | None, sx: np.ndarray) -> np.ndarray: monkeypatch.setattr(SpectroData, "_to_db", mock_to_db) sd = SpectroData.from_audio_data( - data=AudioData(mocked_value=[0, 1, 2]), + data=MockedAudioData(mocked_value=[0, 1, 2]), fft=ShortTimeFFT(hamming(512), hop=128, fs=48_000), )