summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/audit.yaml2
-rw-r--r--.github/workflows/unit_tests.yaml15
-rw-r--r--.github/workflows/valid-style.yml6
-rw-r--r--lib/spack/docs/tables/system_prerequisites.csv2
-rw-r--r--lib/spack/spack/main.py26
-rw-r--r--lib/spack/spack/test/util/unparse/unparse.py74
-rw-r--r--lib/spack/spack/util/unparse/unparser.py92
7 files changed, 195 insertions, 22 deletions
diff --git a/.github/workflows/audit.yaml b/.github/workflows/audit.yaml
index 5b463a3e0c..275abb2d53 100644
--- a/.github/workflows/audit.yaml
+++ b/.github/workflows/audit.yaml
@@ -25,7 +25,7 @@ jobs:
python-version: ${{inputs.python_version}}
- name: Install Python packages
run: |
- pip install --upgrade pip six setuptools pytest codecov 'coverage[toml]<=6.2'
+ pip install --upgrade pip six setuptools pytest codecov coverage[toml]
- name: Package audits (with coverage)
if: ${{ inputs.with_coverage == 'true' }}
run: |
diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml
index 80b4593c23..6a21d166f8 100644
--- a/.github/workflows/unit_tests.yaml
+++ b/.github/workflows/unit_tests.yaml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ['2.7', '3.6', '3.7', '3.8', '3.9', '3.10']
+ python-version: ['2.7', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11']
concretizer: ['clingo']
on_develop:
- ${{ github.ref == 'refs/heads/develop' }}
@@ -22,7 +22,7 @@ jobs:
- python-version: 2.7
concretizer: original
on_develop: ${{ github.ref == 'refs/heads/develop' }}
- - python-version: '3.10'
+ - python-version: '3.11'
concretizer: original
on_develop: ${{ github.ref == 'refs/heads/develop' }}
exclude:
@@ -35,6 +35,9 @@ jobs:
- python-version: '3.9'
concretizer: 'clingo'
on_develop: false
+ - python-version: '3.10'
+ concretizer: 'clingo'
+ on_develop: false
steps:
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # @v2
@@ -86,7 +89,7 @@ jobs:
SPACK_TEST_SOLVER: ${{ matrix.concretizer }}
SPACK_TEST_PARALLEL: 2
COVERAGE: true
- UNIT_TEST_COVERAGE: ${{ (matrix.python-version == '3.10') }}
+ UNIT_TEST_COVERAGE: ${{ (matrix.python-version == '3.11') }}
run: |
share/spack/qa/run-unit-tests
- uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70
@@ -101,7 +104,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # @v2
with:
- python-version: '3.10'
+ python-version: '3.11'
- name: Install System packages
run: |
sudo apt-get -y update
@@ -109,7 +112,7 @@ jobs:
sudo apt-get install -y coreutils kcov csh zsh tcsh fish dash bash
- name: Install Python packages
run: |
- pip install --upgrade pip six setuptools pytest codecov coverage[toml]==6.2 pytest-xdist
+ pip install --upgrade pip six setuptools pytest codecov coverage[toml] pytest-xdist
- name: Setup git configuration
run: |
# Need this for the git tests to succeed.
@@ -158,7 +161,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # @v2
with:
- python-version: '3.10'
+ python-version: '3.11'
- name: Install System packages
run: |
sudo apt-get -y update
diff --git a/.github/workflows/valid-style.yml b/.github/workflows/valid-style.yml
index d91f6e958a..a82c786b44 100644
--- a/.github/workflows/valid-style.yml
+++ b/.github/workflows/valid-style.yml
@@ -21,7 +21,7 @@ jobs:
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # @v2
- uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # @v2
with:
- python-version: '3.10'
+ python-version: '3.11'
cache: 'pip'
- name: Install Python Packages
run: |
@@ -40,7 +40,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # @v2
with:
- python-version: '3.10'
+ python-version: '3.11'
cache: 'pip'
- name: Install Python packages
run: |
@@ -57,4 +57,4 @@ jobs:
uses: ./.github/workflows/audit.yaml
with:
with_coverage: ${{ inputs.with_coverage }}
- python_version: '3.10'
+ python_version: '3.11'
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)