X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fpython%2Fm5%2FSimObject.py;h=9c9a9ed0fe2b9a18a59648076e0b9b82248c018f;hb=392c1ce;hp=0c9e738f623268229872ccee582f031f5b797837;hpb=3bb22b7fc3e642533f8bb170e8de1de5e62d976c;p=gem5.git diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index 0c9e738f6..9c9a9ed0f 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 ARM Limited +# Copyright (c) 2017-2020 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -37,13 +37,13 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Authors: Steve Reinhardt -# Nathan Binkert -# Andreas Hansson -# Andreas Sandberg from __future__ import print_function +from __future__ import absolute_import +from six import add_metaclass +import six +if six.PY3: + long = int import sys from types import FunctionType, MethodType, ModuleType @@ -168,10 +168,6 @@ def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header): code('#include "base/str.hh"') code('#include "cxx_config/${name}.hh"') - if simobj._ports.values() != []: - code('#include "mem/mem_object.hh"') - code('#include "mem/port.hh"') - code() code('${member_prefix}DirectoryEntry::DirectoryEntry()'); code('{') @@ -190,11 +186,11 @@ def createCxxConfigDirectoryEntryFile(code, name, simobj, is_header): for port in simobj._ports.values(): is_vector = isinstance(port, m5.params.VectorPort) - is_master = port.role == 'MASTER' + is_requestor = port.role == 'GEM5 REQUESTOR' code('ports["%s"] = new PortDesc("%s", %s, %s);' % (port.name, port.name, cxx_bool(is_vector), - cxx_bool(is_master))) + cxx_bool(is_requestor))) code.dedent() code('}') @@ -411,6 +407,7 @@ class MetaSimObject(type): 'cxx_extra_bases' : list, 'cxx_exports' : list, 'cxx_param_exports' : list, + 'cxx_template_params' : list, } # Attributes that can be set any time keywords = { 'check' : FunctionType } @@ -450,6 +447,8 @@ class MetaSimObject(type): value_dict['cxx_exports'] += cxx_exports if 'cxx_param_exports' not in value_dict: value_dict['cxx_param_exports'] = [] + if 'cxx_template_params' not in value_dict: + value_dict['cxx_template_params'] = [] cls_dict['_value_dict'] = value_dict cls = super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) if 'type' in value_dict: @@ -468,6 +467,12 @@ class MetaSimObject(type): cls._params = multidict() # param descriptions cls._ports = multidict() # port descriptions + # Parameter names that are deprecated. Dict[str, DeprecatedParam] + # The key is the "old_name" so that when the old_name is used in + # python config files, we will use the DeprecatedParam object to + # translate to the new type. + cls._deprecated_params = multidict() + # class or instance attributes cls._values = multidict() # param values cls._hr_values = multidict() # human readable param values @@ -483,8 +488,8 @@ class MetaSimObject(type): if isinstance(c, MetaSimObject): bTotal += 1 if bTotal > 1: - raise TypeError, \ - "SimObjects do not support multiple inheritance" + raise TypeError( + "SimObjects do not support multiple inheritance") base = bases[0] @@ -496,6 +501,7 @@ class MetaSimObject(type): cls._base = base cls._params.parent = base._params cls._ports.parent = base._ports + cls._deprecated_params.parent = base._deprecated_params cls._values.parent = base._values cls._hr_values.parent = base._hr_values cls._children.parent = base._children @@ -533,8 +539,17 @@ class MetaSimObject(type): elif isinstance(val, Port): cls._new_port(key, val) + # Deprecated variable names + elif isinstance(val, DeprecatedParam): + new_name, new_val = cls._get_param_by_value(val.newParam) + # Note: We don't know the (string) name of this variable until + # here, so now we can finish setting up the dep_param. + val.oldName = key + val.newName = new_name + cls._deprecated_params[key] = val + # init-time-only keywords - elif cls.init_keywords.has_key(key): + elif key in cls.init_keywords: cls._set_keyword(key, val, cls.init_keywords[key]) # default: use normal path (ends up in __setattr__) @@ -543,8 +558,8 @@ class MetaSimObject(type): def _set_keyword(cls, keyword, val, kwtype): if not isinstance(val, kwtype): - raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ - (keyword, type(val), kwtype) + raise TypeError('keyword %s has bad type %s (expecting %s)' % \ + (keyword, type(val), kwtype)) if isinstance(val, FunctionType): val = classmethod(val) type.__setattr__(cls, keyword, val) @@ -562,7 +577,7 @@ class MetaSimObject(type): try: hr_value = value value = param.convert(value) - except Exception, e: + except Exception as e: msg = "%s\nError setting param %s.%s to %s\n" % \ (e, cls.__name__, name, value) e.args = (msg, ) @@ -605,6 +620,18 @@ class MetaSimObject(type): cls._port_refs[attr] = ref return ref + def _get_param_by_value(cls, value): + """Given an object, value, return the name and the value from the + internal list of parameter values. If this value can't be found, raise + a runtime error. This will search both the current object and its + parents. + """ + for k,v in cls._value_dict.items(): + if v == value: + return k,v + raise RuntimeError("Cannot find parameter {} in parameter list" + .format(value)) + # Set attribute (called on foo.attr = value when foo is an # instance of class cls). def __setattr__(cls, attr, value): @@ -613,19 +640,19 @@ class MetaSimObject(type): type.__setattr__(cls, attr, value) return - if cls.keywords.has_key(attr): + if attr in cls.keywords: cls._set_keyword(attr, value, cls.keywords[attr]) return - if cls._ports.has_key(attr): + if attr in cls._ports: cls._cls_get_port_ref(attr).connect(value) return if isSimObjectOrSequence(value) and cls._instantiated: - raise RuntimeError, \ + raise RuntimeError( "cannot set SimObject parameter '%s' after\n" \ " class %s has been instantiated or subclassed" \ - % (attr, cls.__name__) + % (attr, cls.__name__)) # check for param param = cls._params.get(attr) @@ -639,8 +666,8 @@ class MetaSimObject(type): return # no valid assignment... raise exception - raise AttributeError, \ - "Class %s has no parameter \'%s\'" % (cls.__name__, attr) + raise AttributeError( + "Class %s has no parameter \'%s\'" % (cls.__name__, attr)) def __getattr__(cls, attr): if attr == 'cxx_class_path': @@ -652,18 +679,27 @@ class MetaSimObject(type): if attr == 'cxx_namespaces': return cls.cxx_class_path[:-1] - if cls._values.has_key(attr): + if attr == 'pybind_class': + return '_COLONS_'.join(cls.cxx_class_path) + + if attr in cls._values: return cls._values[attr] - if cls._children.has_key(attr): + if attr in cls._children: return cls._children[attr] - raise AttributeError, \ - "object '%s' has no attribute '%s'" % (cls.__name__, attr) + try: + return getattr(cls.getCCClass(), attr) + except AttributeError: + raise AttributeError( + "object '%s' has no attribute '%s'" % (cls.__name__, attr)) def __str__(cls): return cls.__name__ + def getCCClass(cls): + return getattr(m5.internal.params, cls.pybind_class) + # See ParamValue.cxx_predecls for description. def cxx_predecls(cls, code): code('#include "params/$cls.hh"') @@ -672,16 +708,13 @@ class MetaSimObject(type): code('#include "${{cls.cxx_header}}"') def pybind_decl(cls, code): - class_path = cls.cxx_class.split('::') - namespaces, classname = class_path[:-1], class_path[-1] - py_class_name = '_COLONS_'.join(class_path) if namespaces else \ - classname; + py_class_name = cls.pybind_class # The 'local' attribute restricts us to the params declared in # the object itself, not including inherited params (which # will also be inherited from the base class's param struct # here). Sort the params based on their key - params = map(lambda (k, v): v, sorted(cls._params.local.items())) + params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items()))) ports = cls._ports.local code('''#include "pybind11/pybind11.h" @@ -726,7 +759,7 @@ module_init(py::module &m_internal) for k, v in sorted(cls._params.local.items()) ] + [ PyBindProperty("port_%s_connection_count" % port.name) - for port in ports.itervalues() + for port in ports.values() ] for exp in param_exports: exp.export(code, "%sParams" % cls) @@ -769,6 +802,7 @@ module_init(py::module &m_internal) code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");', cls, cls._base.type if cls._base else "") + _warned_about_nested_templates = False # Generate the C++ declaration (.hh file) for this SimObject's # param struct. Called from src/SConscript. @@ -777,7 +811,7 @@ module_init(py::module &m_internal) # the object itself, not including inherited params (which # will also be inherited from the base class's param struct # here). Sort the params based on their key - params = map(lambda (k, v): v, sorted(cls._params.local.items())) + params = list(map(lambda k_v: k_v[1], sorted(cls._params.local.items()))) ports = cls._ports.local try: ptypes = [p.ptype for p in params] @@ -786,7 +820,78 @@ module_init(py::module &m_internal) print(params) raise - class_path = cls._value_dict['cxx_class'].split('::') + class CxxClass(object): + def __init__(self, sig, template_params=[]): + # Split the signature into its constituent parts. This could + # potentially be done with regular expressions, but + # it's simple enough to pick appart a class signature + # manually. + parts = sig.split('<', 1) + base = parts[0] + t_args = [] + if len(parts) > 1: + # The signature had template arguments. + text = parts[1].rstrip(' \t\n>') + arg = '' + # Keep track of nesting to avoid splitting on ","s embedded + # in the arguments themselves. + depth = 0 + for c in text: + if c == '<': + depth = depth + 1 + if depth > 0 and not \ + self._warned_about_nested_templates: + self._warned_about_nested_templates = True + print('Nested template argument in cxx_class.' + ' This feature is largely untested and ' + ' may not work.') + elif c == '>': + depth = depth - 1 + elif c == ',' and depth == 0: + t_args.append(arg.strip()) + arg = '' + else: + arg = arg + c + if arg: + t_args.append(arg.strip()) + # Split the non-template part on :: boundaries. + class_path = base.split('::') + + # The namespaces are everything except the last part of the + # class path. + self.namespaces = class_path[:-1] + # And the class name is the last part. + self.name = class_path[-1] + + self.template_params = template_params + self.template_arguments = [] + # Iterate through the template arguments and their values. This + # will likely break if parameter packs are used. + for arg, param in zip(t_args, template_params): + type_keys = ('class', 'typename') + # If a parameter is a type, parse it recursively. Otherwise + # assume it's a constant, and store it verbatim. + if any(param.strip().startswith(kw) for kw in type_keys): + self.template_arguments.append(CxxClass(arg)) + else: + self.template_arguments.append(arg) + + def declare(self, code): + # First declare any template argument types. + for arg in self.template_arguments: + if isinstance(arg, CxxClass): + arg.declare(code) + # Re-open the target namespace. + for ns in self.namespaces: + code('namespace $ns {') + # If this is a class template... + if self.template_params: + code('template <${{", ".join(self.template_params)}}>') + # The actual class declaration. + code('class ${{self.name}};') + # Close the target namespaces. + for ns in reversed(self.namespaces): + code('} // namespace $ns') code('''\ #ifndef __PARAMS__${cls}__ @@ -802,18 +907,16 @@ module_init(py::module &m_internal) if cls == SimObject: code('''#include ''') + cxx_class = CxxClass(cls._value_dict['cxx_class'], + cls._value_dict['cxx_template_params']) + # A forward class declaration is sufficient since we are just # declaring a pointer. - for ns in class_path[:-1]: - code('namespace $ns {') - code('class $0;', class_path[-1]) - for ns in reversed(class_path[:-1]): - code('} // namespace $ns') - code() + cxx_class.declare(code) for param in params: param.cxx_predecls(code) - for port in ports.itervalues(): + for port in ports.values(): port.cxx_predecls(code) code() @@ -846,7 +949,7 @@ module_init(py::module &m_internal) for param in params: param.cxx_decl(code) - for port in ports.itervalues(): + for port in ports.values(): port.cxx_decl(code) code.dedent() @@ -875,9 +978,11 @@ def cxxMethod(*args, **kwargs): """Decorator to export C++ functions to Python""" def decorate(func): - name = func.func_name + name = func.__name__ override = kwargs.get("override", False) cxx_name = kwargs.get("cxx_name", name) + return_value_policy = kwargs.get("return_value_policy", None) + static = kwargs.get("static", False) args, varargs, keywords, defaults = inspect.getargspec(func) if varargs or keywords: @@ -886,22 +991,25 @@ def cxxMethod(*args, **kwargs): # Create tuples of (argument, default) if defaults: - args = args[:-len(defaults)] + zip(args[-len(defaults):], defaults) + args = args[:-len(defaults)] + \ + list(zip(args[-len(defaults):], defaults)) # Don't include self in the argument list to PyBind args = args[1:] @wraps(func) def cxx_call(self, *args, **kwargs): - ccobj = self.getCCObject() + ccobj = self.getCCClass() if static else self.getCCObject() return getattr(ccobj, name)(*args, **kwargs) @wraps(func) def py_call(self, *args, **kwargs): - return self.func(*args, **kwargs) + return func(self, *args, **kwargs) f = py_call if override else cxx_call - f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args) + f.__pybind = PyBindMethod(name, cxx_name=cxx_name, args=args, + return_value_policy=return_value_policy, + static=static) return f @@ -934,18 +1042,78 @@ class ParamInfo(object): if not "created" in self.__dict__: self.__dict__[name] = value +class SimObjectCliWrapperException(Exception): + def __init__(self, message): + super(Exception, self).__init__(message) + +class SimObjectCliWrapper(object): + """ + Wrapper class to restrict operations that may be done + from the command line on SimObjects. + + Only parameters may be set, and only children may be accessed. + + Slicing allows for multiple simultaneous assignment of items in + one statement. + """ + + def __init__(self, sim_objects): + self.__dict__['_sim_objects'] = list(sim_objects) + + def __getattr__(self, key): + return SimObjectCliWrapper(sim_object._children[key] + for sim_object in self._sim_objects) + + def __setattr__(self, key, val): + for sim_object in self._sim_objects: + if key in sim_object._params: + if sim_object._params[key].isCmdLineSettable(): + setattr(sim_object, key, val) + else: + raise SimObjectCliWrapperException( + 'tried to set or unsettable' \ + 'object parameter: ' + key) + else: + raise SimObjectCliWrapperException( + 'tried to set or access non-existent' \ + 'object parameter: ' + key) + + def __getitem__(self, idx): + """ + Extends the list() semantics to also allow tuples, + for example object[1, 3] selects items 1 and 3. + """ + out = [] + if isinstance(idx, tuple): + for t in idx: + out.extend(self[t]._sim_objects) + else: + if isinstance(idx, int): + _range = range(idx, idx + 1) + elif not isinstance(idx, slice): + raise SimObjectCliWrapperException( \ + 'invalid index type: ' + repr(idx)) + for sim_object in self._sim_objects: + if isinstance(idx, slice): + _range = range(*idx.indices(len(sim_object))) + out.extend(sim_object[i] for i in _range) + return SimObjectCliWrapper(out) + + def __iter__(self): + return iter(self._sim_objects) + # The SimObject class is the root of the special hierarchy. Most of # the code in this class deals with the configuration hierarchy itself # (parent/child node relationships). +@add_metaclass(MetaSimObject) class SimObject(object): # Specify metaclass. Any class inheriting from SimObject will # get this metaclass. - __metaclass__ = MetaSimObject type = 'SimObject' abstract = True cxx_header = "sim/sim_object.hh" - cxx_extra_bases = [ "Drainable", "Serializable" ] + cxx_extra_bases = [ "Drainable", "Serializable", "Stats::Group" ] eventq_index = Param.UInt32(Parent.eventq_index, "Event Queue Index") cxx_exports = [ @@ -953,8 +1121,6 @@ class SimObject(object): PyBindMethod("initState"), PyBindMethod("memInvalidate"), PyBindMethod("memWriteback"), - PyBindMethod("regStats"), - PyBindMethod("resetStats"), PyBindMethod("regProbePoints"), PyBindMethod("regProbeListeners"), PyBindMethod("startup"), @@ -1061,7 +1227,7 @@ class SimObject(object): # Do children before parameter values so that children that # are also param values get cloned properly. self._children = {} - for key,val in ancestor._children.iteritems(): + for key,val in ancestor._children.items(): self.add_child(key, val(_memo=memo_dict)) # Inherit parameter values from class using multidict so @@ -1070,7 +1236,7 @@ class SimObject(object): self._values = multidict(ancestor._values) self._hr_values = multidict(ancestor._hr_values) # clone SimObject-valued parameters - for key,val in ancestor._values.iteritems(): + for key,val in ancestor._values.items(): val = tryAsSimObjectOrVector(val) if val is not None: self._values[key] = val(_memo=memo_dict) @@ -1078,10 +1244,10 @@ class SimObject(object): # clone port references. no need to use a multidict here # since we will be creating new references for all ports. self._port_refs = {} - for key,val in ancestor._port_refs.iteritems(): + for key,val in ancestor._port_refs.items(): self._port_refs[key] = val.clone(self, memo_dict) # apply attribute assignments from keyword args, if any - for key,val in kwargs.iteritems(): + for key,val in kwargs.items(): setattr(self, key, val) # "Clone" the current instance by creating another instance of @@ -1095,13 +1261,13 @@ class SimObject(object): # no memo_dict: must be top-level clone operation. # this is only allowed at the root of a hierarchy if self._parent: - raise RuntimeError, "attempt to clone object %s " \ + raise RuntimeError("attempt to clone object %s " \ "not at the root of a tree (parent = %s)" \ - % (self, self._parent) + % (self, self._parent)) # create a new dict and use that. memo_dict = {} kwargs['_memo'] = memo_dict - elif memo_dict.has_key(self): + elif self in memo_dict: # clone already done & memoized return memo_dict[self] return self.__class__(_ancestor = self, **kwargs) @@ -1117,13 +1283,18 @@ class SimObject(object): return ref def __getattr__(self, attr): - if self._ports.has_key(attr): + if attr in self._deprecated_params: + dep_param = self._deprecated_params[attr] + dep_param.printWarning(self._name, self.__class__.__name__) + return getattr(self, self._deprecated_params[attr].newName) + + if attr in self._ports: return self._get_port_ref(attr) - if self._values.has_key(attr): + if attr in self._values: return self._values[attr] - if self._children.has_key(attr): + if attr in self._children: return self._children[attr] # If the attribute exists on the C++ object, transparently @@ -1139,7 +1310,7 @@ class SimObject(object): err_string += "\n (C++ object is not yet constructed," \ " so wrapped C++ methods are unavailable.)" - raise AttributeError, err_string + raise AttributeError(err_string) # Set attribute (called on foo.attr = value when foo is an # instance of class cls). @@ -1149,7 +1320,12 @@ class SimObject(object): object.__setattr__(self, attr, value) return - if self._ports.has_key(attr): + if attr in self._deprecated_params: + dep_param = self._deprecated_params[attr] + dep_param.printWarning(self._name, self.__class__.__name__) + return setattr(self, self._deprecated_params[attr].newName, value) + + if attr in self._ports: # set up port connection self._get_port_ref(attr).connect(value) return @@ -1159,7 +1335,7 @@ class SimObject(object): try: hr_value = value value = param.convert(value) - except Exception, e: + except Exception as e: msg = "%s\nError setting param %s.%s to %s\n" % \ (e, self.__class__.__name__, attr, value) e.args = (msg, ) @@ -1183,8 +1359,8 @@ class SimObject(object): return # no valid assignment... raise exception - raise AttributeError, "Class %s has no parameter %s" \ - % (self.__class__.__name__, attr) + raise AttributeError("Class %s has no parameter %s" \ + % (self.__class__.__name__, attr)) # this hack allows tacking a '[0]' onto parameters that may or may @@ -1192,7 +1368,7 @@ class SimObject(object): def __getitem__(self, key): if key == 0: return self - raise IndexError, "Non-zero index '%s' to SimObject" % key + raise IndexError("Non-zero index '%s' to SimObject" % key) # this hack allows us to iterate over a SimObject that may # not be a vector, so we can call a loop over it and get just one @@ -1237,7 +1413,7 @@ class SimObject(object): if child.has_parent(): warn("add_child('%s'): child '%s' already has parent", name, child.get_name()) - if self._children.has_key(name): + if name in self._children: # This code path had an undiscovered bug that would make it fail # at runtime. It had been here for a long time and was only # exposed by a buggy script. Changes here will probably not be @@ -1253,7 +1429,7 @@ class SimObject(object): # that when we instantiate all the parameter objects we're still # inside the configuration hierarchy. def adoptOrphanParams(self): - for key,val in self._values.iteritems(): + for key,val in self._values.items(): if not isSimObjectVector(val) and isSimObjectSequence(val): # need to convert raw SimObject sequences to # SimObjectVector class so we can call has_parent() @@ -1274,6 +1450,13 @@ class SimObject(object): return self._name return ppath + "." + self._name + def path_list(self): + if self._parent: + return self._parent.path_list() + [ self._name, ] + else: + # Don't include the root node + return [] + def __str__(self): return self.path() @@ -1288,32 +1471,32 @@ class SimObject(object): return self, True found_obj = None - for child in self._children.itervalues(): + for child in self._children.values(): visited = False if hasattr(child, '_visited'): visited = getattr(child, '_visited') if isinstance(child, ptype) and not visited: if found_obj != None and child != found_obj: - raise AttributeError, \ + raise AttributeError( 'parent.any matched more than one: %s %s' % \ - (found_obj.path, child.path) + (found_obj.path, child.path)) found_obj = child # search param space - for pname,pdesc in self._params.iteritems(): + for pname,pdesc in self._params.items(): if issubclass(pdesc.ptype, ptype): match_obj = self._values[pname] if found_obj != None and found_obj != match_obj: - raise AttributeError, \ + raise AttributeError( 'parent.any matched more than one: %s and %s' % \ - (found_obj.path, match_obj.path) + (found_obj.path, match_obj.path)) found_obj = match_obj return found_obj, found_obj != None def find_all(self, ptype): all = {} # search children - for child in self._children.itervalues(): + for child in self._children.values(): # a child could be a list, so ensure we visit each item if isinstance(child, list): children = child @@ -1329,7 +1512,7 @@ class SimObject(object): child_all, done = child.find_all(ptype) all.update(dict(zip(child_all, [done] * len(child_all)))) # search param space - for pname,pdesc in self._params.iteritems(): + for pname,pdesc in self._params.items(): if issubclass(pdesc.ptype, ptype): match_obj = self._values[pname] if not isproxy(match_obj) and not isNullPointer(match_obj): @@ -1342,7 +1525,7 @@ class SimObject(object): return self def unproxyParams(self): - for param in self._params.iterkeys(): + for param in self._params.keys(): value = self._values.get(param) if value != None and isproxy(value): try: @@ -1355,7 +1538,7 @@ class SimObject(object): # Unproxy ports in sorted order so that 'append' operations on # vector ports are done in a deterministic fashion. - port_names = self._ports.keys() + port_names = list(self._ports.keys()) port_names.sort() for port_name in port_names: port = self._port_refs.get(port_name) @@ -1432,7 +1615,7 @@ class SimObject(object): cc_params = cc_params_struct() cc_params.name = str(self) - param_names = self._params.keys() + param_names = list(self._params.keys()) param_names.sort() for param in param_names: value = self._values.get(param) @@ -1456,7 +1639,7 @@ class SimObject(object): else: setattr(cc_params, param, value) - port_names = self._ports.keys() + port_names = list(self._ports.keys()) port_names.sort() for port_name in port_names: port = self._port_refs.get(port_name, None) @@ -1476,7 +1659,7 @@ class SimObject(object): if not self._ccObject: # Make sure this object is in the configuration hierarchy if not self._parent and not isRoot(self): - raise RuntimeError, "Attempt to instantiate orphan node" + raise RuntimeError("Attempt to instantiate orphan node") # Cycles in the configuration hierarchy are not supported. This # will catch the resulting recursion and stop. self._ccObject = -1 @@ -1484,8 +1667,8 @@ class SimObject(object): params = self.getCCParams() self._ccObject = params.create() elif self._ccObject == -1: - raise RuntimeError, "%s: Cycle found in configuration hierarchy." \ - % self.path() + raise RuntimeError("%s: Cycle found in configuration hierarchy." \ + % self.path()) return self._ccObject def descendants(self): @@ -1493,7 +1676,7 @@ class SimObject(object): # The order of the dict is implementation dependent, so sort # it based on the key (name) to ensure the order is the same # on all hosts - for (name, child) in sorted(self._children.iteritems()): + for (name, child) in sorted(self._children.items()): for obj in child.descendants(): yield obj @@ -1505,12 +1688,16 @@ class SimObject(object): def getValue(self): return self.getCCObject() + @cxxMethod(return_value_policy="reference") + def getPort(self, if_name, idx): + pass + # Create C++ port connections corresponding to the connections in # _port_refs def connectPorts(self): # Sort the ports based on their attribute name to ensure the # order is the same on all hosts - for (attr, portRef) in sorted(self._port_refs.iteritems()): + for (attr, portRef) in sorted(self._port_refs.items()): portRef.ccConnect() # Default function for generating the device structure. @@ -1520,11 +1707,46 @@ class SimObject(object): yield # make this function a (null) generator def recurseDeviceTree(self, state): - for child in [getattr(self, c) for c in self._children]: + for child in self._children.values(): for item in child: # For looping over SimObjectVectors - if isinstance(item, SimObject): - for dt in item.generateDeviceTree(state): - yield dt + for dt in item.generateDeviceTree(state): + yield dt + + # On a separate method otherwise certain buggy Python versions + # would fail with: SyntaxError: unqualified exec is not allowed + # in function 'apply_config' + def _apply_config_get_dict(self): + return { + child_name: SimObjectCliWrapper( + iter(self._children[child_name])) + for child_name in self._children + } + + def apply_config(self, params): + """ + exec a list of Python code strings contained in params. + + The only exposed globals to those strings are the child + SimObjects of this node. + + This function is intended to allow users to modify SimObject + parameters from the command line with Python statements. + """ + d = self._apply_config_get_dict() + for param in params: + exec(param, d) + + def get_simobj(self, simobj_path): + """ + Get all sim objects that match a given string. + + The format is the same as that supported by SimObjectCliWrapper. + + :param simobj_path: Current state to be in. + :type simobj_path: str + """ + d = self._apply_config_get_dict() + return eval(simobj_path, d) # Function to provide to C++ so it can look up instances based on paths def resolveSimObject(name): @@ -1570,7 +1792,7 @@ def tryAsSimObjectOrVector(value): def coerceSimObjectOrVector(value): value = tryAsSimObjectOrVector(value) if value is None: - raise TypeError, "SimObject or SimObjectVector expected" + raise TypeError("SimObject or SimObjectVector expected") return value baseClasses = allClasses.copy()