From 3583fb6830285cf163d7b37cc6d0dd00cae7b1fa Mon Sep 17 00:00:00 2001 From: Nathan Binkert Date: Tue, 22 Mar 2005 00:53:01 -0500 Subject: [PATCH] Use the multidict in the python config stuff. Makes code a bit 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 | 176 +++++++++++++---------------------------- python/m5/multidict.py | 158 ++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 121 deletions(-) create mode 100644 python/m5/multidict.py diff --git a/python/m5/config.py b/python/m5/config.py index 74490f473..68098bf14 100644 --- a/python/m5/config.py +++ b/python/m5/config.py @@ -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 index 000000000..d0c27fa8e --- /dev/null +++ b/python/m5/multidict.py @@ -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