diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/docs/tables/system_prerequisites.csv | 2 | ||||
-rw-r--r-- | lib/spack/spack/main.py | 26 | ||||
-rw-r--r-- | lib/spack/spack/test/util/unparse/unparse.py | 74 | ||||
-rw-r--r-- | lib/spack/spack/util/unparse/unparser.py | 92 |
4 files changed, 182 insertions, 12 deletions
diff --git a/lib/spack/docs/tables/system_prerequisites.csv b/lib/spack/docs/tables/system_prerequisites.csv index 5f661883d3..af8dcafffa 100644 --- a/lib/spack/docs/tables/system_prerequisites.csv +++ b/lib/spack/docs/tables/system_prerequisites.csv @@ -1,5 +1,5 @@ Name, Supported Versions, Notes, Requirement Reason -Python, 2.7/3.6-3.10, , Interpreter for Spack +Python, 2.7/3.6-3.11, , Interpreter for Spack C/C++ Compilers, , , Building software make, , , Build software patch, , , Build software diff --git a/lib/spack/spack/main.py b/lib/spack/spack/main.py index 4c3f19d75d..a9a9d20df7 100644 --- a/lib/spack/spack/main.py +++ b/lib/spack/spack/main.py @@ -343,17 +343,21 @@ class SpackArgumentParser(argparse.ArgumentParser): self._remove_action(self._actions[-1]) self.subparsers = self.add_subparsers(metavar="COMMAND", dest="command") - # each command module implements a parser() function, to which we - # pass its subparser for setup. - module = spack.cmd.get_module(cmd_name) - - # build a list of aliases - alias_list = [k for k, v in aliases.items() if v == cmd_name] - - subparser = self.subparsers.add_parser( - cmd_name, aliases=alias_list, help=module.description, description=module.description - ) - module.setup_parser(subparser) + if cmd_name not in self.subparsers._name_parser_map: + # each command module implements a parser() function, to which we + # pass its subparser for setup. + module = spack.cmd.get_module(cmd_name) + + # build a list of aliases + alias_list = [k for k, v in aliases.items() if v == cmd_name] + + subparser = self.subparsers.add_parser( + cmd_name, + aliases=alias_list, + help=module.description, + description=module.description, + ) + module.setup_parser(subparser) # return the callable function for the command return spack.cmd.get_command(cmd_name) diff --git a/lib/spack/spack/test/util/unparse/unparse.py b/lib/spack/spack/test/util/unparse/unparse.py index 49009ae98e..82148c9dc8 100644 --- a/lib/spack/spack/test/util/unparse/unparse.py +++ b/lib/spack/spack/test/util/unparse/unparse.py @@ -178,6 +178,71 @@ async def f(): """ +match_literal = """\ +match status: + case 400: + return "Bad request" + case 404 | 418: + return "Not found" + case _: + return "Something's wrong with the internet" +""" + +match_with_noop = """\ +match status: + case 400: + return "Bad request" +""" + +match_literal_and_variable = """\ +match point: + case (0, 0): + print("Origin") + case (0, y): + print(f"Y={y}") + case (x, 0): + print(f"X={x}") + case (x, y): + print(f"X={x}, Y={y}") + case _: + raise ValueError("Not a point") +""" + + +match_classes = """\ +class Point: + x: int + y: int + +def location(point): + match point: + case Point(x=0, y=0): + print("Origin is the point's location.") + case Point(x=0, y=y): + print(f"Y={y} and the point is on the y-axis.") + case Point(x=x, y=0): + print(f"X={x} and the point is on the x-axis.") + case Point(): + print("The point is located somewhere else on the plane.") + case _: + print("Not a point") +""" + +match_nested = """\ +match points: + case []: + print("No points in the list.") + case [Point(0, 0)]: + print("The origin is the only point in the list.") + case [Point(x, y)]: + print(f"A single point {x}, {y} is in the list.") + case [Point(0, y1), Point(0, y2)]: + print(f"Two points on the Y axis at {y1}, {y2} are in the list.") + case _: + print("Something else is found in the list.") +""" + + def check_ast_roundtrip(code1, filename="internal", mode="exec"): ast1 = compile(str(code1), filename, mode, ast.PyCF_ONLY_AST) code2 = spack.util.unparse.unparse(ast1) @@ -512,3 +577,12 @@ def test_async_with(): @pytest.mark.skipif(sys.version_info < (3, 5), reason="Not supported < 3.5") def test_async_with_as(): check_ast_roundtrip(async_with_as) + + +@pytest.mark.skipif(sys.version_info < (3, 10), reason="Not supported < 3.10") +@pytest.mark.parametrize( + "literal", + [match_literal, match_with_noop, match_literal_and_variable, match_classes, match_nested], +) +def test_match_literal(literal): + check_ast_roundtrip(literal) diff --git a/lib/spack/spack/util/unparse/unparser.py b/lib/spack/spack/util/unparse/unparser.py index a46d19fa76..c204aea25a 100644 --- a/lib/spack/spack/util/unparse/unparser.py +++ b/lib/spack/spack/util/unparse/unparser.py @@ -1243,3 +1243,95 @@ class Unparser: if node.optional_vars: self.write(" as ") self.dispatch(node.optional_vars) + + def visit_Match(self, node): + self.fill("match ") + self.dispatch(node.subject) + with self.block(): + for case in node.cases: + self.dispatch(case) + + def visit_match_case(self, node): + self.fill("case ") + self.dispatch(node.pattern) + if node.guard: + self.write(" if ") + self.dispatch(node.guard) + with self.block(): + self.dispatch(node.body) + + def visit_MatchValue(self, node): + self.dispatch(node.value) + + def visit_MatchSingleton(self, node): + self._write_constant(node.value) + + def visit_MatchSequence(self, node): + with self.delimit("[", "]"): + interleave(lambda: self.write(", "), self.dispatch, node.patterns) + + def visit_MatchStar(self, node): + name = node.name + if name is None: + name = "_" + self.write("*{}".format(name)) + + def visit_MatchMapping(self, node): + def write_key_pattern_pair(pair): + k, p = pair + self.dispatch(k) + self.write(": ") + self.dispatch(p) + + with self.delimit("{", "}"): + keys = node.keys + interleave( + lambda: self.write(", "), + write_key_pattern_pair, + zip(keys, node.patterns), + ) + rest = node.rest + if rest is not None: + if keys: + self.write(", ") + self.write("**{}".format(rest)) + + def visit_MatchClass(self, node): + self.set_precedence(_Precedence.ATOM, node.cls) + self.dispatch(node.cls) + with self.delimit("(", ")"): + patterns = node.patterns + interleave(lambda: self.write(", "), self.dispatch, patterns) + attrs = node.kwd_attrs + if attrs: + + def write_attr_pattern(pair): + attr, pattern = pair + self.write("{}=".format(attr)) + self.dispatch(pattern) + + if patterns: + self.write(", ") + interleave( + lambda: self.write(", "), + write_attr_pattern, + zip(attrs, node.kwd_patterns), + ) + + def visit_MatchAs(self, node): + name = node.name + pattern = node.pattern + if name is None: + self.write("_") + elif pattern is None: + self.write(node.name) + else: + with self.require_parens(_Precedence.TEST, node): + self.set_precedence(_Precedence.BOR, node.pattern) + self.dispatch(node.pattern) + self.write(" as {}".format(node.name)) + + def visit_MatchOr(self, node): + with self.require_parens(_Precedence.BOR, node): + self.set_precedence(pnext(_Precedence.BOR), *node.patterns) + interleave(lambda: self.write(" | "), self.dispatch, node.patterns) |