Use the multidict in the python config stuff. Makes code a bit
authorNathan Binkert <binkertn@umich.edu>
Tue, 22 Mar 2005 05:53:01 +0000 (00:53 -0500)
committerNathan Binkert <binkertn@umich.edu>
Tue, 22 Mar 2005 05:53:01 +0000 (00:53 -0500)
cleaner.

python/m5/config.py:
    Use the multidict instead of the separately coded _getparam
    and _getvalue stuff.  While we're at it, when we see a default
    parameter, we stick it into the dictionary right away.

--HG--
extra : convert_revision : d6f6f5cc454a479e27718ec7952cd7559229ebe7

python/m5/config.py
python/m5/multidict.py [new file with mode: 0644]

index 74490f473e91dd81473a4100e49156c831ff7ca9..68098bf14743b17c83bdfeb07b05f10ec2186d02 100644 (file)
@@ -29,6 +29,7 @@ import os, re, sys, types, inspect
 
 from m5 import panic
 from convert import *
+from multidict import multidict
 
 noDot = False
 try:
@@ -325,17 +326,22 @@ class MetaConfigNode(type):
         super(MetaConfigNode, cls).__init__(name, bases, dict)
 
         # initialize required attributes
-        cls._params = {}
-        cls._values = {}
+        cls._params = multidict()
+        cls._values = multidict()
         cls._param_types = {}
         cls._bases = [c for c in cls.__mro__ if isConfigNode(c)]
         cls._anon_subclass_counter = 0
 
-        # If your parent has a value in it that's a config node, clone
-        # it.  Do this now so if we update any of the values'
-        # attributes we are updating the clone and not the original.
-        for base in cls._bases:
-            for key,val in base._values.iteritems():
+        # We don't support multiple inheritence.  If you want to, you
+        # must fix multidict to deal with it properly.
+        sob = [ base for base in bases \
+                if issubclass(base, ParamType) and base != ParamType ]
+
+        if len(sob) == 1:
+            # If your parent has a value in it that's a config node, clone
+            # it.  Do this now so if we update any of the values'
+            # attributes we are updating the clone and not the original.
+            for key,val in sob[0]._values.iteritems():
 
                 # don't clone if (1) we're about to overwrite it with
                 # a local setting or (2) we've already cloned a copy
@@ -345,12 +351,19 @@ class MetaConfigNode(type):
 
                 if isConfigNode(val):
                     cls._values[key] = val()
-                elif isSimObjSequence(val):
+                elif isSimObjSequence(val) and len(val):
                     cls._values[key] = [ v() for v in val ]
-                elif isNullPointer(val):
-                    cls._values[key] = val
 
-        # process param types from _init_dict first, as these may be needed
+            cls._params.parent = sob[0]._params
+            cls._values.parent = sob[0]._values
+
+        elif len(sob) > 1:
+            panic("""\
+The config hierarchy only supports single inheritence of SimObject
+classes. You're trying to derive from:
+%s""" % str(sob))
+
+        # process param types from _init_dict, as these may be needed
         # by param descriptions also in _init_dict
         for key,val in cls._init_dict.items():
             if isinstance(val, type) and issubclass(val, ParamType):
@@ -362,9 +375,7 @@ class MetaConfigNode(type):
         for key,val in cls._init_dict.items():
             # param descriptions
             if isinstance(val, _Param):
-                cls._params[key] = val
-                # try to resolve local param types in local param_types scope
-                val.maybe_resolve_type(cls._param_types)
+                cls._new_param(key, val)
 
             # init-time-only keywords
             elif cls.init_keywords.has_key(key):
@@ -387,99 +398,6 @@ class MetaConfigNode(type):
             else:
                 setattr(cls, key, val)
 
-
-    def _isvalue(cls, name):
-        for c in cls._bases:
-            if c._params.has_key(name):
-                return True
-
-        for c in cls._bases:
-            if c._values.has_key(name):
-                return True
-
-        return False
-
-    # generator that iterates across all parameters for this class and
-    # all classes it inherits from
-    def _getparams(cls):
-        params = {}
-        for c in cls._bases:
-            for p,v in c._params.iteritems():
-                if not params.has_key(p):
-                    params[p] = v
-        return params
-
-    # Lookup a parameter description by name in the given class.
-    def _getparam(cls, name, default = AttributeError):
-        for c in cls._bases:
-            if c._params.has_key(name):
-                return c._params[name]
-        if isSubClass(default, Exception):
-            raise default, \
-                  "object '%s' has no attribute '%s'" % (cls.__name__, name)
-        else:
-            return default
-
-    def _hasvalue(cls, name):
-        for c in cls._bases:
-            if c._values.has_key(name):
-                return True
-
-        return False
-
-    def _getvalues(cls):
-        values = {}
-        for i,c in enumerate(cls._bases):
-            for p,v in c._values.iteritems():
-                if not values.has_key(p):
-                    values[p] = v
-            for p,v in c._params.iteritems():
-                if not values.has_key(p) and hasattr(v, 'default'):
-                    try:
-                        v.valid(v.default)
-                    except TypeError:
-                        panic("Invalid default %s for param %s in node %s"
-                              % (v.default,p,cls.__name__))
-                    v = v.default
-                    cls._setvalue(p, v)
-                    values[p] = v
-
-        return values
-
-    def _getvalue(cls, name, default = AttributeError):
-        value = None
-        for c in cls._bases:
-            if c._values.has_key(name):
-                value = c._values[name]
-                break
-        if value is not None:
-            return value
-
-        param = cls._getparam(name, None)
-        if param is not None and hasattr(param, 'default'):
-            param.valid(param.default)
-            value = param.default
-            cls._setvalue(name, value)
-            return value
-
-        if isSubClass(default, Exception):
-            raise default, 'value for %s not found' % name
-        else:
-            return default
-
-    def _setvalue(cls, name, value):
-        cls._values[name] = value
-
-    def __getattr__(cls, attr):
-        if cls._isvalue(attr):
-            return Value(cls, attr)
-
-        if attr == '_cpp_param_decl' and hasattr(cls, 'type'):
-            return cls.type + '*'
-
-        raise AttributeError, \
-              "object '%s' has no attribute '%s'" % (cls.__name__, attr)
-
     def _set_keyword(cls, keyword, val, kwtype):
         if not isinstance(val, kwtype):
             raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \
@@ -488,6 +406,13 @@ class MetaConfigNode(type):
             val = classmethod(val)
         type.__setattr__(cls, keyword, val)
 
+    def _new_param(cls, name, value):
+        cls._params[name] = value
+        if hasattr(value, 'default'):
+            cls._values[name] = value.default
+        # try to resolve local param types in local param_types scope
+        value.maybe_resolve_type(cls._param_types)
+
     # Set attribute (called on foo.attr = value when foo is an
     # instance of class cls).
     def __setattr__(cls, attr, value):
@@ -501,23 +426,33 @@ class MetaConfigNode(type):
             return
 
         # must be SimObject param
-        param = cls._getparam(attr, None)
+        param = cls._params.get(attr, None)
         if param:
             # It's ok: set attribute by delegating to 'object' class.
             # Note the use of param.make_value() to verify/canonicalize
             # the assigned value
             try:
                 param.valid(value)
-            except:
-                panic("Error setting param %s.%s to %s\n" % \
-                      (cls.__name__, attr, value))
-            cls._setvalue(attr, value)
+            except Exception, e:
+                panic("Exception: %s\nError setting param %s.%s to %s\n" % \
+                      (e, cls.__name__, attr, value))
+            cls._values[attr] = value
         elif isConfigNode(value) or isSimObjSequence(value):
-            cls._setvalue(attr, value)
+            cls._values[attr] = value
         else:
             raise AttributeError, \
                   "Class %s has no parameter %s" % (cls.__name__, attr)
 
+    def __getattr__(cls, attr):
+        if cls._params.has_key(attr) or cls._values.has_key(attr):
+            return Value(cls, attr)
+
+        if attr == '_cpp_param_decl' and hasattr(cls, 'type'):
+            return cls.type + '*'
+
+        raise AttributeError, \
+              "object '%s' has no attribute '%s'" % (cls.__name__, attr)
+
     def add_child(cls, instance, name, child):
         if isNullPointer(child) or instance.top_child_names.has_key(name):
             return
@@ -547,7 +482,7 @@ class MetaConfigNode(type):
         if hasattr(cls, 'check'):
             cls.check()
 
-        for key,value in cls._getvalues().iteritems():
+        for key,value in cls._values.iteritems():
             if isConfigNode(value):
                 cls.add_child(instance, key, value)
             if isinstance(value, (list, tuple)):
@@ -555,11 +490,10 @@ class MetaConfigNode(type):
                 if len(vals):
                     cls.add_child(instance, key, vals)
 
-        for pname,param in cls._getparams().iteritems():
-            try:
-                value = cls._getvalue(pname)
-            except:
-                panic('Error getting %s' % pname)
+        for pname,param in cls._params.iteritems():
+            value = cls._values.get(pname, None)
+            if value is None:
+                panic('Error getting %s from %s' % (pname, name))
 
             try:
                 if isConfigNode(value):
@@ -618,7 +552,7 @@ class ConfigNode(object):
         cls._anon_subclass_counter += 1
         return cls.__metaclass__(name, (cls, ), kwargs)
 
-class ParamContext(ConfigNode):
+class ParamContext(ConfigNode,ParamType):
     pass
 
 class MetaSimObject(MetaConfigNode):
@@ -879,7 +813,7 @@ class Value(object):
         super(Value, self).__setattr__('obj', obj)
 
     def _getattr(self):
-        return self.obj._getvalue(self.attr)
+        return self.obj._values.get(self.attr)
 
     def __setattr__(self, attr, value):
         setattr(self._getattr(), attr, value)
diff --git a/python/m5/multidict.py b/python/m5/multidict.py
new file mode 100644 (file)
index 0000000..d0c27fa
--- /dev/null
@@ -0,0 +1,158 @@
+__all__ = [ 'multidict' ]
+
+class multidict(object):
+    __nodefault = object()
+    def __init__(self, parent = {}, **kwargs):
+        self.dict = dict(**kwargs)
+        self.parent = parent
+        self.deleted = {}
+
+    def __str__(self):
+        return str(dict(self.items()))
+
+    def __repr__(self):
+        return `dict(self.items())`
+
+    def __contains__(self, key):
+        return self.dict.has_key(key) or self.parent.has_key(key)
+
+    def __delitem__(self, key):
+        try:
+            del self.dict[key]
+        except KeyError, e:
+            if key in self.parent:
+                self.deleted[key] = True
+            else:
+                raise KeyError, e
+
+    def __setitem__(self, key, value):
+        self.deleted.pop(key, False)
+        self.dict[key] = value
+
+    def __getitem__(self, key):
+        try:
+            return self.dict[key]
+        except KeyError, e:
+            if not self.deleted.get(key, False) and key in self.parent:
+                return self.parent[key]
+            else:
+                raise KeyError, e
+
+    def __len__(self):
+        return len(self.dict) + len(self.parent)
+
+    def next(self):
+        for key,value in self.dict.items():
+            yield key,value
+
+        if self.parent:
+            for key,value in self.parent.next():
+                if key not in self.dict and key not in self.deleted:
+                    yield key,value
+
+    def has_key(self, key):
+        return key in self
+
+    def iteritems(self):
+        for item in self.next():
+            yield item
+
+    def items(self):
+        return [ item for item in self.next() ]
+
+    def iterkeys(self):
+        for key,value in self.next():
+            yield key
+
+    def keys(self):
+        return [ key for key,value in self.next() ]
+
+    def itervalues(self):
+        for key,value in self.next():
+            yield value
+
+    def values(self):
+        return [ value for key,value in self.next() ]
+
+    def get(self, key, default=__nodefault):
+        try:
+            return self[key]
+        except KeyError, e:
+            if default != self.__nodefault:
+                return default
+            else:
+                raise KeyError, e
+
+    def setdefault(self, key, default):
+        try:
+            return self[key]
+        except KeyError:
+            self.deleted.pop(key, False)
+            self.dict[key] = default
+            return default
+
+    def _dump(self):
+        print 'multidict dump'
+        node = self
+        while isinstance(node, multidict):
+            print '    ', node.dict
+            node = node.parent
+
+    def _dumpkey(self, key):
+        values = []
+        node = self
+        while isinstance(node, multidict):
+            if key in node.dict:
+                values.append(node.dict[key])
+            node = node.parent
+        print key, values
+
+if __name__ == '__main__':
+    test1 = multidict()
+    test2 = multidict(test1)
+    test3 = multidict(test2)
+    test4 = multidict(test3)
+
+    test1['a'] = 'test1_a'
+    test1['b'] = 'test1_b'
+    test1['c'] = 'test1_c'
+    test1['d'] = 'test1_d'
+    test1['e'] = 'test1_e'
+
+    test2['a'] = 'test2_a'
+    del test2['b']
+    test2['c'] = 'test2_c'
+    del test1['a']
+
+    test2.setdefault('f', multidict)
+
+    print 'test1>', test1.items()
+    print 'test2>', test2.items()
+    #print test1['a']
+    print test1['b']
+    print test1['c']
+    print test1['d']
+    print test1['e']
+
+    print test2['a']
+    #print test2['b']
+    print test2['c']
+    print test2['d']
+    print test2['e']
+
+    for key in test2.iterkeys():
+        print key
+
+    test2.get('g', 'foo')
+    #test2.get('b')
+    test2.get('b', 'bar')
+    test2.setdefault('b', 'blah')
+    print test1
+    print test2
+    print `test2`
+
+    print len(test2)
+
+    test3['a'] = [ 0, 1, 2, 3 ]
+
+    print test4