SEBA-324 Automatic offsetting of reverse ids
Change-Id: I1bb0bfb245656b36a466f1550093d896043586f4
diff --git a/lib/xos-genx/xos-genx-tests/test_generator.py b/lib/xos-genx/xos-genx-tests/test_generator.py
index 06da7ee..3daa594 100644
--- a/lib/xos-genx/xos-genx-tests/test_generator.py
+++ b/lib/xos-genx/xos-genx-tests/test_generator.py
@@ -33,6 +33,7 @@
BASE_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/base.xproto")
TEST_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/test.xproto")
FIELDTEST_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/fieldtest.xproto")
+REVERSEFIELDTEST_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/reversefieldtest.xproto")
FILTERTEST_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/filtertest.xproto")
SKIP_DJANGO_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/skip_django.xproto")
VROUTER_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/vrouterport.xproto")
@@ -222,6 +223,31 @@
_assert_field("Slice", "slice_field", 101)
_assert_field("Slice", "site", 102)
+ def test_field_numbers(self):
+ args = XOSProcessorArgs(files = [REVERSEFIELDTEST_XPROTO],
+ target = FIELDTEST_TARGET)
+ output = XOSProcessor.process(args)
+
+ def _assert_field(modelname, fieldname, id):
+ self.assertIn("%s,%s,%s" % (modelname, fieldname, id), output)
+
+ # rel_int1s_ids is the reverse link from RelatedToIntermediate1. It gets the related id with no offset, so it
+ # will be assigned 1001. rel_leaf1as_ids inherits from Intermediate1, so its reverse links will all be offset
+ # by 100
+ _assert_field("Leaf1a", "rel_int1s_ids", 1001)
+ _assert_field("Leaf1a", "rel_leaf1as_ids", 1101)
+
+ # rel_int2s_ids is the reverse link from RelatedToIntermediate1. It gets the related id with no offset, so it
+ # will be assigned 1001. rel_leaf1bs_ids inherits from Intermediate1, so its reverse links will all be offset
+ # by 100
+ _assert_field("Leaf1b", "rel_int1s_ids", 1001)
+ _assert_field("Leaf1b", "rel_leaf1bs_ids", 1101)
+
+ # There are no reverse numbers specified for Intermediate2 or Leaf2, so xproto will fall back to automatic
+ # numbering starting at 1900.
+ _assert_field("Leaf2", "rel_int2s_ids", 1900)
+ _assert_field("Leaf2", "rel_leaf2s_ids", 1901)
+
def test_unfiltered(self):
""" With no include_* args, should get all models """
args = XOSProcessorArgs(files = [FILTERTEST_XPROTO],
diff --git a/lib/xos-genx/xos-genx-tests/xproto/reversefieldtest.xproto b/lib/xos-genx/xos-genx-tests/xproto/reversefieldtest.xproto
new file mode 100644
index 0000000..93b04e3
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xproto/reversefieldtest.xproto
@@ -0,0 +1,42 @@
+option app_label = "core";
+
+message XOSBase {
+ optional string base_field = 2 [default = "stuff", max_length = 1024];
+ optional string base_field2 = 3 [default = "stuff", max_length = 1024];
+}
+
+message Intermediate1(XOSBase) {
+}
+
+message Leaf1a(Intermediate1) {
+}
+
+message Leaf1b(Intermediate1) {
+}
+
+
+message Intermediate2(XOSBase) {
+}
+
+message Leaf2(Intermediate2) {
+}
+
+message RelatedToIntermediate1(XOSBase) {
+ required manytoone int1->Intermediate1:rel_int1s = 2:1001 [help_text = "The Intermediate1 this model is attached to", null = False, db_index = True, blank = False];
+}
+
+message RelatedToLeaf1a(XOSBase) {
+ required manytoone leaf1a->Leaf1a:rel_leaf1as = 2:1001 [help_text = "The Leaf1a this model is attached to", null = False, db_index = True, blank = False];
+}
+
+message RelatedToLeaf1b(XOSBase) {
+ required manytoone leaf1b->Leaf1b:rel_leaf1bs = 2:1001 [help_text = "The Leaf1b this model is attached to", null = False, db_index = True, blank = False];
+}
+
+message RelatedToIntermediate2(XOSBase) {
+ required manytoone int2->Intermediate2:rel_int2s = 2 [help_text = "The Intermediate2 this model is attached to", null = False, db_index = True, blank = False];
+}
+
+message RelatedToLeaf2(XOSBase) {
+ required manytoone leaf2->Leaf2:rel_leaf2s = 2 [help_text = "The Leaf2 this model is attached to", null = False, db_index = True, blank = False];
+}
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/base.py b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
index af1c241..e11d2ec 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/base.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
@@ -203,7 +203,10 @@
if b in table:
base_rlinks = xproto_base_rlinks(table[b], table)
- model_rlinks = table[b]['rlinks']
+ model_rlinks = [x.copy() for x in table[b]['rlinks']]
+ for link in model_rlinks:
+ link["accessor"] = b
+
links.extend(base_rlinks)
links.extend(model_rlinks)
@@ -212,31 +215,48 @@
def xproto_rlinks(m, table):
""" Return the reverse links for the xproto message `m`.
- If the link includes a reverse_id, then it will be used for the protobuf field id. If there is no
- reverse_id, then one will automatically be allocated started at id 1900. It is incouraged that all links
- include reverse_ids, so that field identifiers are deterministic across all protobuf messages.
+ If the link includes a reverse_id, then it will be used for the protobuf field id. Each level of inheritance
+ will add an offset of 100 to the supplied reverse_id.
+
+ If there is no reverse_id, then one will automatically be allocated started at id 1900. It is encouraged that
+ all links include reverse_ids, so that field identifiers are deterministic across all protobuf messages.
"""
- index = 1900
- links = xproto_base_rlinks(m, table) + m["rlinks"]
+ model_rlinks = [x.copy() for x in m["rlinks"]]
+ for link in model_rlinks:
+ link["accessor"] = m["fqn"]
+
+ links = xproto_base_rlinks(m, table) + model_rlinks
links = [x for x in links if ("+" not in x["src_port"]) and ("+" not in x["dst_port"])]
- for link in links:
- if link["reverse_id"]:
- link["id"] = int(link["reverse_id"])
- else:
- link["id"] = index
- index += 1
+ if links:
+ last_accessor = links[0]["accessor"]
+ offset = 0
+ index = 1900
+ for link in links:
+ if (link["accessor"] != last_accessor):
+ last_accessor = link["accessor"]
+ offset += 100
- # check for duplicates
- links_by_number={}
- for link in links:
- id = link["id"]
- dup=links_by_number.get(id)
- if dup:
- raise Exception("Field %s has duplicate number %d with field %s in model %s" % (link["src_port"], id, link["src_port"], m["name"]))
- links_by_number[id] = link
+ if link["reverse_id"]:
+ # Statically numbered reverse links. Use the id that the developer supplied, adding the offset based on
+ # inheritance depth.
+ link["id"] = int(link["reverse_id"]) + offset
+ else:
+ # Automatically numbered reverse links. These will eventually go away.
+ link["id"] = index
+ index += 1
+
+ # check for duplicates
+ links_by_number={}
+ for link in links:
+ id = link["id"]
+ dup=links_by_number.get(id)
+ if dup:
+ raise Exception("Field %s has duplicate number %d in model %s with reverse field %s" %
+ (link["src_port"], id, m["name"], dup["src_port"]))
+ links_by_number[id] = link
return links