diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..cb2ed23 --- /dev/null +++ b/.flake8 @@ -0,0 +1,10 @@ +[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/flake8.yml b/.github/workflows/linter.yml similarity index 97% rename from .github/workflows/flake8.yml rename to .github/workflows/linter.yml index f383de9..2f8baef 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/linter.yml @@ -1,4 +1,4 @@ -name: Flake8 +name: Linter on: push: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68827d5..92a17ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,8 +7,18 @@ # $ pre-commit install # $ pre-commit run --all-files # +# +default_language_version: + python: python3.8 + repos: -- repo: https://github.com/pycqa/flake8 - rev: '5.0.4' + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.276 + hooks: + - id: ruff + - repo: https://github.com/codespell-project/codespell + rev: v2.2.4 hooks: - - id: flake8 + - id: codespell + additional_dependencies: + - tomli diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8520e24..762af9d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -32,7 +32,7 @@ Version 2.4.0 (2022-01-10) Version 2.3.0 (2021-12-16) -------------------------- -* Changed: udpate to ``audobject >0.6.1`` +* Changed: update to ``audobject >0.6.1`` Version 2.2.0 (2021-07-23) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 258a131..065672c 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -18,7 +18,7 @@ you should get the newest development version from Github_:: git clone https://github.com/audeering/opensmile-python/ cd opensmile-python - # Create virutal environment for this project + # Create virtual environment for this project # e.g. # virtualenv --python="python3" $HOME/.envs/opensmile-python # source $HOME/.envs/opensmile-python/bin/activate @@ -37,9 +37,11 @@ Coding Convention ----------------- We follow the PEP8_ convention for Python code -and check for correct syntax with flake8_. -Exceptions are defined under the ``[flake8]`` section -in :file:`setup.cfg`. +and check for correct syntax with ruff_. +In addition, +we check for common spelling errors with codespell_. +Both tools and possible exceptions +are defined in :file:`pyproject.toml`. The checks are executed in the CI using `pre-commit`_. You can enable those checks locally by executing:: @@ -48,22 +50,26 @@ You can enable those checks locally by executing:: pre-commit install pre-commit run --all-files -Afterwards flake8_ is executed +Afterwards ruff_ and codespell_ are executed every time you create a commit. -You can also install flake8_ +You can also install ruff_ and codespell_ and call it directly:: - pip install flake8 # consider system wide installation - flake8 + pip install ruff codespell # consider system wide installation + ruff check . + codespell It can be restricted to specific folders:: - flake8 audfoo/ tests/ + ruff check opensmile/ tests/ + codespell opensmile/ tests/ + +.. _codespell: https://github.com/codespell-project/codespell/ .. _PEP8: http://www.python.org/dev/peps/pep-0008/ -.. _flake8: https://flake8.pycqa.org/en/latest/index.html .. _pre-commit: https://pre-commit.com +.. _ruff: https://beta.ruff.rs Building the Documentation @@ -86,7 +92,7 @@ It is also possible to automatically check if all links are still valid:: python -m sphinx docs/ build/sphinx/html -b linkcheck -.. _Sphinx: http://sphinx-doc.org/ +.. _Sphinx: http://sphinx-doc.org Running the Tests @@ -101,7 +107,7 @@ To execute the tests, simply run:: python -m pytest -.. _pytest: https://pytest.org/ +.. _pytest: https://pytest.org Creating a New Release diff --git a/LICENSE b/LICENSE index fd9e2bd..d2eb082 100644 --- a/LICENSE +++ b/LICENSE @@ -138,7 +138,7 @@ In return for the above rights, you agree: captures the intent of the original language. -Commerical license options +Commercial license options ========================== For commercial and other licensing options, please contact us at audEERING diff --git a/README.rst b/README.rst index c037fa0..bd501f6 100644 --- a/README.rst +++ b/README.rst @@ -125,14 +125,14 @@ Please cite openSMILE in your publications by citing the following paper: .. |tests| image:: https://github.com/audeering/opensmile-python/workflows/Test/badge.svg :target: https://github.com/audeering/opensmile-python/actions?query=workflow%3ATest :alt: Test status -.. |coverage| image:: https://codecov.io/gh/audeering/opensmile-python/branch/master/graph/badge.svg?token=PUA9P2UJW1 +.. |coverage| image:: https://codecov.io/gh/audeering/opensmile-python/branch/main/graph/badge.svg?token=PUA9P2UJW1 :target: https://codecov.io/gh/audeering/opensmile-python :alt: code coverage .. |docs| image:: https://img.shields.io/pypi/v/opensmile?label=docs    :target: https://audeering.github.io/opensmile-python/    :alt: opensmile's documentation .. |license| image:: https://img.shields.io/badge/license-audEERING-red.svg -    :target: https://github.com/audeering/opensmile-python/blob/master/LICENSE +    :target: https://github.com/audeering/opensmile-python/blob/main/LICENSE    :alt: opensmile's audEERING license .. |python-versions| image:: https://img.shields.io/pypi/pyversions/opensmile.svg :target: https://pypi.org/project/opensmile/ diff --git a/docs/conf.py b/docs/conf.py index 9e51404..93ce221 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,20 +1,20 @@ -import configparser from datetime import date import os import shutil +import toml + import audeer -config = configparser.ConfigParser() -config.read(os.path.join('..', 'setup.cfg')) +config = toml.load(audeer.path('..', 'pyproject.toml')) # Project ----------------------------------------------------------------- -author = config['metadata']['author'] +project = config['project']['name'] +author = ', '.join(author['name'] for author in config['project']['authors']) copyright = f'2020-{date.today().year} audEERING GmbH' -project = config['metadata']['name'] version = audeer.git_repo_version() title = 'Documentation' diff --git a/docs/index.rst b/docs/index.rst index bb54c4b..517fced 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,3 @@ -.. documentation master file - .. include:: ../README.rst .. toctree:: diff --git a/docs/requirements.txt b/docs/requirements.txt index 62891cb..ef3c2c2 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,3 +5,4 @@ sphinx sphinx-audeering-theme >=1.2.1 sphinx-autodoc-typehints sphinx-copybutton +toml diff --git a/opensmile/__init__.py b/opensmile/__init__.py index e68d70b..4449d9d 100644 --- a/opensmile/__init__.py +++ b/opensmile/__init__.py @@ -1,11 +1,7 @@ from opensmile.core.config import config -from opensmile.core.define import ( - FeatureSet, - FeatureLevel, -) -from opensmile.core.smile import ( - Smile, -) +from opensmile.core.define import FeatureLevel +from opensmile.core.define import FeatureSet +from opensmile.core.smile import Smile __all__ = [] diff --git a/opensmile/core/config/compare/ComParE_2016.conf b/opensmile/core/config/compare/ComParE_2016.conf index 616bdc4..fc8d4c2 100644 --- a/opensmile/core/config/compare/ComParE_2016.conf +++ b/opensmile/core/config/compare/ComParE_2016.conf @@ -3,7 +3,7 @@ ///////// updated version of ComParE 2013 set, numerical fixes ////////////////// ///////// ////////////////// ///////// (c) 2014-2016 by audEERING, ////////////////// -///////// All rights reserverd. See file COPYING for details. ////////////////// +///////// All rights reserved. See file COPYING for details. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager] diff --git a/opensmile/core/config/compare/ComParE_2016_core.lld.conf.inc b/opensmile/core/config/compare/ComParE_2016_core.lld.conf.inc index 595c1e9..413a12d 100644 --- a/opensmile/core/config/compare/ComParE_2016_core.lld.conf.inc +++ b/opensmile/core/config/compare/ComParE_2016_core.lld.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for ComParE < ////////////////// ///////// ////////////////// ///////// (c) audEERING GmbH, ////////////////// -///////// All rights reserverd. ////////////////// +///////// All rights reserved. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager] diff --git a/opensmile/core/config/shared/standard_data_output.conf.inc b/opensmile/core/config/shared/standard_data_output.conf.inc index 4218543..167c268 100755 --- a/opensmile/core/config/shared/standard_data_output.conf.inc +++ b/opensmile/core/config/shared/standard_data_output.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for data output < ////////////////// ///////// ////////////////// ///////// (c) 2014-2016 audEERING, ////////////////// -///////// All rights reserverd. See file COPYING for details ////////////////// +///////// All rights reserved. See file COPYING for details ////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -12,7 +12,7 @@ for the batch extraction GUI, and supports LLD and Functionals (summaries) saving. - It requires the main extrator configuration file to provide the following + It requires the main extractor configuration file to provide the following data memory levels: lld, lld_de, and func */ diff --git a/opensmile/core/config/shared/standard_data_output_no_lld_de.conf.inc b/opensmile/core/config/shared/standard_data_output_no_lld_de.conf.inc index 392b1c9..e5629df 100644 --- a/opensmile/core/config/shared/standard_data_output_no_lld_de.conf.inc +++ b/opensmile/core/config/shared/standard_data_output_no_lld_de.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for data output < ////////////////// ///////// ////////////////// ///////// (c) 2014-2016 audEERING, ////////////////// -///////// All rights reserverd. See file COPYING for details ////////////////// +///////// All rights reserved. See file COPYING for details ////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -12,7 +12,7 @@ for the batch extraction GUI, and supports LLD and Functionals (summaries) saving. - It requires the main extrator configuration file to provide the following + It requires the main extractor configuration file to provide the following data memory levels: lld and func */ diff --git a/opensmile/core/config/shared/standard_external_data_output_multi.conf.inc b/opensmile/core/config/shared/standard_external_data_output_multi.conf.inc index 1feefa4..dae1ada 100644 --- a/opensmile/core/config/shared/standard_external_data_output_multi.conf.inc +++ b/opensmile/core/config/shared/standard_external_data_output_multi.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for wave input < ////////////////// ///////// ////////////////// ///////// (c) audEERING GmbH, ////////////////// -///////// All rights reserverd. ////////////////// +///////// All rights reserved. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager] diff --git a/opensmile/core/config/shared/standard_external_data_output_single.conf.inc b/opensmile/core/config/shared/standard_external_data_output_single.conf.inc index 6bde54f..a0dc8a8 100644 --- a/opensmile/core/config/shared/standard_external_data_output_single.conf.inc +++ b/opensmile/core/config/shared/standard_external_data_output_single.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for wave input < ////////////////// ///////// ////////////////// ///////// (c) audEERING GmbH, ////////////////// -///////// All rights reserverd. ////////////////// +///////// All rights reserved. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager] @@ -12,4 +12,4 @@ instance[extsink].type=cExternalSink [extsink:cExternalSink] reader.dmLevel = \cm[sinkLevel{func}:sink level] -blocksize = \cm[blockSize{1}:block size] \ No newline at end of file +blocksize = \cm[blockSize{1}:block size] diff --git a/opensmile/core/config/shared/standard_external_wave_input.conf.inc b/opensmile/core/config/shared/standard_external_wave_input.conf.inc index 3ee47a6..47a93ab 100644 --- a/opensmile/core/config/shared/standard_external_wave_input.conf.inc +++ b/opensmile/core/config/shared/standard_external_wave_input.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for wave input < ////////////////// ///////// ////////////////// ///////// (c) audEERING GmbH, ////////////////// -///////// All rights reserverd. ////////////////// +///////// All rights reserved. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager] diff --git a/opensmile/core/config/shared/standard_wave_input.conf.inc b/opensmile/core/config/shared/standard_wave_input.conf.inc index f0830f8..20ec962 100644 --- a/opensmile/core/config/shared/standard_wave_input.conf.inc +++ b/opensmile/core/config/shared/standard_wave_input.conf.inc @@ -2,7 +2,7 @@ ///////// > openSMILE configuration file for wave input < ////////////////// ///////// ////////////////// ///////// (c) audEERING GmbH, ////////////////// -///////// All rights reserverd. ////////////////// +///////// All rights reserved. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager] diff --git a/opensmile/core/define.py b/opensmile/core/define.py index 08aa71d..0805fdf 100644 --- a/opensmile/core/define.py +++ b/opensmile/core/define.py @@ -5,7 +5,7 @@ class FeatureSet(enum.Enum): - r""" Enumeration of standard feature sets. + r"""Enumeration of standard feature sets. The following feature sets are available: @@ -63,7 +63,7 @@ def encode_type(self): class FeatureLevel(enum.Enum): - r""" Enumeration of standard feature levels. + r"""Enumeration of standard feature levels. * :attr:`LowLevelDescriptors` - low-level descriptors (LLD) calculated over a sliding window diff --git a/opensmile/core/SMILEapi.py b/opensmile/core/lib.py similarity index 87% rename from opensmile/core/SMILEapi.py rename to opensmile/core/lib.py index 07a2d85..3ce82fa 100644 --- a/opensmile/core/SMILEapi.py +++ b/opensmile/core/lib.py @@ -1,16 +1,16 @@ +from ctypes import CFUNCTYPE +from ctypes import POINTER +from ctypes import Structure from ctypes import byref from ctypes import c_char from ctypes import c_char_p from ctypes import c_double -from ctypes import c_int from ctypes import c_float +from ctypes import c_int from ctypes import c_long from ctypes import c_void_p from ctypes import cast from ctypes import cdll -from ctypes import CFUNCTYPE -from ctypes import POINTER -from ctypes import Structure import json import os import platform @@ -88,9 +88,7 @@ def platform_name(): class ComponentMessage(Structure): # pragma: no cover - """ - An openSMILE component message. - """ + """An openSMILE component message.""" _fields_ = [ ("_msgtype", c_char * CMSG_typenameLen), @@ -132,9 +130,13 @@ def msgtext(self): return self._msgtext.decode("utf-8") def unpack_json(self): - """ - Unpacks a component message that wraps a JSON object and returns the + """Unpacks component message. + + It unpackas a component message + that wraps a JSON object + and returns the JSON data as a dictionary. + """ if self.msgtype != "_CONTAINER" or self.msgname != "jsonObject": raise ValueError("Message does not contain JSON data") @@ -258,9 +260,7 @@ def c_char_p_arr(x): class OpenSmileException(Exception): # pragma: no cover - """ - Exception thrown for internal openSMILE errors. - """ + """Exception thrown for internal openSMILE errors.""" def __init__(self, code: int, message: Optional[str] = None): self.code = code @@ -274,9 +274,7 @@ def __str__(self): class OpenSMILE(object): # pragma: no cover - """ - The main class implementing the interface to openSMILE. - """ + """The main class implementing the interface to openSMILE.""" def __init__(self): self._smileobj = None @@ -284,10 +282,7 @@ def __init__(self): def initialize(self, config_file: str, options: Dict[str, Any] = None, loglevel: int = 2, debug: bool = False, console_output: bool = False, log_file: str = None): - """ - Initializes openSMILE with the provided config file and command-line - options. - """ + """Initializes openSMILE with config file and CLI options.""" self._smileobj = smileapi.smile_new() if self._smileobj is None: raise OpenSmileException(SMILE_FAIL, @@ -309,12 +304,12 @@ def initialize(self, config_file: str, options: Dict[str, Any] = None, def external_source_write_data(self, component_name: str, data: np.ndarray) -> bool: - """ - Writes a data buffer to the specified instance of a cExternalSource - component. + """Writes data buffer to instance of a cExternalSource. + + Returns True if the data was written successfully, + otherwise returns False + (e.g. if the internal buffer of the component is full). - Returns True if the data was written successfully, otherwise returns - False (e.g. if the internal buffer of the component is full). """ if len(data.shape) != 1: raise ValueError("data parameter must have exactly one dimension") @@ -333,14 +328,16 @@ def external_source_write_data(self, component_name: str, self._check_smile_result(result) def external_source_set_eoi(self, component_name: str): - """ - Signals the end of the input for the specified cExternalSource - component instance. - Attempts to write more data to the component after calling this + """Signals end of the input for cExternalSource. + + Attempts to write more data to the component + after calling this method will fail. - Returns True if the end-of-input signal was set successfully, - otherwise False. + Returns ``True`` + if the end-of-input signal was set successfully, + otherwise ``False``. + """ self._check_smile_result( smileapi.smile_extsource_set_external_eoi(self._smileobj, @@ -349,15 +346,16 @@ def external_source_set_eoi(self, component_name: str): def external_audio_source_write_data(self, component_name: str, data: bytes) -> bool: - """ - Writes a data buffer to the specified instance of a - cExternalAudioSource component. + """Writes data buffer to cExternalAudioSource. + The data must match the specified data format for the component (sample size, number of channels, etc.). - Returns True if the data was written successfully, otherwise - returns False + Returns ``True`` + if the data was written successfully, + otherwise returns ``False`` (e.g. if the internal buffer of the component is full). + """ result = smileapi.smile_extaudiosource_write_data(self._smileobj, bytes(component_name, @@ -371,14 +369,16 @@ def external_audio_source_write_data(self, component_name: str, self._check_smile_result(result) def external_audio_source_set_eoi(self, component_name: str): - """ - Signals the end of the input for the specified cExternalAudioSource - component instance. - Attempts to write more data to the component after calling this method + """Signals end of input for cExternalAudioSource. + + Attempts to write more data to the component + after calling this method will fail. - Returns True if the end-of-input signal was set successfully, - otherwise False. + Returns ``True`` + if the end-of-input signal was set successfully, + otherwise ``False``. + """ self._check_smile_result( smileapi.smile_extaudiosource_set_external_eoi(self._smileobj, @@ -388,11 +388,12 @@ def external_audio_source_set_eoi(self, component_name: str): def external_sink_set_callback(self, component_name: str, callback: Callable[[np.ndarray], None]): - """ - Sets the callback function for the specified cExternalSink - component instance. - The function will get called whenever another openSMILE component + """Sets callback for cExternalSink. + + The function will get called + whenever another openSMILE component writes data to the cExternalSink component. + """ def internal_callback(data, vector_size, param): @@ -411,11 +412,12 @@ def internal_callback(data, vector_size, param): def external_sink_set_callback_ex(self, component_name: str, callback: Callable[[np.ndarray], None]): - """ - Sets the extended callback function for the specified - cExternalSink component instance. - The function will get called whenever another openSMILE component + """Sets extended callback for cExternalSink. + + The function will get called + whenever another openSMILE component writes data to the cExternalSink component. + """ def internal_callback_ex(data, nt, n, meta: POINTER(FrameMetaData), _): @@ -455,10 +457,11 @@ def external_sink_get_element_name(self, component_name: str, def external_message_interface_set_callback(self, component_name: str, callback: Callable[ [ComponentMessage], None]): - """ - Sets the callback function for the specified cExternalMessageInterface - component instance. - The function will get called whenever the component receives a message. + """Sets callback for cExternalMessageInterface. + + The function will get called + whenever the component receives a message. + """ # we need to keep a reference to any callback objects as otherwise @@ -479,10 +482,11 @@ def internal_callback(message: POINTER(ComponentMessage), param): def external_message_interface_set_json_callback(self, component_name: str, callback: Callable[ [Dict], None]): - """ - Sets the callback function for the specified cExternalMessageInterface - component instance. - The function will get called whenever the component receives a message. + """Sets callback for cExternalMessageInterface. + + The function will get called + whenever the component receives a message. + """ # we need to keep a reference to any callback objects as otherwise @@ -498,34 +502,36 @@ def internal_callback(json_message: bytes, param): self._smileobj, bytes(component_name, "ascii"), cb, None)) def run(self): - """ - Starts processing and blocks until finished. - """ + """Starts processing and blocks until finished.""" self._check_smile_result(smileapi.smile_run(self._smileobj)) def abort(self): - """ - Requests abortion of the current run. + """Requests abortion of the current run. Note that openSMILE does not immediately stop after this function returns. It might continue to run for a short while until the run method returns. + """ self._check_smile_result(smileapi.smile_abort(self._smileobj)) def reset(self): - """ - Resets the internal state of openSMILE after a run has finished or - was aborted. After resetting, you may call 'run' again without the - need to call 'initialize' first. You must re-register any - cExternalSink/cExternalMessageInterface callbacks, though. + """Resets internal state of openSMILE. + + The internal state is reset + after a run has finished or was aborted. + After resetting, + you may call 'run' again + without the need to call 'initialize' first. + You must re-register any + cExternalSink/cExternalMessageInterface callbacks, + though. + """ self._check_smile_result(smileapi.smile_reset(self._smileobj)) def free(self): - """ - Frees any internal resources allocated by openSMILE. - """ + """Frees any internal resources allocated by openSMILE.""" if self._smileobj is not None: smileapi.smile_free(self._smileobj) self._smileobj = None @@ -542,9 +548,10 @@ def _check_smile_result(self, result: int): def process(config_file: str, options: Dict[str, Any], inputs: Dict[str, np.ndarray], outputs: List[str]) \ -> Dict[str, np.ndarray]: - """ - Runs the specified config file on a set of input data buffers and - returns the specified set of output buffers. + """Runs config on a set of input buffers. + + Returns the specified set of output buffers. + """ opensmile = OpenSMILE() opensmile.initialize(config_file, options) diff --git a/opensmile/core/smile.py b/opensmile/core/smile.py index 001d0fa..c54529a 100644 --- a/opensmile/core/smile.py +++ b/opensmile/core/smile.py @@ -10,17 +10,13 @@ import audinterface import audobject -from opensmile.core.SMILEapi import ( - OpenSMILE, - FrameMetaData, -) from opensmile.core.config import config -from opensmile.core.define import ( - FeatureLevel, - FeatureLevelResolver, - FeatureSet, - FeatureSetResolver, -) +from opensmile.core.define import FeatureLevel +from opensmile.core.define import FeatureLevelResolver +from opensmile.core.define import FeatureSet +from opensmile.core.define import FeatureSetResolver +from opensmile.core.lib import FrameMetaData +from opensmile.core.lib import OpenSMILE class Smile(audinterface.Feature, audobject.Object): @@ -207,7 +203,6 @@ def config_name(self) -> str: @property def config_path(self) -> str: r"""Return file path of config file.""" - if type(self.feature_set) is FeatureSet: config_path = os.path.join( self.default_config_root, @@ -243,7 +238,7 @@ def _check_deltas_available(self): ) def _check_deprecated(self): - r"""Check if feature set is deprecated""" + r"""Check if feature set is deprecated.""" deprecated_feature_sets = { # deprecated: recommended FeatureSet.GeMAPS: FeatureSet.GeMAPSv01b, FeatureSet.GeMAPSv01a: FeatureSet.GeMAPSv01b, @@ -266,7 +261,6 @@ def _extract( sampling_rate: int, ) -> (pd.TimedeltaIndex, pd.TimedeltaIndex, np.ndarray): r"""Run feature extraction.""" - signal = signal.copy() signal *= 32768 signal = signal.astype(np.int16) @@ -370,10 +364,14 @@ def _series_to_frame( self, series: pd.Series, ) -> pd.DataFrame: - r"""Usually, we need to figure out start and end times from - ``win_dur`` and ``hop_dur``. But since openSMILE provides segment - times, we can skip this step and use them directly.""" + r"""Convert series to frame. + Usually, we need to figure out start and end times + from ``win_dur`` and ``hop_dur``. + But since openSMILE provides segment times, + we can skip this step and use them directly. + + """ frames = [None] * len(series) if len(series.index.levels) == 3: for idx, ((file, start, end), values) in enumerate(series.items()): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c178d61 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,210 @@ +# ===== PROJECT =========================================================== +# +[project] +name = 'opensmile' +authors = [ + {name = 'Christop Hausner'}, + {name = 'Johannes Wagner', email= 'jwagner@audeering.com'}, + {name = 'Hagen Wierstorf', email = 'hwierstorf@audeering.com'}, +] +description = 'Python wrapper for common openSMILE feature sets' +readme = 'README.rst' +license = {file = 'LICENSE'} +keywords = [ + 'audio', + 'tools', + 'feature', + 'opensmile', + 'audeering', +] +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Science/Research', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Topic :: Scientific/Engineering', +] +dependencies = [ + 'audobject >=0.6.1', + 'audinterface >=0.7.0', +] +# Get version dynamically from git +# (needs setuptools_scm tools config below) +dynamic = ['version'] + +[project.urls] +repository = 'https://github.com/audeering/opensmile-python/' +documentation = 'https://audeering.github.io/opensmile-python/' + + +# ===== BUILD-SYSTEM ====================================================== +# +# Requirements for building the Python package +[build-system] +requires = ['setuptools>=45', 'setuptools_scm[toml]>=6.2'] +build-backend = 'setuptools.build_meta' + + +# ===== TOOL ============================================================== +# + +# ----- codespell --------------------------------------------------------- +[tool.codespell] +builtin = 'clear,rare,informal,usage,names' +skip = './opensmile.egg-info,./build,./docs/api,./docs/_templates,./docs/examples' + + +# ----- pytest ------------------------------------------------------------ +# +[tool.pytest.ini_options] +cache_dir = '.cache/pytest' +xfail_strict = true +addopts = ''' + --doctest-plus + --cov=opensmile + --cov-fail-under=100 + --cov-report term-missing + --cov-report xml +''' + + +# ----- ruff -------------------------------------------------------------- +# +[tool.ruff] +select = [ + 'D', # pydocstyle + 'E', # pycodestyle errors + 'F', # Pyflakes + 'I', # isort + 'N', # pep8-naming + 'W', # pycodestyle warnings +] + +extend-ignore = [ + 'D100', # Missing docstring in public module + 'D103', # Missing docstring in public function + 'D104', # Missing docstring in public package + 'D107', # Missing docstring in `__init__` +] + +line-length = 79 + +cache-dir = '.cache/ruff' + +[tool.ruff.per-file-ignores] +'__init__.py' = [ + 'F401', # * imported but unused +] +'define.py' = [ + 'N815', # e.g. Variable `eGeMAPS` in class scope should not be mixedCase + 'D102', # Missing docstring in public method +] +'lib.py' = [ + 'D101', # Missing docstring in public class + 'D102', # Missing docstring in public method + 'D105', # Missing docstring in magic method + 'N818', # Exception name `OpenSmileException` should be named with an Error suffix +] + + +# ----- I: isort ----- +# +# Check correct order/syntax of import statements +# +[tool.ruff.isort] + +# All from imports have their own line, e.g. +# +# from .utils import util_a +# from .utils import util_b +# +force-single-line = true + +# Sort by module names +# and not import before from, e.g. +# +# from datetime import date +# import os +# +force-sort-within-sections = true + +# Ensure we have two empty lines +# after last import +lines-after-imports = 2 + +# Group all audEERING packages into a separate section, e.g. +# +# import os +# +# import numpy as np +# +# import audb +# +section-order = [ + 'future', + 'standard-library', + 'third-party', + 'audeering', + 'first-party', + 'local-folder', +] +[tool.ruff.isort.sections] +'audeering' = [ + 'audb', + 'audbackend', + 'audeer', + 'audfactory', + 'audformat', + 'audiofile', + 'audinterface', + 'audmath', + 'audmetric', + 'audobject', + 'audonnx', + 'audplot', + 'audresample', + 'audsp', + 'audtorch', + 'sphinx-audeering-theme', +] + + +# ----- N: pep8-naming ----- +# +# Check variable/class names follow PEP8 naming convention +# +[tool.ruff.pep8-naming] +ignore-names = [ + 'config', # allow lowercase class name + 'test_*', # allow uppercase name when testing a class +] + + +# ----- W: pycodestyle ----- +# +# Check docstrings follow selected convention +# +[tool.ruff.pydocstyle] +convention = 'google' + + +# ----- setuptools -------------------------------------------------------- +# +# Disable package data to only select the matching binary in setup.py +[tool.setuptools] +include-package-data = false +# Find all (sub-)modules of the Python package +[tool.setuptools.packages.find] + + +# ----- setuptools_scm ---------------------------------------------------- +# +# Use setuptools_scm to get version from git +[tool.setuptools_scm] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ee75045..0000000 --- a/setup.cfg +++ /dev/null @@ -1,52 +0,0 @@ -[metadata] -name = opensmile -author = Johannes Wagner, Christoph Hausner, Hagen Wierstorf -author_email = jwagner@audeering.com, chausner@audeering.com, hwierstorf@audeering.com -url = https://github.com/audeering/opensmile-python/ -project_urls = - Documentation = https://audeering.github.io/opensmile-python/ -description = Python wrapper for common openSMILE feature sets -long_description = file: README.rst -license = audEERING -license_file = LICENSE -keywords = audio, tools, feature, opensmile, audeering -platforms= any -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: Other/Proprietary License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Topic :: Scientific/Engineering - -[options] -install_requires = - audobject >=0.6.1 - audinterface >=0.7.0 -setup_requires = - setuptools_scm - -[tool:pytest] -addopts = - --doctest-plus - --cov=opensmile - --cov-fail-under=100 - --cov-report term-missing - --cov-report xml -xfail_strict = true - -[flake8] -exclude = - .eggs, - build, -extend-ignore = - # math, https://github.com/PyCQA/pycodestyle/issues/513 - W503, -per-file-ignores = - # ignore unused and * imports - __init__.py: F401, F403, diff --git a/tests/conftest.py b/tests/conftest.py index 3689e8a..ff7ca35 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,13 @@ -import audeer -import os import glob +import os -import pytest import pandas as pd +import pytest + +import audeer import audiofile as af -from opensmile.core.SMILEapi import platform_name +from opensmile.core.lib import platform_name pytest.ROOT = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/test.conf b/tests/test.conf index 62f34ad..7ea487e 100644 --- a/tests/test.conf +++ b/tests/test.conf @@ -3,7 +3,7 @@ ///////// updated version of ComParE 2013 set, numerical fixes ////////////////// ///////// ////////////////// ///////// (c) 2014-2016 by audEERING, ////////////////// -///////// All rights reserverd. See file COPYING for details. ////////////////// +///////// All rights reserved. See file COPYING for details. ////////////////// /////////////////////////////////////////////////////////////////////////////////////// [componentInstances:cComponentManager]