summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorVanessasaurus <814322+vsoch@users.noreply.github.com>2021-05-25 12:40:08 -0600
committerGitHub <noreply@github.com>2021-05-25 12:40:08 -0600
commit3cef5663d8c04228b1da4e890fe023b58b97548f (patch)
treeb94cc6c81b1ff1c1d41ab8e8452cda4f34828b4a /lib
parentb44bb952eb29572fe8bcf9eab65d8baa24d3b994 (diff)
downloadspack-3cef5663d8c04228b1da4e890fe023b58b97548f.tar.gz
spack-3cef5663d8c04228b1da4e890fe023b58b97548f.tar.bz2
spack-3cef5663d8c04228b1da4e890fe023b58b97548f.tar.xz
spack-3cef5663d8c04228b1da4e890fe023b58b97548f.zip
adding json export for spack blame (#23417)
I would like to be able to export (and save and then load programatically) spack blame metadata, so this commit adds a spack blame --json argument, along with developer docs for it Signed-off-by: vsoch <vsoch@users.noreply.github.com> Co-authored-by: vsoch <vsoch@users.noreply.github.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/docs/developer_guide.rst44
-rw-r--r--lib/spack/spack/cmd/blame.py67
-rw-r--r--lib/spack/spack/test/cmd/blame.py24
3 files changed, 121 insertions, 14 deletions
diff --git a/lib/spack/docs/developer_guide.rst b/lib/spack/docs/developer_guide.rst
index 7ae6938eda..0ceb5d84a1 100644
--- a/lib/spack/docs/developer_guide.rst
+++ b/lib/spack/docs/developer_guide.rst
@@ -867,6 +867,50 @@ just like you would with the normal ``python`` command.
.. _cmd-spack-url:
+
+^^^^^^^^^^^^^^^
+``spack blame``
+^^^^^^^^^^^^^^^
+
+Spack blame is a way to quickly see contributors to packages or files
+in the spack repository. You should provide a target package name or
+file name to the command. Here is an example asking to see contributions
+for the package "python":
+
+.. code-block:: console
+
+ $ spack blame python
+ LAST_COMMIT LINES % AUTHOR EMAIL
+ 2 weeks ago 3 0.3 Mickey Mouse <cheddar@gmouse.org>
+ a month ago 927 99.7 Minnie Mouse <swiss@mouse.org>
+
+ 2 weeks ago 930 100.0
+
+
+By default, you will get a table view (shown above) sorted by date of contribution,
+with the most recent contribution at the top. If you want to sort instead
+by percentage of code contribution, then add ``-p``:
+
+.. code-block:: console
+
+ $ spack blame -p python
+
+
+And to see the git blame view, add ``-g`` instead:
+
+
+.. code-block:: console
+
+ $ spack blame -g python
+
+
+Finally, to get a json export of the data, add ``--json``:
+
+.. code-block:: console
+
+ $ spack blame --json python
+
+
^^^^^^^^^^^^^
``spack url``
^^^^^^^^^^^^^
diff --git a/lib/spack/spack/cmd/blame.py b/lib/spack/spack/cmd/blame.py
index f61ed03b4a..0f11e73919 100644
--- a/lib/spack/spack/cmd/blame.py
+++ b/lib/spack/spack/cmd/blame.py
@@ -5,11 +5,13 @@
import os
import re
+import sys
import llnl.util.tty as tty
from llnl.util.lang import pretty_date
from llnl.util.filesystem import working_dir
from llnl.util.tty.colify import colify_table
+import spack.util.spack_json as sjson
import spack.paths
import spack.repo
@@ -33,12 +35,57 @@ def setup_parser(subparser):
view_group.add_argument(
'-g', '--git', dest='view', action='store_const', const='git',
help='show git blame output instead of summary')
+ subparser.add_argument(
+ "--json", action="store_true", default=False,
+ help="output blame as machine-readable json records")
subparser.add_argument(
'package_or_file', help='name of package to show contributions for, '
'or path to a file in the spack repo')
+def print_table(rows, last_mod, total_lines, emails):
+ """
+ Given a set of rows with authors and lines, print a table.
+ """
+ table = [['LAST_COMMIT', 'LINES', '%', 'AUTHOR', 'EMAIL']]
+ for author, nlines in rows:
+ table += [[
+ pretty_date(last_mod[author]),
+ nlines,
+ round(nlines / float(total_lines) * 100, 1),
+ author,
+ emails[author]]]
+
+ table += [[''] * 5]
+ table += [[pretty_date(max(last_mod.values())), total_lines, '100.0'] +
+ [''] * 3]
+
+ colify_table(table)
+
+
+def dump_json(rows, last_mod, total_lines, emails):
+ """
+ Dump the blame as a json object to the terminal.
+ """
+ result = {}
+ authors = []
+ for author, nlines in rows:
+ authors.append({
+ "last_commit": pretty_date(last_mod[author]),
+ "lines": nlines,
+ "percentage": round(nlines / float(total_lines) * 100, 1),
+ "author": author,
+ "email": emails[author]
+ })
+
+ result['authors'] = authors
+ result["totals"] = {"last_commit": pretty_date(max(last_mod.values())),
+ "lines": total_lines, "percentage": "100.0"}
+
+ sjson.dump(result, sys.stdout)
+
+
def blame(parser, args):
# make sure this is a git repo
if not spack_is_git_repo():
@@ -96,18 +143,10 @@ def blame(parser, args):
else: # args.view == 'percent'
rows = sorted(counts.items(), key=lambda t: t[1], reverse=True)
- # Print a nice table with authors and emails
- table = [['LAST_COMMIT', 'LINES', '%', 'AUTHOR', 'EMAIL']]
- for author, nlines in rows:
- table += [[
- pretty_date(last_mod[author]),
- nlines,
- round(nlines / float(total_lines) * 100, 1),
- author,
- emails[author]]]
-
- table += [[''] * 5]
- table += [[pretty_date(max(last_mod.values())), total_lines, '100.0'] +
- [''] * 3]
+ # Dump as json
+ if args.json:
+ dump_json(rows, last_mod, total_lines, emails)
- colify_table(table)
+ # Print a nice table with authors and emails
+ else:
+ print_table(rows, last_mod, total_lines, emails)
diff --git a/lib/spack/spack/test/cmd/blame.py b/lib/spack/spack/test/cmd/blame.py
index 5ca69bb7b4..40d3e92ea1 100644
--- a/lib/spack/spack/test/cmd/blame.py
+++ b/lib/spack/spack/test/cmd/blame.py
@@ -6,6 +6,7 @@
import pytest
from llnl.util.filesystem import working_dir
+import spack.util.spack_json as sjson
import spack.paths
import spack.cmd
@@ -44,6 +45,29 @@ def test_blame_file(mock_packages):
assert 'EMAIL' in out
+def test_blame_json(mock_packages):
+ """Ensure that we can output json as a blame."""
+ with working_dir(spack.paths.prefix):
+ out = blame('--json', 'mpich')
+
+ # Test loading the json, and top level keys
+ loaded = sjson.load(out)
+ assert "authors" in out
+ assert "totals" in out
+
+ # Authors should be a list
+ assert len(loaded['authors']) > 0
+
+ # Each of authors and totals has these shared keys
+ keys = ["last_commit", "lines", "percentage"]
+ for key in keys:
+ assert key in loaded['totals']
+
+ # But authors is a list of multiple
+ for key in keys + ["author", "email"]:
+ assert key in loaded['authors'][0]
+
+
def test_blame_by_git(mock_packages, capfd):
"""Sanity check the blame command to make sure it works."""
with capfd.disabled():