If default variant is not filtered by only or no filters then only default variant is chosen. This behavior was used for optimizing of speed of Cartesian config. If variants don't have default variant then everything works as usual. Default variant must be in variants with with_default exactly one times. The default variant could be filtered by only, no filter. If default variant is filtered from variants then variants works same as usual variants with default variant. For calling Cartesian config from command line is used option -d/--defaults: ../virttest/cartesian_config.py -d cfg/cc.cfg For calling Cartesian config from python: c = Parser(args[0], defaults=options.defaults, debug=options.debug) ********* example: variants name=tests: - wait: run = "wait" variants: - long: time = short_time - short: long time = logn_time - test2: run = "test1" variants name=virt_system, with_default: - @linux: - windows: variants name=host_os, with_default: - linux: image = linux variants with_default: - ubuntu: - @fedora: - windows: image = windows variants: - @XP: - WIN7: only host_os>windows In this case is chosen from host_os variants windows variant. host_os>windows was choosen because default variant linux was filtered. Next step is select one variant from guest_os. There will be chosen only default variant linux because not filtered and virt_system variant is with with_default. There is no default variant in tests variants because that all of tests will be chosen. ******** output: dict 1: host_os>windows.tests>wait.long dep = [] host_os = windows image = windows name = host_os>windows.XP.virt_system>linux.tests>wait.long run = wait shortname = host_os>windows.tests>wait.long tests = wait time = short_time virt_system = linux dict 2: host_os>windows.tests>wait.short dep = ['host_os>windows.XP.virt_system>linux.tests>wait.long'] host_os = windows image = windows name = host_os>windows.XP.virt_system>linux.tests>wait.short run = wait shortname = host_os>windows.tests>wait.short tests = wait time = logn_time virt_system = linux dict 3: host_os>windows.tests>test2 dep = [] host_os = windows image = windows name = host_os>windows.XP.virt_system>linux.tests>test2 run = test1 shortname = host_os>windows.tests>test2 tests = test2 virt_system = linux dict 4: host_os>windows.WIN7.tests>wait.long dep = [] host_os = windows image = windows name = host_os>windows.WIN7.virt_system>linux.tests>wait.long run = wait shortname = host_os>windows.WIN7.tests>wait.long tests = wait time = short_time virt_system = linux dict 5: host_os>windows.WIN7.tests>wait.short dep = ['host_os>windows.WIN7.virt_system>linux.tests>wait.long'] host_os = windows image = windows name = host_os>windows.WIN7.virt_system>linux.tests>wait.short run = wait shortname = host_os>windows.WIN7.tests>wait.short tests = wait time = logn_time virt_system = linux dict 6: host_os>windows.WIN7.tests>test2 dep = [] host_os = windows image = windows name = host_os>windows.WIN7.virt_system>linux.tests>test2 run = test1 shortname = host_os>windows.WIN7.tests>test2 tests = test2 virt_system = linux Signed-off-by: Jiří Župka <jzupka@xxxxxxxxxx> --- virttest/cartesian_config.py | 82 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/virttest/cartesian_config.py b/virttest/cartesian_config.py index 04ed2b5..6cd0e88 100755 --- a/virttest/cartesian_config.py +++ b/virttest/cartesian_config.py @@ -131,6 +131,10 @@ class ParserError: return "%s (%s:%s)" % (self.msg, self.filename, self.linenum) +class MissingDefault: + pass + + class MissingIncludeError: def __init__(self, line, filename, linenum): self.line = line @@ -225,6 +229,7 @@ class Node(object): self.labels = set() self.append_to_shortname = False self.failed_cases = collections.deque() + self.default = False def dump(self, indent, recurse=False): @@ -407,14 +412,17 @@ class Parser(object): @see: https://github.com/autotest/autotest/wiki/KVMAutotest-CartesianConfigParametersIntro """ - def __init__(self, filename=None, debug=False): + def __init__(self, filename=None, defaults=False, debug=False): """ Initialize the parser and optionally parse a file. @param filename: Path of the file to parse. + @param defaults: If True adds only defaults variant from variants + if there is some. @param debug: Whether to turn on debugging output. """ self.node = Node() + self.defaults = defaults self.debug = debug if filename: self.parse_file(filename) @@ -611,10 +619,18 @@ class Parser(object): # Recurse into children count = 0 - for n in node.children: - for d in self.get_dicts(n, ctx, new_content, shortname, dep): - count += 1 - yield d + if self.defaults: + for n in node.children: + for d in self.get_dicts(n, ctx, new_content, shortname, dep): + count += 1 + yield d + if n.default and count: + break + else: + for n in node.children: + for d in self.get_dicts(n, ctx, new_content, shortname, dep): + count += 1 + yield d # Reached leaf? if not node.children: self._debug(" reached leaf, returning it") @@ -656,7 +672,8 @@ class Parser(object): print s % args - def _parse_variants(self, cr, node, prev_indent=-1, var_name=None): + def _parse_variants(self, cr, node, prev_indent=-1, var_name=None, + with_default=False): """ Read and parse lines from a FileReader object until a line with an indent level lower than or equal to prev_indent is encountered. @@ -665,8 +682,10 @@ class Parser(object): @param node: A node to operate on. @param prev_indent: The indent level of the "parent" block. @param var_name: Variants name + @param with_default: Variants take only default variant. @return: A node object. """ + already_default = False node4 = Node() while True: @@ -694,7 +713,9 @@ class Parser(object): node2.labels = node.labels node3 = self._parse(cr, node2, prev_indent=indent) - node3.name = [Label(var_name, n) for n in name.lstrip("@").split(".")] + is_default = name.startswith("@") + name = name.lstrip("@") + node3.name = [Label(var_name, n) for n in name.split(".")] node3.dep = [Label(var_name, d) for d in dep.replace(",", " ").split()] if var_name: @@ -702,12 +723,33 @@ class Parser(object): op_match = _ops_exp.search(l) node3.content += [(cr.filename, linenum, Op(l, op_match))] - is_default = name.startswith("@") - - node4.children += [node3] + node3.append_to_shortname = not is_default + + if with_default and self.defaults: + """ + Relevant only if defaults is True and + variants is with default. + """ + if is_default: + if not already_default: + node3.default = True + already_default = True + else: + raise MissingDefault + if node3.default: + # Move default variant in front of rest of all variants. + # Speed optimization. + node4.children.insert(0, node3) + else: + node4.children += [node3] + else: + node4.children += [node3] node4.labels.update(node3.labels) node4.labels.update(node3.name) + if with_default and not already_default: + raise MissingDefault + return node4 @@ -751,6 +793,7 @@ class Parser(object): char in "._-=,"): raise ParserError("Illegal characters in variants", line, cr.filename, linenum) + with_default = False var_name = None if name: block = name.split(",") @@ -761,12 +804,20 @@ class Parser(object): raise ParserError("Missing name of variants", line, cr.filename, linenum) var_name = oper[1].strip() + elif "with_default" in oper[0]: + with_default = True else: raise ParserError("Ilegal variants param", line, cr.filename, linenum) - node = self._parse_variants(cr, node, prev_indent=indent, - var_name=var_name) - var_name=name) + try: + node = self._parse_variants(cr, node, prev_indent=indent, + var_name=var_name, + with_default=with_default) + except MissingDefault: + raise ParserError("There must be exactly one default " + "variant in variants with param " + "with_default.", + line, cr.filename, linenum) continue # Parse 'include' statements @@ -1040,12 +1091,15 @@ if __name__ == "__main__": help="show dict contents") parser.add_option("-r", "--repr", dest="repr_mode", action="store_true", help="Output parsing results Python format") + parser.add_option("-d", "--defaults", dest="defaults", action="store_true", + help="use only default variant of variants if there" + " is some") options, args = parser.parse_args() if not args: parser.error("filename required") - c = Parser(args[0], debug=options.debug) + c = Parser(args[0], defaults=options.defaults, debug=options.debug) for s in args[1:]: c.parse_string(s) -- 1.8.1.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html