python: Add DeprecatedParam type
There are times when we need to change the name of parameter, but this breaks the external-facing python API used in configuration files. Using this "type" for a parameter will warn users that they are using the old name, but allow for backwards compatibility. Declaring a SimObject parameter of type `DeprecatedParam` allows the python configuration files to use the old name transparently. This leverages some of the SimObject magic to remember the names of deprecated parameters and the DeprecatedParam object stores the "translation" from old name to new name. This has been tested with Ports, "normal" parameters, and SimObject parameters. It has not been tested with checkpointing as there are no checkpointing tests in gem5 right now. The testing was manually adding some deprecated params and checking that config scripts still run correctly that use the old, deprecated, variables. Change-Id: I0465a748c08a24278d6b1a9d9ee1bcd67baa5b13 Signed-off-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/31954 Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Maintainer: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
committed by
Jason Lowe-Power
parent
df639a1c28
commit
b1245973be
@@ -467,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
|
||||
@@ -495,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
|
||||
@@ -532,6 +539,15 @@ 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 key in cls.init_keywords:
|
||||
cls._set_keyword(key, val, cls.init_keywords[key])
|
||||
@@ -604,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):
|
||||
@@ -1255,6 +1283,11 @@ class SimObject(object):
|
||||
return ref
|
||||
|
||||
def __getattr__(self, 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)
|
||||
|
||||
@@ -1287,6 +1320,11 @@ class SimObject(object):
|
||||
object.__setattr__(self, attr, value)
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
@@ -2155,6 +2155,70 @@ class PortParamDesc(with_metaclass(Singleton, object)):
|
||||
ptype_str = 'Port'
|
||||
ptype = Port
|
||||
|
||||
class DeprecatedParam(object):
|
||||
"""A special type for deprecated parameter variable names.
|
||||
|
||||
There are times when we need to change the name of parameter, but this
|
||||
breaks the external-facing python API used in configuration files. Using
|
||||
this "type" for a parameter will warn users that they are using the old
|
||||
name, but allow for backwards compatibility.
|
||||
|
||||
Usage example:
|
||||
In the following example, the `time` parameter is changed to `delay`.
|
||||
|
||||
```
|
||||
class SomeDevice(SimObject):
|
||||
delay = Param.Latency('1ns', 'The time to wait before something')
|
||||
time = DeprecatedParam(delay, '`time` is now called `delay`')
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(self, new_param, message=''):
|
||||
"""new_param: the new parameter variable that users should be using
|
||||
instead of this parameter variable.
|
||||
message: an optional message to print when warning the user
|
||||
"""
|
||||
self.message = message
|
||||
self.newParam = new_param
|
||||
# Note: We won't know the string variable names until later in the
|
||||
# SimObject initialization process. Note: we expect that the setters
|
||||
# will be called when the SimObject type (class) is initialized so
|
||||
# these variables should be filled in before the instance of the
|
||||
# SimObject with this parameter is constructed
|
||||
self._oldName = ''
|
||||
self._newName = ''
|
||||
|
||||
@property
|
||||
def oldName(self):
|
||||
assert(self._oldName != '') # should already be set
|
||||
return self._oldName
|
||||
|
||||
@oldName.setter
|
||||
def oldName(self, name):
|
||||
assert(self._oldName == '') # Cannot "re-set" this value
|
||||
self._oldName = name
|
||||
|
||||
@property
|
||||
def newName(self):
|
||||
assert(self._newName != '') # should already be set
|
||||
return self._newName
|
||||
|
||||
@newName.setter
|
||||
def newName(self, name):
|
||||
assert(self._newName == '') # Cannot "re-set" this value
|
||||
self._newName = name
|
||||
|
||||
def printWarning(self, instance_name, simobj_name):
|
||||
"""Issue a warning that this variable name should not be used.
|
||||
|
||||
instance_name: str, the name of the instance used in python
|
||||
simobj_name: str, the name of the SimObject type
|
||||
"""
|
||||
if not self.message:
|
||||
self.message = "See {} for more information".format(simobj_name)
|
||||
warn('{}.{} is deprecated. {}'.format(
|
||||
instance_name, self._oldName, self.message))
|
||||
|
||||
baseEnums = allEnums.copy()
|
||||
baseParams = allParams.copy()
|
||||
|
||||
@@ -2180,4 +2244,6 @@ __all__ = ['Param', 'VectorParam',
|
||||
'NextEthernetAddr', 'NULL',
|
||||
'Port', 'RequestPort', 'ResponsePort', 'MasterPort', 'SlavePort',
|
||||
'VectorPort', 'VectorRequestPort', 'VectorResponsePort',
|
||||
'VectorMasterPort', 'VectorSlavePort']
|
||||
'VectorMasterPort', 'VectorSlavePort',
|
||||
'DeprecatedParam',
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user