Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: Use Typer for CLI #1675

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion lower-bound-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# core
scipy==1.1.0
click==7.0
typer==0.0.2
tqdm==4.56.0
jsonschema==3.0.0
jsonpatch==1.15
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ include_package_data = True
python_requires = >=3.7
install_requires =
scipy>=1.1.0 # requires numpy, which is required by pyhf and tensorflow
click>=7.0 # for console scripts
typer>=0.0.2 # for console scripts
tqdm>=4.56.0 # for readxml
jsonschema>=3.0.0 # for utils
jsonpatch>=1.15
Expand Down
2 changes: 0 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from setuptools import setup

extras_require = {
'shellcomplete': ['click_completion'],
'tensorflow': [
'tensorflow~=2.3,!=2.3.0', # c.f. https://github.com/tensorflow/tensorflow/pull/40789
'tensorflow-probability~=0.11',
Expand All @@ -27,7 +26,6 @@
extras_require['backends']
+ extras_require['xmlio']
+ extras_require['contrib']
+ extras_require['shellcomplete']
+ [
'pytest~=6.0',
'pytest-cov>=2.5.1',
Expand Down
6 changes: 3 additions & 3 deletions src/pyhf/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""The pyhf command line interface."""
from pyhf.cli.cli import pyhf as cli
# from pyhf.cli.cli import pyhf as cli
from pyhf.cli.cli import typer_click_object as cli
from pyhf.cli.rootio import cli as rootio
from pyhf.cli.spec import cli as spec
from pyhf.cli.infer import cli as infer
from pyhf.cli.complete import cli as complete
from pyhf.contrib import cli as contrib

__all__ = ['cli', 'rootio', 'spec', 'infer', 'complete', 'contrib']
__all__ = ['cli', 'rootio', 'spec', 'infer', 'contrib']


def __dir__():
Expand Down
95 changes: 64 additions & 31 deletions src/pyhf/cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,91 @@
"""The pyhf Command Line Interface."""
import logging
from typing import Optional

import click
import typer

from pyhf import __version__
from pyhf.cli import rootio, spec, infer, patchset, complete
from pyhf.cli import rootio, spec, infer, patchset
from pyhf.contrib import cli as contrib
from pyhf import utils

logging.basicConfig()
log = logging.getLogger(__name__)

app = typer.Typer()

def _print_citation(ctx, param, value):

# REMOVE THIS. CURRENTLY IN AS EXAMPLE
@app.command()
def top():
"""
Top level command, form Typer
"""
typer.echo("The Typer app is at the top level")


def _version_callback(value: bool):
if value:
typer.echo(f"pyhf, v{__version__}")
raise typer.Exit()


def _print_citation(ctx, value):
if not value or ctx.resilient_parsing:
return
click.echo(utils.citation())
ctx.exit()
typer.echo(utils.citation())
raise typer.Exit()


@app.callback()
def callback(
version: Optional[bool] = typer.Option(
None, "--version", callback=_version_callback, is_eager=True
),
citation: Optional[bool] = typer.Option(
None,
"--cite",
"--citation",
help="Print the BibTeX citation for this software.",
callback=_print_citation,
is_eager=True,
is_flag=True, # Needed?
expose_value=False, # Needed?
),
):
"""
Typer app, including Click subapp

Top-level CLI entrypoint.
"""


@click.group(context_settings=dict(help_option_names=['-h', '--help']))
@click.version_option(version=__version__)
@click.option(
"--cite",
"--citation",
help="Print the bibtex citation for this software",
default=False,
is_flag=True,
callback=_print_citation,
expose_value=False,
is_eager=True,
)
def pyhf():
"""Top-level CLI entrypoint."""


# pyhf.add_command(rootio.cli)
pyhf.add_command(rootio.json2xml)
pyhf.add_command(rootio.xml2json)
typer_click_object = typer.main.get_command(app)

typer_click_object.add_command(pyhf)

# pyhf.add_command(spec.cli)
pyhf.add_command(spec.inspect)
pyhf.add_command(spec.prune)
pyhf.add_command(spec.rename)
pyhf.add_command(spec.combine)
pyhf.add_command(spec.digest)
pyhf.add_command(spec.sort)
# typer_click_object.add_command(rootio.cli)
typer_click_object.add_command(rootio.json2xml)
typer_click_object.add_command(rootio.xml2json)

# pyhf.add_command(infer.cli)
pyhf.add_command(infer.fit)
pyhf.add_command(infer.cls)
# typer_click_object.add_command(spec.cli)
typer_click_object.add_command(spec.inspect)
typer_click_object.add_command(spec.prune)
typer_click_object.add_command(spec.rename)
typer_click_object.add_command(spec.combine)
typer_click_object.add_command(spec.digest)
typer_click_object.add_command(spec.sort)

pyhf.add_command(patchset.cli)
# typer_click_object.add_command(infer.cli)
typer_click_object.add_command(infer.fit)
typer_click_object.add_command(infer.cls)

pyhf.add_command(complete.cli)
typer_click_object.add_command(patchset.cli)

pyhf.add_command(contrib.cli)
typer_click_object.add_command(contrib.cli)
30 changes: 0 additions & 30 deletions src/pyhf/cli/complete.py

This file was deleted.

15 changes: 3 additions & 12 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
from click.testing import CliRunner
import sys
import importlib


# FIXME
# pyhf.cli.complete was removed given typer supports completion
# so need to rewrite and fix this
def test_shllcomplete_cli(isolate_modules):
from pyhf.cli.complete import cli

runner = CliRunner()
result = runner.invoke(cli, ['bash'])
assert 'complete -F _pyhf_completion -o default pyhf' in result.output


def test_shllcomplete_cli_missing_extra(isolate_modules):
sys.modules['click_completion'] = None
importlib.reload(sys.modules['pyhf.cli.complete'])
from pyhf.cli.complete import cli

runner = CliRunner()
result = runner.invoke(cli, ['bash'])
assert 'You can install it with the shellcomplete extra' in result.output
2 changes: 1 addition & 1 deletion tests/test_public_api_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_top_level_public_api():


def test_cli_public_api():
assert dir(pyhf.cli) == ["cli", "complete", "contrib", "infer", "rootio", "spec"]
assert dir(pyhf.cli) == ["cli", "contrib", "infer", "rootio", "spec"]


def test_compat_public_api():
Expand Down