loosen criteria on CNF object roots
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 5 Dec 2017 10:38:40 +0000 (11:38 +0100)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Mon, 5 Nov 2018 11:16:39 +0000 (12:16 +0100)
Single CNF_VARs are technically malformed since CNF file is
always a list of vars. However, any isolated CNF line can be
trivially converted to a valid CNF_VAR by wrapping it in a list
ctor, so do that automatically when determining the root of a
CNF_VAR.

src/cnfvar.py

index d62d6cf..75857c7 100644 (file)
@@ -246,13 +246,12 @@ def is_cnf(root):
     well-formed member of the argument will cause the predicate to bail out
     with an exception during traversal.
     """
-    t_root = type(root)
-    if t_root is list:
+    if isinstance (root, list):
         cnf = root
-    elif t_root is dict:
-        cnf = root["cnf"]
     else:
-        raise InvalidCNF(root)
+        cnf = cnf_root (root)
+    if cnf is None:
+        raise InvalidCNF (root)
     return walk_cnf(cnf, False, is_valid, {}) is not None
 
 #
@@ -591,10 +590,42 @@ def format_cnf_vars(da, var):
     return (depth, acc)
 
 
-def cnf_root(root):
+CNF_FIELD_MANDATORY = set ([ "varname", "data", "instance" ])
+CNF_FIELD_OPTIONAL  = set ([ "parent", "children", "comment", "number" ])
+CNF_FIELD_KNOWN     = CNF_FIELD_MANDATORY | CNF_FIELD_OPTIONAL
+
+
+def is_cnf_var (obj):
+    """
+    Applies if all mandatory fields of a CNF_VAR and no unknown fields are
+    present.
+
+    .. XXX: validate field types.
+    """
+    assert isinstance (obj, dict)
+
+    for f in CNF_FIELD_MANDATORY:
+        if obj.get (f, None) is None:
+            return False
+
+    for f in obj:
+        if f not in CNF_FIELD_KNOWN:
+            return False
+
+    return True
+
+
+def cnf_root (root):
+    """
+    Of a given object, return the cnf root. This may be either a list
+    containing the object itself, if it satisfies the criteria for a CNF_VAR,
+    or the object contained in its toplevel field *cnf*.
+    """
     if not isinstance(root, dict):
         raise TypeError(
             "Expected dictionary of CNF_VARs, got %s." % type(root))
+    if is_cnf_var (root):
+        return [ root ]
     cnf = root.get("cnf", None)
     if not isinstance(cnf, list):
         raise TypeError("Expected list of CNF_VARs, got %s." % type(cnf))