From: Olivier Guilyardi Date: Thu, 26 Nov 2009 19:48:05 +0000 (+0000) Subject: add new core: X-Git-Tag: 0.3.2~227 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=28695c4e8f36e1fa828e225d96449beb750ed088;p=timeside.git add new core: - less intrusive component/interface mechanism - no need for a component manager - one can create several instances of a component (no singleton) - component constructors do not need component manager anymore - unit test included --- diff --git a/newcore.py b/newcore.py new file mode 100644 index 0000000..6797923 --- /dev/null +++ b/newcore.py @@ -0,0 +1,90 @@ +# -*- 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 . + + + +# 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 both interfaces and components: +# +# - all descendants of a class implementing a given interface are also considered +# to implement this interface +# - a class implementing a given interface is also considered to implement all +# the ascendants of this interface + +__all__ = ['Component', 'implements', 'Interface', 'implementations'] + +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): + + 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[:] + 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]) + extend_unique(result, cls.__subclasses__()) + + subinterfaces = interface.__subclasses__() + if subinterfaces: + for i in subinterfaces: + find_implementations(i, result) + diff --git a/tests/testnewcore.py b/tests/testnewcore.py new file mode 100644 index 0000000..ccbf003 --- /dev/null +++ b/tests/testnewcore.py @@ -0,0 +1,86 @@ + +from timeside.newcore import * +from sys import stdout + +class I1(Interface): + pass + +class I2(Interface): + pass + +class I3(Interface): + pass + +class I4(Interface): + pass + +class I5(Interface): + pass + +class I6(I5): + pass + +class I7(Interface): + pass + +class I8(Interface): + pass + +class C1(Component): + implements(I1) + +class C2(Component): + implements(I2, I3) + +class C3(Component): + implements(I4) + +class C4(Component): + implements(I4) + +class C5(Component): + implements(I6) + +class C6(Component): + implements(I7) + +class C7(C6): + pass + +class C8(Component): + implements(I8) + +class C9(C8): + implements(I8) + +def list_equals(list1, list2): + if len(list1) != len(list2): + return False + + for item in list1: + if not item in list2: + return False + + for item in list2: + if not item in list1: + return False + + return True + +def test(desc, actual, expected): + stdout.write(desc + ": ") + if list_equals(actual, expected): + stdout.write("OK\n") + else: + stdout.write("FAILED\n") + stdout.write("actual: " + str(actual) + "\n") + stdout.write("expected: " + str(expected) + "\n") + + +test("Test a component implementing one interface", implementations(I1), [C1]) +test("Test a component implementing two interfaces (1/2)", implementations(I2), [C2]) +test("Test a component implementing two interfaces (2/2)", implementations(I3), [C2]) +test("Test an interface implemented by two components", implementations(I4), [C3, C4]) +test("Test whether a component implements an interface's parent", implementations(I5), [C5]) +test("Test whether a child component implements the interface implemented by its parent", implementations(I7), [C6, C7]) +test("Test implementation redundancy across descendants", implementations(I8), [C8, C9])