From 8e4a24224800970c541587dc1545f261305b4803 Mon Sep 17 00:00:00 2001 From: Hagen Wierstorf Date: Tue, 18 Jun 2024 17:01:33 +0200 Subject: [PATCH] ruff format, Python 3.10 as default (#58) --- .flake8 | 10 -- .github/workflows/doc.yml | 2 +- .github/workflows/linter.yml | 4 +- .github/workflows/publish.yml | 4 +- .github/workflows/test.yml | 6 +- .pre-commit-config.yaml | 6 +- CONTRIBUTING.rst | 5 +- audresample/__init__.py | 1 + audresample/core/api.py | 60 ++++++------ audresample/core/define.py | 11 ++- audresample/core/lib.py | 35 +++---- docs/conf.py | 73 +++++++------- docs/usage.rst | 39 ++------ pyproject.toml | 20 ++-- setup.py | 41 ++++---- tests/test-assets/generate_test_assets.py | 9 +- tests/test_remix.py | 53 +++++------ tests/test_resample.py | 110 +++++++++++++++------- 18 files changed, 251 insertions(+), 238 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index cb2ed23..0000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -exclude = - .eggs, - build, -extend-ignore = - # math, https://github.com/PyCQA/pycodestyle/issues/513 - W503, -per-file-ignores = - # ignore unused imports - __init__.py: F401 diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 5bcf7d4..dd2edb8 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - python-version: [ '3.8' ] + python-version: [ '3.10' ] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 2f8baef..1f5613e 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.10' - name: Install pre-commit hooks run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a3545e6..ebdb6b2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,10 +29,10 @@ jobs: with: fetch-depth: 2 - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.10' - name: Install build dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aa64fca..a1c1c7f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,12 +13,12 @@ jobs: strategy: matrix: os: [ ubuntu-20.04, macOS-latest, windows-latest ] - python-version: [ '3.8' ] + python-version: [ '3.10' ] include: - os: ubuntu-latest - python-version: '3.9' + python-version: '3.8' - os: ubuntu-latest - python-version: '3.10' + python-version: '3.9' - os: ubuntu-latest python-version: '3.11' - os: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 92a17ee..119cb13 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,13 +9,15 @@ # # default_language_version: - python: python3.8 + python: python3.10 repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.276 + rev: v0.1.8 hooks: - id: ruff + args: [ --fix ] + - id: ruff-format - repo: https://github.com/codespell-project/codespell rev: v2.2.4 hooks: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 03792d5..b7780fd 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -41,7 +41,7 @@ Coding Convention ----------------- We follow the PEP8_ convention for Python code -and check for correct syntax with ruff_. +and use ruff_ as a linter and code formatter. In addition, we check for common spelling errors with codespell_. Both tools and possible exceptions @@ -61,7 +61,8 @@ You can also install ruff_ and codespell_ and call it directly:: pip install ruff codespell # consider system wide installation - ruff check . + ruff check --fix . # lint all Python files, and fix any fixable errors + ruff format . # format code of all Python files codespell It can be restricted to specific folders:: diff --git a/audresample/__init__.py b/audresample/__init__.py index 1cfbd4f..57a02ab 100644 --- a/audresample/__init__.py +++ b/audresample/__init__.py @@ -11,6 +11,7 @@ # Dynamically get the version of the installed module try: import importlib.metadata + __version__ = importlib.metadata.version(__name__) except Exception: # pragma: no cover importlib = None # pragma: no cover diff --git a/audresample/core/api.py b/audresample/core/api.py index b1b20c7..4b31e70 100644 --- a/audresample/core/api.py +++ b/audresample/core/api.py @@ -9,23 +9,22 @@ def _check_signal( - signal: np.ndarray, + signal: np.ndarray, ) -> np.ndarray: r"""Ensure float32 and two dimensions.""" if signal.ndim > 2: raise RuntimeError( - f"Input signal must have 1 or 2 dimension, " - f"got {signal.ndim}." + f"Input signal must have 1 or 2 dimension, " f"got {signal.ndim}." ) return np.atleast_2d(signal) def am_fm_synth( - num_samples: int, - num_channels: int, - sampling_rate: int, - *, - dtype=np.float32, + num_samples: int, + num_channels: int, + sampling_rate: int, + *, + dtype=np.float32, ) -> np.ndarray: r"""Synthesizes an AM/FM signal. @@ -56,19 +55,19 @@ def am_fm_synth( # No reinitialisation (to get true stereo) for t in range(num_samples): sig[idx, t] = g * np.cos(ph_fm) - sig[idx, t] *= ((1 - g_am) + g_am * np.square(np.cos(ph_am))) + sig[idx, t] *= (1 - g_am) + g_am * np.square(np.cos(ph_am)) ph_am += omega_am / 2 ph_fm += omega0_car + omega_dev * np.cos(omega_mod * t) return sig def remix( - signal: np.ndarray, - channels: typing.Union[int, typing.Sequence[int]] = None, - mixdown: bool = False, - *, - upmix: str = None, - always_copy: bool = False, + signal: np.ndarray, + channels: typing.Union[int, typing.Sequence[int]] = None, + mixdown: bool = False, + *, + upmix: str = None, + always_copy: bool = False, ) -> np.ndarray: r"""Remix a signal. @@ -136,14 +135,14 @@ def remix( f"You can use the 'upmix' argument " f"to increase available channels." ) - elif upmix == 'zeros': + elif upmix == "zeros": signal_ex = np.zeros( (max_channel, signal.shape[1]), dtype=signal.dtype, ) - signal_ex[:signal.shape[0], :] = signal + signal_ex[: signal.shape[0], :] = signal signal = signal_ex - elif upmix == 'repeat': + elif upmix == "repeat": # Upmix signal with [0, 1, 0, 1, ...] num_repetitions = int(np.ceil(max_channel / signal.shape[0])) signal_ex = np.concatenate([signal] * num_repetitions, axis=0) @@ -166,12 +165,12 @@ def remix( def resample( - signal: np.ndarray, - original_rate: int, - target_rate: int, - *, - quality: define.ResampleQuality = config.DEFAULT_RESAMPLE_QUALITY, - always_copy: bool = False, + signal: np.ndarray, + original_rate: int, + target_rate: int, + *, + quality: define.ResampleQuality = config.DEFAULT_RESAMPLE_QUALITY, + always_copy: bool = False, ) -> np.ndarray: r"""Resample signal to a new sampling rate. @@ -198,8 +197,7 @@ def resample( # We can only handle float32 signals if signal.dtype != np.float32: raise RuntimeError( - 'Input signal must be of type float32/single, ' - f'got {signal.dtype}.' + "Input signal must be of type float32/single, " f"got {signal.dtype}." ) if original_rate == target_rate or signal.size == 0: @@ -209,7 +207,9 @@ def resample( return signal converter_config = lib.init_converter_config( - float(original_rate), float(target_rate), ord(quality), + float(original_rate), + float(target_rate), + ord(quality), ) channels = signal.shape[0] @@ -224,7 +224,11 @@ def resample( signal_in_p = x.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) signal_out_p = y.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) lib.audresample_oneshot( - converter_config, signal_in_p, num_in, signal_out_p, num_out, + converter_config, + signal_in_p, + num_in, + signal_out_p, + num_out, ) return target diff --git a/audresample/core/define.py b/audresample/core/define.py index 1a304d9..2b0563d 100644 --- a/audresample/core/define.py +++ b/audresample/core/define.py @@ -1,7 +1,8 @@ class ResampleQuality: r"""Quality levels for resampling.""" - QUICK = 'q' - LOW = 'l' - MEDIUM = 'm' - HIGH = 'h' - VERY_HIGH = 'v' + + QUICK = "q" + LOW = "l" + MEDIUM = "m" + HIGH = "h" + VERY_HIGH = "v" diff --git a/audresample/core/lib.py b/audresample/core/lib.py index c189e14..1242226 100644 --- a/audresample/core/lib.py +++ b/audresample/core/lib.py @@ -32,34 +32,34 @@ def platform_name(): system = platform.system() machine = platform.machine().lower() - if system == 'Linux': # pragma: no cover - system = 'manylinux_2_17' - elif system == 'Windows': # pragma: no cover - system = 'win' - elif system == 'Darwin': # pragma: no cover - if machine == 'x86_64': - system = 'macosx_10_4' + if system == "Linux": # pragma: no cover + system = "manylinux_2_17" + elif system == "Windows": # pragma: no cover + system = "win" + elif system == "Darwin": # pragma: no cover + if machine == "x86_64": + system = "macosx_10_4" else: - system = 'macosx_11_0' + system = "macosx_11_0" else: # pragma: no cover - raise RuntimeError(f'Unsupported platform {system}') + raise RuntimeError(f"Unsupported platform {system}") - return f'{system}_{machine}' + return f"{system}_{machine}" # load library root = os.path.dirname(os.path.realpath(__file__)) -bin_path = os.path.join(root, 'bin') +bin_path = os.path.join(root, "bin") plat_name = platform_name() -if 'linux' in plat_name: # pragma: no cover - library = 'libaudresample.so' -elif 'macos' in plat_name: # pragma: no cover - library = 'libaudresample.dylib' -elif 'win' in plat_name: # pragma: no cover - library = 'audresample.dll' +if "linux" in plat_name: # pragma: no cover + library = "libaudresample.so" +elif "macos" in plat_name: # pragma: no cover + library = "libaudresample.dylib" +elif "win" in plat_name: # pragma: no cover + library = "audresample.dll" lib_path = os.path.join(bin_path, plat_name, library) @@ -68,6 +68,7 @@ def platform_name(): # resample + class ConverterConfig(ctypes.Structure): # noqa: D101 _fields_ = [ ("srIn", ctypes.c_double), diff --git a/docs/conf.py b/docs/conf.py index 8795f21..ccd397d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,58 +7,58 @@ import audeer -config = toml.load(audeer.path('..', 'pyproject.toml')) +config = toml.load(audeer.path("..", "pyproject.toml")) # Project ----------------------------------------------------------------- -project = config['project']['name'] -author = ', '.join(author['name'] for author in config['project']['authors']) -copyright = f'2020-{date.today().year} audEERING GmbH' +project = config["project"]["name"] +author = ", ".join(author["name"] for author in config["project"]["authors"]) +copyright = f"2020-{date.today().year} audEERING GmbH" version = audeer.git_repo_version() -title = 'Documentation' +title = "Documentation" # General ----------------------------------------------------------------- -master_doc = 'index' +master_doc = "index" extensions = [] -source_suffix = '.rst' +source_suffix = ".rst" exclude_patterns = [ - 'build', - 'tests', - 'Thumbs.db', - '.DS_Store', - 'api-src', + "build", + "tests", + "Thumbs.db", + ".DS_Store", + "api-src", ] -templates_path = ['_templates'] +templates_path = ["_templates"] pygments_style = None extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', # support for Google-style docstrings - 'sphinx.ext.autosummary', - 'sphinx_autodoc_typehints', - 'sphinx.ext.viewcode', - 'sphinx.ext.intersphinx', - 'sphinx_copybutton', - 'jupyter_sphinx', + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", # support for Google-style docstrings + "sphinx.ext.autosummary", + "sphinx_autodoc_typehints", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_copybutton", + "jupyter_sphinx", ] napoleon_use_ivar = True # List of class attributes autodoc_inherit_docstrings = False # disable docstring inheritance autodoc_default_options = { - 'members': True, + "members": True, } intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', None), - 'numpy': ('https://docs.scipy.org/doc/numpy/', None), + "python": ("https://docs.python.org/3/", None), + "numpy": ("https://docs.scipy.org/doc/numpy/", None), } # Disable Gitlab as we need to sign in -linkcheck_ignore = ['https://gitlab.audeering.com'] +linkcheck_ignore = ["https://gitlab.audeering.com"] # Ignore package dependencies during building the docs autodoc_mock_imports = [ - 'numpy', + "numpy", ] # Disable auto-generation of TOC entries in the API @@ -68,28 +68,27 @@ # HTML -------------------------------------------------------------------- -html_theme = 'sphinx_audeering_theme' +html_theme = "sphinx_audeering_theme" html_theme_options = { - 'display_version': True, - 'logo_only': False, - 'footer_links': False, + "display_version": True, + "logo_only": False, + "footer_links": False, } html_context = { - 'display_github': True, + "display_github": True, } html_title = title html_css_files = [ - 'css/custom.css', + "css/custom.css", ] # Copy API (sub-)module RST files to docs/api/ folder --------------------- -audeer.rmdir('api') -audeer.mkdir('api') -api_src_files = audeer.list_file_names('api-src') +audeer.rmdir("api") +audeer.mkdir("api") +api_src_files = audeer.list_file_names("api-src") api_dst_files = [ - audeer.path('api', os.path.basename(src_file)) - for src_file in api_src_files + audeer.path("api", os.path.basename(src_file)) for src_file in api_src_files ] for src_file, dst_file in zip(api_src_files, api_dst_files): shutil.copyfile(src_file, dst_file) diff --git a/docs/usage.rst b/docs/usage.rst index 9b305fd..65018a2 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -40,11 +40,7 @@ using :meth:`audresample.am_fm_synth`. sampling_rate = 16000 num_samples = 16500 num_channels = 3 - signal = audresample.am_fm_synth( - num_samples, - num_channels, - sampling_rate, - ) + signal = audresample.am_fm_synth(num_samples, num_channels, sampling_rate) signal.shape .. jupyter-execute:: @@ -56,10 +52,7 @@ Mixdown signal to mono. .. jupyter-execute:: - mixed = audresample.remix( - signal, - mixdown=True, - ) + mixed = audresample.remix(signal, mixdown=True) mixed.shape .. jupyter-execute:: @@ -71,10 +64,7 @@ Select the last channel. .. jupyter-execute:: - mixed = audresample.remix( - signal, - channels=-1, - ) + mixed = audresample.remix(signal, channels=-1) mixed.shape .. jupyter-execute:: @@ -86,10 +76,7 @@ Select the second and first channel. .. jupyter-execute:: - mixed = audresample.remix( - signal, - channels=[1, 0], - ) + mixed = audresample.remix(signal, channels=[1, 0]) mixed.shape .. jupyter-execute:: @@ -101,11 +88,7 @@ Mixdown first and second channel to mono. .. jupyter-execute:: - mixed = audresample.remix( - signal, - channels=[0, 1], - mixdown=True, - ) + mixed = audresample.remix(signal, channels=[0, 1], mixdown=True) mixed.shape .. jupyter-execute:: @@ -127,11 +110,7 @@ using :meth:`audresample.am_fm_synth`. original_rate = 48000 num_original = 16000 num_channels = 2 - signal = audresample.am_fm_synth( - num_original, - num_channels, - original_rate, - ) + signal = audresample.am_fm_synth(num_original, num_channels, original_rate) signal.shape .. jupyter-execute:: @@ -145,11 +124,7 @@ Resample signal to 8kHz using .. jupyter-execute:: target_rate = 8000 - resampled = audresample.resample( - signal, - original_rate, - target_rate, - ) + resampled = audresample.resample(signal, original_rate, target_rate) resampled.shape .. jupyter-execute:: diff --git a/pyproject.toml b/pyproject.toml index 295f399..3467211 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,12 @@ addopts = ''' # ----- ruff -------------------------------------------------------------- # [tool.ruff] +cache-dir = '.cache/ruff' + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] select = [ 'D', # pydocstyle 'E', # pycodestyle errors @@ -96,11 +102,7 @@ extend-ignore = [ 'D107', # Missing docstring in `__init__` ] -line-length = 79 - -cache-dir = '.cache/ruff' - -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] '__init__.py' = [ 'F401', # * imported but unused ] @@ -110,7 +112,7 @@ cache-dir = '.cache/ruff' # # Check correct order/syntax of import statements # -[tool.ruff.isort] +[tool.ruff.lint.isort] # All from imports have their own line, e.g. # @@ -147,7 +149,7 @@ section-order = [ 'first-party', 'local-folder', ] -[tool.ruff.isort.sections] +[tool.ruff.lint.isort.sections] 'audeering' = [ 'audb', 'audbackend', @@ -172,7 +174,7 @@ section-order = [ # # Check variable/class names follow PEP8 naming convention # -[tool.ruff.pep8-naming] +[tool.ruff.lint.pep8-naming] ignore-names = [ 'config', # allow lowercase class name 'test_*', # allow uppercase name when testing a class @@ -183,7 +185,7 @@ ignore-names = [ # # Check docstrings follow selected convention # -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = 'google' diff --git a/setup.py b/setup.py index 2d55f7c..635f86c 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ # Include only the platform specific pre-compiled binary. # For sources see https://github.com/audeering/audresamplelib + def platform_name(): r"""Platform name used in pip tag. @@ -36,43 +37,41 @@ def platform_name(): system = platform.system() machine = platform.machine().lower() - if system == 'Linux': # pragma: no cover - system = 'manylinux_2_17' - elif system == 'Windows': # pragma: no cover - system = 'win' - elif system == 'Darwin': # pragma: no cover - if machine == 'x86_64': - system = 'macosx_10_4' + if system == "Linux": # pragma: no cover + system = "manylinux_2_17" + elif system == "Windows": # pragma: no cover + system = "win" + elif system == "Darwin": # pragma: no cover + if machine == "x86_64": + system = "macosx_10_4" else: - system = 'macosx_11_0' + system = "macosx_11_0" else: # pragma: no cover - raise RuntimeError(f'Unsupported platform {system}') + raise RuntimeError(f"Unsupported platform {system}") - return f'{system}_{machine}' + return f"{system}_{machine}" # Look for enrionment variable PLAT_NAME # to be able to enforce # different platform names # in CI on the same runner -plat_name = os.environ.get('PLAT_NAME', platform_name()) +plat_name = os.environ.get("PLAT_NAME", platform_name()) -if 'linux' in plat_name: - library = '*.so' -elif 'macos' in plat_name: - library = '*.dylib' -elif 'win' in plat_name: - library = '*.dll' +if "linux" in plat_name: + library = "*.so" +elif "macos" in plat_name: + library = "*.dylib" +elif "win" in plat_name: + library = "*.dll" setuptools.setup( - package_data={ - 'audresample.core': [f'bin/{plat_name}/{library}'] - }, + package_data={"audresample.core": [f"bin/{plat_name}/{library}"]}, # python -m build --wheel # does no longer accept the --plat-name option, # but we can set the desired platform as an option # (https://stackoverflow.com/a/75010995) options={ - 'bdist_wheel': {'plat_name': plat_name}, + "bdist_wheel": {"plat_name": plat_name}, }, ) diff --git a/tests/test-assets/generate_test_assets.py b/tests/test-assets/generate_test_assets.py index 01aba12..1a35c18 100644 --- a/tests/test-assets/generate_test_assets.py +++ b/tests/test-assets/generate_test_assets.py @@ -10,16 +10,13 @@ for n_ch in channel_list: for sr_in in sr_list: - wav_original = f'original__sr_{sr_in}__channels_{n_ch}.wav' - x = audsp.utils.am_fm_synth( - dur, num_channels=n_ch, sampling_rate=sr_in - ) + wav_original = f"original__sr_{sr_in}__channels_{n_ch}.wav" + x = audsp.utils.am_fm_synth(dur, num_channels=n_ch, sampling_rate=sr_in) af.write(wav_original, x, sr_in) for sr_out in sr_list: if sr_out == sr_in: continue - wav = f'resampled__sr-in_{sr_in}__sr-out_{sr_out}__channels' \ - f'_{n_ch}.wav' + wav = f"resampled__sr-in_{sr_in}__sr-out_{sr_out}__channels" f"_{n_ch}.wav" tfm = sox.Transformer() tfm.rate(sr_out) tfm.build(wav_original, wav) diff --git a/tests/test_remix.py b/tests/test_remix.py index c5b7532..6477c58 100644 --- a/tests/test_remix.py +++ b/tests/test_remix.py @@ -31,7 +31,7 @@ def mixdown(signal): @pytest.mark.parametrize( - 'signal, channels, mixdown, upmix, always_copy, expect', + "signal, channels, mixdown, upmix, always_copy, expect", [ # empty signal ( @@ -62,7 +62,7 @@ def mixdown(signal): np.zeros((1, 0), dtype=np.float32), 1, False, - 'repeat', + "repeat", False, np.zeros((1, 0), dtype=np.float32), ), @@ -70,7 +70,7 @@ def mixdown(signal): np.zeros((1, 0), dtype=np.float32), 1, False, - 'zeros', + "zeros", False, np.zeros((1, 0), dtype=np.float32), ), @@ -78,7 +78,7 @@ def mixdown(signal): np.zeros((1, 0), dtype=np.float32), [0, 2], False, - 'zeros', + "zeros", False, np.zeros((2, 0), dtype=np.float32), ), @@ -127,7 +127,7 @@ def mixdown(signal): np.ones((1, 16000), np.float32), 0, True, - 'zeros', + "zeros", False, np.ones((1, 16000), dtype=np.float32), ), @@ -135,7 +135,7 @@ def mixdown(signal): np.ones((1, 16000), np.float32), 1, True, - 'repeat', + "repeat", False, np.ones((1, 16000), dtype=np.float32), ), @@ -143,7 +143,7 @@ def mixdown(signal): np.ones((1, 16000), np.float32), 1, True, - 'zeros', + "zeros", False, np.zeros((1, 16000), dtype=np.float32), ), @@ -151,7 +151,7 @@ def mixdown(signal): np.ones((1, 16000), np.float32), -2, True, - 'zeros', + "zeros", False, np.ones((1, 16000), dtype=np.float32), ), @@ -159,7 +159,7 @@ def mixdown(signal): np.ones((1, 16000), np.float32), [0, 2], False, - 'zeros', + "zeros", False, np.concatenate( [ @@ -172,7 +172,7 @@ def mixdown(signal): np.ones((1, 16000), np.float32), [0, 2], True, - 'zeros', + "zeros", False, 0.5 * np.ones((1, 16000), dtype=np.float32), ), @@ -229,7 +229,7 @@ def mixdown(signal): set_ones(np.zeros((3, 16000), np.float32), 0), [3, 0, 0], False, - 'zeros', + "zeros", False, set_ones(np.zeros((3, 16000), np.float32), [1, 2]), ), @@ -237,7 +237,7 @@ def mixdown(signal): set_ones(np.zeros((3, 16000), np.float32), 0), [3, 0, 0], False, - 'repeat', + "repeat", False, np.ones((3, 16000), np.float32), ), @@ -245,7 +245,7 @@ def mixdown(signal): set_ones(np.zeros((3, 16000), np.float32), 0), [-6, 0, 0], False, - 'repeat', + "repeat", False, np.ones((3, 16000), np.float32), ), @@ -259,12 +259,12 @@ def mixdown(signal): mixdown(audresample.am_fm_synth(16000, 2, 16000)), ), ( - np.zeros((3, 16000), dtype='float64'), + np.zeros((3, 16000), dtype="float64"), None, True, None, False, - np.zeros((1, 16000), dtype='float64'), + np.zeros((1, 16000), dtype="float64"), ), ( audresample.am_fm_synth(16000, 3, 16000), @@ -325,20 +325,20 @@ def mixdown(signal): np.zeros((2, 16000)), 2, False, - 'fancy', + "fancy", False, None, marks=pytest.mark.xfail(raises=ValueError), ), - ] + ], ) def test_remix_signal( - signal, - channels, - mixdown, - upmix, - always_copy, - expect, + signal, + channels, + mixdown, + upmix, + always_copy, + expect, ): result = audresample.remix( signal, @@ -349,12 +349,7 @@ def test_remix_signal( ) assert signal.dtype == expect.dtype np.testing.assert_equal(result, expect) - if ( - signal.size > 0 - and channels is None - and not mixdown - and signal.ndim == 2 - ): + if signal.size > 0 and channels is None and not mixdown and signal.ndim == 2: if always_copy: assert id(signal) != id(result) else: diff --git a/tests/test_resample.py b/tests/test_resample.py index 2c42ec9..6cd1280 100644 --- a/tests/test_resample.py +++ b/tests/test_resample.py @@ -9,94 +9,137 @@ import audresample -resampled_wavs = glob('tests/test-assets/resampled__*.wav') +resampled_wavs = glob("tests/test-assets/resampled__*.wav") @pytest.mark.parametrize( - 'signal, original_rate, target_rate, always_copy', + "signal, original_rate, target_rate, always_copy", [ # empty signal ( - np.zeros(0, dtype=np.float32), 16000, 8000, False, + np.zeros(0, dtype=np.float32), + 16000, + 8000, + False, ), ( - np.zeros((1, 0), dtype=np.float32), 16000, 8000, False, + np.zeros((1, 0), dtype=np.float32), + 16000, + 8000, + False, ), # original_rate == target_rate without copy ( - np.zeros((16000,), dtype=np.float32), 16000, 16000, False, + np.zeros((16000,), dtype=np.float32), + 16000, + 16000, + False, ), ( - np.zeros((16000, 1), dtype=np.float32), 16000, 16000, False, + np.zeros((16000, 1), dtype=np.float32), + 16000, + 16000, + False, ), ( - np.zeros((16000, 3), dtype=np.float32), 16000, 16000, False, + np.zeros((16000, 3), dtype=np.float32), + 16000, + 16000, + False, ), # original_rate == target_rate with copy ( - np.zeros((16000,), dtype=np.float32), 16000, 16000, True, + np.zeros((16000,), dtype=np.float32), + 16000, + 16000, + True, ), ( - np.zeros((16000, 1), dtype=np.float32), 16000, 16000, True, + np.zeros((16000, 1), dtype=np.float32), + 16000, + 16000, + True, ), ( - np.zeros((16000, 3), dtype=np.float32), 16000, 16000, True, + np.zeros((16000, 3), dtype=np.float32), + 16000, + 16000, + True, ), # original_rate != target_rate ( - np.zeros((16000,), dtype=np.float32), 16000, 8000, False, + np.zeros((16000,), dtype=np.float32), + 16000, + 8000, + False, ), ( - np.zeros((16000, 1), dtype=np.float32), 16000, 8000, False, + np.zeros((16000, 1), dtype=np.float32), + 16000, + 8000, + False, ), ( - np.zeros((16000, 3), dtype=np.float32), 16000, 8000, False, + np.zeros((16000, 3), dtype=np.float32), + 16000, + 8000, + False, ), ( - audresample.am_fm_synth(16000, 2, 16000), 16000, 8000, False, + audresample.am_fm_synth(16000, 2, 16000), + 16000, + 8000, + False, ), # wrong input shape pytest.param( - np.zeros((16000, 2, 3)), 16000, 16000, False, + np.zeros((16000, 2, 3)), + 16000, + 16000, + False, marks=pytest.mark.xfail(raises=RuntimeError), ), # wrong input datatype pytest.param( - np.zeros((16000,), dtype=np.float64), 16000, 16000, False, + np.zeros((16000,), dtype=np.float64), + 16000, + 16000, + False, marks=pytest.mark.xfail(raises=RuntimeError), ), pytest.param( - np.zeros((16000,), dtype=int), 16000, 16000, False, + np.zeros((16000,), dtype=int), + 16000, + 16000, + False, marks=pytest.mark.xfail(raises=RuntimeError), ), - ] + ], ) def test_resample_signal(signal, original_rate, target_rate, always_copy): y = audresample.resample( - signal, original_rate, target_rate, always_copy=always_copy, + signal, + original_rate, + target_rate, + always_copy=always_copy, ) assert y.ndim == 2 assert y.dtype == np.float32 - if original_rate == target_rate and \ - signal.dtype == np.float32 and \ - signal.ndim == 2: + if original_rate == target_rate and signal.dtype == np.float32 and signal.ndim == 2: if always_copy: assert id(signal) != id(y) else: assert id(signal) == id(y) -@pytest.mark.parametrize( - 'resampled_wav', resampled_wavs -) +@pytest.mark.parametrize("resampled_wav", resampled_wavs) def test_resample_file(resampled_wav): + identifiers = path.splitext(path.basename(resampled_wav))[0].split("__") - identifiers = path.splitext(path.basename(resampled_wav))[0].split('__') - - sr_in = int(identifiers[1].split('_')[-1]) - sr_out = int(identifiers[2].split('_')[-1]) - n_ch = int(identifiers[3].split('_')[-1]) - wav_in = f'tests/test-assets/original__sr_{sr_in}__channels_{n_ch}.wav' + sr_in = int(identifiers[1].split("_")[-1]) + sr_out = int(identifiers[2].split("_")[-1]) + n_ch = int(identifiers[3].split("_")[-1]) + wav_in = f"tests/test-assets/original__sr_{sr_in}__channels_{n_ch}.wav" x, sr = af.read(wav_in, always_2d=True) assert sr == sr_in @@ -105,7 +148,10 @@ def test_resample_file(resampled_wav): assert sr == sr_out y = audresample.resample( - x, sr_in, sr_out, quality=audresample.define.ResampleQuality.HIGH, + x, + sr_in, + sr_out, + quality=audresample.define.ResampleQuality.HIGH, ) assert y.shape[0] == n_ch