Merge into master from pull request #186:
pyloxi: parse unknown subclasses as the superclass (https://github.com/floodlight/loxigen/pull/186)
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index 7e28a0c..3b56308 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -150,12 +150,6 @@
pack='%s.pack()',
unpack='%s.unpack(%%s)' % pyclass)
-# Special case for lists of hello_elem, which must ignore unknown types
-type_data_map['list(of_hello_elem_t)'] = OFTypeData(
- init='[]',
- pack='loxi.generic_util.pack_list(%s)',
- unpack='util.unpack_list_hello_elem(%s)')
-
## Public interface
def lookup_type_data(oftype, version):
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index f902ae8..dfcd1fc 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -1,9 +1,19 @@
:: superclass_pyname = ofclass.superclass.pyname if ofclass.superclass else "loxi.OFObject"
:: from loxi_ir import *
:: import py_gen.oftype
+:: import py_gen.util as util
:: type_members = [m for m in ofclass.members if type(m) == OFTypeMember]
-:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
+:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember or
+:: type(m) == OFDiscriminatorMember]
+:: if ofclass.virtual:
+:: discriminator_fmts = { 1: "B", 2: "!H", 4: "!L" }
+:: discriminator_fmt = discriminator_fmts[ofclass.discriminator.length]
+:: #endif
class ${ofclass.pyname}(${superclass_pyname}):
+:: if ofclass.virtual:
+ subtypes = {}
+
+:: #endif
:: for m in type_members:
${m.name} = ${m.value}
:: #endfor
@@ -29,6 +39,13 @@
@staticmethod
def unpack(reader):
+:: if ofclass.virtual:
+ subtype, = reader.peek(${repr(discriminator_fmt)}, ${ofclass.discriminator.offset})
+ subclass = ${ofclass.pyname}.subtypes.get(subtype)
+ if subclass:
+ return subclass.unpack(reader)
+
+:: #endif
obj = ${ofclass.pyname}()
:: include("_unpack.py", ofclass=ofclass)
return obj
diff --git a/py_gen/templates/_unpack.py b/py_gen/templates/_unpack.py
index 81f74c7..cce9de3 100644
--- a/py_gen/templates/_unpack.py
+++ b/py_gen/templates/_unpack.py
@@ -42,7 +42,7 @@
:: elif type(m) == OFTypeMember:
_${m.name} = ${gen_unpack_expr(m.oftype, 'reader', version=version)}
assert(_${m.name} == ${m.value})
-:: elif type(m) == OFDataMember:
+:: elif type(m) == OFDataMember or type(m) == OFDiscriminatorMember:
:: if m.name in field_length_members:
:: reader_expr = 'reader.slice(_%s)' % field_length_members[m.name].name
:: else:
diff --git a/py_gen/templates/_virtual_ofclass.py b/py_gen/templates/_virtual_ofclass.py
deleted file mode 100644
index 1e5dcc2..0000000
--- a/py_gen/templates/_virtual_ofclass.py
+++ /dev/null
@@ -1,23 +0,0 @@
-:: import py_gen.util as util
-:: superclass_pyname = ofclass.superclass.pyname if ofclass.superclass else "loxi.OFObject"
-:: fmts = { 1: "B", 2: "!H", 4: "!L" }
-:: fmt = fmts[ofclass.discriminator.length]
-:: trail = ' '.join([x.pyname for x in util.ancestors(ofclass)])
-class ${ofclass.pyname}(${superclass_pyname}):
- subtypes = {}
-
- @staticmethod
- def unpack(reader):
- subtype, = reader.peek(${repr(fmt)}, ${ofclass.discriminator.offset})
- try:
- subclass = ${ofclass.pyname}.subtypes[subtype]
- except KeyError:
- raise loxi.ProtocolError("unknown ${trail} subtype %#x" % subtype)
- return subclass.unpack(reader)
-
-:: # Register with our superclass
-:: if ofclass.superclass:
-:: type_field_name = ofclass.superclass.discriminator.name
-:: type_value = ofclass.member_by_name(type_field_name).value
-${superclass_pyname}.subtypes[${type_value}] = ${ofclass.pyname}
-:: #endif
diff --git a/py_gen/templates/module.py b/py_gen/templates/module.py
index 02f0002..dfe23e8 100644
--- a/py_gen/templates/module.py
+++ b/py_gen/templates/module.py
@@ -52,11 +52,7 @@
import loxi.generic_util
:: for ofclass in ofclasses:
-:: if ofclass.virtual:
-:: include('_virtual_ofclass.py', ofclass=ofclass)
-:: else:
:: include('_ofclass.py', ofclass=ofclass)
-:: #endif
:: #endfor
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index d690939..85181dc 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -173,16 +173,6 @@
x >>= 1
return value
-def unpack_list_hello_elem(reader):
- def deserializer(reader):
- typ, length, = reader.peek('!HH')
- reader = reader.slice(length)
- try:
- return common.hello_elem.unpack(reader)
- except loxi.ProtocolError:
- return None
- return [x for x in loxi.generic_util.unpack_list(reader, deserializer) if x != None]
-
def pack_checksum_128(value):
return struct.pack("!QQ", (value >> 64) & MASK64, value & MASK64)
diff --git a/py_gen/tests/of10.py b/py_gen/tests/of10.py
index 250000f..7e10335 100644
--- a/py_gen/tests/of10.py
+++ b/py_gen/tests/of10.py
@@ -112,10 +112,10 @@
with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
- def test_invalid_action_type(self):
+ def test_unknown_action_type(self):
buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
- with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action subtype'):
- loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
+ result = loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
+ self.assertEquals(result, [ofp.action.action(type=0xfffe)])
class TestConstants(unittest.TestCase):
def test_ports(self):
@@ -200,11 +200,19 @@
test_klasses = [x for x in ofp.message.__dict__.values()
if type(x) == type
and issubclass(x, ofp.message.message)
- and hasattr(x, 'pack')]
+ and not hasattr(x, 'subtypes')]
for klass in test_klasses:
self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
+ def test_parse_unknown_message(self):
+ import loxi
+ import loxi.of10 as ofp
+
+ buf = "\x01\xfe\x00\x08\x12\x34\x56\x78"
+ msg = ofp.message.parse_message(buf)
+ self.assertIsInstance(msg, ofp.message.message)
+
class TestUtils(unittest.TestCase):
def test_pretty_wildcards(self):
self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
@@ -231,7 +239,7 @@
for klass in mod.__dict__.values()
if isinstance(klass, type) and
issubclass(klass, loxi.OFObject) and
- hasattr(klass, 'pack')]
+ not hasattr(klass, 'subtypes')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
diff --git a/py_gen/tests/of11.py b/py_gen/tests/of11.py
index d620509..7ae6d8a 100644
--- a/py_gen/tests/of11.py
+++ b/py_gen/tests/of11.py
@@ -69,7 +69,7 @@
for klass in mod.__dict__.values()
if isinstance(klass, type) and
issubclass(klass, loxi.OFObject) and
- hasattr(klass, 'pack')]
+ not hasattr(klass, 'subtypes')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
diff --git a/py_gen/tests/of12.py b/py_gen/tests/of12.py
index 4774672..c463c50 100644
--- a/py_gen/tests/of12.py
+++ b/py_gen/tests/of12.py
@@ -78,7 +78,7 @@
for klass in mod.__dict__.values()
if isinstance(klass, type) and
issubclass(klass, loxi.OFObject) and
- hasattr(klass, 'pack')]
+ not hasattr(klass, 'subtypes')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 8e258df..c5a16b2 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -60,18 +60,6 @@
self.assertTrue(hasattr(loxi.of13, "message"))
self.assertTrue(hasattr(loxi.of13, "oxm"))
-class TestCommon(unittest.TestCase):
- def test_list_hello_elem_unpack(self):
- buf = ''.join([
- '\x00\x01\x00\x04', # versionbitmap
- '\x00\x00\x00\x04', # unknown type
- '\x00\x01\x00\x04', # versionbitmap
- ])
- l = ofp.util.unpack_list_hello_elem(OFReader(buf))
- self.assertEquals(len(l), 2)
- self.assertTrue(isinstance(l[0], ofp.hello_elem_versionbitmap))
- self.assertTrue(isinstance(l[1], ofp.hello_elem_versionbitmap))
-
# The majority of the serialization tests are created here using the files in
# the test_data directory.
class TestDataFiles(unittest.TestCase):
@@ -91,7 +79,7 @@
for klass in mod.__dict__.values()
if isinstance(klass, type) and
issubclass(klass, loxi.OFObject) and
- hasattr(klass, 'pack')]
+ not hasattr(klass, 'subtypes')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):