Skip to content

Commit fd0bb76

Browse files
authored
feat: Add utility to get software citation and CLI interface (#1210)
* Add citation utility to pyhf * Add CLI functionality to print out pyhf citation
1 parent 0370dbd commit fd0bb76

File tree

7 files changed

+80
-8
lines changed

7 files changed

+80
-8
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ tag = True
99

1010
[bumpversion:file:README.rst]
1111

12-
[bumpversion:file:docs/bib/preferred.bib]
12+
[bumpversion:file:src/pyhf/data/citation.bib]
1313

1414
[bumpversion:file:.zenodo.json]
1515

docs/bib/preferred.bib

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/bib/preferred.bib

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../src/pyhf/data/citation.bib

src/pyhf/cli/cli.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,31 @@
66
from ..version import __version__
77
from . import rootio, spec, infer, patchset, complete
88
from ..contrib import cli as contrib
9+
from .. import utils
910

1011
logging.basicConfig()
1112
log = logging.getLogger(__name__)
1213

1314

15+
def _print_citation(ctx, param, value):
16+
if not value or ctx.resilient_parsing:
17+
return
18+
click.echo(utils.citation())
19+
ctx.exit()
20+
21+
1422
@click.group(context_settings=dict(help_option_names=['-h', '--help']))
1523
@click.version_option(version=__version__)
24+
@click.option(
25+
"--cite",
26+
"--citation",
27+
help="Print the bibtex citation for this software",
28+
default=False,
29+
is_flag=True,
30+
callback=_print_citation,
31+
expose_value=False,
32+
is_eager=True,
33+
)
1634
def pyhf():
1735
"""Top-level CLI entrypoint."""
1836

src/pyhf/data/citation.bib

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@software{pyhf,
2+
author = "{Heinrich, Lukas and Feickert, Matthew and Stark, Giordon}",
3+
title = "{pyhf: v0.5.3}",
4+
version = {0.5.3},
5+
doi = {10.5281/zenodo.1169739},
6+
url = {https://github.com/scikit-hep/pyhf},
7+
}

src/pyhf/utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,33 @@ def remove_prefix(text, prefix):
132132
if text.startswith(prefix):
133133
return text[len(prefix) :]
134134
return text
135+
136+
137+
def citation(oneline=False):
138+
"""
139+
Get the bibtex citation for pyhf
140+
141+
Example:
142+
143+
>>> import pyhf
144+
>>> pyhf.utils.citation(True)
145+
'@software{pyhf, author = "{Heinrich, Lukas and Feickert, Matthew and Stark, Giordon}", title = "{pyhf: v0.5.3}", version = {0.5.3}, doi = {10.5281/zenodo.1169739}, url = {https://github.com/scikit-hep/pyhf},}'
146+
147+
Keyword Args:
148+
oneline (:obj:`bool`): Whether to provide citation with new lines (default) or as a one-liner.
149+
150+
Returns:
151+
citation (:obj:`str`): The citation for this software
152+
"""
153+
path = Path(
154+
pkg_resources.resource_filename(
155+
__name__, str(Path('data').joinpath('citation.bib'))
156+
)
157+
)
158+
with path.open() as fp:
159+
# remove end-of-file newline if there is one
160+
data = fp.read().strip()
161+
162+
if oneline:
163+
data = ''.join(data.splitlines())
164+
return data

tests/test_scripts.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ def test_version(script_runner):
2424
assert elapsed < 1.0
2525

2626

27+
@pytest.mark.parametrize("flag", ["--cite", "--citation"])
28+
def test_citation(script_runner, flag):
29+
command = f'pyhf {flag}'
30+
start = time.time()
31+
ret = script_runner.run(*shlex.split(command))
32+
end = time.time()
33+
elapsed = end - start
34+
assert ret.success
35+
assert ret.stdout.startswith('@software{pyhf,')
36+
# ensure there's not \n\n at the end
37+
assert ret.stdout.endswith('}\n')
38+
# make sure it took less than a second
39+
assert elapsed < 1.0
40+
41+
2742
# see test_import.py for the same (detailed) test
2843
def test_import_prepHistFactory(tmpdir, script_runner):
2944
temp = tmpdir.join("parsed_output.json")

tests/test_utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,11 @@ def test_remove_prefix():
6464
assert pyhf.utils.remove_prefix('abcDEF123', 'abc') == 'DEF123'
6565
assert pyhf.utils.remove_prefix('abcDEF123', 'Abc') == 'abcDEF123'
6666
assert pyhf.utils.remove_prefix('abcDEF123', '123') == 'abcDEF123'
67+
68+
69+
@pytest.mark.parametrize('oneline', [False, True])
70+
def test_citation(oneline):
71+
citation = pyhf.utils.citation(oneline)
72+
assert citation
73+
if oneline:
74+
assert '\n' not in citation

0 commit comments

Comments
 (0)