From 7bf26408bcf257b4870e949d92463fadd20091fc Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 02:17:26 -0500 Subject: [PATCH 01/10] Start to get typer working with Click c.f. https://typer.tiangolo.com/tutorial/using-click/#click-group --- src/pyhf/cli/__init__.py | 3 ++- src/pyhf/cli/cli.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/pyhf/cli/__init__.py b/src/pyhf/cli/__init__.py index 0d65039661..8fe856c55e 100644 --- a/src/pyhf/cli/__init__.py +++ b/src/pyhf/cli/__init__.py @@ -1,5 +1,6 @@ """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 diff --git a/src/pyhf/cli/cli.py b/src/pyhf/cli/cli.py index a1a486fe54..a44c407cf3 100644 --- a/src/pyhf/cli/cli.py +++ b/src/pyhf/cli/cli.py @@ -2,6 +2,7 @@ import logging import click +import typer from pyhf import __version__ from pyhf.cli import rootio, spec, infer, patchset, complete @@ -11,6 +12,23 @@ logging.basicConfig() log = logging.getLogger(__name__) +app = typer.Typer() + + +@app.command() +def top(): + """ + Top level command, form Typer + """ + typer.echo("The Typer app is at the top level") + + +@app.callback() +def callback(): + """ + Typer app, including Click subapp + """ + def _print_citation(ctx, param, value): if not value or ctx.resilient_parsing: @@ -56,3 +74,7 @@ def pyhf(): pyhf.add_command(complete.cli) pyhf.add_command(contrib.cli) + +typer_click_object = typer.main.get_command(app) + +typer_click_object.add_command(pyhf, "pyhf") From 8ca5263f0c127966e1ffc48a7637ec987adc3c99 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 02:23:40 -0500 Subject: [PATCH 02/10] Get CLI back from typer_click_object --- src/pyhf/cli/cli.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/pyhf/cli/cli.py b/src/pyhf/cli/cli.py index a44c407cf3..9257bd8b0a 100644 --- a/src/pyhf/cli/cli.py +++ b/src/pyhf/cli/cli.py @@ -53,28 +53,28 @@ 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) -# 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(pyhf) -# pyhf.add_command(infer.cli) -pyhf.add_command(infer.fit) -pyhf.add_command(infer.cls) +# typer_click_object.add_command(rootio.cli) +typer_click_object.add_command(rootio.json2xml) +typer_click_object.add_command(rootio.xml2json) -pyhf.add_command(patchset.cli) +# 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(complete.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(contrib.cli) +typer_click_object.add_command(patchset.cli) -typer_click_object = typer.main.get_command(app) +typer_click_object.add_command(complete.cli) -typer_click_object.add_command(pyhf, "pyhf") +typer_click_object.add_command(contrib.cli) From 08edc286d19ac4b50bcc11e73241a19087d77f05 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 02:40:30 -0500 Subject: [PATCH 03/10] Get version callback working https://typer.tiangolo.com/tutorial/options/version/#fix-with-is_eager used to be: pyhf, version {version} now is: pyhf, v{version} --- src/pyhf/cli/cli.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pyhf/cli/cli.py b/src/pyhf/cli/cli.py index 9257bd8b0a..cdea05b6be 100644 --- a/src/pyhf/cli/cli.py +++ b/src/pyhf/cli/cli.py @@ -1,5 +1,6 @@ """The pyhf Command Line Interface.""" import logging +from typing import Optional import click import typer @@ -23,10 +24,22 @@ def top(): 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() + + @app.callback() -def callback(): +def callback( + version: Optional[bool] = typer.Option( + None, "--version", callback=_version_callback, is_eager=True + ) +): """ Typer app, including Click subapp + + Top-level CLI entrypoint. """ @@ -38,7 +51,6 @@ def _print_citation(ctx, param, value): @click.group(context_settings=dict(help_option_names=['-h', '--help'])) -@click.version_option(version=__version__) @click.option( "--cite", "--citation", From 39f222d403fa20fab8c90d3a7c6fabe2ed434831 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 02:53:38 -0500 Subject: [PATCH 04/10] Get cite callback working --- src/pyhf/cli/cli.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/pyhf/cli/cli.py b/src/pyhf/cli/cli.py index cdea05b6be..3a4ca36882 100644 --- a/src/pyhf/cli/cli.py +++ b/src/pyhf/cli/cli.py @@ -30,11 +30,28 @@ def _version_callback(value: bool): raise typer.Exit() +def _print_citation(ctx, value): + if not value or ctx.resilient_parsing: + return + 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 @@ -43,24 +60,7 @@ def callback( """ -def _print_citation(ctx, param, value): - if not value or ctx.resilient_parsing: - return - click.echo(utils.citation()) - ctx.exit() - - @click.group(context_settings=dict(help_option_names=['-h', '--help'])) -@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.""" From 780f055bcdf5c12c2b4bba0e3b8376988168a7e4 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 02:54:20 -0500 Subject: [PATCH 05/10] add note to remove --- src/pyhf/cli/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pyhf/cli/cli.py b/src/pyhf/cli/cli.py index 3a4ca36882..630c2d4440 100644 --- a/src/pyhf/cli/cli.py +++ b/src/pyhf/cli/cli.py @@ -16,6 +16,7 @@ app = typer.Typer() +# REMOVE THIS. CURRENTLY IN AS EXAMPLE @app.command() def top(): """ From 3d4275839f875c967ccd7015c9180a65a1b06345 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 03:09:06 -0500 Subject: [PATCH 06/10] switch to typer --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 9b399beb30..24e50db3cc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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.4.0 # for console scripts tqdm>=4.56.0 # for readxml jsonschema>=3.0.0 # for utils jsonpatch>=1.15 From 6a25ab4c8e7dd32fe494da5b0ee69f4014c530e9 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 03:10:24 -0500 Subject: [PATCH 07/10] typer v0.0.2 sets lower bound of click 7.0 --- lower-bound-requirements.txt | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lower-bound-requirements.txt b/lower-bound-requirements.txt index 8e9232ec37..93d912410a 100644 --- a/lower-bound-requirements.txt +++ b/lower-bound-requirements.txt @@ -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 diff --git a/setup.cfg b/setup.cfg index 24e50db3cc..0e1154efba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 - typer>=0.4.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 From 5324d113a2aaddb4f228468401572aac08a8d0d6 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 03:12:43 -0500 Subject: [PATCH 08/10] Drop click_completion --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 4773d6ffdf..938edf5585 100644 --- a/setup.py +++ b/setup.py @@ -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', @@ -27,7 +26,6 @@ extras_require['backends'] + extras_require['xmlio'] + extras_require['contrib'] - + extras_require['shellcomplete'] + [ 'pytest~=6.0', 'pytest-cov>=2.5.1', From 6180d8893980c6bf1c251ff3e65291c6e8596b4f Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 11:33:53 -0500 Subject: [PATCH 09/10] remove code for shell complete through click_complete --- src/pyhf/cli/__init__.py | 3 +-- src/pyhf/cli/complete.py | 30 ------------------------------ tests/test_cli.py | 13 +------------ tests/test_public_api_repr.py | 2 +- 4 files changed, 3 insertions(+), 45 deletions(-) delete mode 100644 src/pyhf/cli/complete.py diff --git a/src/pyhf/cli/__init__.py b/src/pyhf/cli/__init__.py index 8fe856c55e..bc9b1fceda 100644 --- a/src/pyhf/cli/__init__.py +++ b/src/pyhf/cli/__init__.py @@ -4,10 +4,9 @@ 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__(): diff --git a/src/pyhf/cli/complete.py b/src/pyhf/cli/complete.py deleted file mode 100644 index 8864c11388..0000000000 --- a/src/pyhf/cli/complete.py +++ /dev/null @@ -1,30 +0,0 @@ -'''Shell completions for pyhf.''' -import click - -try: - import click_completion - - click_completion.init() - - @click.command(help='Generate shell completion code.', name='completions') - @click.argument( - 'shell', - required=False, - type=click_completion.DocumentedChoice(click_completion.core.shells), - ) - def cli(shell): - '''Generate shell completion code for various shells.''' - click.echo(click_completion.core.get_code(shell, prog_name='pyhf')) - - -except ImportError: - - @click.command(help='Generate shell completion code.', name='completions') - @click.argument('shell', default=None) - def cli(shell): - '''Placeholder for shell completion code generatioon function if necessary dependency is missing.''' - click.secho( - 'This requires the click_completion module.\n' - 'You can install it with the shellcomplete extra:\n' - 'python -m pip install pyhf[shellcomplete]' - ) diff --git a/tests/test_cli.py b/tests/test_cli.py index 8b2bda812c..b8463049b4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,21 +1,10 @@ from click.testing import CliRunner -import sys -import importlib +# FIXME 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 diff --git a/tests/test_public_api_repr.py b/tests/test_public_api_repr.py index ccb4738d63..1e6b853f4e 100644 --- a/tests/test_public_api_repr.py +++ b/tests/test_public_api_repr.py @@ -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(): From 524ee17d93e6af0ab743a741a12a6723bf32dc09 Mon Sep 17 00:00:00 2001 From: Matthew Feickert Date: Thu, 28 Oct 2021 11:39:15 -0500 Subject: [PATCH 10/10] remove complete as deleted --- src/pyhf/cli/cli.py | 4 +--- tests/test_cli.py | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pyhf/cli/cli.py b/src/pyhf/cli/cli.py index 630c2d4440..0e54c2a8f3 100644 --- a/src/pyhf/cli/cli.py +++ b/src/pyhf/cli/cli.py @@ -6,7 +6,7 @@ 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 @@ -88,6 +88,4 @@ def pyhf(): typer_click_object.add_command(patchset.cli) -typer_click_object.add_command(complete.cli) - typer_click_object.add_command(contrib.cli) diff --git a/tests/test_cli.py b/tests/test_cli.py index b8463049b4..51f4deba47 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2,6 +2,8 @@ # 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