From: Thomas Fillon Date: Fri, 16 May 2014 16:05:22 +0000 (+0200) Subject: Clean-up code for checking external librairies availability X-Git-Tag: 0.5.5~1^2~20 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=b1e96bea92c8c2b53bef35a6ea2835d9beca158b;p=timeside.git Clean-up code for checking external librairies availability - Enhance code to check if Aubio, Yaafe and Vamp host are available - Corresponding processors should not be shown in processors list - TimeSide can now be installed without all or any of these librairies - Tests ands Doctests will passed (for doctest I had to skip some new server sub-modules) --- diff --git a/tests/test_aubio_melenergy.py b/tests/test_aubio_melenergy.py index 3416354..abdc730 100755 --- a/tests/test_aubio_melenergy.py +++ b/tests/test_aubio_melenergy.py @@ -1,25 +1,27 @@ #! /usr/bin/env python -from unit_timeside import * -from timeside.decoder.file import FileDecoder -from timeside.analyzer import WITH_AUBIO -if WITH_AUBIO: - from timeside.analyzer.aubio.aubio_melenergy import AubioMelEnergy +from unit_timeside import unittest, TestRunner import os +from timeside.decoder.file import FileDecoder +from timeside.core import get_processor +from timeside import _WITH_AUBIO + -@unittest.skipIf(not WITH_AUBIO, 'Aubio library is not available') +@unittest.skipIf(not _WITH_AUBIO, 'Aubio library is not available') class TestAubioMelEnergy(unittest.TestCase): def setUp(self): - self.analyzer = AubioMelEnergy() + self.analyzer = get_processor('aubio_melenergy')() def testOnSweep(self): "runs on sweep" - self.source = os.path.join (os.path.dirname(__file__), "samples", "sweep.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "sweep.wav") def testOnGuitar(self): "runs on guitar" - self.source = os.path.join (os.path.dirname(__file__), "samples", "guitar.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "guitar.wav") def tearDown(self): decoder = FileDecoder(self.source) diff --git a/tests/test_aubio_mfcc.py b/tests/test_aubio_mfcc.py index 3626aab..1071b5e 100755 --- a/tests/test_aubio_mfcc.py +++ b/tests/test_aubio_mfcc.py @@ -1,18 +1,17 @@ #! /usr/bin/env python -from unit_timeside import * -from timeside.decoder.file import FileDecoder -from timeside.analyzer import WITH_AUBIO -if WITH_AUBIO: - from timeside.analyzer.aubio.aubio_mfcc import AubioMfcc +from unit_timeside import unittest, TestRunner import os +from timeside.decoder.file import FileDecoder +from timeside.core import get_processor +from timeside import _WITH_AUBIO -@unittest.skipIf(not WITH_AUBIO, 'Aubio library is not available') +@unittest.skipIf(not _WITH_AUBIO, 'Aubio library is not available') class TestAubioMfcc(unittest.TestCase): def setUp(self): - self.analyzer = AubioMfcc() + self.analyzer = get_processor('aubio_mfcc')() def testOnSweep(self): "runs on sweep" diff --git a/tests/test_aubio_pitch.py b/tests/test_aubio_pitch.py index b4904ed..bf2a43b 100755 --- a/tests/test_aubio_pitch.py +++ b/tests/test_aubio_pitch.py @@ -1,26 +1,27 @@ #! /usr/bin/env python -from unit_timeside import * -from timeside.decoder.file import FileDecoder -from timeside.analyzer import WITH_AUBIO -if WITH_AUBIO: - from timeside.analyzer.aubio.aubio_pitch import AubioPitch +from unit_timeside import unittest, TestRunner import os +from timeside.decoder.file import FileDecoder +from timeside.core import get_processor +from timeside import _WITH_AUBIO -@unittest.skipIf(not WITH_AUBIO, 'Aubio library is not available') +@unittest.skipIf(not _WITH_AUBIO, 'Aubio library is not available') class TestAubioPitch(unittest.TestCase): def setUp(self): - self.analyzer = AubioPitch() + self.analyzer = get_processor('aubio_pitch')() def testOnSweep(self): "runs on sweep" - self.source = os.path.join (os.path.dirname(__file__), "samples", "sweep.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "sweep.wav") def testOnGuitar(self): "runs on guitar" - self.source = os.path.join (os.path.dirname(__file__), "samples", "guitar.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "guitar.wav") def tearDown(self): decoder = FileDecoder(self.source) diff --git a/tests/test_aubio_specdesc.py b/tests/test_aubio_specdesc.py index de03a3e..2f34a1e 100755 --- a/tests/test_aubio_specdesc.py +++ b/tests/test_aubio_specdesc.py @@ -1,26 +1,27 @@ #! /usr/bin/env python -from unit_timeside import * -from timeside.decoder.file import FileDecoder -from timeside.analyzer import WITH_AUBIO -if WITH_AUBIO: - from timeside.analyzer.aubio.aubio_specdesc import AubioSpecdesc +from unit_timeside import unittest, TestRunner import os +from timeside.decoder.file import FileDecoder +from timeside.core import get_processor +from timeside import _WITH_AUBIO -@unittest.skipIf(not WITH_AUBIO, 'Aubio library is not available') +@unittest.skipIf(not _WITH_AUBIO, 'Aubio library is not available') class TestAubioSpecdesc(unittest.TestCase): def setUp(self): - self.analyzer = AubioSpecdesc() + self.analyzer = get_processor('aubio_specdesc')() def testOnSweep(self): "runs on sweep" - self.source = os.path.join (os.path.dirname(__file__), "samples", "sweep.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "sweep.wav") def testOnGuitar(self): "runs on guitar" - self.source = os.path.join (os.path.dirname(__file__), "samples", "guitar.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "guitar.wav") def tearDown(self): decoder = FileDecoder(self.source) diff --git a/tests/test_aubio_temporal.py b/tests/test_aubio_temporal.py index 77be813..b0850fa 100755 --- a/tests/test_aubio_temporal.py +++ b/tests/test_aubio_temporal.py @@ -1,26 +1,27 @@ #! /usr/bin/env python -from unit_timeside import * -from timeside.decoder.file import FileDecoder -from timeside.analyzer import WITH_AUBIO -if WITH_AUBIO: - from timeside.analyzer.aubio.aubio_temporal import AubioTemporal +from unit_timeside import unittest, TestRunner import os +from timeside.decoder.file import FileDecoder +from timeside.core import get_processor +from timeside import _WITH_AUBIO -@unittest.skipIf(not WITH_AUBIO, 'Aubio library is not available') +@unittest.skipIf(not _WITH_AUBIO, 'Aubio library is not available') class TestAubioTemporal(unittest.TestCase): def setUp(self): - self.analyzer = AubioTemporal() + self.analyzer = get_processor('aubio_temporal')() def testOnSweep(self): "runs on sweep" - self.source = os.path.join (os.path.dirname(__file__), "samples", "sweep.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "sweep.wav") def testOnGuitar(self): "runs on guitar" - self.source = os.path.join (os.path.dirname(__file__), "samples", "guitar.wav") + self.source = os.path.join(os.path.dirname(__file__), + "samples", "guitar.wav") def tearDown(self): decoder = FileDecoder(self.source) diff --git a/tests/test_run_all_doctests.py b/tests/test_run_all_doctests.py index 2ca5f4f..aef1a8a 100755 --- a/tests/test_run_all_doctests.py +++ b/tests/test_run_all_doctests.py @@ -24,7 +24,7 @@ import unittest from unit_timeside import TestRunner import doctest import timeside -import pkgutil +from timeside.tools.package import discover_modules def load_tests(loader, tests, ignore): @@ -32,10 +32,7 @@ def load_tests(loader, tests, ignore): finder = doctest.DocTestFinder(exclude_empty=False) # Create tests for doctest in timeside modules and sub-modules - modules_list = [modname for _, modname, _ in pkgutil.walk_packages( - path=timeside.__path__, - prefix=timeside.__name__ + '.', - onerror=lambda x: None)] + modules_list = discover_modules(timeside.__name__) for module in modules_list: _tmp = __import__(module, fromlist=['DOCTEST_ALIAS']) @@ -43,6 +40,7 @@ def load_tests(loader, tests, ignore): DOCTEST_ALIAS = _tmp.DOCTEST_ALIAS except AttributeError: DOCTEST_ALIAS = {} + tests.addTests(doctest.DocTestSuite(module, extraglobs=DOCTEST_ALIAS, test_finder=finder)) diff --git a/tests/test_yaafe.py b/tests/test_yaafe.py index 61486f1..64a99b0 100755 --- a/tests/test_yaafe.py +++ b/tests/test_yaafe.py @@ -2,14 +2,14 @@ from unit_timeside import * from timeside.decoder.file import FileDecoder -from timeside.analyzer import WITH_YAAFE -if WITH_YAAFE: +from timeside import _WITH_YAAFE +if _WITH_YAAFE: from timeside.analyzer.yaafe import Yaafe from yaafelib import DataFlow, FeaturePlan import os -@unittest.skipIf(not WITH_YAAFE, 'Yaafe library is not available') +@unittest.skipIf(not _WITH_YAAFE, 'Yaafe library is not available') class TestYaafe(unittest.TestCase): def setUp(self): diff --git a/timeside/__init__.py b/timeside/__init__.py index 60f73f9..741f153 100644 --- a/timeside/__init__.py +++ b/timeside/__init__.py @@ -3,45 +3,27 @@ from __future__ import absolute_import from . import api from . import core -from . import decoder -from . import analyzer -from . import grapher -from . import encoder __version__ = '0.5.5' -__all__ = ['api', 'core', 'decoder', 'analyzer', 'grapher', 'encoder'] +# Check Availability of external Audio feature extraction librairies +from .tools import package as ts_package +_WITH_AUBIO = ts_package.check_aubio() +_WITH_YAAFE = ts_package.check_yaafe() +_WITH_VAMP = ts_package.check_vamp() -def _discover_modules(): - import sys - import pkgutil - import importlib +_packages_with_processors = ['decoder', 'analyzer', 'encoder', 'grapher'] - #pkg_path = os.path.abspath() +__all__ = ['api', 'core'] +__all__.extend(_packages_with_processors) - #__import__(pkg) +for _sub_pkg in _packages_with_processors: + ts_package.discover_modules(_sub_pkg, __name__) - proc_modules = ['decoder', 'analyzer', 'encoder', 'grapher'] - - for module in proc_modules: - pkg = '.'.join([__name__, module]) - importlib.import_module(pkg) - package = sys.modules[pkg] - prefix = pkg + "." - - for importer, modname, ispkg in pkgutil.walk_packages(package.__path__, - prefix): - try: - importlib.import_module(modname) - #__import__(modname) - except ImportError as e: - if e.message.count('yaafelib'): - print 'No Yaafe' - elif e.message.count('aubio'): - print 'No Aubio' - else: - raise e - -_discover_modules() +# Clean-up +del ts_package +del _packages_with_processors +del _sub_pkg +del absolute_import diff --git a/timeside/analyzer/__init__.py b/timeside/analyzer/__init__.py index 062ee5c..e69de29 100644 --- a/timeside/analyzer/__init__.py +++ b/timeside/analyzer/__init__.py @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - -# ----- Load external libraries ------ -# Aubio -try: - WITH_AUBIO = True - import aubio -except ImportError: - WITH_AUBIO = False -else: - del aubio - -# Yaafe -try: - WITH_YAAFE = True - import yaafelib -except ImportError: - WITH_YAAFE = False -else: - del yaafelib - -# Vamp Plugins -try: - from . vamp_plugin import VampSimpleHost - VampSimpleHost.SimpleHostProcess(['-v']) - WITH_VAMP = True -except OSError: - WITH_VAMP = False diff --git a/timeside/analyzer/limsi_sad.py b/timeside/analyzer/limsi_sad.py index 9044c52..df9603d 100644 --- a/timeside/analyzer/limsi_sad.py +++ b/timeside/analyzer/limsi_sad.py @@ -23,11 +23,8 @@ from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer import timeside -#from timeside.analyzer import WITH_YAAFE -WITH_YAAFE = True -if WITH_YAAFE: - from yaafe import Yaafe - import yaafelib + +import yaafelib import numpy as N import pickle import os.path diff --git a/timeside/analyzer/vamp_plugin.py b/timeside/analyzer/vamp_plugin.py index 0ce4ed1..dbe7185 100644 --- a/timeside/analyzer/vamp_plugin.py +++ b/timeside/analyzer/vamp_plugin.py @@ -27,6 +27,29 @@ import subprocess import numpy as np +def simple_host_process(argslist): + """Call vamp-simple-host""" + + vamp_host = 'vamp-simple-hostqq' + command = [vamp_host] + command.extend(argslist) + # try ? + stdout = subprocess.check_output( + command, stderr=subprocess.STDOUT).splitlines() + + return stdout + + +# Raise an exception if Vamp Host is missing +from ..exceptions import VampImportError +try: + simple_host_process(['-v']) + WITH_VAMP = True +except OSError: + WITH_VAMP = False + raise VampImportError + + class VampSimpleHost(Analyzer): """Vamp plugins library interface analyzer""" @@ -120,7 +143,7 @@ class VampSimpleHost(Analyzer): args = [plugin, wavfile] - stdout = VampSimpleHost.SimpleHostProcess(args) # run vamp-simple-host + stdout = simple_host_process(args) # run vamp-simple-host stderr = stdout[0:8] # stderr containing file and process information res = stdout[8:] # stdout containg the feature data @@ -158,19 +181,6 @@ class VampSimpleHost(Analyzer): @staticmethod def get_plugins_list(): arg = ['--list-outputs'] - stdout = VampSimpleHost.SimpleHostProcess(arg) + stdout = simple_host_process(arg) return [line.split(':')[1:] for line in stdout] - - @staticmethod - def SimpleHostProcess(argslist): - """Call vamp-simple-host""" - - vamp_host = 'vamp-simple-host' - command = [vamp_host] - command.extend(argslist) - # try ? - stdout = subprocess.check_output( - command, stderr=subprocess.STDOUT).splitlines() - - return stdout diff --git a/timeside/analyzer/yaafe.py b/timeside/analyzer/yaafe.py index 5312766..9f5c919 100644 --- a/timeside/analyzer/yaafe.py +++ b/timeside/analyzer/yaafe.py @@ -25,10 +25,8 @@ Module Yaafe Analyzer from timeside.core import implements, interfacedoc from timeside.analyzer.core import Analyzer from timeside.api import IAnalyzer -#from timeside.analyzer import WITH_YAAFE -WITH_YAAFE = True -if WITH_YAAFE: - import yaafelib + +import yaafelib import numpy from timeside.analyzer.preprocessors import downmix_to_mono diff --git a/timeside/core.py b/timeside/core.py index 723a182..b40920c 100644 --- a/timeside/core.py +++ b/timeside/core.py @@ -21,7 +21,7 @@ from .component import Component, MetaComponent, abstract from .component import implements, implementations, interfacedoc from .api import IProcessor -from .exceptions import Error, ApiError +from .exceptions import Error, PIDError, ApiError import re import numpy @@ -229,7 +229,7 @@ def processors(interface=IProcessor, recurse=True): def get_processor(processor_id): """Return a processor by its id""" if not processor_id in _processors: - raise Error("No processor registered with id: '%s'" + raise PIDError("No processor registered with id: '%s'" % processor_id) return _processors[processor_id] diff --git a/timeside/exceptions.py b/timeside/exceptions.py index 3f36990..191171e 100644 --- a/timeside/exceptions.py +++ b/timeside/exceptions.py @@ -45,3 +45,11 @@ class SubProcessError(Error): return "%s ; command: %s; error: %s" % (self.message, self.command, error) + + +class PIDError(KeyError): + "Exception for reporting missing Processor ID in registered processors" + + +class VampImportError(ImportError): + "Can't import module depending on Vamp because vamp host is missing" diff --git a/timeside/grapher/render_analyzers.py b/timeside/grapher/render_analyzers.py index 79f6560..4d6bf52 100644 --- a/timeside/grapher/render_analyzers.py +++ b/timeside/grapher/render_analyzers.py @@ -12,7 +12,7 @@ # (at your option) any later version. # TimeSide is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of +# but _WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. @@ -20,10 +20,10 @@ # along with TimeSide. If not, see . from __future__ import division -from timeside.core import implements, interfacedoc, abstract, get_processor -from timeside.api import IGrapher -from core import Grapher -from .. import analyzer +from ..core import implements, interfacedoc, abstract, get_processor +from ..api import IGrapher +from .core import Grapher +from ..exceptions import PIDError class DisplayAnalyzer(Grapher): @@ -95,13 +95,14 @@ class DisplayAnalyzer(Grapher): # From here define new Grapher based on Analyzers -if analyzer.WITH_AUBIO: +try: aubiopitch = get_processor('aubio_pitch') DisplayAubioPitch = DisplayAnalyzer.create(analyzer=aubiopitch, result_id='aubio_pitch.pitch', grapher_id='grapher_aubio_pitch', grapher_name='Aubio Pitch') - +except PIDError: + pass odf = get_processor('odf') DisplayOnsetDetectionFunction = DisplayAnalyzer.create(analyzer=odf, @@ -113,6 +114,7 @@ DisplayWaveform = DisplayAnalyzer.create(analyzer=wav, result_id='waveform_analyzer', grapher_id='grapher_waveform', grapher_name='Waveform from Analyzer') + irit4hz = get_processor('irit_speech_4hz') Display4hzSpeechSegmentation = DisplayAnalyzer.create(analyzer=irit4hz, result_id='irit_speech_4hz.segments', diff --git a/timeside/tools/package.py b/timeside/tools/package.py new file mode 100644 index 0000000..9178725 --- /dev/null +++ b/timeside/tools/package.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2013-2014 Thomas Fillon + +# This file is part of TimeSide. + +# TimeSide is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# TimeSide is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with TimeSide. If not, see . + +# Author: Thomas Fillon + +from ..exceptions import VampImportError + +from importlib import import_module +import warnings + + +def discover_modules(subpackage, package=None): + import pkgutil + + if package: + _pkg = import_module('.' + subpackage, package) + else: + _pkg = import_module(subpackage) + + pkg_path = _pkg.__path__ + pkg_prefix = _pkg.__name__ + '.' + + _list = [import_module_with_exceptions(modname) + for importer, modname, ispkg + in pkgutil.walk_packages(pkg_path, pkg_prefix)] + + modules_list = [mod for mod in _list if mod is not None] + return modules_list + + +def import_module_with_exceptions(name, package=None): + """Wrapper around importlib.import_module to import TimeSide subpackage + and ignoring ImportError if Aubio, Yaafe and Vamp Host are not available""" + + from timeside import _WITH_AUBIO, _WITH_YAAFE, _WITH_VAMP + + if name.count('.server.'): + # TODO: + # Temporary skip all timeside.server submodules before check dependencies + return + try: + import_module(name, package) + except VampImportError: + # No Vamp Host + if _WITH_VAMP: + raise VampImportError + else: + # Ignore Vamp ImportError + return + except ImportError as e: + if str(e).count('yaafelib') and not _WITH_YAAFE: + # Ignore Yaafe ImportError + return + elif str(e).count('aubio') and not _WITH_AUBIO: + # Ignore Aubio ImportError + return + elif str(e).count('DJANGO_SETTINGS_MODULE'): + # Ignore module requiring DJANGO_SETTINGS_MODULE in environnement + return + else: + raise e + return name + + +# Check Availability of external Audio feature extraction librairies +def check_aubio(): + "Check Aubio availability" + try: + import aubio + except ImportError: + warnings.warn('Aubio librairy is not available', ImportWarning, + stacklevel=2) + _WITH_AUBIO = False + else: + _WITH_AUBIO = True + del aubio + + return _WITH_AUBIO + + +def check_yaafe(): + "Check Aubio availability" + try: + import yaafelib + except ImportError: + warnings.warn('Yaafe librairy is not available', ImportWarning, + stacklevel=2) + _WITH_YAAFE = False + else: + _WITH_YAAFE = True + del yaafelib + return _WITH_YAAFE + + +def check_vamp(): + "Check Vamp host availability" + + try: + from ..analyzer import vamp_plugin + except VampImportError: + warnings.warn('Vamp host is not available', ImportWarning, + stacklevel=2) + _WITH_VAMP = False + else: + _WITH_VAMP = True + del vamp_plugin + + return _WITH_VAMP