From 61333dc60662149333f79adb8077ad4ec969791a Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 7 Jun 2019 23:14:08 +0200 Subject: spack list: latest version (JSON) (#11652) List the latest version of each package in JSON encoding. Preparation for consumption for a "spack badge" service. --- lib/spack/spack/cmd/list.py | 42 ++++++++++++++++++++++++++++++++++++++++ lib/spack/spack/test/cmd/list.py | 9 +++++++++ lib/spack/spack/test/versions.py | 14 +++++++++++++- lib/spack/spack/version.py | 17 ++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/lib/spack/spack/cmd/list.py b/lib/spack/spack/cmd/list.py index 9fe2809097..570dcc7e27 100644 --- a/lib/spack/spack/cmd/list.py +++ b/lib/spack/spack/cmd/list.py @@ -13,6 +13,7 @@ import os import re import sys import math +import json import llnl.util.tty as tty from llnl.util.tty.colify import colify @@ -20,6 +21,7 @@ from llnl.util.tty.colify import colify import spack.dependency import spack.repo import spack.cmd.common.arguments as arguments +from spack.version import VersionList description = "list and search available packages" section = "basic" @@ -116,6 +118,46 @@ def rows_for_ncols(elts, ncols): yield row +def get_dependencies(pkg): + all_deps = {} + for deptype in spack.dependency.all_deptypes: + deps = pkg.dependencies_of_type(deptype) + all_deps[deptype] = [d for d in deps] + + return all_deps + + +@formatter +def version_json(pkg_names, out): + """Print all packages with their latest versions.""" + pkgs = [spack.repo.get(name) for name in pkg_names] + + out.write('[\n') + + # output name and latest version for each package + pkg_latest = ",\n".join([ + ' {{"name": "{0}",\n' + ' "latest_version": "{1}",\n' + ' "versions": {2},\n' + ' "homepage": "{3}",\n' + ' "file": "{4}",\n' + ' "maintainers": {5},\n' + ' "dependencies": {6}' + '}}'.format( + pkg.name, + VersionList(pkg.versions).preferred(), + json.dumps([str(v) for v in reversed(sorted(pkg.versions))]), + pkg.homepage, + github_url(pkg), + json.dumps(pkg.maintainers), + json.dumps(get_dependencies(pkg)) + ) for pkg in pkgs + ]) + out.write(pkg_latest) + # important: no trailing comma in JSON arrays + out.write('\n]\n') + + @formatter def html(pkg_names, out): """Print out information on all packages in Sphinx HTML. diff --git a/lib/spack/spack/test/cmd/list.py b/lib/spack/spack/test/cmd/list.py index 3cd8207ba2..6590b9ab2a 100644 --- a/lib/spack/spack/test/cmd/list.py +++ b/lib/spack/spack/test/cmd/list.py @@ -44,6 +44,15 @@ def test_list_format_name_only(): assert 'hdf5' in output +@pytest.mark.maybeslow +def test_list_format_version_json(): + output = list('--format', 'version_json') + assert ' {"name": "cloverleaf3d",' in output + assert ' {"name": "hdf5",' in output + import json + json.loads(output) + + @pytest.mark.maybeslow def test_list_format_html(): output = list('--format', 'html') diff --git a/lib/spack/spack/test/versions.py b/lib/spack/spack/test/versions.py index 11da83df8c..b39da0c698 100644 --- a/lib/spack/spack/test/versions.py +++ b/lib/spack/spack/test/versions.py @@ -9,7 +9,7 @@ where it makes sense. """ import pytest -from spack.version import Version, ver +from spack.version import Version, VersionList, ver def assert_ver_lt(a, b): @@ -548,3 +548,15 @@ def test_get_item(): # Raise TypeError on tuples with pytest.raises(TypeError): b.__getitem__(1, 2) + + +def test_list_highest(): + vl = VersionList(['master', '1.2.3', 'develop', '3.4.5', 'foobar']) + assert vl.highest() == Version('develop') + assert vl.lowest() == Version('foobar') + assert vl.highest_numeric() == Version('3.4.5') + + vl2 = VersionList(['master', 'develop']) + assert vl2.highest_numeric() is None + assert vl2.preferred() == Version('develop') + assert vl2.lowest() == Version('master') diff --git a/lib/spack/spack/version.py b/lib/spack/spack/version.py index 0235ce0182..286943119f 100644 --- a/lib/spack/spack/version.py +++ b/lib/spack/spack/version.py @@ -643,6 +643,23 @@ class VersionList(object): else: return self[-1].highest() + def highest_numeric(self): + """Get the highest numeric version in the list.""" + numeric_versions = list(filter( + lambda v: str(v) not in infinity_versions, + self.versions)) + if not any(numeric_versions): + return None + else: + return numeric_versions[-1].highest() + + def preferred(self): + """Get the preferred (latest) version in the list.""" + latest = self.highest_numeric() + if latest is None: + latest = self.highest() + return latest + @coerced def overlaps(self, other): if not other or not self: -- cgit v1.2.3-60-g2f50