anaconda-cli-base 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anaconda_cli_base-0.1.0/.gitignore +46 -0
- anaconda_cli_base-0.1.0/LICENSE +27 -0
- anaconda_cli_base-0.1.0/Makefile +28 -0
- anaconda_cli_base-0.1.0/PKG-INFO +61 -0
- anaconda_cli_base-0.1.0/README.md +39 -0
- anaconda_cli_base-0.1.0/environment-dev.yml +12 -0
- anaconda_cli_base-0.1.0/pyproject.toml +127 -0
- anaconda_cli_base-0.1.0/src/anaconda_cli_base/__init__.py +8 -0
- anaconda_cli_base-0.1.0/src/anaconda_cli_base/_version.py +4 -0
- anaconda_cli_base-0.1.0/src/anaconda_cli_base/cli.py +26 -0
- anaconda_cli_base-0.1.0/src/anaconda_cli_base/console.py +51 -0
- anaconda_cli_base-0.1.0/src/anaconda_cli_base/plugins.py +34 -0
- anaconda_cli_base-0.1.0/src/anaconda_cli_base/py.typed +0 -0
- anaconda_cli_base-0.1.0/tests/__init__.py +0 -0
- anaconda_cli_base-0.1.0/tests/conftest.py +35 -0
- anaconda_cli_base-0.1.0/tests/test_cli.py +26 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
env/
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
*.egg-info
|
|
12
|
+
|
|
13
|
+
# Unit test / coverage reports
|
|
14
|
+
htmlcov/
|
|
15
|
+
.coverage
|
|
16
|
+
.coverage.*
|
|
17
|
+
coverage.xml
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.tox/
|
|
20
|
+
|
|
21
|
+
# Sphinx documentation
|
|
22
|
+
docs/_build/
|
|
23
|
+
|
|
24
|
+
# Jupyter Notebook
|
|
25
|
+
.ipynb_checkpoints
|
|
26
|
+
|
|
27
|
+
# dotenv
|
|
28
|
+
.env
|
|
29
|
+
|
|
30
|
+
# virtualenv
|
|
31
|
+
.venv
|
|
32
|
+
venv/
|
|
33
|
+
|
|
34
|
+
# mypy
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
|
|
37
|
+
# vscode ide stuff
|
|
38
|
+
*.code-workspace
|
|
39
|
+
.history
|
|
40
|
+
.vscode
|
|
41
|
+
|
|
42
|
+
# Version file generated by setuptools-scm
|
|
43
|
+
/libs/*/src/*/_version.py
|
|
44
|
+
|
|
45
|
+
# Stored tokens (for dev)
|
|
46
|
+
/tokens.json
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Anaconda, Inc.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright
|
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
|
11
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
|
13
|
+
documentation and/or other materials provided with the distribution.
|
|
14
|
+
* Neither the name of the copyright holder nor the names of its
|
|
15
|
+
contributors may be used to endorse or promote products
|
|
16
|
+
derived from this software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
22
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
25
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Conda-related paths
|
|
2
|
+
conda_env_dir ?= ./env
|
|
3
|
+
|
|
4
|
+
# Command aliases
|
|
5
|
+
CONDA_EXE ?= conda
|
|
6
|
+
CONDA_RUN := $(CONDA_EXE) run --prefix $(conda_env_dir) --no-capture-output
|
|
7
|
+
|
|
8
|
+
help: ## Display help on all Makefile targets
|
|
9
|
+
@@grep -h '^[a-zA-Z]' $(MAKEFILE_LIST) | awk -F ':.*?## ' 'NF==2 {printf " %-20s%s\n", $$1, $$2}' | sort
|
|
10
|
+
|
|
11
|
+
setup: ## Setup local dev conda environment
|
|
12
|
+
$(CONDA_EXE) env $(shell [ -d $(conda_env_dir) ] && echo update || echo create) -p $(conda_env_dir) --file environment-dev.yml
|
|
13
|
+
|
|
14
|
+
test: ## Run all the unit tests
|
|
15
|
+
$(CONDA_RUN) pytest
|
|
16
|
+
|
|
17
|
+
tox: ## Run tox to test in isolated environments
|
|
18
|
+
$(CONDA_RUN) tox
|
|
19
|
+
|
|
20
|
+
clean: ## Clean up cache and temporary files
|
|
21
|
+
find . -name \*.py[cod] -delete
|
|
22
|
+
rm -rf .pytest_cache .mypy_cache .tox build dist
|
|
23
|
+
|
|
24
|
+
clean-all: clean ## Clean up, including build files and conda environment
|
|
25
|
+
find . -name \*.egg-info -delete
|
|
26
|
+
rm -rf $(conda_env_dir)
|
|
27
|
+
|
|
28
|
+
.PHONY: $(MAKECMDGOALS)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: anaconda-cli-base
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A base CLI entrypoint supporting Anaconda CLI plugins
|
|
5
|
+
License: BSD-3-Clause
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Requires-Dist: readchar
|
|
9
|
+
Requires-Dist: rich
|
|
10
|
+
Requires-Dist: typer
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-mock; extra == 'dev'
|
|
15
|
+
Requires-Dist: tox; extra == 'dev'
|
|
16
|
+
Requires-Dist: types-requests; extra == 'dev'
|
|
17
|
+
Provides-Extra: publish
|
|
18
|
+
Requires-Dist: build; extra == 'publish'
|
|
19
|
+
Requires-Dist: twine; extra == 'publish'
|
|
20
|
+
Requires-Dist: wheel; extra == 'publish'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# anaconda-cli-base
|
|
24
|
+
|
|
25
|
+
A base CLI entrypoint supporting Anaconda CLI plugins
|
|
26
|
+
|
|
27
|
+
## Registering plugins
|
|
28
|
+
|
|
29
|
+
Subcommands can be registered as follows:
|
|
30
|
+
|
|
31
|
+
```toml
|
|
32
|
+
# In pyproject.toml
|
|
33
|
+
|
|
34
|
+
[project.entry-points."anaconda_cli.subcommand"]
|
|
35
|
+
auth = "anaconda_cloud_auth.cli:app"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
In the example above:
|
|
39
|
+
|
|
40
|
+
* `"anaconda_cloud_cli.subcommand"` is the required string to use for registration. The quotes are important.
|
|
41
|
+
* `auth` is the name of the new subcommand, i.e. `anaconda auth`
|
|
42
|
+
* `anaconda_cloud_auth.cli:app` signifies the object named `app` in the `anaconda_cloud_auth.cli` module is the entry point for the subcommand.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Setup for development
|
|
46
|
+
|
|
47
|
+
Ensure you have `conda` installed.
|
|
48
|
+
Then run:
|
|
49
|
+
```shell
|
|
50
|
+
make setup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Run the unit tests
|
|
54
|
+
```shell
|
|
55
|
+
make test
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Run the unit tests across isolated environments with tox
|
|
59
|
+
```shell
|
|
60
|
+
make tox
|
|
61
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# anaconda-cli-base
|
|
2
|
+
|
|
3
|
+
A base CLI entrypoint supporting Anaconda CLI plugins
|
|
4
|
+
|
|
5
|
+
## Registering plugins
|
|
6
|
+
|
|
7
|
+
Subcommands can be registered as follows:
|
|
8
|
+
|
|
9
|
+
```toml
|
|
10
|
+
# In pyproject.toml
|
|
11
|
+
|
|
12
|
+
[project.entry-points."anaconda_cli.subcommand"]
|
|
13
|
+
auth = "anaconda_cloud_auth.cli:app"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
In the example above:
|
|
17
|
+
|
|
18
|
+
* `"anaconda_cloud_cli.subcommand"` is the required string to use for registration. The quotes are important.
|
|
19
|
+
* `auth` is the name of the new subcommand, i.e. `anaconda auth`
|
|
20
|
+
* `anaconda_cloud_auth.cli:app` signifies the object named `app` in the `anaconda_cloud_auth.cli` module is the entry point for the subcommand.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Setup for development
|
|
24
|
+
|
|
25
|
+
Ensure you have `conda` installed.
|
|
26
|
+
Then run:
|
|
27
|
+
```shell
|
|
28
|
+
make setup
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Run the unit tests
|
|
32
|
+
```shell
|
|
33
|
+
make test
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Run the unit tests across isolated environments with tox
|
|
37
|
+
```shell
|
|
38
|
+
make tox
|
|
39
|
+
```
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "hatchling.build"
|
|
3
|
+
requires = ["hatchling", "hatch-vcs>=0.3", "setuptools-scm>=7.1"]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
dependencies = [
|
|
7
|
+
"rich",
|
|
8
|
+
"readchar",
|
|
9
|
+
"typer"
|
|
10
|
+
]
|
|
11
|
+
description = "A base CLI entrypoint supporting Anaconda CLI plugins"
|
|
12
|
+
dynamic = ["version"]
|
|
13
|
+
license = {text = "BSD-3-Clause"}
|
|
14
|
+
name = "anaconda-cli-base"
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
requires-python = ">=3.8"
|
|
17
|
+
|
|
18
|
+
[project.entry-points."anaconda_cli.main"]
|
|
19
|
+
anaconda = "anaconda_cli_base.cli:app"
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
dev = [
|
|
23
|
+
"mypy",
|
|
24
|
+
"pytest",
|
|
25
|
+
"pytest-mock",
|
|
26
|
+
"tox",
|
|
27
|
+
"types-requests"
|
|
28
|
+
]
|
|
29
|
+
publish = [
|
|
30
|
+
"build",
|
|
31
|
+
"twine",
|
|
32
|
+
"wheel"
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.distutils.bdist_wheel]
|
|
36
|
+
universal = true
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.hooks.vcs]
|
|
39
|
+
version-file = "src/anaconda_cli_base/_version.py"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.sdist]
|
|
42
|
+
include = [
|
|
43
|
+
"/src/anaconda_cli_base",
|
|
44
|
+
"/pyproject.toml",
|
|
45
|
+
"/tests",
|
|
46
|
+
"/Makefile",
|
|
47
|
+
"/environment-dev.yml"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.hatch.version]
|
|
51
|
+
source = "vcs"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.version.raw-options]
|
|
54
|
+
git_describe_command = "git describe --dirty --tags --long --match 'anaconda-cli-base-*'"
|
|
55
|
+
root = "../.."
|
|
56
|
+
|
|
57
|
+
[tool.isort]
|
|
58
|
+
force_single_line = true
|
|
59
|
+
profile = "black"
|
|
60
|
+
|
|
61
|
+
[tool.mypy]
|
|
62
|
+
disallow_untyped_defs = true
|
|
63
|
+
files = [
|
|
64
|
+
"src/**/*.py",
|
|
65
|
+
"tests/**/*.py"
|
|
66
|
+
]
|
|
67
|
+
python_version = "3.8"
|
|
68
|
+
|
|
69
|
+
[[tool.mypy.overrides]]
|
|
70
|
+
ignore_missing_imports = true
|
|
71
|
+
module = "rich_click.*"
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
markers = [
|
|
75
|
+
"integration: Integration tests requiring a browser"
|
|
76
|
+
]
|
|
77
|
+
norecursedirs = ["env", "envs", ".tox"]
|
|
78
|
+
|
|
79
|
+
[tool.tox]
|
|
80
|
+
legacy_tox_ini = """
|
|
81
|
+
[tox]
|
|
82
|
+
envlist = py38,py39,py310,py311,mypy
|
|
83
|
+
isolated_build = True
|
|
84
|
+
|
|
85
|
+
[gh-actions]
|
|
86
|
+
python =
|
|
87
|
+
3.8: py38, mypy
|
|
88
|
+
3.9: py39, mypy
|
|
89
|
+
3.10: py310, mypy
|
|
90
|
+
3.11: py311
|
|
91
|
+
|
|
92
|
+
[testenv]
|
|
93
|
+
deps =
|
|
94
|
+
mypy
|
|
95
|
+
pytest
|
|
96
|
+
pytest-mock
|
|
97
|
+
conda_deps =
|
|
98
|
+
anaconda-client >=1.12.0
|
|
99
|
+
conda_channels =
|
|
100
|
+
anaconda-cloud
|
|
101
|
+
defaults
|
|
102
|
+
conda-forge
|
|
103
|
+
commands = pytest -m "not integration"
|
|
104
|
+
|
|
105
|
+
[testenv:mypy]
|
|
106
|
+
deps =
|
|
107
|
+
mypy
|
|
108
|
+
pytest
|
|
109
|
+
pytest-mock
|
|
110
|
+
types-requests
|
|
111
|
+
typer
|
|
112
|
+
rich
|
|
113
|
+
commands = mypy
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
[tool.vendoring]
|
|
117
|
+
destination = "src/anaconda_cli_base/_vendor/"
|
|
118
|
+
namespace = "anaconda_cli_base._vendor"
|
|
119
|
+
patches-dir = "tools/vendoring/patches"
|
|
120
|
+
protected-files = ["__init__.py", "requirements.txt"]
|
|
121
|
+
requirements = "src/anaconda_cli_base/_vendor/requirements.txt"
|
|
122
|
+
|
|
123
|
+
[tool.vendoring.license.directories]
|
|
124
|
+
setuptools = "pkg_resources"
|
|
125
|
+
|
|
126
|
+
[tool.vendoring.license.fallback-urls]
|
|
127
|
+
readchar = "https://raw.githubusercontent.com/magmax/python-readchar/master/LICENCE"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from anaconda_cli_base import __version__
|
|
6
|
+
from anaconda_cli_base import console
|
|
7
|
+
from anaconda_cli_base.plugins import load_registered_subcommands
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(add_completion=False, help="Welcome to the Anaconda CLI!")
|
|
10
|
+
|
|
11
|
+
load_registered_subcommands(app)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.callback(invoke_without_command=True, no_args_is_help=True)
|
|
15
|
+
def main(
|
|
16
|
+
version: Optional[bool] = typer.Option(
|
|
17
|
+
None, "--version", help="Show version and exit."
|
|
18
|
+
)
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Anaconda CLI."""
|
|
21
|
+
if version:
|
|
22
|
+
console.print(
|
|
23
|
+
f"Anaconda CLI, version [cyan]{__version__}[/cyan]",
|
|
24
|
+
style="bold green",
|
|
25
|
+
)
|
|
26
|
+
raise typer.Exit()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from readchar import key
|
|
4
|
+
from readchar import readkey
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.live import Live
|
|
7
|
+
from rich.style import Style
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
__all__ = ["console", "select_from_list"]
|
|
11
|
+
|
|
12
|
+
SELECTED = Style(color="green", bold=True)
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _generate_table(header: str, rows: List[str], selected: int) -> Table:
|
|
18
|
+
table = Table(box=None)
|
|
19
|
+
|
|
20
|
+
table.add_column(header)
|
|
21
|
+
|
|
22
|
+
for i, row in enumerate(rows):
|
|
23
|
+
if i == selected:
|
|
24
|
+
style = SELECTED
|
|
25
|
+
value = f"* {row}"
|
|
26
|
+
else:
|
|
27
|
+
style = None
|
|
28
|
+
value = f" {row}"
|
|
29
|
+
table.add_row(value, style=style)
|
|
30
|
+
|
|
31
|
+
return table
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def select_from_list(prompt: str, choices: List[str]) -> str:
|
|
35
|
+
"""Dynamically select from a list of choices, by using the up/down keys."""
|
|
36
|
+
# inspired by https://github.com/Textualize/rich/discussions/1785#discussioncomment-1883808
|
|
37
|
+
items = choices.copy()
|
|
38
|
+
|
|
39
|
+
selected = 0
|
|
40
|
+
with Live(_generate_table(prompt, items, selected), auto_refresh=False) as live:
|
|
41
|
+
while ch := readkey():
|
|
42
|
+
if ch == key.UP or ch == "k":
|
|
43
|
+
selected = max(0, selected - 1)
|
|
44
|
+
if ch == key.DOWN or ch == "j":
|
|
45
|
+
selected = min(len(items) - 1, selected + 1)
|
|
46
|
+
if ch == key.ENTER:
|
|
47
|
+
live.stop()
|
|
48
|
+
return items[selected]
|
|
49
|
+
live.update(_generate_table(prompt, items, selected), refresh=True)
|
|
50
|
+
|
|
51
|
+
raise ValueError("Unreachable")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from importlib.metadata import entry_points
|
|
3
|
+
from sys import version_info
|
|
4
|
+
|
|
5
|
+
from typer import Typer
|
|
6
|
+
from typer.models import DefaultPlaceholder
|
|
7
|
+
|
|
8
|
+
log = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
PLUGIN_GROUP_NAME = "anaconda_cli.subcommand"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_registered_subcommands(app: Typer) -> None:
|
|
14
|
+
"""Load all subcommands from plugins."""
|
|
15
|
+
# The API was changed in Python 3.10, see https://docs.python.org/3/library/importlib.metadata.html#entry-points
|
|
16
|
+
if version_info.major == 3 and version_info.minor <= 9:
|
|
17
|
+
subcommand_entry_points = entry_points().get(PLUGIN_GROUP_NAME, [])
|
|
18
|
+
else:
|
|
19
|
+
subcommand_entry_points = entry_points().select(group=PLUGIN_GROUP_NAME) # type: ignore
|
|
20
|
+
|
|
21
|
+
for subcommand_entry_point in subcommand_entry_points:
|
|
22
|
+
# Load the entry point and register it as a subcommand with the base CLI app
|
|
23
|
+
subcommand_app = subcommand_entry_point.load() # type: ignore
|
|
24
|
+
|
|
25
|
+
# Allow plugins to disable this if they explicitly want to, but otherwise make True the default
|
|
26
|
+
if isinstance(subcommand_app.info.no_args_is_help, DefaultPlaceholder):
|
|
27
|
+
subcommand_app.info.no_args_is_help = True
|
|
28
|
+
|
|
29
|
+
app.add_typer(subcommand_app, name=subcommand_entry_point.name) # type: ignore
|
|
30
|
+
log.debug(
|
|
31
|
+
"Loaded subcommand '%s' from '%s'",
|
|
32
|
+
subcommand_entry_point.name, # type: ignore
|
|
33
|
+
subcommand_entry_point.value, # type: ignore
|
|
34
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from mypy_extensions import VarArg
|
|
9
|
+
from typer.testing import CliRunner
|
|
10
|
+
from typer.testing import Result
|
|
11
|
+
|
|
12
|
+
from anaconda_cli_base.cli import app
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from _pytest.monkeypatch import MonkeyPatch
|
|
16
|
+
|
|
17
|
+
CLIInvoker = Callable[[VarArg(str)], Result]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture()
|
|
21
|
+
def tmp_cwd(monkeypatch: MonkeyPatch, tmp_path: Path) -> Path:
|
|
22
|
+
"""Create & return a temporary directory after setting current working directory to it."""
|
|
23
|
+
monkeypatch.chdir(tmp_path)
|
|
24
|
+
return tmp_path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture()
|
|
28
|
+
def invoke_cli(tmp_cwd: Path) -> CLIInvoker:
|
|
29
|
+
"""Returns a function, which can be used to call the CLI from within a temporary directory."""
|
|
30
|
+
runner = CliRunner()
|
|
31
|
+
|
|
32
|
+
def f(*args: str) -> Result:
|
|
33
|
+
return runner.invoke(app, args)
|
|
34
|
+
|
|
35
|
+
return f
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Tuple
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from anaconda_cli_base import __version__
|
|
6
|
+
|
|
7
|
+
from .conftest import CLIInvoker
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
"args",
|
|
12
|
+
[
|
|
13
|
+
pytest.param((), id="no-args"),
|
|
14
|
+
pytest.param(("--help",), id="--help"),
|
|
15
|
+
],
|
|
16
|
+
)
|
|
17
|
+
def test_cli_help(invoke_cli: CLIInvoker, args: Tuple[str]) -> None:
|
|
18
|
+
result = invoke_cli(*args)
|
|
19
|
+
assert result.exit_code == 0
|
|
20
|
+
assert "Welcome to the Anaconda CLI!" in result.stdout
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_cli_version(invoke_cli: CLIInvoker) -> None:
|
|
24
|
+
result = invoke_cli("--version")
|
|
25
|
+
assert result.exit_code == 0
|
|
26
|
+
assert f"Anaconda CLI, version {__version__}" in result.stdout
|