Ticket #13: wildcards support in connector metadata, making the world better for...
authorClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Wed, 27 Apr 2011 15:15:15 +0000 (17:15 +0200)
committerClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Wed, 27 Apr 2011 15:15:15 +0000 (17:15 +0200)
src/nepi/core/connector.py [new file with mode: 0644]
src/nepi/core/design.py
src/nepi/core/execute.py

diff --git a/src/nepi/core/connector.py b/src/nepi/core/connector.py
new file mode 100644 (file)
index 0000000..7cd415f
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Common connector base classes
+"""
+
+import sys
+
+class ConnectorTypeBase(object):
+    def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
+        super(ConnectorTypeBase, self).__init__()
+        if max == -1:
+            max = sys.maxint
+        elif max <= 0:
+            raise RuntimeError, "The maximum number of connections allowed need to be more than 0"
+        if min < 0:
+            raise RuntimeError, "The minimum number of connections allowed needs to be at least 0"
+        # connector_type_id -- univoquely identifies a connector type 
+        # across testbeds
+        self._connector_type_id = self.make_connector_type_id(
+            testbed_id, factory_id, name)
+        # name -- display name for the connector type
+        self._name = name
+        # max -- maximum amount of connections that this type support, 
+        # -1 for no limit
+        self._max = max
+        # min -- minimum amount of connections required by this type of connector
+        self._min = min
+
+    @property
+    def connector_type_id(self):
+        return self._connector_type_id
+
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def max(self):
+        return self._max
+
+    @property
+    def min(self):
+        return self._min
+    
+    @staticmethod
+    def make_connector_type_id(testbed_id, factory_id, name):
+        testbed_id = testbed_id.lower() if testbed_id else None
+        factory_id = factory_id.lower() if factory_id else None
+        name = name.lower() if name else None
+        return (testbed_id, factory_id, name)
+    
+    @staticmethod
+    def _type_resolution_order(connector_type_id):
+        testbed_id, factory_id, name = connector_type_id
+        
+        # the key is always a candidate
+        yield connector_type_id
+        
+        # Try wildcard combinations
+        if (testbed_id, None, name) != connector_type_id:
+            yield (testbed_id, None, name)
+        if (None, factory_id, name) != connector_type_id:
+            yield (None, factory_id, name)
+        if (None, None, name) != connector_type_id:
+            yield (None, None, name)
+
+
index cd0c384..efdd938 100644 (file)
@@ -6,71 +6,43 @@ Experiment design API
 """
 
 from nepi.core.attributes import AttributesMap, Attribute
+from nepi.core.connector import ConnectorTypeBase
 from nepi.core.metadata import Metadata
 from nepi.util import validation
 from nepi.util.guid import GuidGenerator
 from nepi.util.graphical_info import GraphicalInfo
 from nepi.util.parser._xml import XmlExperimentParser
 import sys
+    
+
 
-class ConnectorType(object):
+class ConnectorType(ConnectorTypeBase):
     def __init__(self, testbed_id, factory_id, name, help, max = -1, min = 0):
-        super(ConnectorType, self).__init__()
-        if max == -1:
-            max = sys.maxint
-        elif max <= 0:
-                raise RuntimeError(
-             "The maximum number of connections allowed need to be more than 0")
-        if min < 0:
-            raise RuntimeError(
-             "The minimum number of connections allowed needs to be at least 0")
-        # connector_type_id -- univoquely identifies a connector type 
-        # across testbeds
-        self._connector_type_id = (testbed_id.lower(), factory_id.lower(), 
-                name.lower())
-        # name -- display name for the connector type
-        self._name = name
+        super(ConnectorType, self).__init__(testbed_id, factory_id, name, max, min)
+        
         # help -- help text
         self._help = help
-        # max -- maximum amount of connections that this type support, 
-        # -1 for no limit
-        self._max = max
-        # min -- minimum amount of connections required by this type of connector
-        self._min = min
+        
         # allowed_connections -- keys in the dictionary correspond to the 
         # connector_type_id for possible connections. The value indicates if
         # the connection is allowed accros different testbed instances
         self._allowed_connections = dict()
 
-    @property
-    def connector_type_id(self):
-        return self._connector_type_id
-
     @property
     def help(self):
         return self._help
 
-    @property
-    def name(self):
-        return self._name
-
-    @property
-    def max(self):
-        return self._max
-
-    @property
-    def min(self):
-        return self._min
-
     def add_allowed_connection(self, testbed_id, factory_id, name, can_cross):
-        self._allowed_connections[(testbed_id.lower(), 
-            factory_id.lower(), name.lower())] = can_cross
+        type_id = self.make_connector_type_id(testbed_id, factory_id, name)
+        self._allowed_connections[type_id] = can_cross
 
     def can_connect(self, connector_type_id, testbed_guid1, testbed_guid2):
-        if not connector_type_id in self._allowed_connections.keys():
+        for lookup_type_id in self._type_resolution_order(connector_type_id):
+            if lookup_type_id in self._allowed_connections:
+                can_cross = self._allowed_connections[lookup_type_id]
+                return can_cross or (testbed_guid1 == testbed_guid2)
+        else:
             return False
-        can_cross = self._allowed_connections[connector_type_id]
-        return can_cross or (testbed_guid1 == testbed_guid2)
 
 class Connector(object):
     """A Connector sepcifies the connection points in an Object"""
index f977802..2fa16c2 100644 (file)
@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 from nepi.core.attributes import Attribute, AttributesMap
+from nepi.core.connector import ConnectorTypeBase
 from nepi.util import proxy, validation
 from nepi.util.constants import STATUS_FINISHED, TIME_NOW
 from nepi.util.parser._xml import XmlExperimentParser
@@ -15,28 +16,9 @@ ATTRIBUTE_PATTERN_BASE = re.compile(r"\{#\[(?P<label>[-a-zA-Z0-9._]*)\](?P<expr>
 ATTRIBUTE_PATTERN_GUID_SUB = r"{#[%(guid)s]%(expr)s#}"
 COMPONENT_PATTERN = re.compile(r"(?P<kind>[a-z]*)\[(?P<index>.*)\]")
 
-class ConnectorType(object):
+class ConnectorType(ConnectorTypeBase):
     def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
-        super(ConnectorType, self).__init__()
-        if max == -1:
-            max = sys.maxint
-        elif max <= 0:
-                raise RuntimeError(
-             "The maximum number of connections allowed need to be more than 0")
-        if min < 0:
-            raise RuntimeError(
-             "The minimum number of connections allowed needs to be at least 0")
-        # connector_type_id -- univoquely identifies a connector type 
-        # across testbeds
-        self._connector_type_id = (testbed_id.lower(), factory_id.lower(), 
-                name.lower())
-        # name -- display name for the connector type
-        self._name = name
-        # max -- maximum amount of connections that this type support, 
-        # -1 for no limit
-        self._max = max
-        # min -- minimum amount of connections required by this type of connector
-        self._min = min
+        super(ConnectorType, self).__init__(testbed_id, factory_id, name, max, min)
         # from_connections -- connections where the other connector is the "From"
         # to_connections -- connections where the other connector is the "To"
         # keys in the dictionary correspond to the 
@@ -49,49 +31,37 @@ class ConnectorType(object):
         self._from_connections = dict()
         self._to_connections = dict()
 
-    @property
-    def connector_type_id(self):
-        return self._connector_type_id
-
-    @property
-    def name(self):
-        return self._name
-
-    @property
-    def max(self):
-        return self._max
-
-    @property
-    def min(self):
-        return self._min
-
     def add_from_connection(self, testbed_id, factory_id, name, can_cross, code):
-        self._from_connections[(testbed_id.lower(), factory_id.lower(),
-            name.lower())] = (can_cross, code)
+        type_id = self.make_connector_type_id(testbed_id, factory_id, name)
+        self._from_connections[type_id] = (can_cross, code)
 
     def add_to_connection(self, testbed_id, factory_id, name, can_cross, code):
-        self._to_connections[(testbed_id.lower(), factory_id.lower(), 
-            name.lower())] = (can_cross, code)
+        type_id = self.make_connector_type_id(testbed_id, factory_id, name)
+        self._to_connections[type_id] = (can_cross, code)
 
     def can_connect(self, testbed_id, factory_id, name, count, 
             must_cross = False):
-        connector_type_id = (testbed_id.lower(), factory_id.lower(),
-            name.lower())
-        if connector_type_id in self._from_connections:
-            (can_cross, code) = self._from_connections[connector_type_id]
-        elif connector_type_id in self._to_connections:
-            (can_cross, code) = self._to_connections[connector_type_id]
+        connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
+        for lookup_type_id in self._type_resolution_order(connector_type_id):
+            if lookup_type_id in self._from_connections:
+                (can_cross, code) = self._from_connections[lookup_type_id]
+            elif lookup_type_id in self._to_connections:
+                (can_cross, code) = self._to_connections[lookup_type_id]
+            else:
+                # keey trying
+                continue
+            return not must_cross or can_cross
         else:
             return False
-        return not must_cross or can_cross
 
     def code_to_connect(self, testbed_id, factory_id, name):
-        connector_type_id = (testbed_id.lower(), factory_id.lower(), 
-            name.lower())        
-        if not connector_type_id in self._to_connections.keys():
+        connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
+        for lookup_type_id in self._type_resolution_order(connector_type_id):
+            if lookup_type_id in self._to_connections:
+                (can_cross, code) = self._to_connections[lookup_type_id]
+                return code
+        else:
             return False
-        (can_cross, code) = self._to_connections[connector_type_id]
-        return code
 
 # TODO: create_function, start_function, stop_function, status_function 
 # need a definition!