]> git.parisson.com Git - timeside.git/commitdiff
Clean-up code for checking external librairies availability
authorThomas Fillon <thomas@parisson.com>
Fri, 16 May 2014 16:05:22 +0000 (18:05 +0200)
committerThomas Fillon <thomas@parisson.com>
Fri, 16 May 2014 16:05:22 +0000 (18:05 +0200)
- 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)

16 files changed:
tests/test_aubio_melenergy.py
tests/test_aubio_mfcc.py
tests/test_aubio_pitch.py
tests/test_aubio_specdesc.py
tests/test_aubio_temporal.py
tests/test_run_all_doctests.py
tests/test_yaafe.py
timeside/__init__.py
timeside/analyzer/__init__.py
timeside/analyzer/limsi_sad.py
timeside/analyzer/vamp_plugin.py
timeside/analyzer/yaafe.py
timeside/core.py
timeside/exceptions.py
timeside/grapher/render_analyzers.py
timeside/tools/package.py [new file with mode: 0644]

index 34163543ecc3cc68e288f17b901c85fb60263866..abdc7300b4dab1393c2da0163779c2b33c55626e 100755 (executable)
@@ -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)
index 3626aab881478c3875d37b60577bbd0385eb61ff..1071b5e7266e54c6dcd7e3085797e29320a33897 100755 (executable)
@@ -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"
index b4904eddaf695b765ad7cc13106ffd0f08008d5e..bf2a43bb5842e0c52284b2bc688e2d59c5e8d417 100755 (executable)
@@ -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)
index de03a3eaa4467b1bbd55f37175441b573c99b76e..2f34a1e9db1f32f8a27bc87649baca15c7e1cc55 100755 (executable)
@@ -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)
index 77be8130c79c8f585ec8a204032be154bd552eb0..b0850fa4488ae445b595b4b6d094c47d8aa42af9 100755 (executable)
@@ -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)
index 2ca5f4f8a76bb2087ba6fbdf12ace201df80d23c..aef1a8a215f326f3cf23ec1f8a76285319db816a 100755 (executable)
@@ -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))
 
index 61486f16c30665dea4f3f96e5269b19d277916b2..64a99b046cb638235d8c372c36cd962b1c390b0b 100755 (executable)
@@ -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):
index 60f73f984558d1e5e830f015444424878fa01793..741f15312890d4c4a6a8e752894b13068ab49d12 100644 (file)
@@ -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
index 062ee5c0b2be077d05096d4f81f08772a3372c05..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -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
index 9044c5287971e0cef684e2ecd123be5d27dc3423..df9603d68acbd28b1f7ee6a5ced8114b12f1ce81 100644 (file)
@@ -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
index 0ce4ed1dd54929679a23dc6a22e5f79bfc4335aa..dbe7185fac90de5bea94d73a71cc466a69d1d032 100644 (file)
@@ -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
index 5312766e386f2c900c31c991955e60f666c96a94..9f5c91968631681448fd1e5e2e42393b0d5c71e4 100644 (file)
@@ -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
 
index 723a1822eecc2df47145e3df3266b9056c2fb6f3..b40920c8ad6c3afab47e833a534d17ef2d3471df 100644 (file)
@@ -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]
index 3f369900b9ab855974605a76dcc2d3fb938fa104..191171ead4c25900669f4e08d4c10041cebcf479 100644 (file)
@@ -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"
index 79f65607cc4b7bda10022aba0134e148e3fc08a1..4d6bf52192e4731c9ece32458cca53d6b38b917b 100644 (file)
@@ -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.
 
 # along with TimeSide.  If not, see <http://www.gnu.org/licenses/>.
 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 (file)
index 0000000..9178725
--- /dev/null
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2013-2014 Thomas Fillon <thomas.fillon@parisson.com>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Author: Thomas Fillon <thomas.fillon@parisson.com>
+
+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