Skip to content

Commit 0b5b2c4

Browse files
authored
feat: add initial cli (#32)
Fixes #1
1 parent 96a7258 commit 0b5b2c4

File tree

10 files changed

+124
-7
lines changed

10 files changed

+124
-7
lines changed

.github/workflows/docs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ jobs:
3434
path: ~/.cache/pip
3535
- name: Install pre-requisites (e.g. hatch)
3636
run: python -m pip install --require-hashes --requirement=.github/requirements/ci.txt
37+
- run: hatch run docs:cli
3738
- run: hatch run docs:build
3839
if: github.event_name == 'pull_request'
3940
- run: |

.github/workflows/test.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: Install pre-requisites (e.g. hatch)
2828
run: python -m pip install --require-hashes --requirement=.github/requirements/ci.txt
2929
- name: Run test suite
30-
run: hatch run cov
30+
run: hatch run cov -s
3131
- name: Generate coverage report
3232
run: |
3333
export TOTAL_COV=$(hatch run cov-total)
@@ -70,7 +70,8 @@ jobs:
7070
python-version: '3.12'
7171
cache: pip
7272
- run: python -Im pip install --editable .[dev]
73-
- run: python -Ic 'import re3data.__about__; print(re3data.__about__.__version__)'
73+
- run: python -Ic 'import re3data; print(re3data.__version__)'
74+
- run: re3data --version
7475
- name: Set up pipdeptree
7576
if: matrix.os == 'ubuntu-latest'
7677
run: python -m pip install --require-hashes --requirement=.github/requirements/ci.txt

.pre-commit-config.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ repos:
5555
hooks:
5656
- id: mdformat
5757
args: [--number, --wrap=120, --ignore-missing-references]
58-
exclude: CHANGELOG.md|.changelog.md|docs/src/api
58+
exclude: CHANGELOG.md|.changelog.md|docs/src/cli.md
5959
additional_dependencies:
6060
- mdformat-mkdocs[recommended]>=v2.0.7
6161

@@ -73,7 +73,8 @@ repos:
7373
args: [--config-file=pyproject.toml]
7474
additional_dependencies:
7575
- httpx>=0.27
76-
- pytest>=8.1
76+
- pytest>=8.2
77+
- typer>=0.12
7778

7879
- repo: https://github.com/scientific-python/cookie
7980
rev: 28d1a53da26f9daff6d9a49c50260421ad6d05e0 # frozen: 2024.04.23

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ watch:
1515

1616
nav:
1717
- Home: index.md
18+
- CLI Reference: cli.md
1819
- Meta:
1920
- Contributor Guide: contributing.md
2021
- License: license.md

docs/src/cli.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# CLI Reference
2+
3+
python-re3data.
4+
5+
**Usage**:
6+
7+
```console
8+
$ re3data [OPTIONS] COMMAND [ARGS]...
9+
```
10+
11+
**Options**:
12+
13+
* `-V, --version`: Show python-re3data version and exit.
14+
* `--install-completion`: Install completion for the current shell.
15+
* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
16+
* `--help`: Show this message and exit.

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ optional-dependencies.cli = [
4949
]
5050
optional-dependencies.dev = [
5151
"pre-commit~=3.7",
52+
"python-re3data[cli]",
5253
]
5354
optional-dependencies.docs = [
5455
"mike~=2.1",
@@ -112,10 +113,12 @@ cov-total = """
112113

113114
[tool.hatch.envs.docs]
114115
features = [
116+
"cli",
115117
"docs",
116118
]
117119
template = "docs"
118120
[tool.hatch.envs.docs.scripts]
121+
cli = "typer src/re3data/_cli.py utils docs --name=re3data --title='CLI Reference' --output docs/src/cli.md"
119122
build = "mkdocs build --config-file=docs/mkdocs.yml"
120123
serve = "mkdocs serve --verbose --config-file=docs/mkdocs.yml"
121124
deploy = "mike deploy --push --update-aliases $(hatch version) latest --config-file=docs/mkdocs.yml"
@@ -220,6 +223,7 @@ source = [
220223
]
221224
omit = [
222225
"__about__.py",
226+
"__main__.py",
223227
]
224228

225229
[tool.coverage.report]
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#
33
# SPDX-License-Identifier: MIT
44

5-
from re3data import __version__
5+
"""Entry point for python-re3data."""
66

7+
from re3data._cli import app
78

8-
def test_version() -> None:
9-
assert __version__
9+
app(prog_name="re3data")

src/re3data/_cli.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""re3data command line interface."""
2+
3+
import logging
4+
import sys
5+
import typing
6+
7+
logger = logging.getLogger(__name__)
8+
9+
try:
10+
import typer
11+
except ImportError:
12+
logger.error("`typer` is missing. Please run 'pip install python-re3data[cli]' to use the CLI.")
13+
sys.exit(1)
14+
15+
CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
16+
17+
app = typer.Typer(no_args_is_help=True, context_settings=CONTEXT_SETTINGS)
18+
19+
20+
def _version_callback(show_version: bool) -> None:
21+
# Ref: https://typer.tiangolo.com/tutorial/options/version/
22+
from re3data import __version__
23+
24+
if show_version:
25+
typer.echo(f"{__version__}")
26+
raise typer.Exit
27+
28+
29+
@app.callback(context_settings=CONTEXT_SETTINGS)
30+
def callback(
31+
version: typing.Annotated[
32+
bool,
33+
typer.Option(
34+
"--version",
35+
"-V",
36+
help="Show python-re3data version and exit.",
37+
callback=_version_callback,
38+
),
39+
] = False,
40+
) -> None:
41+
"""python-re3data."""

src/re3data/py.typed

Whitespace-only changes.

tests/integration/test_cli.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# SPDX-FileCopyrightText: 2024 Heinz-Alexander Fütterer
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import sys
6+
from importlib import reload
7+
from unittest.mock import patch
8+
9+
import pytest
10+
from typer.testing import CliRunner
11+
12+
from re3data import __version__
13+
from re3data._cli import app
14+
15+
runner = CliRunner()
16+
17+
HELP_OPTION_NAMES = ("-h", "--help")
18+
VERSION_OPTION_NAMES = ("-V", "--version")
19+
20+
21+
def test_no_args_displays_help() -> None:
22+
result = runner.invoke(app)
23+
assert result.exit_code == 0
24+
assert "Usage" in result.output
25+
assert "Options" in result.output
26+
assert "Show this message and exit" in result.output
27+
28+
29+
@pytest.mark.parametrize("help_option_name", HELP_OPTION_NAMES)
30+
def test_help_option_displays_help(help_option_name: str) -> None:
31+
result = runner.invoke(app, [help_option_name])
32+
assert result.exit_code == 0
33+
assert "Usage" in result.output
34+
assert "Options" in result.output
35+
assert "Show this message and exit" in result.output
36+
37+
38+
@pytest.mark.parametrize("version_option_name", VERSION_OPTION_NAMES)
39+
def test_version_option_displays_version(version_option_name: str) -> None:
40+
result = runner.invoke(app, [version_option_name])
41+
assert result.exit_code == 0
42+
assert __version__ in result.output
43+
44+
45+
def test_typer_missing_message(caplog: pytest.LogCaptureFixture) -> None:
46+
# act as if "typer" is not installed
47+
with patch.dict(sys.modules, {"typer": None}):
48+
with pytest.raises(SystemExit) as exc_info:
49+
reload(sys.modules["re3data._cli"])
50+
assert exc_info.type == SystemExit
51+
assert exc_info.value.code == 1
52+
assert "Please run 'pip install python-re3data[cli]'" in caplog.text

0 commit comments

Comments
 (0)