From 8dff74b26fbfafad4ea9a12d20ad57b8094b0bc9 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Sun, 13 Mar 2005 00:36:06 -0500 Subject: [PATCH] Clean up import situation... key things are: - global tracking of legitimate param types in separate dict keeps us from having to import objects in config.py, which gets rid of nasty circular dependence - use __all__ in config.py and restrict imports from mpy_importer to reduce m5 namespace pollution python/m5/__init__.py: Try to limit namespace pollution by only importing what's needed from mpy_importer. Explicitly set up legal parameter types rather than relying on eval(). python/m5/config.py: Use empty ParamType base class to distinguish types that are legitimate SimObject params. Explicitly add these to global param_types map. Rework CheckedInt and Range classes a little bit to fit in better with the model. No need to import objects here any longer. Add __all__ list to specify subset of names that get exported on 'from m5.config import *'. --HG-- extra : convert_revision : 01c6e82e0b175fc9b3df25dd0cc80ecd842680bc --- python/m5/__init__.py | 12 ++- python/m5/config.py | 220 +++++++++++++++++++++++++----------------- 2 files changed, 141 insertions(+), 91 deletions(-) diff --git a/python/m5/__init__.py b/python/m5/__init__.py index 7cb3a32c6..3d54a83da 100644 --- a/python/m5/__init__.py +++ b/python/m5/__init__.py @@ -1,7 +1,11 @@ -from mpy_importer import * -from config import * -from objects import * +from mpy_importer import AddToPath, LoadMpyFile -cpp_classes = MetaSimObject.cpp_classes +from config import * +config.add_param_types(config.__dict__) + +from objects import * +config.add_param_types(objects.__dict__) + +cpp_classes = config.MetaSimObject.cpp_classes cpp_classes.sort() diff --git a/python/m5/config.py b/python/m5/config.py index 2c5a70b25..a4248192a 100644 --- a/python/m5/config.py +++ b/python/m5/config.py @@ -27,7 +27,6 @@ from __future__ import generators import os, re, sys, types, inspect -from mpy_importer import AddToPath, LoadMpyFile from smartdict import SmartDict from convert import * @@ -210,6 +209,23 @@ class_decorator = 'M5M5_SIMOBJECT_' expr_decorator = 'M5M5_EXPRESSION_' dot_decorator = '_M5M5_DOT_' +param_types = {} + +class ParamType(object): + pass + +def add_param_types(ctx): + if isinstance(ctx, types.DictType): + source_dict = ctx + elif isinstance(ctx, types.ModuleType): + source_dict = ctx.__dict__ + else: + raise TypeError, \ + "m5.config.add_param_types requires dict or module as arg" + for key,val in source_dict.iteritems(): + if isinstance(val, type) and issubclass(val, ParamType): + param_types[key] = val + # The metaclass for ConfigNode (and thus for everything that derives # from ConfigNode, including SimObject). This class controls how new # classes that derive from ConfigNode are instantiated, and provides @@ -247,7 +263,7 @@ class MetaConfigNode(type): # initialize required attributes cls._params = {} cls._values = {} - cls._enums = {} + cls._param_types = {} cls._bases = [c for c in cls.__mro__ if isConfigNode(c)] cls._anon_subclass_counter = 0 @@ -270,19 +286,26 @@ class MetaConfigNode(type): elif isNullPointer(val): cls._values[key] = val - # now process _init_dict items + # 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): + cls._param_types[key] = val + if not issubclass(val, ConfigNode): + del cls._init_dict[key] + + # now process remaining _init_dict items + 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) # init-time-only keywords elif cls.init_keywords.has_key(key): cls._set_keyword(key, val, cls.init_keywords[key]) - # enums - elif isinstance(val, type) and issubclass(val, Enum): - cls._enums[key] = val - # See description of decorators in the importer.py file. # We just strip off the expr_decorator now since we don't # need from this point on. @@ -419,7 +442,11 @@ class MetaConfigNode(type): # It's ok: set attribute by delegating to 'object' class. # Note the use of param.make_value() to verify/canonicalize # the assigned value - param.valid(value) + try: + param.valid(value) + except: + panic("Error setting param %s.%s to %s\n" % \ + (cls.__name__, attr, value)) cls._setvalue(attr, value) elif isConfigNode(value) or isSimObjSequence(value): cls._setvalue(attr, value) @@ -562,7 +589,7 @@ class MetaSimObject(MetaConfigNode): def _cpp_decl(cls): name = cls.__name__ code = "" - code += "\n".join([e.cpp_declare() for e in cls._enums.values()]) + code += "\n".join([e.cpp_declare() for e in cls._param_types.values()]) code += "\n" param_names = cls._params.keys() param_names.sort() @@ -854,13 +881,24 @@ class _Param(object): if not hasattr(self, 'desc'): raise TypeError, 'desc attribute missing' + def maybe_resolve_type(self, context): + # check if already resolved... don't use hasattr(), + # as that calls __getattr__() + if self.__dict__.has_key('ptype'): + return + try: + self.ptype = context[self.ptype_string] + except KeyError: + # no harm in trying... we'll try again later using global scope + pass + def __getattr__(self, attr): if attr == 'ptype': try: - self.ptype = eval(self.ptype_string) + self.ptype = param_types[self.ptype_string] return self.ptype except: - raise TypeError, 'Param.%s: undefined type' % self.ptype_string + panic("undefined Param type %s" % self.ptype_string) else: raise AttributeError, "'%s' object has no attribute '%s'" % \ (type(self).__name__, attr) @@ -887,19 +925,10 @@ class _ParamProxy(object): # E.g., Param.Int(5, "number of widgets") def __call__(self, *args, **kwargs): - # Param type could be defined only in context of caller (e.g., - # for locally defined Enum subclass). Need to go look up the - # type in that enclosing scope. - caller_frame = inspect.stack()[1][0] - ptype = caller_frame.f_locals.get(self.ptype, None) - if not ptype: ptype = caller_frame.f_globals.get(self.ptype, None) - if not ptype: ptype = globals().get(self.ptype, None) - # ptype could still be None due to circular references... we'll - # try one more time to evaluate lazily when ptype is first needed. - # In the meantime we'll save the type name as a string. - if not ptype: ptype = self.ptype - return _Param(ptype, *args, **kwargs) + return _Param(self.ptype, *args, **kwargs) + # Strange magic to theoretically allow dotted names as Param classes, + # e.g., Param.Foo.Bar(...) to have a param of type Foo.Bar def __getattr__(self, attr): if attr == '__bases__': raise AttributeError, '' @@ -975,8 +1004,33 @@ VectorParam = _VectorParamProxy(None) # to correspond to distinct C++ types as well. # ##################################################################### -# Integer parameter type. -class _CheckedInt(object): + + +# Metaclass for bounds-checked integer parameters. See CheckedInt. +class CheckedIntType(type): + def __init__(cls, name, bases, dict): + super(CheckedIntType, cls).__init__(name, bases, dict) + + # CheckedInt is an abstract base class, so we actually don't + # want to do any processing on it... the rest of this code is + # just for classes that derive from CheckedInt. + if name == 'CheckedInt': + return + + if not (hasattr(cls, 'min') and hasattr(cls, 'max')): + if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): + panic("CheckedInt subclass %s must define either\n" \ + " 'min' and 'max' or 'size' and 'unsigned'\n" \ + % name); + if cls.unsigned: + cls.min = 0 + cls.max = 2 ** cls.size - 1 + else: + cls.min = -(2 ** (cls.size - 1)) + cls.max = (2 ** (cls.size - 1)) - 1 + + cls._cpp_param_decl = cls.cppname + def _convert(cls, value): if isinstance(value, bool): return int(value) @@ -987,86 +1041,72 @@ class _CheckedInt(object): if isinstance(value, (str, float)): value = long(float(value)) - if not cls._min <= value <= cls._max: + if not cls.min <= value <= cls.max: raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ - (cls._min, value, cls._max) + (cls.min, value, cls.max) return value - _convert = classmethod(_convert) def _string(cls, value): return str(value) - _string = classmethod(_string) -class CheckedInt(type): - def __new__(cls, cppname, min, max): - # New class derives from _CheckedInt base with proper bounding - # parameters - dict = { '_cpp_param_decl' : cppname, '_min' : min, '_max' : max } - return type.__new__(cls, cppname, (_CheckedInt, ), dict) +# Abstract superclass for bounds-checked integer parameters. This +# class is subclassed to generate parameter classes with specific +# bounds. Initialization of the min and max bounds is done in the +# metaclass CheckedIntType.__init__. +class CheckedInt(ParamType): + __metaclass__ = CheckedIntType -class CheckedIntType(CheckedInt): - def __new__(cls, cppname, size, unsigned): - dict = {} - if unsigned: - min = 0 - max = 2 ** size - 1 - else: - min = -(2 ** (size - 1)) - max = (2 ** (size - 1)) - 1 +class Int(CheckedInt): cppname = 'int'; size = 32; unsigned = False +class Unsigned(CheckedInt): cppname = 'unsigned'; size = 32; unsigned = True - return super(cls, CheckedIntType).__new__(cls, cppname, min, max) +class Int8(CheckedInt): cppname = 'int8_t'; size = 8; unsigned = False +class UInt8(CheckedInt): cppname = 'uint8_t'; size = 8; unsigned = True +class Int16(CheckedInt): cppname = 'int16_t'; size = 16; unsigned = False +class UInt16(CheckedInt): cppname = 'uint16_t'; size = 16; unsigned = True +class Int32(CheckedInt): cppname = 'int32_t'; size = 32; unsigned = False +class UInt32(CheckedInt): cppname = 'uint32_t'; size = 32; unsigned = True +class Int64(CheckedInt): cppname = 'int64_t'; size = 64; unsigned = False +class UInt64(CheckedInt): cppname = 'uint64_t'; size = 64; unsigned = True -Int = CheckedIntType('int', 32, False) -Unsigned = CheckedIntType('unsigned', 32, True) +class Counter(CheckedInt): cppname = 'Counter'; size = 64; unsigned = True +class Addr(CheckedInt): cppname = 'Addr'; size = 64; unsigned = True +class Tick(CheckedInt): cppname = 'Tick'; size = 64; unsigned = True -Int8 = CheckedIntType('int8_t', 8, False) -UInt8 = CheckedIntType('uint8_t', 8, True) -Int16 = CheckedIntType('int16_t', 16, False) -UInt16 = CheckedIntType('uint16_t', 16, True) -Int32 = CheckedIntType('int32_t', 32, False) -UInt32 = CheckedIntType('uint32_t', 32, True) -Int64 = CheckedIntType('int64_t', 64, False) -UInt64 = CheckedIntType('uint64_t', 64, True) - -Counter = CheckedIntType('Counter', 64, True) -Addr = CheckedIntType('Addr', 64, True) -Tick = CheckedIntType('Tick', 64, True) - -Percent = CheckedInt('int', 0, 100) +class Percent(CheckedInt): cppname = 'int'; min = 0; max = 100 class Pair(object): def __init__(self, first, second): self.first = first self.second = second -class _Range(object): +class MetaRange(type): + def __init__(cls, name, bases, dict): + super(MetaRange, cls).__init__(name, bases, dict) + if name == 'Range': + return + cls._cpp_param_decl = 'Range<%s>' % cls.type._cpp_param_decl + def _convert(cls, value): if not isinstance(value, Pair): raise TypeError, 'value %s is not a Pair' % value - return Pair(cls._type._convert(value.first), - cls._type._convert(value.second)) - _convert = classmethod(_convert) + return Pair(cls.type._convert(value.first), + cls.type._convert(value.second)) def _string(cls, value): - return '%s:%s' % (cls._type._string(value.first), - cls._type._string(value.second)) - _string = classmethod(_string) + return '%s:%s' % (cls.type._string(value.first), + cls.type._string(value.second)) + +class Range(ParamType): + __metaclass__ = MetaRange def RangeSize(start, size): return Pair(start, start + size - 1) -class Range(type): - def __new__(cls, type): - dict = { '_cpp_param_decl' : 'Range<%s>' % type._cpp_param_decl, - '_type' : type } - clsname = 'Range_' + type.__name__ - return super(cls, Range).__new__(cls, clsname, (_Range, ), dict) - -AddrRange = Range(Addr) +class AddrRange(Range): type = Addr # Boolean parameter type. -class Bool(object): +class Bool(ParamType): _cpp_param_decl = 'bool' def _convert(value): t = type(value) @@ -1094,7 +1134,7 @@ class Bool(object): _string = staticmethod(_string) # String-valued parameter. -class String(object): +class String(ParamType): _cpp_param_decl = 'string' # Constructor. Value must be Python string. @@ -1134,7 +1174,7 @@ class NextEthernetAddr(object): self.value = self.addr self.addr = IncEthernetAddr(self.addr, inc) -class EthernetAddr(object): +class EthernetAddr(ParamType): _cpp_param_decl = 'EthAddr' def _convert(cls, value): @@ -1241,7 +1281,7 @@ class MetaEnum(type): return s # Base class for enum types. -class Enum(object): +class Enum(ParamType): __metaclass__ = MetaEnum vals = [] @@ -1261,8 +1301,8 @@ class Enum(object): # # Some memory range specifications use this as a default upper bound. -MAX_ADDR = Addr._max -MaxTick = Tick._max +MAX_ADDR = Addr.max +MaxTick = Tick.max # For power-of-two sizing, e.g. 64*K gives an integer value 65536. K = 1024 @@ -1274,9 +1314,6 @@ G = K*M # The final hook to generate .ini files. Called from configuration # script once config is built. def instantiate(root): - if not issubclass(root, Root): - raise AttributeError, 'Can only instantiate the Root of the tree' - instance = root.instantiate('root') instance.fixup() instance.display() @@ -1297,8 +1334,17 @@ def instantiate(root): # parameters to be set by keyword in the constructor. Note that most # of the heavy lifting for the SimObject param handling is done in the # MetaConfigNode metaclass. -class SimObject(ConfigNode): +class SimObject(ConfigNode, ParamType): __metaclass__ = MetaSimObject type = 'SimObject' -from objects import * + +__all__ = ['env', + 'ConfigNode', 'SimObject', 'ParamContext', 'Param', 'VectorParam', + 'Super', 'Enum', + 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', + 'Int32', 'UInt32', 'Int64', 'UInt64', + 'Counter', 'Addr', 'Tick', 'Percent', + 'Pair', 'RangeSize', 'AddrRange', 'MAX_ADDR', 'NULL', 'K', 'M', + 'NextEthernetAddr', + 'instantiate']