python: Make meta class declarations Python 3 safe
authorAndreas Sandberg <andreas.sandberg@arm.com>
Fri, 25 Jan 2019 11:40:53 +0000 (11:40 +0000)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Tue, 10 Mar 2020 09:35:56 +0000 (09:35 +0000)
Python 2.x and Python 3 use different meta class syntax. Fix this by
implementing meta classes using the add_metaclass decorator in the six
Python library.

Due to the way meta classes are implemented in six,
MetaParamValue.__new__ seems to be called twice for some classes. This
triggers an assertion which when param that checks that Param types
have only been registered once. I have turned this assertion into a
warning.

The assertion was triggered in params.CheckedInt and params.Enum. It
seems like the cause of the issue is that these classes have their own
meta classes (CheckedIntType and MetaEnum) that inherit from
MetaParamValue and a base class (ParamValue) that also inherits from
MetaParamValue.

Change-Id: I5dea08bf0558cfca57897a124cb131c78114e59e
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/26083
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>

src/python/m5/SimObject.py
src/python/m5/params.py
src/python/m5/util/code_formatter.py
src/python/m5/util/pybind.py

index b76db5c319cf557d3a2ec6db43485bc008fd257b..32ac7d1ddc2d01ffb7141a2384fd0294e9eb7131 100644 (file)
@@ -40,6 +40,7 @@
 
 from __future__ import print_function
 from __future__ import absolute_import
+from six import add_metaclass
 import six
 if six.PY3:
     long = int
@@ -1073,10 +1074,10 @@ class SimObjectCliWrapper(object):
 # 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
 
index 3593f95bbc3f9e7453840398121ca25c3f5b62b2..2ea614e95a6d0a41761d2423a82080c20a6a583a 100644 (file)
@@ -55,6 +55,7 @@
 #####################################################################
 
 from __future__ import print_function
+from six import add_metaclass
 import six
 if six.PY3:
     long = int
@@ -87,15 +88,17 @@ allParams = {}
 class MetaParamValue(type):
     def __new__(mcls, name, bases, dct):
         cls = super(MetaParamValue, mcls).__new__(mcls, name, bases, dct)
-        assert name not in allParams
+        if name in allParams:
+            warn("%s already exists in allParams. This may be caused by the " \
+                 "Python 2.7 compatibility layer." % (name, ))
         allParams[name] = cls
         return cls
 
 
 # Dummy base class to identify types that are legitimate for SimObject
 # parameters.
+@add_metaclass(MetaParamValue)
 class ParamValue(object):
-    __metaclass__ = MetaParamValue
     cmd_line_settable = False
 
     # Generate the code needed as a prerequisite for declaring a C++
@@ -233,8 +236,8 @@ class ParamDesc(object):
 # that the value is a vector (list) of the specified type instead of a
 # single value.
 
+@add_metaclass(MetaParamValue)
 class VectorParamValue(list):
-    __metaclass__ = MetaParamValue
     def __setattr__(self, attr, value):
         raise AttributeError("Not allowed to set %s on '%s'" % \
                              (attr, type(self).__name__))
@@ -585,8 +588,8 @@ class CheckedIntType(MetaParamValue):
 # class is subclassed to generate parameter classes with specific
 # bounds.  Initialization of the min and max bounds is done in the
 # metaclass CheckedIntType.__init__.
+@add_metaclass(CheckedIntType)
 class CheckedInt(NumericParamValue):
-    __metaclass__ = CheckedIntType
     cmd_line_settable = True
 
     def _check(self):
@@ -1294,7 +1297,6 @@ allEnums = {}
 # Metaclass for Enum types
 class MetaEnum(MetaParamValue):
     def __new__(mcls, name, bases, dict):
-        assert name not in allEnums
 
         cls = super(MetaEnum, mcls).__new__(mcls, name, bases, dict)
         allEnums[name] = cls
@@ -1445,8 +1447,8 @@ module_init(py::module &m_internal)
 
 
 # Base class for enum types.
+@add_metaclass(MetaEnum)
 class Enum(ParamValue):
-    __metaclass__ = MetaEnum
     vals = []
     cmd_line_settable = True
 
@@ -1499,8 +1501,8 @@ class Enum(ParamValue):
         return self.value
 
 # This param will generate a scoped c++ enum and its python bindings.
+@add_metaclass(MetaEnum)
 class ScopedEnum(Enum):
-    __metaclass__ = MetaEnum
     vals = []
     cmd_line_settable = True
 
@@ -1787,8 +1789,8 @@ class MemoryBandwidth(float,ParamValue):
 # make_param_value() above that lets these be assigned where a
 # SimObject is required.
 # only one copy of a particular node
+@add_metaclass(Singleton)
 class NullSimObject(object):
-    __metaclass__ = Singleton
     _name = 'Null'
 
     def __call__(cls):
@@ -2155,9 +2157,8 @@ VectorSlavePort = VectorResponsePort
 # 'Fake' ParamDesc for Port references to assign to the _pdesc slot of
 # proxy objects (via set_param_desc()) so that proxy error messages
 # make sense.
+@add_metaclass(Singleton)
 class PortParamDesc(object):
-    __metaclass__ = Singleton
-
     ptype_str = 'Port'
     ptype = Port
 
index 8d48d0f76682fc3c3af4bbd5f008b85903507dde..9870430d639de736439d2830fbb1f146e970b438 100644 (file)
@@ -25,6 +25,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 from __future__ import print_function
+from six import add_metaclass
 
 try:
     import builtins
@@ -112,9 +113,8 @@ class code_formatter_meta(type):
                 }
         cls.pattern = re.compile(pat, re.VERBOSE | re.DOTALL | re.MULTILINE)
 
+@add_metaclass(code_formatter_meta)
 class code_formatter(object):
-    __metaclass__ = code_formatter_meta
-
     delim = r'$'
     ident = r'[_A-z]\w*'
     pos = r'[0-9]+'
index f165044289affa95038068138920c7b5194b9241..18df3bb60c3bf44b886afe4cdcb1515f8b2860ee 100644 (file)
 
 from __future__ import print_function
 from __future__ import absolute_import
+from six import add_metaclass
 
 from abc import *
 
+@add_metaclass(ABCMeta)
 class PyBindExport(object):
-    __metaclass__ = ABCMeta
-
     @abstractmethod
     def export(self, code, cname):
         pass