From 7cd82b24dbc203874612e369d1899020d7b23f18 Mon Sep 17 00:00:00 2001 From: Olivier Guilyardi Date: Fri, 27 Nov 2009 16:26:11 +0000 Subject: [PATCH] - move generic component/interface architecture from core to component.py - add Processor, MetaProcessor and processors() to core - add IProcessor base interface for all processor interfaces - let IAnalyzer, IGrapher, IEncoder and IDecoder subclass IProcessor - let all actual processor classes descend from Processor - use processors() instead of implementations() in test.py - add listprocessors.py test script --- __init__.py | 2 +- analyze/api.py | 4 +- analyze/core.py | 3 +- api.py | 24 ++++++++++ core.py | 82 ++++++++--------------------------- decode/api.py | 5 ++- encode/api.py | 5 ++- encode/core.py | 3 +- graph/api.py | 4 +- graph/spectrogram_audiolab.py | 2 +- graph/waveform_audiolab.py | 2 +- tests/listprocessors.py | 12 +++++ tests/test.py | 8 ++-- 13 files changed, 74 insertions(+), 82 deletions(-) create mode 100644 api.py create mode 100644 tests/listprocessors.py diff --git a/__init__.py b/__init__.py index 5a4f345..67efe02 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,5 @@ -import core +from core import * import decode import encode import analyze diff --git a/analyze/api.py b/analyze/api.py index 8091c61..46d4ab9 100644 --- a/analyze/api.py +++ b/analyze/api.py @@ -19,9 +19,9 @@ # Author: Guillaume Pellerin -from timeside.core import * +from timeside.api import IProcessor -class IAnalyzer(Interface): +class IAnalyzer(IProcessor): """Media item analyzer driver interface""" @staticmethod diff --git a/analyze/core.py b/analyze/core.py index fdbcb41..c2371d2 100644 --- a/analyze/core.py +++ b/analyze/core.py @@ -26,7 +26,8 @@ import optparse, math, sys import numpy import scikits.audiolab as audiolab -class AudioProcessor(Component): +# FIXME: AudioProcessor: wrong name, should be Analyzer or AnalyzerCore +class AudioProcessor(Processor): def __init__(self): self.fft_size = 2048 diff --git a/api.py b/api.py new file mode 100644 index 0000000..e2f62b9 --- /dev/null +++ b/api.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2009 Olivier Guilyardi +# +# 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 . + +from timeside.component import Interface + +class IProcessor(Interface): + pass + diff --git a/core.py b/core.py index 817a931..94e636c 100644 --- a/core.py +++ b/core.py @@ -17,78 +17,32 @@ # You should have received a copy of the GNU General Public License # along with TimeSide. If not, see . +from timeside.component import * +from timeside.api import IProcessor - -# This file defines an object interface mechanism and a way to determine -# which components implements a given interface -# -# For example, the following defines the Music class as implementing the -# listenable interface. -# -# class Listenable(Interface): -# pass -# -# class Music(Component): -# implements(Listenable) -# -# Several class can implements a such interface, and it is possible to -# discover which class implements it with implementations(): -# -# list_of_classes = implementations(Listenable) -# -# This mechanism support inheritance of interfaces: a class implementing a given -# interface is also considered to implement all the ascendants of this interface. -# -# However, inheritance is not supported for components. The descendants of a class -# implementing a given interface are not automatically considered to implement this -# interface too. - -__all__ = ['Component', 'implements', 'Interface', 'implementations', 'TimeSideError'] +__all__ = ['Processor', 'Component', 'implements', 'processors', 'TimeSideError'] class TimeSideError(Exception): """Exception base class for errors in TimeSide.""" # FIXME: is this redundant with Django's error handling ? - # FIXME: this class doesn't belong to the core - -class Interface(object): - """Marker base class for interfaces.""" - -def implements(*interfaces): - _implements.extend(interfaces) - -def implementations(interface): - result = [] - find_implementations(interface, result) - return result -_implementations = [] -_implements = [] - -class ComponentMeta(type): +_processors = [] +class MetaProcessor(MetaComponent): + """Metaclass of the Processor class, used mainly for ensuring uniqueness of + processor id's""" def __new__(cls, name, bases, d): - new_class = type.__new__(cls, name, bases, d) - if _implements: - for i in _implements: - _implementations.append((i, new_class)) - del _implements[:] + new_class = MetaComponent.__new__(cls, name, bases, d) + id = "fixme" + _processors.append((id, new_class)) return new_class -class Component(object): - __metaclass__ = ComponentMeta - -def extend_unique(list1, list2): - for item in list2: - if item not in list1: - list1.append(item) - -def find_implementations(interface, result): - for i, cls in _implementations: - if (i == interface): - extend_unique(result, [cls]) - - subinterfaces = interface.__subclasses__() - if subinterfaces: - for i in subinterfaces: - find_implementations(i, result) +class Processor(Component): + """Base component class of all processors""" + __metaclass__ = MetaProcessor +def processors(interface=IProcessor, recurse=True): + """Returns the processors implementing a given interface and, if recurse, + any of the descendants of this interface.""" + return implementations(interface, recurse) + diff --git a/decode/api.py b/decode/api.py index c25d15f..e3e6744 100644 --- a/decode/api.py +++ b/decode/api.py @@ -19,9 +19,10 @@ # You should have received a copy of the GNU General Public License # along with TimeSide. If not, see . -from timeside.core import Interface, TimeSideError +from timeside.api import IProcessor +from timeside.core import TimeSideError -class IDecoder(Interface): +class IDecoder(IProcessor): """Decoder driver interface""" @staticmethod diff --git a/encode/api.py b/encode/api.py index ee9134e..9314c82 100644 --- a/encode/api.py +++ b/encode/api.py @@ -19,9 +19,10 @@ # You should have received a copy of the GNU General Public License # along with TimeSide. If not, see . -from timeside.core import Interface, TimeSideError +from timeside.core import TimeSideError +from timeside.api import IProcessor -class IEncoder(Interface): +class IEncoder(IProcessor): """Encoder driver interface""" def __init__(self, output, nchannels, samplerate): diff --git a/encode/core.py b/encode/core.py index 8ba366f..73b8818 100644 --- a/encode/core.py +++ b/encode/core.py @@ -52,8 +52,7 @@ class SubProcessPipe: self.input = self.proc.stdin self.output = self.proc.stdout - -class EncoderCore(Component): +class EncoderCore(Processor): """Defines the main parts of the encoding tools : paths, metadata parsing, data streaming thru system command""" diff --git a/graph/api.py b/graph/api.py index 1f9667a..af1ed7a 100644 --- a/graph/api.py +++ b/graph/api.py @@ -20,9 +20,9 @@ # Author: Guillaume Pellerin # Author: Olivier Guilyardi -from timeside.core import * +from timeside.api import IProcessor -class IGrapher(Interface): +class IGrapher(IProcessor): """Media item visualizer driver interface""" @staticmethod diff --git a/graph/spectrogram_audiolab.py b/graph/spectrogram_audiolab.py index 9687a25..dc14fe1 100644 --- a/graph/spectrogram_audiolab.py +++ b/graph/spectrogram_audiolab.py @@ -24,7 +24,7 @@ from timeside.graph.api import IGrapher from tempfile import NamedTemporaryFile from timeside.graph.wav2png import * -class SpectrogramGrapherAudiolab(Component): +class SpectrogramGrapherAudiolab(Processor): """Spectrogram graph driver (python style thanks to wav2png.py and scikits.audiolab)""" implements(IGrapher) diff --git a/graph/waveform_audiolab.py b/graph/waveform_audiolab.py index 8a7dd0e..4aeefc3 100644 --- a/graph/waveform_audiolab.py +++ b/graph/waveform_audiolab.py @@ -24,7 +24,7 @@ from timeside.graph.api import IGrapher from tempfile import NamedTemporaryFile from timeside.graph.wav2png import * -class WaveFormGrapherAudiolab(Component): +class WaveFormGrapherAudiolab(Processor): """WaveForm graph driver (python style thanks to wav2png.py and scikits.audiolab)""" implements(IGrapher) diff --git a/tests/listprocessors.py b/tests/listprocessors.py new file mode 100644 index 0000000..bf12c42 --- /dev/null +++ b/tests/listprocessors.py @@ -0,0 +1,12 @@ +import timeside + +def list_processors(interface, prefix=""): + print prefix + interface.__name__ + subinterfaces = interface.__subclasses__() + for i in subinterfaces: + list_processors(i, prefix + " ") + processors = timeside.processors(interface, False) + for p in processors: + print prefix + " " + p.__name__ + +list_processors(timeside.api.IProcessor) diff --git a/tests/test.py b/tests/test.py index c282f94..38cd4c9 100755 --- a/tests/test.py +++ b/tests/test.py @@ -8,7 +8,7 @@ from timeside.core import * class TestAnalyzers(Component): - analyzers = implementations(timeside.analyze.IAnalyzer) + analyzers = processors(timeside.analyze.IAnalyzer) def list(self): analyzers = [] @@ -35,7 +35,7 @@ class TestAnalyzers(Component): class TestDecoders(Component): - decoders = implementations(timeside.decode.IDecoder) + decoders = processors(timeside.decode.IDecoder) def list(self): decoders_list = [] @@ -72,7 +72,7 @@ class TestDecoders(Component): f.close() class TestEncoders(Component): - encoders = implementations(timeside.encode.IEncoder) + encoders = processors(timeside.encode.IEncoder) def list(self): encoders = [] @@ -109,7 +109,7 @@ class TestEncoders(Component): class TestGraphers(Component): - graphers = implementations(timeside.graph.IGrapher) + graphers = processors(timeside.graph.IGrapher) def list(self): graphers = [] -- 2.39.5