From c3fcc8a0ca5cb5ec11bd3460fd8c4e9a62314603 Mon Sep 17 00:00:00 2001 From: yomguy Date: Thu, 24 Sep 2009 15:45:35 +0000 Subject: [PATCH] add core then ExtensionPoint class, fix module names --- analysis/core.py | 1 - analysis/samplerate.py | 51 +++----- analysis/vamp/core.py | 2 +- core.py | 219 ++++++++++++++++++++++++++++++++++ graph/spectrogram_audiolab.py | 1 - graph/wav2png.py | 1 + graph/waveform_audiolab.py | 1 - 7 files changed, 240 insertions(+), 36 deletions(-) create mode 100644 core.py diff --git a/analysis/core.py b/analysis/core.py index 135babc..9c2a2ce 100644 --- a/analysis/core.py +++ b/analysis/core.py @@ -21,7 +21,6 @@ # Bram de Jong # Guillaume Pellerin -from django.conf import settings from timeside.core import * import optparse, math, sys import numpy diff --git a/analysis/samplerate.py b/analysis/samplerate.py index 6daae7f..0970fcb 100644 --- a/analysis/samplerate.py +++ b/analysis/samplerate.py @@ -1,39 +1,26 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2008 Parisson SARL - -# This software is a computer program whose purpose is to backup, analyse, -# transcode and stream any audio content with its metadata over a web frontend. - -# This software is governed by the CeCILL license under French law and -# abiding by the rules of distribution of free software. You can use, -# modify and/ or redistribute the software under the terms of the CeCILL -# license as circulated by CEA, CNRS and INRIA at the following URL -# "http://www.cecill.info". - -# As a counterpart to the access to the source code and rights to copy, -# modify and redistribute granted by the license, users are provided only -# with a limited warranty and the software's author, the holder of the -# economic rights, and the successive licensors have only limited -# liability. - -# In this respect, the user's attention is drawn to the risks associated -# with loading, using, modifying and/or developing or reproducing the -# software by the user in light of its specific status of free software, -# that may mean that it is complicated to manipulate, and that also -# therefore means that it is reserved for developers and experienced -# professionals having in-depth computer knowledge. Users are therefore -# encouraged to load and test the software's suitability as regards their -# requirements in conditions enabling the security of their systems and/or -# data to be ensured and, more generally, to use and operate it in the -# same conditions as regards security. - -# The fact that you are presently reading this means that you have had -# knowledge of the CeCILL license and that you accept its terms. # +# Copyright (c) 2007-2009 Guillaume Pellerin + +# 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: Guillaume Pellerin -from telemeta.analysis.core import * -from telemeta.analysis.api import IMediaItemAnalyzer +from timeside.analysis.core import * +from timeside.analysis.api import IMediaItemAnalyzer import numpy class SampleRateAnalyzer(AudioProcessor): diff --git a/analysis/vamp/core.py b/analysis/vamp/core.py index 06519f6..bc32def 100644 --- a/analysis/vamp/core.py +++ b/analysis/vamp/core.py @@ -112,7 +112,7 @@ class VampCoreAnalyzer: yield __chunk -class VampProcessError(TelemetaError): +class VampProcessError(TimeSideError): def __init__(self, message, command, subprocess): self.message = message diff --git a/core.py b/core.py new file mode 100644 index 0000000..96b1347 --- /dev/null +++ b/core.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007 Samalyse SARL +# Copyright (C) 2003-2005 Edgewall Software +# Copyright (C) 2003-2004 Jonas Borgström +# Copyright (C) 2004-2005 Christopher Lenz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +#3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +#THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +#OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +#ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +#DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +#GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +#INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +#IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +#OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +#IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Author: Jonas Borgström +# Christopher Lenz +# Olivier Guilyardi + + +__all__ = ['Component', 'ExtensionPoint', 'implements', 'Interface', + 'TimeSideError'] + + +class TimeSideError(Exception): + """Exception base class for errors in TimeSide.""" + # FIXME: is this redundant with Django's error handling ? + +# def __init__(self, message, title=None, show_traceback=False): +# Exception.__init__(self, message) +# self.message = message +# self.title = title +# self.show_traceback = show_traceback + + +class Interface(object): + """Marker base class for extension point interfaces.""" + + +class ExtensionPoint(property): + """Marker class for extension points in components.""" + + def __init__(self, interface): + """Create the extension point. + + @param interface: the `Interface` subclass that defines the protocol + for the extension point + """ + property.__init__(self, self.extensions) + self.interface = interface + self.__doc__ = 'List of components that implement `%s`' % \ + self.interface.__name__ + + def extensions(self, component): + """Return a list of components that declare to implement the extension + point interface.""" + extensions = ComponentMeta._registry.get(self.interface, []) + return filter(None, [component.compmgr[cls] for cls in extensions]) + + def __repr__(self): + """Return a textual representation of the extension point.""" + return '' % self.interface.__name__ + + +class ComponentMeta(type): + """Meta class for components. + + Takes care of component and extension point registration. + """ + _components = [] + _registry = {} + + def __new__(cls, name, bases, d): + """Create the component class.""" + + d['_implements'] = _implements[:] + del _implements[:] + + new_class = type.__new__(cls, name, bases, d) + if name == 'Component': + # Don't put the Component base class in the registry + return new_class + + # Only override __init__ for Components not inheriting ComponentManager + if True not in [issubclass(x, ComponentManager) for x in bases]: + # Allow components to have a no-argument initializer so that + # they don't need to worry about accepting the component manager + # as argument and invoking the super-class initializer + init = d.get('__init__') + if not init: + # Because we're replacing the initializer, we need to make sure + # that any inherited initializers are also called. + for init in [b.__init__._original for b in new_class.mro() + if issubclass(b, Component) + and '__init__' in b.__dict__]: + break + def maybe_init(self, compmgr, init=init, cls=new_class): + if cls not in compmgr.components: + compmgr.components[cls] = self + if init: + init(self) + maybe_init._original = init + new_class.__init__ = maybe_init + + if d.get('abstract'): + # Don't put abstract component classes in the registry + return new_class + + ComponentMeta._components.append(new_class) + for interface in d.get('_implements', []): + ComponentMeta._registry.setdefault(interface, []).append(new_class) + for base in [base for base in bases if hasattr(base, '_implements')]: + for interface in base._implements: + ComponentMeta._registry.setdefault(interface, []).append(new_class) + + return new_class + + +_implements = [] + +def implements(*interfaces): + """Can be used in the class definiton of `Component` subclasses to declare + the extension points that are extended. + """ + _implements.extend(interfaces) + + +class Component(object): + """Base class for components. + + Every component can declare what extension points it provides, as well as + what extension points of other components it extends. + """ + __metaclass__ = ComponentMeta + + def __new__(cls, *args, **kwargs): + """Return an existing instance of the component if it has already been + activated, otherwise create a new instance. + """ + # If this component is also the component manager, just invoke that + if issubclass(cls, ComponentManager): + self = super(Component, cls).__new__(cls) + self.compmgr = self + return self + + # The normal case where the component is not also the component manager + compmgr = args[0] + self = compmgr.components.get(cls) + if self is None: + self = super(Component, cls).__new__(cls) + self.compmgr = compmgr + compmgr.component_activated(self) + return self + + +class ComponentManager(object): + """The component manager keeps a pool of active components.""" + + def __init__(self): + """Initialize the component manager.""" + self.components = {} + self.enabled = {} + if isinstance(self, Component): + self.components[self.__class__] = self + + def __contains__(self, cls): + """Return wether the given class is in the list of active components.""" + return cls in self.components + + def __getitem__(self, cls): + """Activate the component instance for the given class, or return the + existing the instance if the component has already been activated.""" + if cls not in self.enabled: + self.enabled[cls] = self.is_component_enabled(cls) + if not self.enabled[cls]: + return None + component = self.components.get(cls) + if not component: + if cls not in ComponentMeta._components: + raise TimeSideError, 'Component "%s" not registered' % cls.__name__ + try: + component = cls(self) + except TypeError, e: + raise TimeSideError, 'Unable to instantiate component %r (%s)' \ + % (cls, e) + return component + + def component_activated(self, component): + """Can be overridden by sub-classes so that special initialization for + components can be provided. + """ + + def is_component_enabled(self, cls): + """Can be overridden by sub-classes to veto the activation of a + component. + + If this method returns False, the component with the given class will + not be available. + """ + return True diff --git a/graph/spectrogram_audiolab.py b/graph/spectrogram_audiolab.py index b156b86..e29e83a 100644 --- a/graph/spectrogram_audiolab.py +++ b/graph/spectrogram_audiolab.py @@ -21,7 +21,6 @@ from timeside.core import * from timeside.graph.api import IMediaItemGrapher -from django.conf import settings from tempfile import NamedTemporaryFile from timeside.graph.wav2png import * diff --git a/graph/wav2png.py b/graph/wav2png.py index 8b0cce3..cd6b27e 100644 --- a/graph/wav2png.py +++ b/graph/wav2png.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # wav2png.py -- converts wave files to wave file and spectrogram images # Copyright (C) 2008 MUSIC TECHNOLOGY GROUP (MTG) diff --git a/graph/waveform_audiolab.py b/graph/waveform_audiolab.py index 6fd2b07..59d9a50 100644 --- a/graph/waveform_audiolab.py +++ b/graph/waveform_audiolab.py @@ -21,7 +21,6 @@ from timeside.core import * from timeside.graph.api import IMediaItemGrapher -from django.conf import settings from tempfile import NamedTemporaryFile from timeside.graph.wav2png import * -- 2.39.5